diff --git a/app/src/main/java/com/example/notesai/presentation/dialogs/CategoryDialog.kt b/app/src/main/java/com/example/notesai/presentation/dialogs/CategoryDialog.kt index a6bd2df..bc8108e 100644 --- a/app/src/main/java/com/example/notesai/presentation/dialogs/CategoryDialog.kt +++ b/app/src/main/java/com/example/notesai/presentation/dialogs/CategoryDialog.kt @@ -1,33 +1,16 @@ +// File: presentation/dialogs/CategoryDialog.kt package com.example.notesai.presentation.dialogs +import androidx.compose.animation.* +import androidx.compose.animation.core.* import androidx.compose.foundation.background import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.aspectRatio -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Check -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TextFieldDefaults -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.material3.* +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -35,6 +18,8 @@ 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 androidx.compose.ui.unit.sp +import com.example.notesai.util.Constants @Composable fun CategoryDialog( @@ -44,91 +29,128 @@ fun CategoryDialog( var name by remember { mutableStateOf("") } var selectedGradient by remember { mutableStateOf(0) } - val gradients = listOf( - Pair(0xFF6366F1L, 0xFFA855F7L), - Pair(0xFFEC4899L, 0xFFF59E0BL), - Pair(0xFF8B5CF6L, 0xFFEC4899L), - Pair(0xFF06B6D4L, 0xFF3B82F6L), - Pair(0xFF10B981L, 0xFF059669L), - Pair(0xFFF59E0BL, 0xFFEF4444L), - Pair(0xFF6366F1L, 0xFF8B5CF6L), - Pair(0xFFEF4444L, 0xFFDC2626L) - ) - AlertDialog( onDismissRequest = onDismiss, - containerColor = Color(0xFF1E293B), + containerColor = Constants.AppColors.Surface, + shape = RoundedCornerShape(20.dp), title = { Text( "Buat Kategori Baru", - color = Color.White, - fontWeight = FontWeight.Bold + color = Constants.AppColors.OnBackground, + fontWeight = FontWeight.Bold, + fontSize = 20.sp ) }, text = { - Column { + Column( + verticalArrangement = Arrangement.spacedBy(20.dp) + ) { + // Input Nama OutlinedTextField( value = name, onValueChange = { name = it }, - label = { Text("Nama Kategori", color = Color(0xFF94A3B8)) }, + label = { + Text( + "Nama Kategori", + color = Constants.AppColors.OnSurfaceVariant + ) + }, + placeholder = { + Text( + "Contoh: Pekerjaan, Personal", + color = Constants.AppColors.OnSurfaceTertiary, + fontSize = 14.sp + ) + }, modifier = Modifier.fillMaxWidth(), - colors = TextFieldDefaults.colors( - focusedTextColor = Color.White, - unfocusedTextColor = Color.White, - focusedContainerColor = Color(0xFF334155), - unfocusedContainerColor = Color(0xFF334155), - cursorColor = Color(0xFFA855F7), - focusedIndicatorColor = Color(0xFFA855F7), - unfocusedIndicatorColor = Color(0xFF64748B) + colors = OutlinedTextFieldDefaults.colors( + focusedTextColor = Constants.AppColors.OnBackground, + unfocusedTextColor = Constants.AppColors.OnSurface, + focusedContainerColor = Constants.AppColors.SurfaceVariant, + unfocusedContainerColor = Constants.AppColors.SurfaceVariant, + cursorColor = Constants.AppColors.Primary, + focusedBorderColor = Constants.AppColors.Primary, + unfocusedBorderColor = Color.Transparent ), - shape = RoundedCornerShape(12.dp) + shape = RoundedCornerShape(12.dp), + singleLine = true ) - Spacer(modifier = Modifier.height(20.dp)) - Text( - "Pilih Gradient:", - style = MaterialTheme.typography.bodyMedium, - color = Color.White, - fontWeight = FontWeight.SemiBold - ) + // Gradient Selector + Column( + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + "Pilih Warna:", + style = MaterialTheme.typography.bodyMedium, + color = Constants.AppColors.OnSurface, + fontWeight = FontWeight.SemiBold, + fontSize = 14.sp + ) - Spacer(modifier = Modifier.height(12.dp)) + Constants.AppColors.CategoryColors.chunked(4).forEach { row -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + row.forEach { gradient -> + val globalIndex = Constants.AppColors.CategoryColors.indexOf(gradient) + val isSelected = selectedGradient == globalIndex - gradients.chunked(4).forEach { row -> - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.spacedBy(8.dp) - ) { - row.forEachIndexed { index, gradient -> - val globalIndex = gradients.indexOf(gradient) - Box( - modifier = Modifier - .weight(1f) - .aspectRatio(1f) - .clip(RoundedCornerShape(12.dp)) - .background( - brush = Brush.linearGradient( - colors = listOf( - Color(gradient.first), - Color(gradient.second) + // Scale animation + val scale by animateFloatAsState( + targetValue = if (isSelected) 1.1f else 1f, + animationSpec = spring( + dampingRatio = Spring.DampingRatioMediumBouncy, + stiffness = Spring.StiffnessMedium + ), + label = "scale" + ) + + Box( + modifier = Modifier + .weight(1f) + .aspectRatio(1f) + .clip(RoundedCornerShape(12.dp)) + .background( + brush = Brush.linearGradient( + colors = listOf( + Color(gradient.first), + Color(gradient.second) + ) ) ) - ) - .clickable { selectedGradient = globalIndex }, - contentAlignment = Alignment.Center - ) { - if (selectedGradient == globalIndex) { - Icon( - Icons.Default.Check, - contentDescription = null, - tint = Color.White, - modifier = Modifier.size(24.dp) - ) + .clickable { selectedGradient = globalIndex } + .then( + if (isSelected) Modifier + else Modifier + ), + contentAlignment = Alignment.Center + ) { + // Check icon dengan animation + this@Row.AnimatedVisibility( + visible = isSelected, + enter = scaleIn() + fadeIn(), + exit = scaleOut() + fadeOut() + ) { + Surface( + color = Color.White.copy(alpha = 0.9f), + shape = RoundedCornerShape(8.dp) + ) { + Icon( + Icons.Default.Check, + contentDescription = null, + tint = Color(gradient.first), + modifier = Modifier + .padding(6.dp) + .size(20.dp) + ) + } + } } } } } - Spacer(modifier = Modifier.height(8.dp)) } } }, @@ -136,27 +158,37 @@ fun CategoryDialog( Button( onClick = { if (name.isNotBlank()) { - val gradient = gradients[selectedGradient] + val gradient = Constants.AppColors.CategoryColors[selectedGradient] onSave(name, gradient.first, gradient.second) } }, enabled = name.isNotBlank(), colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent + containerColor = Constants.AppColors.Primary, + disabledContainerColor = Constants.AppColors.Primary.copy(alpha = 0.5f) ), - modifier = Modifier.background( - brush = Brush.linearGradient( - colors = listOf(Color(0xFF6366F1), Color(0xFFA855F7)) - ), - shape = RoundedCornerShape(8.dp) - ) + shape = RoundedCornerShape(12.dp), + modifier = Modifier.height(48.dp) ) { - Text("Simpan", color = Color.White, fontWeight = FontWeight.Bold) + Text( + "Simpan", + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = 15.sp + ) } }, dismissButton = { - TextButton(onClick = onDismiss) { - Text("Batal", color = Color(0xFF94A3B8)) + TextButton( + onClick = onDismiss, + shape = RoundedCornerShape(12.dp), + modifier = Modifier.height(48.dp) + ) { + Text( + "Batal", + color = Constants.AppColors.OnSurfaceVariant, + fontSize = 15.sp + ) } } ) diff --git a/app/src/main/java/com/example/notesai/presentation/dialogs/NoteDialog.kt b/app/src/main/java/com/example/notesai/presentation/dialogs/NoteDialog.kt index 2edbd7c..5f4b88c 100644 --- a/app/src/main/java/com/example/notesai/presentation/dialogs/NoteDialog.kt +++ b/app/src/main/java/com/example/notesai/presentation/dialogs/NoteDialog.kt @@ -1,31 +1,18 @@ package com.example.notesai.presentation.dialogs -import androidx.compose.foundation.background -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.width +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.AlertDialog -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.OutlinedTextField -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.material3.TextFieldDefaults -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.material.icons.Icons +import androidx.compose.material.icons.filled.Delete +import androidx.compose.material3.* +import androidx.compose.runtime.* 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 androidx.compose.ui.unit.sp import com.example.notesai.data.model.Note +import com.example.notesai.util.Constants @Composable fun NoteDialog( @@ -36,89 +23,189 @@ fun NoteDialog( ) { var title by remember { mutableStateOf(note?.title ?: "") } var description by remember { mutableStateOf(note?.description ?: "") } + var showDeleteConfirm by remember { mutableStateOf(false) } + + // Delete confirmation dialog + if (showDeleteConfirm) { + AlertDialog( + onDismissRequest = { showDeleteConfirm = false }, + containerColor = Constants.AppColors.Surface, + shape = RoundedCornerShape(20.dp), + title = { + Text( + "Hapus Catatan?", + color = Constants.AppColors.OnBackground, + fontWeight = FontWeight.Bold + ) + }, + text = { + Text( + "Catatan ini akan dipindahkan ke sampah.", + color = Constants.AppColors.OnSurfaceVariant + ) + }, + confirmButton = { + Button( + onClick = { + onDelete?.invoke() + showDeleteConfirm = false + }, + colors = ButtonDefaults.buttonColors( + containerColor = Constants.AppColors.Error + ), + shape = RoundedCornerShape(12.dp) + ) { + Text("Hapus", color = Color.White, fontWeight = FontWeight.Bold) + } + }, + dismissButton = { + TextButton( + onClick = { showDeleteConfirm = false }, + shape = RoundedCornerShape(12.dp) + ) { + Text("Batal", color = Constants.AppColors.OnSurfaceVariant) + } + } + ) + } AlertDialog( onDismissRequest = onDismiss, - containerColor = Color(0xFF1E293B), + containerColor = Constants.AppColors.Surface, + shape = RoundedCornerShape(20.dp), title = { Text( if (note == null) "Catatan Baru" else "Edit Catatan", - color = Color.White, - fontWeight = FontWeight.Bold + color = Constants.AppColors.OnBackground, + fontWeight = FontWeight.Bold, + fontSize = 20.sp ) }, text = { - Column { + Column( + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // Title Input OutlinedTextField( value = title, onValueChange = { title = it }, - label = { Text("Judul", color = Color(0xFF94A3B8)) }, + label = { + Text( + "Judul", + color = Constants.AppColors.OnSurfaceVariant + ) + }, + placeholder = { + Text( + "Masukkan judul catatan", + color = Constants.AppColors.OnSurfaceTertiary, + fontSize = 14.sp + ) + }, modifier = Modifier.fillMaxWidth(), - colors = TextFieldDefaults.colors( - focusedTextColor = Color.White, - unfocusedTextColor = Color.White, - focusedContainerColor = Color(0xFF334155), - unfocusedContainerColor = Color(0xFF334155), - cursorColor = Color(0xFFA855F7), - focusedIndicatorColor = Color(0xFFA855F7), - unfocusedIndicatorColor = Color(0xFF64748B) + colors = OutlinedTextFieldDefaults.colors( + focusedTextColor = Constants.AppColors.OnBackground, + unfocusedTextColor = Constants.AppColors.OnSurface, + focusedContainerColor = Constants.AppColors.SurfaceVariant, + unfocusedContainerColor = Constants.AppColors.SurfaceVariant, + cursorColor = Constants.AppColors.Primary, + focusedBorderColor = Constants.AppColors.Primary, + unfocusedBorderColor = Color.Transparent ), - shape = RoundedCornerShape(12.dp) + shape = RoundedCornerShape(12.dp), + singleLine = true ) - Spacer(modifier = Modifier.height(12.dp)) - + // Description Input OutlinedTextField( value = description, onValueChange = { description = it }, - label = { Text("Deskripsi", color = Color(0xFF94A3B8)) }, - placeholder = { Text("Tambahkan deskripsi singkat...", color = Color(0xFF64748B)) }, + label = { + Text( + "Deskripsi", + color = Constants.AppColors.OnSurfaceVariant + ) + }, + placeholder = { + Text( + "Tambahkan deskripsi singkat...", + color = Constants.AppColors.OnSurfaceTertiary, + fontSize = 14.sp + ) + }, modifier = Modifier .fillMaxWidth() - .height(120.dp), - maxLines = 5, - colors = TextFieldDefaults.colors( - focusedTextColor = Color.White, - unfocusedTextColor = Color.White, - focusedContainerColor = Color(0xFF334155), - unfocusedContainerColor = Color(0xFF334155), - cursorColor = Color(0xFFA855F7), - focusedIndicatorColor = Color(0xFFA855F7), - unfocusedIndicatorColor = Color(0xFF64748B) + .heightIn(min = 120.dp, max = 200.dp), + colors = OutlinedTextFieldDefaults.colors( + focusedTextColor = Constants.AppColors.OnBackground, + unfocusedTextColor = Constants.AppColors.OnSurface, + focusedContainerColor = Constants.AppColors.SurfaceVariant, + unfocusedContainerColor = Constants.AppColors.SurfaceVariant, + cursorColor = Constants.AppColors.Primary, + focusedBorderColor = Constants.AppColors.Primary, + unfocusedBorderColor = Color.Transparent ), - shape = RoundedCornerShape(12.dp) + shape = RoundedCornerShape(12.dp), + maxLines = 8 ) } }, confirmButton = { - Row { + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + // Delete button (if editing) if (onDelete != null) { - TextButton(onClick = onDelete) { - Text("Hapus", color = Color(0xFFEF4444), fontWeight = FontWeight.Bold) + IconButton( + onClick = { showDeleteConfirm = true }, + modifier = Modifier.size(48.dp) + ) { + Icon( + Icons.Default.Delete, + contentDescription = "Hapus", + tint = Constants.AppColors.Error + ) } - Spacer(modifier = Modifier.width(8.dp)) } + + Spacer(modifier = Modifier.weight(1f)) + + // Cancel button + TextButton( + onClick = onDismiss, + shape = RoundedCornerShape(12.dp), + modifier = Modifier.height(48.dp) + ) { + Text( + "Batal", + color = Constants.AppColors.OnSurfaceVariant, + fontSize = 15.sp + ) + } + + // Save button Button( - onClick = { if (title.isNotBlank()) onSave(title, description) }, + onClick = { + if (title.isNotBlank()) { + onSave(title, description) + } + }, enabled = title.isNotBlank(), colors = ButtonDefaults.buttonColors( - containerColor = Color.Transparent + containerColor = Constants.AppColors.Primary, + disabledContainerColor = Constants.AppColors.Primary.copy(alpha = 0.5f) ), - modifier = Modifier.background( - brush = Brush.linearGradient( - colors = listOf(Color(0xFF6366F1), Color(0xFFA855F7)) - ), - shape = RoundedCornerShape(8.dp) - ) + shape = RoundedCornerShape(12.dp), + modifier = Modifier.height(48.dp) ) { - Text("Simpan", color = Color.White, fontWeight = FontWeight.Bold) + Text( + "Simpan", + color = Color.White, + fontWeight = FontWeight.Bold, + fontSize = 15.sp + ) } } - }, - dismissButton = { - TextButton(onClick = onDismiss) { - Text("Batal", color = Color(0xFF94A3B8)) - } } ) } \ No newline at end of file