Compare commits

...

2 Commits

Author SHA1 Message Date
359d3aa9a8 Merge remote-tracking branch 'origin/master'
# Conflicts:
#	app/src/main/java/com/example/notesai/MainActivity.kt
2025-12-11 15:49:22 +07:00
f2da1792b1 Menyesuaikan Tampilan Drawer Menu dan Halaman Catatan Berbintang 2025-12-11 15:47:41 +07:00

View File

@ -9,6 +9,7 @@ import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
@ -53,6 +54,7 @@ import androidx.compose.material.icons.filled.ContentCopy
import androidx.compose.material.icons.outlined.StarBorder
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.zIndex
import kotlinx.coroutines.delay
// Data Classes
@ -129,7 +131,7 @@ fun NotesApp() {
var showFullScreenNote by remember { mutableStateOf(false) }
var fullScreenNote by remember { mutableStateOf<Note?>(null) }
// Load data dari DataStore - dengan error handling
// Load data dari DataStore
LaunchedEffect(Unit) {
try {
dataStoreManager.categoriesFlow.collect { loadedCategories ->
@ -174,6 +176,7 @@ fun NotesApp() {
}
}
Box(modifier = Modifier.fillMaxSize()) {
Scaffold(
topBar = {
if (!showFullScreenNote) {
@ -181,13 +184,14 @@ fun NotesApp() {
title = when(currentScreen) {
"main" -> if (selectedCategory != null) selectedCategory!!.name else "AI Notes"
"ai" -> "AI Helper"
"starred" -> "Berbintang"
"archive" -> "Arsip"
"trash" -> "Sampah"
else -> "AI Notes"
},
showBackButton = (selectedCategory != null && currentScreen == "main") || currentScreen == "ai",
showBackButton = (selectedCategory != null && currentScreen == "main") || currentScreen == "ai" || currentScreen == "starred",
onBackClick = {
if (currentScreen == "ai") {
if (currentScreen == "ai" || currentScreen == "starred") {
currentScreen = "main"
} else {
selectedCategory = null
@ -324,6 +328,22 @@ fun NotesApp() {
notes = notes.filter { it.categoryId != category.id }
}
)
"starred" -> StarredNotesScreen(
notes = notes,
categories = categories,
onNoteClick = { note ->
fullScreenNote = note
showFullScreenNote = true
},
onMenuClick = { drawerState = true },
onBack = { currentScreen = "main" },
onUnpin = { note ->
notes = notes.map {
if (it.id == note.id) it.copy(isPinned = false)
else it
}
}
)
"ai" -> AIHelperScreen(
categories = categories,
notes = notes.filter { !it.isDeleted }
@ -361,25 +381,6 @@ fun NotesApp() {
}
}
// Drawer with Animation
AnimatedVisibility(
visible = drawerState,
enter = fadeIn() + slideInHorizontally(),
exit = fadeOut() + slideOutHorizontally()
) {
DrawerMenu(
currentScreen = currentScreen,
onDismiss = { drawerState = false },
onItemClick = { screen ->
currentScreen = screen
selectedCategory = null
drawerState = false
showSearch = false
searchQuery = ""
}
)
}
// Dialogs
if (showCategoryDialog) {
CategoryDialog(
@ -437,6 +438,31 @@ fun NotesApp() {
}
}
}
// Drawer with Animation - DI LUAR SCAFFOLD agar di atas semua
AnimatedVisibility(
visible = drawerState,
enter = fadeIn() + slideInHorizontally(
initialOffsetX = { -it }
),
exit = fadeOut() + slideOutHorizontally(
targetOffsetX = { -it }
),
modifier = Modifier.zIndex(100f) // Z-index tinggi
) {
DrawerMenu(
currentScreen = currentScreen,
onDismiss = { drawerState = false },
onItemClick = { screen ->
currentScreen = screen
selectedCategory = null
drawerState = false
showSearch = false
searchQuery = ""
}
)
}
}
}
@OptIn(ExperimentalMaterial3Api::class)
@ -952,12 +978,15 @@ fun NoteCard(
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Top
) {
// Judul
Text(
note.title,
style = MaterialTheme.typography.titleMedium,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
color = Color.White,
modifier = Modifier.weight(1f)
modifier = Modifier.weight(1f),
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
IconButton(
onClick = onPinClick,
@ -966,24 +995,43 @@ fun NoteCard(
Icon(
if (note.isPinned) Icons.Filled.Star else Icons.Outlined.StarBorder,
contentDescription = "Pin",
tint = if (note.isPinned) Color(0xFFFBBF24) else Color(0xFF94A3B8),
tint = if (note.isPinned) Color(0xFFFBBF24) else Color.Gray,
modifier = Modifier.size(18.dp)
)
}
}
// Deskripsi
if (note.content.isNotEmpty()) {
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(12.dp))
Text(
text = "Deskripsi",
style = MaterialTheme.typography.labelSmall,
color = Color(0xFF94A3B8),
fontWeight = FontWeight.SemiBold
)
Spacer(modifier = Modifier.height(4.dp))
Text(
note.content,
style = MaterialTheme.typography.bodyMedium,
maxLines = 6,
maxLines = 4,
overflow = TextOverflow.Ellipsis,
color = Color(0xFFCBD5E1),
lineHeight = 20.sp
)
}
Spacer(modifier = Modifier.height(12.dp))
// Divider
Divider(
color = Color(0xFF334155),
thickness = 1.dp
)
Spacer(modifier = Modifier.height(8.dp))
// Timestamp
Text(
dateFormat.format(Date(note.timestamp)),
style = MaterialTheme.typography.bodySmall,
@ -1002,20 +1050,32 @@ fun DrawerMenu(
Box(
modifier = Modifier
.fillMaxSize()
.background(Color.Black.copy(alpha = 0.6f))
.clickable(onClick = onDismiss)
.statusBarsPadding() // Padding untuk status bar
.background(Color.Black.copy(alpha = 0.5f))
.clickable(
onClick = onDismiss,
indication = null,
interactionSource = remember { MutableInteractionSource() }
)
) {
Card(
modifier = Modifier
.fillMaxHeight()
.width(280.dp)
.clickable(enabled = false) {},
shape = RoundedCornerShape(0.dp),
.width(250.dp)
.align(Alignment.CenterStart)
.clickable(
onClick = {},
indication = null,
interactionSource = remember { MutableInteractionSource() }
),
shape = RoundedCornerShape(topEnd = 0.dp, bottomEnd = 0.dp),
colors = CardDefaults.cardColors(
containerColor = Color(0xFF1E293B)
)
),
elevation = CardDefaults.cardElevation(defaultElevation = 16.dp)
) {
Column(modifier = Modifier.fillMaxSize()) {
// Header Drawer dengan tombol close
Box(
modifier = Modifier
.fillMaxWidth()
@ -1024,14 +1084,19 @@ fun DrawerMenu(
colors = listOf(Color(0xFF6366F1), Color(0xFFA855F7))
)
)
.padding(32.dp)
.padding(24.dp)
) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Column {
Icon(
Icons.Default.Create,
contentDescription = null,
tint = Color.White,
modifier = Modifier.size(40.dp)
modifier = Modifier.size(36.dp)
)
Spacer(modifier = Modifier.height(12.dp))
Text(
@ -1046,10 +1111,30 @@ fun DrawerMenu(
color = Color.White.copy(0.8f)
)
}
// // Tombol Close
// IconButton(
// onClick = onDismiss,
// modifier = Modifier
// .size(40.dp)
// .background(
// Color.White.copy(alpha = 0.2f),
// shape = CircleShape
// )
// ) {
// Icon(
// Icons.Default.Close,
// contentDescription = "Tutup Menu",
// tint = Color.White,
// modifier = Modifier.size(24.dp)
// )
// }
}
}
Spacer(modifier = Modifier.height(16.dp))
// Menu Items
MenuItem(
icon = Icons.Default.Home,
text = "Beranda",
@ -1073,6 +1158,21 @@ fun DrawerMenu(
text = "Sampah",
isSelected = currentScreen == "trash"
) { onItemClick("trash") }
Spacer(modifier = Modifier.weight(1f))
// Footer
Divider(
color = Color.White.copy(alpha = 0.1f),
modifier = Modifier.padding(horizontal = 16.dp)
)
Text(
text = "Version 1.0.0",
style = MaterialTheme.typography.bodySmall,
color = Color.White.copy(alpha = 0.5f),
modifier = Modifier.padding(16.dp)
)
}
}
}
@ -1803,10 +1903,11 @@ fun ChatBubble(
horizontalArrangement = if (message.isUser) Arrangement.End else Arrangement.Start
) {
if (!message.isUser) {
// Ganti ikon bintang dengan ikon robot/sparkles
Icon(
Icons.Default.AutoAwesome,
Icons.Default.AutoAwesome, // Atau bisa diganti dengan ikon lain seperti AutoAwesome
contentDescription = null,
tint = Color(0xFFFBBF24),
tint = Color(0xFF6366F1), // Warna ungu/biru untuk AI
modifier = Modifier
.size(32.dp)
.padding(end = 8.dp)
@ -2176,118 +2277,39 @@ fun TrashNoteCard(
}
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun StarredNotesScreen(
notes: List<Note>,
categories: List<Category>,
onNoteClick: (Note) -> Unit,
onMenuClick: () -> Unit,
onBack: () -> Unit
onBack: () -> Unit,
onUnpin: (Note) -> Unit
) {
val starredNotes = notes.filter { it.isPinned && !it.isArchived && !it.isDeleted }
Scaffold(
containerColor = MaterialTheme.colorScheme.background,
topBar = {
TopAppBar(
title = {
Text(
"Berbintang",
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onBackground
)
},
navigationIcon = {
IconButton(onClick = onBack) {
Icon(
Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "Kembali",
tint = MaterialTheme.colorScheme.onBackground
)
}
},
actions = {
IconButton(onClick = onMenuClick) {
Icon(
Icons.Default.Menu,
contentDescription = "Menu",
tint = MaterialTheme.colorScheme.onBackground
)
}
},
colors = TopAppBarDefaults.topAppBarColors(
containerColor = Color.Transparent
)
)
}
) { padding ->
if (starredNotes.isEmpty()) {
// Tampilan kosong
Box(
modifier = Modifier
.fillMaxSize()
.padding(padding),
contentAlignment = Alignment.Center
) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
modifier = Modifier.padding(horizontal = 32.dp)
) {
Icon(
Icons.Default.Star,
contentDescription = null,
tint = Color(0xFF64748B),
modifier = Modifier.size(80.dp)
EmptyState(
icon = Icons.Default.Star,
message = "Belum ada catatan berbintang",
subtitle = "Catatan yang ditandai berbintang akan muncul di sini"
)
Spacer(modifier = Modifier.height(16.dp))
Text(
"Belum Ada Catatan Berbintang",
style = MaterialTheme.typography.titleMedium,
color = Color(0xFF64748B),
fontWeight = FontWeight.SemiBold,
textAlign = TextAlign.Center
)
Spacer(modifier = Modifier.height(8.dp))
Text(
"Tandai catatan penting dengan bintang agar mudah ditemukan",
style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF94A3B8),
textAlign = TextAlign.Center
)
}
}
} else {
// Daftar catatan berbintang
LazyColumn(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(horizontal = 16.dp),
contentPadding = PaddingValues(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
item {
Spacer(modifier = Modifier.height(8.dp))
Text(
text = "${starredNotes.size} catatan berbintang",
style = MaterialTheme.typography.bodyMedium,
color = Color(0xFF64748B),
modifier = Modifier.padding(start = 4.dp, bottom = 4.dp)
)
}
items(starredNotes) { note ->
val category = categories.find { it.id == note.categoryId }
StarredNoteCard(
note = note,
onClick = { onNoteClick(note) }
categoryName = category?.name ?: "Unknown",
onClick = { onNoteClick(note) },
onUnpin = { onUnpin(note) }
)
}
item {
Spacer(modifier = Modifier.height(16.dp))
}
}
}
}
}
@ -2295,114 +2317,90 @@ fun StarredNotesScreen(
@Composable
fun StarredNoteCard(
note: Note,
onClick: () -> Unit
categoryName: String,
onClick: () -> Unit,
onUnpin: () -> Unit
) {
val dateFormat = remember { SimpleDateFormat("dd MMM yyyy · HH:mm", Locale("id", "ID")) }
Card(
modifier = Modifier
.fillMaxWidth()
.clickable(onClick = onClick),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
),
shape = RoundedCornerShape(16.dp),
elevation = CardDefaults.cardElevation(
defaultElevation = 2.dp,
pressedElevation = 4.dp
)
colors = CardDefaults.cardColors(
containerColor = Color(0xFF1E293B)
),
elevation = CardDefaults.cardElevation(defaultElevation = 2.dp)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(20.dp)
) {
// Header dengan judul dan ikon bintang
Column(modifier = Modifier.padding(16.dp)) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Top
) {
Text(
text = note.title,
style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
modifier = Modifier.weight(1f)
)
Spacer(modifier = Modifier.width(12.dp))
Box(
modifier = Modifier
.size(32.dp)
.background(
color = Color(0xFFFBBF24).copy(alpha = 0.15f),
shape = CircleShape
),
contentAlignment = Alignment.Center
Column(modifier = Modifier.weight(1f)) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Default.Star,
contentDescription = "Berbintang",
tint = Color(0xFFFBBF24),
modifier = Modifier.size(18.dp)
)
}
}
// Konten catatan
if (note.content.isNotBlank()) {
Spacer(modifier = Modifier.height(12.dp))
Text(
text = note.content,
style = MaterialTheme.typography.bodyLarge,
color = Color(0xFF64748B),
maxLines = 4,
overflow = TextOverflow.Ellipsis,
lineHeight = 24.sp
)
}
// Footer dengan timestamp
Spacer(modifier = Modifier.height(16.dp))
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
Row(
verticalAlignment = Alignment.CenterVertically
) {
Icon(
Icons.Default.Schedule,
contentDescription = null,
tint = Color(0xFF94A3B8),
modifier = Modifier.size(14.dp)
tint = Color(0xFFFBBF24),
modifier = Modifier.size(16.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
note.title,
fontWeight = FontWeight.Bold,
color = Color.White,
style = MaterialTheme.typography.titleMedium
)
}
Spacer(modifier = Modifier.height(4.dp))
Text(
categoryName,
color = Color(0xFF64748B),
style = MaterialTheme.typography.bodySmall
)
}
}
if (note.content.isNotEmpty()) {
Spacer(modifier = Modifier.height(8.dp))
Text(
note.content,
maxLines = 2,
overflow = TextOverflow.Ellipsis,
color = Color(0xFF94A3B8),
style = MaterialTheme.typography.bodyMedium
)
}
Row(
modifier = Modifier
.fillMaxWidth()
.padding(top = 12.dp),
horizontalArrangement = Arrangement.End
) {
TextButton(onClick = onClick) {
Icon(
Icons.Default.Info,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = Color(0xFF6366F1)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = dateFormat.format(Date(note.timestamp)),
style = MaterialTheme.typography.bodySmall,
color = Color(0xFF94A3B8)
)
Text("Lihat Detail", color = Color(0xFF6366F1), fontWeight = FontWeight.Bold)
}
// Badge indicator
Surface(
color = Color(0xFFA855F7).copy(alpha = 0.15f),
shape = RoundedCornerShape(8.dp)
) {
Text(
text = "Penting",
style = MaterialTheme.typography.labelSmall,
color = Color(0xFFA855F7),
fontWeight = FontWeight.SemiBold,
modifier = Modifier.padding(horizontal = 8.dp, vertical = 4.dp)
Spacer(modifier = Modifier.width(8.dp))
TextButton(onClick = onUnpin) {
Icon(
Icons.Outlined.StarBorder,
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = Color(0xFFFBBF24)
)
Spacer(modifier = Modifier.width(4.dp))
Text("Hapus Bintang", color = Color(0xFFFBBF24), fontWeight = FontWeight.Bold)
}
}
}