* **Fitur search beranda** - Cari kategori berdasarkan nama

* **Search filtering real-time** - Kategori otomatis filter saat mengetik
* **Delete kategori dengan UI** - Tombol X di top-right corner setiap kategori
* **Confirmation dialog untuk delete** - Prevent accidental deletion dengan warning message
* **Search di kategori** - Cari catatan berdasarkan judul & isi (case-insensitive)
* **Search empty state** - Tampilkan pesan "Tidak ada hasil" saat search kosong
* **Gradle optimization** - Cleanup dependencies yang tidak diperlukan
This commit is contained in:
202310715051 DENDI YOGIA PRATAMA 2025-12-13 02:01:34 +07:00
parent a1e9cb612c
commit 14bf2f2f24
4 changed files with 122 additions and 25 deletions

View File

@ -4,6 +4,14 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-12-12T17:42:15.072692700Z">
<Target type="DEFAULT_BOOT">
<handle>
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\dendi\.android\avd\Medium_Phone.avd" />
</handle>
</Target>
</DropdownSelection>
<DialogSelection />
</SelectionState> </SelectionState>
</selectionStates> </selectionStates>
</component> </component>

View File

@ -1,5 +1,3 @@
# **AI Notes Changelog** # **AI Notes Changelog**
## **Tim Pengembang** ## **Tim Pengembang**
@ -25,7 +23,6 @@
* Penambahan validasi input form kategori * Penambahan validasi input form kategori
* Tampilan kategori Staggered Grid (2 kolom) * Tampilan kategori Staggered Grid (2 kolom)
* Category Card (ikon folder, nama, jumlah catatan, gradient) * Category Card (ikon folder, nama, jumlah catatan, gradient)
* Long press untuk menghapus kategori
* Empty state kategori * Empty state kategori
* Implementasi LazyVerticalStaggeredGrid * Implementasi LazyVerticalStaggeredGrid
* Gradient preset 8 warna * Gradient preset 8 warna
@ -36,7 +33,6 @@
* Fitur pin untuk catatan penting * Fitur pin untuk catatan penting
* Full-screen editable note view dengan auto-save * Full-screen editable note view dengan auto-save
* Fitur arsip, hapus, dan pin di full-screen mode * Fitur arsip, hapus, dan pin di full-screen mode
* Long press untuk mengarsipkan catatan
* Fitur search catatan (judul + isi) * Fitur search catatan (judul + isi)
* Sorting catatan berdasarkan pin & timestamp * Sorting catatan berdasarkan pin & timestamp
* Implementasi custom TextField dan date formatter * Implementasi custom TextField dan date formatter
@ -84,15 +80,27 @@
* Halaman untuk Catatan Berbintang dan Ikon Pesan Berbintang * Halaman untuk Catatan Berbintang dan Ikon Pesan Berbintang
* Pemberitahuan Konfirmasi untuk Arsip dan Hapus Catatan * Pemberitahuan Konfirmasi untuk Arsip dan Hapus Catatan
## **Sprint 2: Fitur Search & Delete Kategori (Hari Ini)**
* **Fitur search beranda** - Cari kategori berdasarkan nama
* **Search filtering real-time** - Kategori otomatis filter saat mengetik
* **Delete kategori dengan UI** - Tombol X di top-right corner setiap kategori
* **Confirmation dialog untuk delete** - Prevent accidental deletion dengan warning message
* **Search di kategori** - Cari catatan berdasarkan judul & isi (case-insensitive)
* **Search empty state** - Tampilkan pesan "Tidak ada hasil" saat search kosong
* **Gradle optimization** - Cleanup dependencies yang tidak diperlukan
--- ---
## **Fitur Utama Aplikasi** ## **Fitur Utama Aplikasi**
* Sistem kategori dengan gradient * Sistem kategori dengan gradient
* Buat/edit/hapus kategori dengan confirmation dialog
* Buat/edit/hapus catatan * Buat/edit/hapus catatan
* Pin catatan penting * Pin catatan penting
* Full-screen editor * Full-screen editor
* Search catatan * Search kategori di beranda
* Search catatan dalam kategori
* Arsip & Sampah dengan restore/delete permanen * Arsip & Sampah dengan restore/delete permanen
* AI Chat powered by Gemini * AI Chat powered by Gemini
* AI membaca & menganalisis catatan pengguna * AI membaca & menganalisis catatan pengguna
@ -112,4 +120,5 @@
* Dark theme toggle * Dark theme toggle
* Multi-language support * Multi-language support
--- ---

View File

@ -324,8 +324,10 @@ fun NotesApp() {
} }
}, },
onCategoryLongClick = { category -> onCategoryLongClick = { category ->
// Delete kategori dan semua catatan di dalamnya
categories = categories.filter { it.id != category.id } categories = categories.filter { it.id != category.id }
notes = notes.filter { it.categoryId != category.id } notes = notes.filter { it.categoryId != category.id }
selectedCategory = null
} }
) )
"starred" -> StarredNotesScreen( "starred" -> StarredNotesScreen(
@ -828,6 +830,7 @@ fun MainScreen(
) { ) {
Column(modifier = Modifier.fillMaxSize()) { Column(modifier = Modifier.fillMaxSize()) {
if (selectedCategory == null) { if (selectedCategory == null) {
// Beranda: Tampilkan kategori dengan search filtering
if (categories.isEmpty()) { if (categories.isEmpty()) {
EmptyState( EmptyState(
icon = Icons.Default.Create, icon = Icons.Default.Create,
@ -835,20 +838,41 @@ fun MainScreen(
subtitle = "Tekan tombol + untuk memulai" subtitle = "Tekan tombol + untuk memulai"
) )
} else { } else {
LazyVerticalStaggeredGrid( // Filter kategori berdasarkan searchQuery
columns = StaggeredGridCells.Fixed(2), val filteredCategories = if (searchQuery.isEmpty()) {
contentPadding = PaddingValues(16.dp), categories
horizontalArrangement = Arrangement.spacedBy(12.dp), } else {
verticalItemSpacing = 12.dp, categories.filter {
modifier = Modifier.fillMaxSize() it.name.contains(searchQuery, ignoreCase = true)
) { }
items(categories) { category -> }
CategoryCard(
category = category, if (filteredCategories.isEmpty()) {
noteCount = notes.count { it.categoryId == category.id && !it.isDeleted && !it.isArchived }, EmptyState(
onClick = { onCategoryClick(category) }, icon = Icons.Default.Search,
onLongClick = { onCategoryLongClick(category) } message = "Kategori tidak ditemukan",
) subtitle = "Coba kata kunci lain"
)
} else {
LazyVerticalStaggeredGrid(
columns = StaggeredGridCells.Fixed(2),
contentPadding = PaddingValues(16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp),
verticalItemSpacing = 12.dp,
modifier = Modifier.fillMaxSize()
) {
items(filteredCategories) { category ->
CategoryCard(
category = category,
noteCount = notes.count { it.categoryId == category.id && !it.isDeleted && !it.isArchived },
onClick = { onCategoryClick(category) },
onLongClick = { onCategoryLongClick(category) },
onDelete = {
// Delete kategori dan semua catatan di dalamnya
onCategoryLongClick(category)
}
)
}
} }
} }
} }
@ -898,8 +922,45 @@ fun CategoryCard(
category: Category, category: Category,
noteCount: Int, noteCount: Int,
onClick: () -> Unit, onClick: () -> Unit,
onLongClick: () -> Unit onLongClick: () -> Unit,
onDelete: () -> Unit = {}
) { ) {
var showDeleteConfirm by remember { mutableStateOf(false) }
// Delete confirmation dialog
if (showDeleteConfirm) {
AlertDialog(
onDismissRequest = { showDeleteConfirm = false },
title = { Text("Hapus Kategori?") },
text = {
Text("Kategori '$${category.name}' dan semua catatan di dalamnya akan dihapus. Tindakan ini tidak dapat dibatalkan.")
},
confirmButton = {
Button(
onClick = {
onDelete()
showDeleteConfirm = false
},
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFFEF4444)
)
) {
Text("Hapus", color = Color.White)
}
},
dismissButton = {
Button(
onClick = { showDeleteConfirm = false },
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFF64748B)
)
) {
Text("Batal", color = Color.White)
}
}
)
}
Card( Card(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@ -945,6 +1006,21 @@ fun CategoryCard(
color = Color.White.copy(0.8f) color = Color.White.copy(0.8f)
) )
} }
// Delete button di top-right corner
IconButton(
onClick = { showDeleteConfirm = true },
modifier = Modifier
.align(Alignment.TopEnd)
.size(40.dp)
) {
Icon(
Icons.Default.Close,
contentDescription = "Hapus kategori",
tint = Color.White.copy(0.7f),
modifier = Modifier.size(20.dp)
)
}
} }
} }
} }
@ -972,7 +1048,11 @@ fun NoteCard(
), ),
elevation = CardDefaults.cardElevation(defaultElevation = 4.dp) elevation = CardDefaults.cardElevation(defaultElevation = 4.dp)
) { ) {
Column(modifier = Modifier.padding(16.dp)) { Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
@ -1023,7 +1103,6 @@ fun NoteCard(
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
// Divider
Divider( Divider(
color = Color(0xFF334155), color = Color(0xFF334155),
thickness = 1.dp thickness = 1.dp
@ -2442,4 +2521,5 @@ fun EmptyState(
) )
} }
} }
} }

View File

@ -1,5 +1,5 @@
[versions] [versions]
agp = "8.13.1" agp = "8.13.2"
kotlin = "2.0.21" kotlin = "2.0.21"
coreKtx = "1.10.1" coreKtx = "1.10.1"
junit = "4.13.2" junit = "4.13.2"