Menyesuaikan Fitur edit dan hapus pada kategori

This commit is contained in:
202310715297 RAIHAN ARIQ MUZAKKI 2025-12-13 15:55:28 +07:00
parent 3f84068d72
commit 4b9cdcbb13
5 changed files with 347 additions and 39 deletions

View File

@ -255,7 +255,7 @@ fun NotesApp() {
) {
when (currentScreen) {
"main" -> MainScreen(
categories = categories,
categories = categories.filter { !it.isDeleted }, // TAMBAHKAN FILTER INI
notes = notes,
selectedCategory = selectedCategory,
searchQuery = searchQuery,
@ -271,28 +271,69 @@ fun NotesApp() {
}
},
onCategoryDelete = { category ->
// Delete kategori dan semua catatan di dalamnya
categories = categories.filter { it.id != category.id }
notes = notes.filter { it.categoryId != category.id }
// UBAH: Jangan filter, tapi set isDeleted = true
categories = categories.map {
if (it.id == category.id) it.copy(isDeleted = true)
else it
}
// Note di dalam kategori juga di-delete
notes = notes.map {
if (it.categoryId == category.id) it.copy(isDeleted = true)
else it
}
selectedCategory = null
},
onCategoryEdit = { category, newName, newGradientStart, newGradientEnd ->
categories = categories.updateWhere(
predicate = { it.id == category.id },
transform = {
categories = categories.map {
if (it.id == category.id) {
it.copy(
name = newName,
gradientStart = newGradientStart,
gradientEnd = newGradientEnd,
timestamp = System.currentTimeMillis()
)
} else {
it
}
)
}
}
)
"trash" -> TrashScreen(
notes = notes.filter { it.isDeleted },
categories = categories, // Pass semua categories (sudah ada yang isDeleted)
onRestoreNote = { note ->
notes = notes.map {
if (it.id == note.id) it.copy(isDeleted = false, isArchived = false)
else it
}
},
onDeleteNotePermanent = { note ->
notes = notes.filter { it.id != note.id }
},
onRestoreCategory = { category ->
// Restore kategori
categories = categories.map {
if (it.id == category.id) it.copy(isDeleted = false)
else it
}
// Restore semua note di dalam kategori
notes = notes.map {
if (it.categoryId == category.id) it.copy(isDeleted = false, isArchived = false)
else it
}
},
onDeleteCategoryPermanent = { category ->
// Hapus kategori permanen
categories = categories.filter { it.id != category.id }
// Hapus semua note di dalam kategori permanen
notes = notes.filter { it.categoryId != category.id }
}
)
"starred" -> StarredNotesScreen(
notes = notes,
categories = categories,
categories = categories.filter { !it.isDeleted }, // FILTER
onNoteClick = { note ->
fullScreenNote = note
showFullScreenNote = true
@ -306,13 +347,10 @@ fun NotesApp() {
}
}
)
"ai" -> AIHelperScreen(
categories = categories,
notes = notes.filter { !it.isDeleted }
)
"archive" -> ArchiveScreen(
notes = notes.filter { it.isArchived && !it.isDeleted },
categories = categories,
categories = categories.filter { !it.isDeleted }, // FILTER
onRestore = { note ->
notes = notes.map {
if (it.id == note.id) it.copy(isArchived = false)
@ -326,18 +364,10 @@ fun NotesApp() {
}
}
)
"trash" -> TrashScreen(
notes = notes.filter { it.isDeleted },
categories = categories,
onRestore = { note ->
notes = notes.map {
if (it.id == note.id) it.copy(isDeleted = false, isArchived = false)
else it
}
},
onDeletePermanent = { note ->
notes = notes.filter { it.id != note.id }
}
"ai" -> AIHelperScreen(
categories = categories.filter { !it.isDeleted }, // FILTER
notes = notes.filter { !it.isDeleted }
)
}
}

View File

@ -8,5 +8,6 @@ data class Category(
val name: String,
val gradientStart: Long,
val gradientEnd: Long,
val timestamp: Long = System.currentTimeMillis()
val timestamp: Long = System.currentTimeMillis(),
val isDeleted: Boolean = false // TAMBAHKAN INI
)

View File

@ -0,0 +1,69 @@
package com.example.notesai.data.model
import android.annotation.SuppressLint
import kotlinx.serialization.Serializable
@SuppressLint("UnsafeOptInUsageError")
@Serializable
data class SerializableCategory(
val id: String,
val name: String,
val gradientStart: Long,
val gradientEnd: Long,
val timestamp: Long,
val isDeleted: Boolean = false // TAMBAHKAN INI
)
@SuppressLint("UnsafeOptInUsageError")
@Serializable
data class SerializableNote(
val id: String,
val categoryId: String,
val title: String,
val content: String,
val timestamp: Long,
val isArchived: Boolean,
val isDeleted: Boolean,
val isPinned: Boolean
)
// Extension functions untuk konversi
fun Category.toSerializable() = SerializableCategory(
id = id,
name = name,
gradientStart = gradientStart,
gradientEnd = gradientEnd,
timestamp = timestamp,
isDeleted = isDeleted // TAMBAHKAN INI
)
fun SerializableCategory.toCategory() = Category(
id = id,
name = name,
gradientStart = gradientStart,
gradientEnd = gradientEnd,
timestamp = timestamp,
isDeleted = isDeleted // TAMBAHKAN INI
)
fun Note.toSerializable() = SerializableNote(
id = id,
categoryId = categoryId,
title = title,
content = content,
timestamp = timestamp,
isArchived = isArchived,
isDeleted = isDeleted,
isPinned = isPinned
)
fun SerializableNote.toNote() = Note(
id = id,
categoryId = categoryId,
title = title,
content = content,
timestamp = timestamp,
isArchived = isArchived,
isDeleted = isDeleted,
isPinned = isPinned
)

View File

@ -1,3 +1,4 @@
// File: presentation/screens/trash/TrashScreen.kt
package com.example.notesai.presentation.screens.trash
import androidx.compose.foundation.layout.Arrangement
@ -6,10 +7,15 @@ import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.example.notesai.presentation.components.EmptyState
import com.example.notesai.presentation.screens.trash.components.TrashNoteCard
import com.example.notesai.presentation.screens.trash.components.TrashCategoryCard
import com.example.notesai.data.model.Note
import com.example.notesai.data.model.Category
@ -17,28 +23,70 @@ import com.example.notesai.data.model.Category
fun TrashScreen(
notes: List<Note>,
categories: List<Category>,
onRestore: (Note) -> Unit,
onDeletePermanent: (Note) -> Unit
onRestoreNote: (Note) -> Unit,
onDeleteNotePermanent: (Note) -> Unit,
onRestoreCategory: (Category) -> Unit,
onDeleteCategoryPermanent: (Category) -> Unit
) {
if (notes.isEmpty()) {
// 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 = "Catatan yang dihapus akan muncul di sini"
subtitle = "Kategori dan catatan yang dihapus akan muncul di sini"
)
} else {
LazyColumn(
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
items(notes) { note ->
val category = categories.find { it.id == note.categoryId }
TrashNoteCard(
note = note,
categoryName = category?.name ?: "Unknown",
onRestore = { onRestore(note) },
onDeletePermanent = { onDeletePermanent(note) }
)
// Section: Kategori Terhapus
if (deletedCategories.isNotEmpty()) {
item {
Text(
"Kategori Terhapus (${deletedCategories.size})",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
color = Color(0xFF94A3B8)
)
}
items(deletedCategories) { category ->
val notesInCategory = notes.count {
it.categoryId == category.id && it.isDeleted
}
TrashCategoryCard(
category = category,
noteCount = notesInCategory,
onRestore = { onRestoreCategory(category) },
onDeletePermanent = { onDeleteCategoryPermanent(category) }
)
}
}
// Section: Catatan Terhapus
if (deletedNotes.isNotEmpty()) {
item {
Text(
"Catatan Terhapus (${deletedNotes.size})",
style = MaterialTheme.typography.titleMedium,
fontWeight = FontWeight.Bold,
color = Color(0xFF94A3B8)
)
}
items(deletedNotes) { note ->
val category = categories.find { it.id == note.categoryId }
TrashNoteCard(
note = note,
categoryName = category?.name ?: "Unknown",
onRestore = { onRestoreNote(note) },
onDeletePermanent = { onDeleteNotePermanent(note) }
)
}
}
}
}

View File

@ -0,0 +1,160 @@
// File: presentation/screens/trash/components/TrashCategoryCard.kt
package com.example.notesai.presentation.screens.trash.components
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material.icons.filled.Folder
import androidx.compose.material.icons.filled.Restore
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Brush
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import com.example.notesai.data.model.Category
@Composable
fun TrashCategoryCard(
category: Category,
noteCount: Int,
onRestore: () -> Unit,
onDeletePermanent: () -> Unit
) {
var showDeleteDialog by remember { mutableStateOf(false) }
// Dialog konfirmasi hapus permanen
if (showDeleteDialog) {
AlertDialog(
onDismissRequest = { showDeleteDialog = false },
title = { Text("Hapus Kategori Permanen?", color = Color.White) },
text = {
Text(
"Kategori '${category.name}' dan $noteCount catatan di dalamnya akan dihapus permanen. Tindakan ini tidak dapat dibatalkan!",
color = Color.White
)
},
confirmButton = {
Button(
onClick = {
onDeletePermanent()
showDeleteDialog = false
},
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFFEF4444)
)
) {
Text("Hapus Permanen", color = Color.White)
}
},
dismissButton = {
Button(
onClick = { showDeleteDialog = false },
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFF64748B)
)
) {
Text("Batal", color = Color.White)
}
},
containerColor = Color(0xFF1E293B)
)
}
Card(
modifier = Modifier.fillMaxWidth(),
shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors(
containerColor = Color(0xFF1E293B)
),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Column(modifier = Modifier.padding(16.dp)) {
// Header dengan gradient
Box(
modifier = Modifier
.fillMaxWidth()
.background(
brush = Brush.linearGradient(
colors = listOf(
Color(category.gradientStart).copy(alpha = 0.3f),
Color(category.gradientEnd).copy(alpha = 0.3f)
)
),
shape = RoundedCornerShape(12.dp)
)
.padding(16.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Default.Folder,
contentDescription = null,
tint = Color.White.copy(0.9f),
modifier = Modifier.size(32.dp)
)
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Text(
category.name,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
color = Color.White
)
Spacer(modifier = Modifier.height(4.dp))
Text(
"$noteCount catatan",
style = MaterialTheme.typography.bodySmall,
color = Color.White.copy(0.8f)
)
}
}
}
Spacer(modifier = Modifier.height(12.dp))
// Info
Text(
"Kategori yang dihapus",
style = MaterialTheme.typography.bodySmall,
color = Color(0xFF64748B)
)
// Action buttons
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp),
horizontalArrangement = Arrangement.End
) {
TextButton(onClick = onRestore) {
Icon(
Icons.Default.Restore,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = Color(0xFF10B981)
)
Spacer(modifier = Modifier.width(4.dp))
Text("Pulihkan", color = Color(0xFF10B981), fontWeight = FontWeight.Bold)
}
Spacer(modifier = Modifier.width(8.dp))
TextButton(onClick = { showDeleteDialog = true }) {
Icon(
Icons.Default.Delete,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = Color(0xFFEF4444)
)
Spacer(modifier = Modifier.width(4.dp))
Text("Hapus Permanen", color = Color(0xFFEF4444), fontWeight = FontWeight.Bold)
}
}
}
}
}