From f0e4396da177286a79e18de78460dbcaeebce63d Mon Sep 17 00:00:00 2001 From: FazriA <202310715082@mhs.ubharajaya.ac.id> Date: Thu, 25 Dec 2025 01:35:21 +0700 Subject: [PATCH] Berikan fungsi pada fitur pencarian pada halaman file berbintang, arsip, dan sampah --- .../screens/archive/ArchiveScreen.kt | 125 ++++++++++-- .../screens/starred/StarredNotesScreen.kt | 104 +++++++++- .../presentation/screens/trash/TrashScreen.kt | 192 +++++++++++++----- 3 files changed, 347 insertions(+), 74 deletions(-) diff --git a/app/src/main/java/com/example/notesai/presentation/screens/archive/ArchiveScreen.kt b/app/src/main/java/com/example/notesai/presentation/screens/archive/ArchiveScreen.kt index 69476d9..83d425a 100644 --- a/app/src/main/java/com/example/notesai/presentation/screens/archive/ArchiveScreen.kt +++ b/app/src/main/java/com/example/notesai/presentation/screens/archive/ArchiveScreen.kt @@ -1,12 +1,30 @@ package com.example.notesai.presentation.screens.archive import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Archive +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.example.notesai.data.model.Note import com.example.notesai.data.model.Category @@ -20,25 +38,98 @@ fun ArchiveScreen( onRestore: (Note) -> Unit, onDelete: (Note) -> Unit ) { - if (notes.isEmpty()) { - EmptyState( - icon = Icons.Default.Archive, - message = "Arsip kosong", - subtitle = "Catatan yang diarsipkan akan muncul di sini" - ) + var searchQuery by remember { mutableStateOf("") } + + // Filter berdasarkan search query + val filteredNotes = if (searchQuery.isBlank()) { + notes } else { - LazyColumn( - contentPadding = PaddingValues(16.dp), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - items(notes) { note -> - val category = categories.find { it.id == note.categoryId } - ArchiveNoteCard( - note = note, - categoryName = category?.name ?: "Unknown", - onRestore = { onRestore(note) }, - onDelete = { onDelete(note) } + notes.filter { note -> + note.title.contains(searchQuery, ignoreCase = true) || + note.content.contains(searchQuery, ignoreCase = true) || + note.description.contains(searchQuery, ignoreCase = true) || + categories.find { it.id == note.categoryId }?.name?.contains(searchQuery, ignoreCase = true) == true + } + } + + Column(modifier = Modifier.fillMaxSize()) { + // Search Bar + OutlinedTextField( + value = searchQuery, + onValueChange = { searchQuery = it }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + placeholder = { + Text( + "Cari catatan arsip...", + color = Color(0xFF64748B) ) + }, + leadingIcon = { + Icon( + Icons.Default.Search, + contentDescription = null, + tint = Color(0xFF64748B) + ) + }, + trailingIcon = { + if (searchQuery.isNotEmpty()) { + IconButton(onClick = { searchQuery = "" }) { + Icon( + Icons.Default.Clear, + contentDescription = "Clear", + tint = Color(0xFF64748B) + ) + } + } + }, + shape = RoundedCornerShape(12.dp), + colors = OutlinedTextFieldDefaults.colors( + focusedContainerColor = Color(0xFF1E293B), + unfocusedContainerColor = Color(0xFF1E293B), + focusedBorderColor = Color(0xFF6366F1), + unfocusedBorderColor = Color(0xFF334155), + focusedTextColor = Color.White, + unfocusedTextColor = Color.White, + cursorColor = Color(0xFF6366F1) + ), + singleLine = true + ) + + // Content + if (filteredNotes.isEmpty()) { + if (searchQuery.isNotEmpty()) { + EmptyState( + icon = Icons.Default.Search, + message = "Tidak ada hasil", + subtitle = "Coba kata kunci lain" + ) + } else { + EmptyState( + icon = Icons.Default.Archive, + message = "Arsip kosong", + subtitle = "Catatan yang diarsipkan akan muncul di sini" + ) + } + } else { + LazyColumn( + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + bottom = 100.dp + ), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + items(filteredNotes) { note -> + val category = categories.find { it.id == note.categoryId } + ArchiveNoteCard( + note = note, + categoryName = category?.name ?: "Unknown", + onRestore = { onRestore(note) }, + onDelete = { onDelete(note) } + ) + } } } } diff --git a/app/src/main/java/com/example/notesai/presentation/screens/starred/StarredNotesScreen.kt b/app/src/main/java/com/example/notesai/presentation/screens/starred/StarredNotesScreen.kt index d9f25bf..0a33a08 100644 --- a/app/src/main/java/com/example/notesai/presentation/screens/starred/StarredNotesScreen.kt +++ b/app/src/main/java/com/example/notesai/presentation/screens/starred/StarredNotesScreen.kt @@ -3,14 +3,32 @@ package com.example.notesai.presentation.screens.starred import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Clear +import androidx.compose.material.icons.filled.Search import androidx.compose.material.icons.filled.Star import androidx.compose.material3.ExperimentalMaterial3Api +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults +import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import com.example.notesai.presentation.components.EmptyState import com.example.notesai.presentation.screens.starred.components.StarredNoteCard @@ -25,27 +43,93 @@ fun StarredNotesScreen( onNoteClick: (Note) -> Unit, onUnpin: (Note) -> Unit ) { - val starredNotes = notes.filter { it.isPinned && !it.isArchived && !it.isDeleted } + var searchQuery by remember { mutableStateOf("") } + + val starredNotes = notes + .filter { it.isPinned && !it.isArchived && !it.isDeleted } .sortedByDescending { it.timestamp } + // Filter berdasarkan search query + val filteredNotes = if (searchQuery.isBlank()) { + starredNotes + } else { + starredNotes.filter { note -> + note.title.contains(searchQuery, ignoreCase = true) || + note.content.contains(searchQuery, ignoreCase = true) || + note.description.contains(searchQuery, ignoreCase = true) + } + } + Column(modifier = Modifier.fillMaxSize()) { - if (starredNotes.isEmpty()) { - EmptyState( - icon = Icons.Default.Star, - message = "Belum ada catatan berbintang", - subtitle = "Catatan yang ditandai berbintang akan muncul di sini" - ) + // Search Bar + OutlinedTextField( + value = searchQuery, + onValueChange = { searchQuery = it }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + placeholder = { + Text( + "Cari catatan berbintang...", + color = Color(0xFF64748B) + ) + }, + leadingIcon = { + Icon( + Icons.Default.Search, + contentDescription = null, + tint = Color(0xFF64748B) + ) + }, + trailingIcon = { + if (searchQuery.isNotEmpty()) { + IconButton(onClick = { searchQuery = "" }) { + Icon( + Icons.Default.Clear, + contentDescription = "Clear", + tint = Color(0xFF64748B) + ) + } + } + }, + shape = RoundedCornerShape(12.dp), + colors = OutlinedTextFieldDefaults.colors( + focusedContainerColor = Color(0xFF1E293B), + unfocusedContainerColor = Color(0xFF1E293B), + focusedBorderColor = Color(0xFF6366F1), + unfocusedBorderColor = Color(0xFF334155), + focusedTextColor = Color.White, + unfocusedTextColor = Color.White, + cursorColor = Color(0xFF6366F1) + ), + singleLine = true + ) + + // Content + if (filteredNotes.isEmpty()) { + if (searchQuery.isNotEmpty()) { + EmptyState( + icon = Icons.Default.Search, + message = "Tidak ada hasil", + subtitle = "Coba kata kunci lain" + ) + } else { + EmptyState( + icon = Icons.Default.Star, + message = "Belum ada catatan berbintang", + subtitle = "Catatan yang ditandai berbintang akan muncul di sini" + ) + } } else { LazyColumn( contentPadding = PaddingValues( start = 16.dp, end = 16.dp, - top = 16.dp, - bottom = 100.dp // Extra space untuk bottom bar + bottom = 100.dp ), verticalArrangement = Arrangement.spacedBy(12.dp) ) { - items(starredNotes) { note -> + items(filteredNotes) { note -> val category = categories.find { it.id == note.categoryId } StarredNoteCard( note = note, diff --git a/app/src/main/java/com/example/notesai/presentation/screens/trash/TrashScreen.kt b/app/src/main/java/com/example/notesai/presentation/screens/trash/TrashScreen.kt index ea0c8e7..87ea6e1 100644 --- a/app/src/main/java/com/example/notesai/presentation/screens/trash/TrashScreen.kt +++ b/app/src/main/java/com/example/notesai/presentation/screens/trash/TrashScreen.kt @@ -1,15 +1,30 @@ -// File: presentation/screens/trash/TrashScreen.kt package com.example.notesai.presentation.screens.trash import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.Clear import androidx.compose.material.icons.filled.Delete +import androidx.compose.material.icons.filled.Search +import androidx.compose.material3.Icon +import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.OutlinedTextField +import androidx.compose.material3.OutlinedTextFieldDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp @@ -28,64 +43,147 @@ fun TrashScreen( onRestoreCategory: (Category) -> Unit, onDeleteCategoryPermanent: (Category) -> Unit ) { + var searchQuery by remember { mutableStateOf("") } + // Filter kategori dan note yang dihapus val deletedCategories = categories.filter { it.isDeleted } val deletedNotes = notes.filter { it.isDeleted } - if (deletedCategories.isEmpty() && deletedNotes.isEmpty()) { - EmptyState( - icon = Icons.Default.Delete, - message = "Sampah kosong", - subtitle = "Kategori dan catatan yang dihapus akan muncul di sini" - ) + // Filter berdasarkan search query + val filteredCategories = if (searchQuery.isBlank()) { + deletedCategories } else { - LazyColumn( - contentPadding = PaddingValues(16.dp), - verticalArrangement = Arrangement.spacedBy(12.dp) - ) { - // Section: Kategori Terhapus - if (deletedCategories.isNotEmpty()) { - item { - Text( - "Kategori Terhapus (${deletedCategories.size})", - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Bold, - color = Color(0xFF94A3B8) - ) - } + deletedCategories.filter { category -> + category.name.contains(searchQuery, ignoreCase = true) + } + } - items(deletedCategories) { category -> - val notesInCategory = notes.count { - it.categoryId == category.id && it.isDeleted + val filteredNotes = if (searchQuery.isBlank()) { + deletedNotes + } else { + deletedNotes.filter { note -> + note.title.contains(searchQuery, ignoreCase = true) || + note.content.contains(searchQuery, ignoreCase = true) || + note.description.contains(searchQuery, ignoreCase = true) || + categories.find { it.id == note.categoryId }?.name?.contains(searchQuery, ignoreCase = true) == true + } + } + + Column(modifier = Modifier.fillMaxSize()) { + // Search Bar + OutlinedTextField( + value = searchQuery, + onValueChange = { searchQuery = it }, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 12.dp), + placeholder = { + Text( + "Cari di sampah...", + color = Color(0xFF64748B) + ) + }, + leadingIcon = { + Icon( + Icons.Default.Search, + contentDescription = null, + tint = Color(0xFF64748B) + ) + }, + trailingIcon = { + if (searchQuery.isNotEmpty()) { + IconButton(onClick = { searchQuery = "" }) { + Icon( + Icons.Default.Clear, + contentDescription = "Clear", + tint = Color(0xFF64748B) + ) } - TrashCategoryCard( - category = category, - noteCount = notesInCategory, - onRestore = { onRestoreCategory(category) }, - onDeletePermanent = { onDeleteCategoryPermanent(category) } - ) } + }, + shape = RoundedCornerShape(12.dp), + colors = OutlinedTextFieldDefaults.colors( + focusedContainerColor = Color(0xFF1E293B), + unfocusedContainerColor = Color(0xFF1E293B), + focusedBorderColor = Color(0xFF6366F1), + unfocusedBorderColor = Color(0xFF334155), + focusedTextColor = Color.White, + unfocusedTextColor = Color.White, + cursorColor = Color(0xFF6366F1) + ), + singleLine = true + ) + + // Content + if (filteredCategories.isEmpty() && filteredNotes.isEmpty()) { + if (searchQuery.isNotEmpty()) { + EmptyState( + icon = Icons.Default.Search, + message = "Tidak ada hasil", + subtitle = "Coba kata kunci lain" + ) + } else { + EmptyState( + icon = Icons.Default.Delete, + message = "Sampah kosong", + subtitle = "Kategori dan catatan yang dihapus akan muncul di sini" + ) } + } else { + LazyColumn( + contentPadding = PaddingValues( + start = 16.dp, + end = 16.dp, + bottom = 100.dp + ), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + // Section: Kategori Terhapus + if (filteredCategories.isNotEmpty()) { + item { + Text( + "Kategori Terhapus (${filteredCategories.size})", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold, + color = Color(0xFF94A3B8), + modifier = Modifier.padding(vertical = 8.dp) + ) + } - // Section: Catatan Terhapus - if (deletedNotes.isNotEmpty()) { - item { - Text( - "Catatan Terhapus (${deletedNotes.size})", - style = MaterialTheme.typography.titleMedium, - fontWeight = FontWeight.Bold, - color = Color(0xFF94A3B8) - ) + items(filteredCategories) { category -> + val notesInCategory = notes.count { + it.categoryId == category.id && it.isDeleted + } + TrashCategoryCard( + category = category, + noteCount = notesInCategory, + onRestore = { onRestoreCategory(category) }, + onDeletePermanent = { onDeleteCategoryPermanent(category) } + ) + } } - items(deletedNotes) { note -> - val category = categories.find { it.id == note.categoryId } - TrashNoteCard( - note = note, - categoryName = category?.name ?: "Unknown", - onRestore = { onRestoreNote(note) }, - onDeletePermanent = { onDeleteNotePermanent(note) } - ) + // Section: Catatan Terhapus + if (filteredNotes.isNotEmpty()) { + item { + Text( + "Catatan Terhapus (${filteredNotes.size})", + style = MaterialTheme.typography.titleMedium, + fontWeight = FontWeight.Bold, + color = Color(0xFF94A3B8), + modifier = Modifier.padding(vertical = 8.dp) + ) + } + + items(filteredNotes) { note -> + val category = categories.find { it.id == note.categoryId } + TrashNoteCard( + note = note, + categoryName = category?.name ?: "Unknown", + onRestore = { onRestoreNote(note) }, + onDeletePermanent = { onDeleteNotePermanent(note) } + ) + } } } }