Menyesuaikan Fitur edit dan hapus pada kategori
This commit is contained in:
parent
3f84068d72
commit
4b9cdcbb13
@ -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 }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
)
|
||||
@ -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
|
||||
)
|
||||
@ -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) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user