Menambahkan Dark/Light theme toggle

This commit is contained in:
202310715082 FAZRI ABDURRAHMAN 2025-12-18 11:25:34 +07:00
parent 0876c82abc
commit 1b5e79166c
18 changed files with 522 additions and 300 deletions

View File

@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-12-13T07:41:36.634314200Z"> <DropdownSelection timestamp="2025-12-18T02:27:11.898714800Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="PhysicalDevice" identifier="serial=RR8T103A6JZ" /> <DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\Fazri Abdurrahman\.android\avd\Medium_Tablet.avd" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

View File

@ -39,11 +39,13 @@ import com.example.notesai.data.model.Note
import com.example.notesai.data.model.Category import com.example.notesai.data.model.Category
import com.example.notesai.util.updateWhere import com.example.notesai.util.updateWhere
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
import com.example.notesai.util.AppColors
// File: MainActivity.kt (Bagian Theme Setup) // File: MainActivity.kt (Bagian Theme Setup)
// Ganti MaterialTheme di setContent dengan ini: // Ganti MaterialTheme di setContent dengan ini:
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
import kotlinx.coroutines.launch
class MainActivity : ComponentActivity() { class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
@ -52,34 +54,34 @@ class MainActivity : ComponentActivity() {
MaterialTheme( MaterialTheme(
colorScheme = darkColorScheme( colorScheme = darkColorScheme(
// Primary colors // Primary colors
primary = Constants.AppColors.Primary, primary = AppColors.Primary,
onPrimary = Color.White, onPrimary = Color.White,
primaryContainer = Constants.AppColors.PrimaryContainer, primaryContainer = AppColors.PrimaryContainer,
onPrimaryContainer = Color.White, onPrimaryContainer = Color.White,
// Secondary colors // Secondary colors
secondary = Constants.AppColors.Secondary, secondary = AppColors.Secondary,
onSecondary = Color.White, onSecondary = Color.White,
secondaryContainer = Constants.AppColors.SecondaryVariant, secondaryContainer = AppColors.SecondaryVariant,
onSecondaryContainer = Color.White, onSecondaryContainer = Color.White,
// Background colors // Background colors
background = Constants.AppColors.Background, background = AppColors.Background,
onBackground = Constants.AppColors.OnBackground, onBackground = AppColors.OnBackground,
// Surface colors // Surface colors
surface = Constants.AppColors.Surface, surface = AppColors.Surface,
onSurface = Constants.AppColors.OnSurface, onSurface = AppColors.OnSurface,
surfaceVariant = Constants.AppColors.SurfaceVariant, surfaceVariant = AppColors.SurfaceVariant,
onSurfaceVariant = Constants.AppColors.OnSurfaceVariant, onSurfaceVariant = AppColors.OnSurfaceVariant,
// Error colors // Error colors
error = Constants.AppColors.Error, error = AppColors.Error,
onError = Color.White, onError = Color.White,
// Other // Other
outline = Constants.AppColors.Border, outline = AppColors.Border,
outlineVariant = Constants.AppColors.Divider outlineVariant = AppColors.Divider
), ),
typography = Typography( typography = Typography(
// Improve typography for better readability // Improve typography for better readability
@ -131,6 +133,17 @@ fun NotesApp() {
var showFullScreenNote by remember { mutableStateOf(false) } var showFullScreenNote by remember { mutableStateOf(false) }
var fullScreenNote by remember { mutableStateOf<Note?>(null) } var fullScreenNote by remember { mutableStateOf<Note?>(null) }
// Theme state
var isDarkTheme by remember { mutableStateOf(true) }
// Load theme preference - SET THEME HERE
LaunchedEffect(Unit) {
dataStoreManager.themeFlow.collect { theme ->
isDarkTheme = theme == "dark"
AppColors.setTheme(isDarkTheme) // SET GLOBAL THEME
}
}
// Load data dari DataStore // Load data dari DataStore
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
try { try {
@ -176,12 +189,10 @@ fun NotesApp() {
} }
} }
// Provide theme colors
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
Scaffold( Scaffold(
// Di MainActivity.kt, ubah bagian topBar menjadi:
topBar = { topBar = {
// Hide TopBar untuk AI Helper screen dan FullScreen Note
if (!showFullScreenNote && currentScreen != "ai") { if (!showFullScreenNote && currentScreen != "ai") {
ModernTopBar( ModernTopBar(
title = when(currentScreen) { title = when(currentScreen) {
@ -210,12 +221,7 @@ fun NotesApp() {
floatingActionButton = { floatingActionButton = {
AnimatedVisibility( AnimatedVisibility(
visible = currentScreen == "main" && !showFullScreenNote, visible = currentScreen == "main" && !showFullScreenNote,
enter = scaleIn( enter = scaleIn() + fadeIn(),
animationSpec = spring(
dampingRatio = Spring.DampingRatioMediumBouncy,
stiffness = Spring.StiffnessLow
)
) + fadeIn(),
exit = scaleOut() + fadeOut() exit = scaleOut() + fadeOut()
) { ) {
FloatingActionButton( FloatingActionButton(
@ -227,19 +233,10 @@ fun NotesApp() {
showCategoryDialog = true showCategoryDialog = true
} }
}, },
containerColor = Constants.AppColors.Primary, containerColor = AppColors.Primary,
contentColor = Color.White, contentColor = Color.White
elevation = FloatingActionButtonDefaults.elevation(
defaultElevation = 8.dp,
pressedElevation = 12.dp
),
modifier = Modifier.size(64.dp)
) { ) {
Icon( Icon(Icons.Default.Add, contentDescription = "Add")
Icons.Default.Add,
contentDescription = if (selectedCategory != null) "Tambah Catatan" else "Tambah Kategori",
modifier = Modifier.size(28.dp)
)
} }
} }
}, },
@ -254,7 +251,8 @@ fun NotesApp() {
onAIClick = { currentScreen = "ai" } onAIClick = { currentScreen = "ai" }
) )
} }
} },
containerColor = AppColors.Background // SET BACKGROUND HERE
) { padding -> ) { padding ->
Box(modifier = Modifier.fillMaxSize()) { Box(modifier = Modifier.fillMaxSize()) {
if (showFullScreenNote && fullScreenNote != null) { if (showFullScreenNote && fullScreenNote != null) {
@ -486,19 +484,15 @@ fun NotesApp() {
} }
} }
// Drawer with Animation - DI LUAR SCAFFOLD agar di atas semua // Drawer with theme toggle
AnimatedVisibility( AnimatedVisibility(
visible = drawerState, visible = drawerState,
enter = fadeIn() + slideInHorizontally( enter = fadeIn() + slideInHorizontally { -it },
initialOffsetX = { -it } exit = fadeOut() + slideOutHorizontally { -it }
),
exit = fadeOut() + slideOutHorizontally(
targetOffsetX = { -it }
),
modifier = Modifier.zIndex(100f) // Z-index tinggi
) { ) {
DrawerMenu( DrawerMenu(
currentScreen = currentScreen, currentScreen = currentScreen,
isDarkTheme = isDarkTheme,
onDismiss = { drawerState = false }, onDismiss = { drawerState = false },
onItemClick = { screen -> onItemClick = { screen ->
currentScreen = screen currentScreen = screen
@ -506,9 +500,15 @@ fun NotesApp() {
drawerState = false drawerState = false
showSearch = false showSearch = false
searchQuery = "" searchQuery = ""
},
onThemeToggle = {
isDarkTheme = !isDarkTheme
AppColors.setTheme(isDarkTheme) // UPDATE THEME
scope.launch {
dataStoreManager.saveTheme(if (isDarkTheme) "dark" else "light")
}
} }
) )
} }
} }
} }

View File

@ -50,7 +50,8 @@ class DataStoreManager(private val context: Context) {
companion object { companion object {
val CATEGORIES_KEY = stringPreferencesKey("categories") val CATEGORIES_KEY = stringPreferencesKey("categories")
val NOTES_KEY = stringPreferencesKey("notes") val NOTES_KEY = stringPreferencesKey("notes")
val CHAT_HISTORY_KEY = stringPreferencesKey("chat_history") // NEW val CHAT_HISTORY_KEY = stringPreferencesKey("chat_history")
val THEME_KEY = stringPreferencesKey("theme") // NEW: "dark" or "light"
} }
private val json = Json { private val json = Json {
@ -219,4 +220,28 @@ class DataStoreManager(private val context: Context) {
e.printStackTrace() e.printStackTrace()
} }
} }
// NEW: Theme Preference Flow
val themeFlow: Flow<String> = context.dataStore.data
.catch { exception ->
if (exception is IOException) {
emit(emptyPreferences())
} else {
throw exception
}
}
.map { preferences ->
preferences[THEME_KEY] ?: "dark" // Default dark theme
}
// NEW: Save Theme Preference
suspend fun saveTheme(theme: String) {
try {
context.dataStore.edit { preferences ->
preferences[THEME_KEY] = theme
}
} catch (e: Exception) {
e.printStackTrace()
}
}
} }

View File

@ -1,8 +1,11 @@
package com.example.notesai.data.model package com.example.notesai.data.model
import android.annotation.SuppressLint
//noinspection UnsafeOptInUsageError
import kotlinx.serialization.Serializable import kotlinx.serialization.Serializable
import java.util.UUID import java.util.UUID
@SuppressLint("UnsafeOptInUsageError")
@Serializable @Serializable
data class Note( data class Note(
val id: String = UUID.randomUUID().toString(), val id: String = UUID.randomUUID().toString(),

View File

@ -26,6 +26,7 @@ import androidx.compose.ui.unit.sp
import com.example.notesai.data.model.ChatHistory import com.example.notesai.data.model.ChatHistory
import com.example.notesai.data.model.Category import com.example.notesai.data.model.Category
import com.example.notesai.data.model.Note import com.example.notesai.data.model.Note
import com.example.notesai.util.AppColors
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -56,7 +57,7 @@ fun ChatHistoryDrawer(
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(Constants.AppColors.Overlay) .background(AppColors.Overlay)
.clickable( .clickable(
onClick = onDismiss, onClick = onDismiss,
indication = null, indication = null,
@ -74,7 +75,7 @@ fun ChatHistoryDrawer(
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
), ),
color = Constants.AppColors.Surface, color = AppColors.Surface,
shadowElevation = Constants.Elevation.ExtraLarge.dp shadowElevation = Constants.Elevation.ExtraLarge.dp
) { ) {
Column( Column(
@ -87,7 +88,7 @@ fun ChatHistoryDrawer(
.background( .background(
brush = Brush.verticalGradient( brush = Brush.verticalGradient(
colors = listOf( colors = listOf(
Constants.AppColors.Primary.copy(alpha = 0.15f), AppColors.Primary.copy(alpha = 0.15f),
Color.Transparent Color.Transparent
) )
) )
@ -103,7 +104,7 @@ fun ChatHistoryDrawer(
modifier = Modifier modifier = Modifier
.size(48.dp) .size(48.dp)
.background( .background(
color = Constants.AppColors.Primary.copy(alpha = 0.2f), color = AppColors.Primary.copy(alpha = 0.2f),
shape = RoundedCornerShape(Constants.Radius.Medium.dp) shape = RoundedCornerShape(Constants.Radius.Medium.dp)
), ),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
@ -111,7 +112,7 @@ fun ChatHistoryDrawer(
Icon( Icon(
Icons.Default.History, Icons.Default.History,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(24.dp) modifier = Modifier.size(24.dp)
) )
} }
@ -119,7 +120,7 @@ fun ChatHistoryDrawer(
Text( Text(
"Riwayat Chat", "Riwayat Chat",
style = MaterialTheme.typography.headlineSmall, style = MaterialTheme.typography.headlineSmall,
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
} }
@ -129,7 +130,7 @@ fun ChatHistoryDrawer(
Text( Text(
"${chatHistories.size} percakapan tersimpan", "${chatHistories.size} percakapan tersimpan",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = Constants.AppColors.OnSurfaceVariant color = AppColors.OnSurfaceVariant
) )
} }
} }
@ -145,7 +146,7 @@ fun ChatHistoryDrawer(
Text( Text(
"Filter Kategori", "Filter Kategori",
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
color = Constants.AppColors.OnSurfaceTertiary, color = AppColors.OnSurfaceTertiary,
fontSize = 12.sp fontSize = 12.sp
) )
@ -156,7 +157,7 @@ fun ChatHistoryDrawer(
onClick = { showCategoryDropdown = !showCategoryDropdown }, onClick = { showCategoryDropdown = !showCategoryDropdown },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = Constants.AppColors.SurfaceVariant containerColor = AppColors.SurfaceVariant
), ),
shape = RoundedCornerShape(12.dp) shape = RoundedCornerShape(12.dp)
) { ) {
@ -174,12 +175,12 @@ fun ChatHistoryDrawer(
Icon( Icon(
Icons.Default.Folder, Icons.Default.Folder,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(18.dp) modifier = Modifier.size(18.dp)
) )
Text( Text(
selectedCategory?.name ?: "Semua Kategori", selectedCategory?.name ?: "Semua Kategori",
color = Constants.AppColors.OnSurface, color = AppColors.OnSurface,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
fontSize = 14.sp fontSize = 14.sp
@ -188,7 +189,7 @@ fun ChatHistoryDrawer(
Icon( Icon(
Icons.Default.ArrowDropDown, Icons.Default.ArrowDropDown,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.OnSurfaceVariant tint = AppColors.OnSurfaceVariant
) )
} }
} }
@ -198,7 +199,7 @@ fun ChatHistoryDrawer(
onDismissRequest = { showCategoryDropdown = false }, onDismissRequest = { showCategoryDropdown = false },
modifier = Modifier modifier = Modifier
.width(280.dp) .width(280.dp)
.background(Constants.AppColors.SurfaceElevated) .background(AppColors.SurfaceElevated)
) { ) {
DropdownMenuItem( DropdownMenuItem(
text = { text = {
@ -209,12 +210,12 @@ fun ChatHistoryDrawer(
Icon( Icon(
Icons.Default.Folder, Icons.Default.Folder,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.OnSurfaceVariant, tint = AppColors.OnSurfaceVariant,
modifier = Modifier.size(18.dp) modifier = Modifier.size(18.dp)
) )
Text( Text(
"Semua Kategori", "Semua Kategori",
color = Constants.AppColors.OnSurface color = AppColors.OnSurface
) )
} }
}, },
@ -228,7 +229,7 @@ fun ChatHistoryDrawer(
Icon( Icon(
Icons.Default.Check, Icons.Default.Check,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(20.dp) modifier = Modifier.size(20.dp)
) )
} }
@ -236,7 +237,7 @@ fun ChatHistoryDrawer(
) )
if (categoriesWithNotes.isNotEmpty()) { if (categoriesWithNotes.isNotEmpty()) {
HorizontalDivider(color = Constants.AppColors.Divider) HorizontalDivider(color = AppColors.Divider)
} }
categoriesWithNotes.forEach { category -> categoriesWithNotes.forEach { category ->
@ -263,12 +264,12 @@ fun ChatHistoryDrawer(
) )
Text( Text(
category.name, category.name,
color = Constants.AppColors.OnSurface color = AppColors.OnSurface
) )
} }
Surface( Surface(
color = Constants.AppColors.Primary.copy(alpha = 0.15f), color = AppColors.Primary.copy(alpha = 0.15f),
shape = RoundedCornerShape(Constants.Radius.Small.dp) shape = RoundedCornerShape(Constants.Radius.Small.dp)
) { ) {
Text( Text(
@ -277,7 +278,7 @@ fun ChatHistoryDrawer(
horizontal = 8.dp, horizontal = 8.dp,
vertical = 2.dp vertical = 2.dp
), ),
color = Constants.AppColors.Primary, color = AppColors.Primary,
fontSize = 11.sp, fontSize = 11.sp,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
@ -294,7 +295,7 @@ fun ChatHistoryDrawer(
Icon( Icon(
Icons.Default.Check, Icons.Default.Check,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(20.dp) modifier = Modifier.size(20.dp)
) )
} }
@ -308,7 +309,7 @@ fun ChatHistoryDrawer(
Spacer(modifier = Modifier.height(Constants.Spacing.Medium.dp)) Spacer(modifier = Modifier.height(Constants.Spacing.Medium.dp))
HorizontalDivider( HorizontalDivider(
color = Constants.AppColors.Divider, color = AppColors.Divider,
modifier = Modifier.padding(horizontal = Constants.Spacing.Large.dp) modifier = Modifier.padding(horizontal = Constants.Spacing.Large.dp)
) )
@ -335,18 +336,18 @@ fun ChatHistoryDrawer(
Icons.Default.ChatBubbleOutline, Icons.Default.ChatBubbleOutline,
contentDescription = null, contentDescription = null,
modifier = Modifier.size(48.dp), modifier = Modifier.size(48.dp),
tint = Constants.AppColors.OnSurfaceVariant tint = AppColors.OnSurfaceVariant
) )
Text( Text(
"Belum ada riwayat chat", "Belum ada riwayat chat",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Constants.AppColors.OnSurfaceVariant, color = AppColors.OnSurfaceVariant,
fontWeight = FontWeight.Medium fontWeight = FontWeight.Medium
) )
Text( Text(
"Mulai chat dengan AI", "Mulai chat dengan AI",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = Constants.AppColors.OnSurfaceTertiary color = AppColors.OnSurfaceTertiary
) )
} }
} }
@ -368,19 +369,19 @@ fun ChatHistoryDrawer(
Icon( Icon(
Icons.Default.Folder, Icons.Default.Folder,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
Text( Text(
categoryInfo.second, categoryInfo.second,
style = MaterialTheme.typography.labelLarge, style = MaterialTheme.typography.labelLarge,
color = Constants.AppColors.OnSurfaceVariant, color = AppColors.OnSurfaceVariant,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
fontSize = 13.sp fontSize = 13.sp
) )
HorizontalDivider( HorizontalDivider(
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
color = Constants.AppColors.Divider color = AppColors.Divider
) )
} }
} }
@ -413,7 +414,7 @@ private fun ChatHistoryItem(
onClick = onClick, onClick = onClick,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = Constants.AppColors.SurfaceVariant containerColor = AppColors.SurfaceVariant
), ),
shape = RoundedCornerShape(Constants.Radius.Medium.dp) shape = RoundedCornerShape(Constants.Radius.Medium.dp)
) { ) {
@ -429,7 +430,7 @@ private fun ChatHistoryItem(
) { ) {
// Message Count // Message Count
Surface( Surface(
color = Constants.AppColors.Primary.copy(alpha = 0.15f), color = AppColors.Primary.copy(alpha = 0.15f),
shape = RoundedCornerShape(Constants.Radius.Small.dp) shape = RoundedCornerShape(Constants.Radius.Small.dp)
) { ) {
Row( Row(
@ -443,12 +444,12 @@ private fun ChatHistoryItem(
Icon( Icon(
Icons.Default.Chat, Icons.Default.Chat,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(12.dp) modifier = Modifier.size(12.dp)
) )
Text( Text(
"${history.messages.size}", "${history.messages.size}",
color = Constants.AppColors.Primary, color = AppColors.Primary,
fontSize = 11.sp, fontSize = 11.sp,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
@ -463,7 +464,7 @@ private fun ChatHistoryItem(
Icon( Icon(
Icons.Default.Delete, Icons.Default.Delete,
contentDescription = "Delete", contentDescription = "Delete",
tint = Constants.AppColors.Error, tint = AppColors.Error,
modifier = Modifier.size(18.dp) modifier = Modifier.size(18.dp)
) )
} }
@ -475,7 +476,7 @@ private fun ChatHistoryItem(
Text( Text(
history.lastMessagePreview, history.lastMessagePreview,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Constants.AppColors.OnSurface, color = AppColors.OnSurface,
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
fontSize = 14.sp fontSize = 14.sp
@ -487,7 +488,7 @@ private fun ChatHistoryItem(
Text( Text(
dateFormat.format(Date(history.timestamp)), dateFormat.format(Date(history.timestamp)),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = Constants.AppColors.OnSurfaceTertiary, color = AppColors.OnSurfaceTertiary,
fontSize = 12.sp fontSize = 12.sp
) )
} }
@ -501,7 +502,7 @@ private fun ChatHistoryItem(
Icon( Icon(
Icons.Default.DeleteForever, Icons.Default.DeleteForever,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Error tint = AppColors.Error
) )
}, },
title = { title = {
@ -520,7 +521,7 @@ private fun ChatHistoryItem(
showDeleteConfirm = false showDeleteConfirm = false
}, },
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
containerColor = Constants.AppColors.Error containerColor = AppColors.Error
) )
) { ) {
Text("Hapus") Text("Hapus")
@ -531,7 +532,7 @@ private fun ChatHistoryItem(
Text("Batal") Text("Batal")
} }
}, },
containerColor = Constants.AppColors.SurfaceElevated containerColor = AppColors.SurfaceElevated
) )
} }
} }

View File

@ -24,18 +24,22 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
import com.example.notesai.util.AppColors
@Composable @Composable
fun DrawerMenu( fun DrawerMenu(
currentScreen: String, currentScreen: String,
isDarkTheme: Boolean,
onDismiss: () -> Unit, onDismiss: () -> Unit,
onItemClick: (String) -> Unit onItemClick: (String) -> Unit,
onThemeToggle: () -> Unit
) { ) {
// Backdrop with blur effect // Backdrop with blur effect
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(Constants.AppColors.Overlay) .background(AppColors.Overlay)
.clickable( .clickable(
onClick = onDismiss, onClick = onDismiss,
indication = null, indication = null,
@ -53,7 +57,7 @@ fun DrawerMenu(
indication = null, indication = null,
interactionSource = remember { MutableInteractionSource() } interactionSource = remember { MutableInteractionSource() }
), ),
color = Constants.AppColors.Surface, color = AppColors.Surface,
shadowElevation = Constants.Elevation.ExtraLarge.dp shadowElevation = Constants.Elevation.ExtraLarge.dp
) { ) {
Column( Column(
@ -66,7 +70,7 @@ fun DrawerMenu(
.background( .background(
brush = Brush.verticalGradient( brush = Brush.verticalGradient(
colors = listOf( colors = listOf(
Constants.AppColors.Primary.copy(alpha = 0.15f), AppColors.Primary.copy(alpha = 0.15f),
Color.Transparent Color.Transparent
) )
) )
@ -79,7 +83,7 @@ fun DrawerMenu(
modifier = Modifier modifier = Modifier
.size(56.dp) .size(56.dp)
.background( .background(
color = Constants.AppColors.Primary.copy(alpha = 0.2f), color = AppColors.Primary.copy(alpha = 0.2f),
shape = RoundedCornerShape(Constants.Radius.Medium.dp) shape = RoundedCornerShape(Constants.Radius.Medium.dp)
), ),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
@ -87,7 +91,7 @@ fun DrawerMenu(
Icon( Icon(
Icons.Default.Create, Icons.Default.Create,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(32.dp) modifier = Modifier.size(32.dp)
) )
} }
@ -97,7 +101,7 @@ fun DrawerMenu(
Text( Text(
Constants.APP_NAME, Constants.APP_NAME,
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineMedium,
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 24.sp fontSize = 24.sp
) )
@ -107,7 +111,7 @@ fun DrawerMenu(
Text( Text(
"Smart Note Taking", "Smart Note Taking",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Constants.AppColors.OnSurfaceVariant, color = AppColors.OnSurfaceVariant,
fontSize = 14.sp fontSize = 14.sp
) )
} }
@ -144,11 +148,26 @@ fun DrawerMenu(
onClick = { onItemClick("trash") } onClick = { onItemClick("trash") }
) )
Spacer(modifier = Modifier.height(Constants.Spacing.Medium.dp))
HorizontalDivider(
color = AppColors.Divider,
modifier = Modifier.padding(horizontal = Constants.Spacing.Medium.dp)
)
Spacer(modifier = Modifier.height(Constants.Spacing.Medium.dp))
// Theme Toggle
ThemeToggleItem(
isDarkTheme = isDarkTheme,
onToggle = onThemeToggle
)
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
// Footer - Version info // Footer - Version info
HorizontalDivider( HorizontalDivider(
color = Constants.AppColors.Divider, color = AppColors.Divider,
modifier = Modifier.padding(horizontal = Constants.Spacing.Medium.dp) modifier = Modifier.padding(horizontal = Constants.Spacing.Medium.dp)
) )
@ -162,13 +181,13 @@ fun DrawerMenu(
Text( Text(
"Version ${Constants.APP_VERSION}", "Version ${Constants.APP_VERSION}",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = Constants.AppColors.OnSurfaceTertiary, color = AppColors.OnSurfaceTertiary,
fontSize = 12.sp fontSize = 12.sp
) )
// Powered by badge // Powered by badge
Surface( Surface(
color = Constants.AppColors.Primary.copy(alpha = 0.1f), color = AppColors.Primary.copy(alpha = 0.1f),
shape = RoundedCornerShape(Constants.Radius.Small.dp) shape = RoundedCornerShape(Constants.Radius.Small.dp)
) { ) {
Row( Row(
@ -179,12 +198,12 @@ fun DrawerMenu(
Icon( Icon(
Icons.Default.AutoAwesome, Icons.Default.AutoAwesome,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(12.dp) modifier = Modifier.size(12.dp)
) )
Text( Text(
"AI", "AI",
color = Constants.AppColors.Primary, color = AppColors.Primary,
fontSize = 11.sp, fontSize = 11.sp,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
@ -196,6 +215,76 @@ fun DrawerMenu(
} }
} }
@Composable
private fun ThemeToggleItem(
isDarkTheme: Boolean,
onToggle: () -> Unit
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = Constants.Spacing.Medium.dp)
.clip(RoundedCornerShape(Constants.Radius.Medium.dp))
.clickable(onClick = onToggle)
.background(AppColors.SurfaceVariant)
.padding(horizontal = Constants.Spacing.Medium.dp, vertical = Constants.Spacing.Medium.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(Constants.Spacing.Medium.dp)
) {
// Icon with background
Box(
modifier = Modifier
.size(40.dp)
.background(
color = AppColors.Primary.copy(alpha = 0.2f),
shape = CircleShape
),
contentAlignment = Alignment.Center
) {
Icon(
if (isDarkTheme) Icons.Default.DarkMode else Icons.Default.LightMode,
contentDescription = null,
tint = AppColors.Primary,
modifier = Modifier.size(20.dp)
)
}
// Text
Column {
Text(
"Tema Aplikasi",
style = MaterialTheme.typography.bodyLarge,
color = AppColors.OnSurface,
fontWeight = FontWeight.SemiBold,
fontSize = 15.sp
)
Text(
if (isDarkTheme) "Mode Gelap" else "Mode Terang",
style = MaterialTheme.typography.bodySmall,
color = AppColors.OnSurfaceTertiary,
fontSize = 12.sp
)
}
}
// Toggle Switch
Switch(
checked = isDarkTheme,
onCheckedChange = { onToggle() },
colors = SwitchDefaults.colors(
checkedThumbColor = Color.White,
checkedTrackColor = AppColors.Primary,
uncheckedThumbColor = Color.White,
uncheckedTrackColor = AppColors.Border
)
)
}
}
@Composable @Composable
private fun DrawerMenuItem( private fun DrawerMenuItem(
icon: ImageVector, icon: ImageVector,
@ -221,7 +310,7 @@ private fun DrawerMenuItem(
.clickable(onClick = onClick) .clickable(onClick = onClick)
.background( .background(
color = if (isSelected) color = if (isSelected)
Constants.AppColors.Primary.copy(alpha = 0.1f) AppColors.Primary.copy(alpha = 0.1f)
else else
Color.Transparent Color.Transparent
) )
@ -234,9 +323,9 @@ private fun DrawerMenuItem(
.size(40.dp) .size(40.dp)
.background( .background(
color = if (isSelected) color = if (isSelected)
Constants.AppColors.Primary.copy(alpha = 0.2f) AppColors.Primary.copy(alpha = 0.2f)
else else
Constants.AppColors.SurfaceVariant, AppColors.SurfaceVariant,
shape = CircleShape shape = CircleShape
), ),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
@ -244,7 +333,7 @@ private fun DrawerMenuItem(
Icon( Icon(
icon, icon,
contentDescription = null, contentDescription = null,
tint = if (isSelected) Constants.AppColors.Primary else Constants.AppColors.OnSurfaceVariant, tint = if (isSelected) AppColors.Primary else AppColors.OnSurfaceVariant,
modifier = Modifier.size(20.dp) modifier = Modifier.size(20.dp)
) )
} }
@ -255,7 +344,7 @@ private fun DrawerMenuItem(
Text( Text(
text, text,
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
color = if (isSelected) Constants.AppColors.Primary else Constants.AppColors.OnSurface, color = if (isSelected) AppColors.Primary else AppColors.OnSurface,
fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Normal, fontWeight = if (isSelected) FontWeight.SemiBold else FontWeight.Normal,
fontSize = 15.sp fontSize = 15.sp
) )

View File

@ -26,6 +26,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.notesai.util.AppColors
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
@Composable @Composable
@ -47,7 +48,7 @@ fun ModernBottomBar(
elevation = Constants.Elevation.Large.dp, elevation = Constants.Elevation.Large.dp,
shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp) shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp)
), ),
color = Constants.AppColors.SurfaceElevated, color = AppColors.SurfaceElevated,
shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp) shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp)
) { ) {
Row( Row(
@ -96,7 +97,7 @@ private fun BottomBarItem(
// Color animation // Color animation
val iconColor by animateColorAsState( val iconColor by animateColorAsState(
targetValue = if (selected) Constants.AppColors.Primary else Constants.AppColors.OnSurfaceVariant, targetValue = if (selected) AppColors.Primary else AppColors.OnSurfaceVariant,
animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM), animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM),
label = "color" label = "color"
) )
@ -125,7 +126,7 @@ private fun BottomBarItem(
modifier = Modifier modifier = Modifier
.size(40.dp) .size(40.dp)
.background( .background(
color = Constants.AppColors.Primary.copy(alpha = 0.15f), color = AppColors.Primary.copy(alpha = 0.15f),
shape = CircleShape shape = CircleShape
) )
) )
@ -148,7 +149,7 @@ private fun BottomBarItem(
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
Text( Text(
label, label,
color = Constants.AppColors.Primary, color = AppColors.Primary,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 12.sp fontSize = 12.sp

View File

@ -19,6 +19,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.notesai.util.AppColors
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@ -48,7 +49,7 @@ fun ModernTopBar(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.shadow(Constants.Elevation.Small.dp), .shadow(Constants.Elevation.Small.dp),
color = Constants.AppColors.Surface color = AppColors.Surface
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@ -63,16 +64,16 @@ fun ModernTopBar(
placeholder = { placeholder = {
Text( Text(
"Cari catatan atau kategori...", "Cari catatan atau kategori...",
color = Constants.AppColors.OnSurfaceVariant, color = AppColors.OnSurfaceVariant,
fontSize = 15.sp fontSize = 15.sp
) )
}, },
colors = TextFieldDefaults.colors( colors = TextFieldDefaults.colors(
focusedContainerColor = Constants.AppColors.SurfaceVariant, focusedContainerColor = AppColors.SurfaceVariant,
unfocusedContainerColor = Constants.AppColors.SurfaceVariant, unfocusedContainerColor = AppColors.SurfaceVariant,
focusedTextColor = Constants.AppColors.OnBackground, focusedTextColor = AppColors.OnBackground,
unfocusedTextColor = Constants.AppColors.OnSurface, unfocusedTextColor = AppColors.OnSurface,
cursorColor = Constants.AppColors.Primary, cursorColor = AppColors.Primary,
focusedIndicatorColor = Color.Transparent, focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent unfocusedIndicatorColor = Color.Transparent
), ),
@ -96,7 +97,7 @@ fun ModernTopBar(
Icon( Icon(
Icons.Default.Close, Icons.Default.Close,
contentDescription = "Close Search", contentDescription = "Close Search",
tint = Constants.AppColors.OnSurfaceVariant tint = AppColors.OnSurfaceVariant
) )
} }
} }
@ -107,7 +108,7 @@ fun ModernTopBar(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.shadow(Constants.Elevation.Small.dp), .shadow(Constants.Elevation.Small.dp),
color = Constants.AppColors.Surface color = AppColors.Surface
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
@ -120,7 +121,7 @@ fun ModernTopBar(
Icon( Icon(
if (showBackButton) Icons.AutoMirrored.Filled.ArrowBack else Icons.Default.Menu, if (showBackButton) Icons.AutoMirrored.Filled.ArrowBack else Icons.Default.Menu,
contentDescription = if (showBackButton) "Back" else "Menu", contentDescription = if (showBackButton) "Back" else "Menu",
tint = Constants.AppColors.OnSurface tint = AppColors.OnSurface
) )
} }
@ -129,7 +130,7 @@ fun ModernTopBar(
title, title,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 20.sp, fontSize = 20.sp,
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
@ -138,7 +139,7 @@ fun ModernTopBar(
Icon( Icon(
Icons.Default.Search, Icons.Default.Search,
contentDescription = "Search", contentDescription = "Search",
tint = Constants.AppColors.OnSurfaceVariant tint = AppColors.OnSurfaceVariant
) )
} }
} }

View File

@ -19,6 +19,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.notesai.util.AppColors
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
@Composable @Composable
@ -31,12 +32,12 @@ fun CategoryDialog(
AlertDialog( AlertDialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
containerColor = Constants.AppColors.Surface, containerColor = AppColors.Surface,
shape = RoundedCornerShape(20.dp), shape = RoundedCornerShape(20.dp),
title = { title = {
Text( Text(
"Buat Kategori Baru", "Buat Kategori Baru",
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 20.sp fontSize = 20.sp
) )
@ -52,24 +53,24 @@ fun CategoryDialog(
label = { label = {
Text( Text(
"Nama Kategori", "Nama Kategori",
color = Constants.AppColors.OnSurfaceVariant color = AppColors.OnSurfaceVariant
) )
}, },
placeholder = { placeholder = {
Text( Text(
"Contoh: Pekerjaan, Personal", "Contoh: Pekerjaan, Personal",
color = Constants.AppColors.OnSurfaceTertiary, color = AppColors.OnSurfaceTertiary,
fontSize = 14.sp fontSize = 14.sp
) )
}, },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = OutlinedTextFieldDefaults.colors( colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Constants.AppColors.OnBackground, focusedTextColor = AppColors.OnBackground,
unfocusedTextColor = Constants.AppColors.OnSurface, unfocusedTextColor = AppColors.OnSurface,
focusedContainerColor = Constants.AppColors.SurfaceVariant, focusedContainerColor = AppColors.SurfaceVariant,
unfocusedContainerColor = Constants.AppColors.SurfaceVariant, unfocusedContainerColor = AppColors.SurfaceVariant,
cursorColor = Constants.AppColors.Primary, cursorColor = AppColors.Primary,
focusedBorderColor = Constants.AppColors.Primary, focusedBorderColor = AppColors.Primary,
unfocusedBorderColor = Color.Transparent unfocusedBorderColor = Color.Transparent
), ),
shape = RoundedCornerShape(12.dp), shape = RoundedCornerShape(12.dp),
@ -83,18 +84,18 @@ fun CategoryDialog(
Text( Text(
"Pilih Warna:", "Pilih Warna:",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Constants.AppColors.OnSurface, color = AppColors.OnSurface,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
fontSize = 14.sp fontSize = 14.sp
) )
Constants.AppColors.CategoryColors.chunked(4).forEach { row -> Constants.CategoryColors.chunked(4).forEach { row ->
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
row.forEach { gradient -> row.forEach { gradient ->
val globalIndex = Constants.AppColors.CategoryColors.indexOf(gradient) val globalIndex = Constants.CategoryColors.indexOf(gradient)
val isSelected = selectedGradient == globalIndex val isSelected = selectedGradient == globalIndex
// Scale animation // Scale animation
@ -120,11 +121,7 @@ fun CategoryDialog(
) )
) )
) )
.clickable { selectedGradient = globalIndex } .clickable { selectedGradient = globalIndex },
.then(
if (isSelected) Modifier
else Modifier
),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
// Check icon dengan animation // Check icon dengan animation
@ -158,14 +155,14 @@ fun CategoryDialog(
Button( Button(
onClick = { onClick = {
if (name.isNotBlank()) { if (name.isNotBlank()) {
val gradient = Constants.AppColors.CategoryColors[selectedGradient] val gradient = Constants.CategoryColors[selectedGradient]
onSave(name, gradient.first, gradient.second) onSave(name, gradient.first, gradient.second)
} }
}, },
enabled = name.isNotBlank(), enabled = name.isNotBlank(),
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
containerColor = Constants.AppColors.Primary, containerColor = AppColors.Primary,
disabledContainerColor = Constants.AppColors.Primary.copy(alpha = 0.5f) disabledContainerColor = AppColors.Primary.copy(alpha = 0.5f)
), ),
shape = RoundedCornerShape(12.dp), shape = RoundedCornerShape(12.dp),
modifier = Modifier.height(48.dp) modifier = Modifier.height(48.dp)
@ -186,7 +183,7 @@ fun CategoryDialog(
) { ) {
Text( Text(
"Batal", "Batal",
color = Constants.AppColors.OnSurfaceVariant, color = AppColors.OnSurfaceVariant,
fontSize = 15.sp fontSize = 15.sp
) )
} }

View File

@ -12,6 +12,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.notesai.data.model.Note import com.example.notesai.data.model.Note
import com.example.notesai.util.AppColors
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
@Composable @Composable
@ -29,19 +30,19 @@ fun NoteDialog(
if (showDeleteConfirm) { if (showDeleteConfirm) {
AlertDialog( AlertDialog(
onDismissRequest = { showDeleteConfirm = false }, onDismissRequest = { showDeleteConfirm = false },
containerColor = Constants.AppColors.Surface, containerColor = AppColors.Surface,
shape = RoundedCornerShape(20.dp), shape = RoundedCornerShape(20.dp),
title = { title = {
Text( Text(
"Hapus Catatan?", "Hapus Catatan?",
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
}, },
text = { text = {
Text( Text(
"Catatan ini akan dipindahkan ke sampah.", "Catatan ini akan dipindahkan ke sampah.",
color = Constants.AppColors.OnSurfaceVariant color = AppColors.OnSurfaceVariant
) )
}, },
confirmButton = { confirmButton = {
@ -51,7 +52,7 @@ fun NoteDialog(
showDeleteConfirm = false showDeleteConfirm = false
}, },
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
containerColor = Constants.AppColors.Error containerColor = AppColors.Error
), ),
shape = RoundedCornerShape(12.dp) shape = RoundedCornerShape(12.dp)
) { ) {
@ -63,7 +64,7 @@ fun NoteDialog(
onClick = { showDeleteConfirm = false }, onClick = { showDeleteConfirm = false },
shape = RoundedCornerShape(12.dp) shape = RoundedCornerShape(12.dp)
) { ) {
Text("Batal", color = Constants.AppColors.OnSurfaceVariant) Text("Batal", color = AppColors.OnSurfaceVariant)
} }
} }
) )
@ -71,12 +72,12 @@ fun NoteDialog(
AlertDialog( AlertDialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
containerColor = Constants.AppColors.Surface, containerColor = AppColors.Surface,
shape = RoundedCornerShape(20.dp), shape = RoundedCornerShape(20.dp),
title = { title = {
Text( Text(
if (note == null) "Catatan Baru" else "Edit Catatan", if (note == null) "Catatan Baru" else "Edit Catatan",
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 20.sp fontSize = 20.sp
) )
@ -92,24 +93,24 @@ fun NoteDialog(
label = { label = {
Text( Text(
"Judul", "Judul",
color = Constants.AppColors.OnSurfaceVariant color = AppColors.OnSurfaceVariant
) )
}, },
placeholder = { placeholder = {
Text( Text(
"Masukkan judul catatan", "Masukkan judul catatan",
color = Constants.AppColors.OnSurfaceTertiary, color = AppColors.OnSurfaceTertiary,
fontSize = 14.sp fontSize = 14.sp
) )
}, },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = OutlinedTextFieldDefaults.colors( colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Constants.AppColors.OnBackground, focusedTextColor = AppColors.OnBackground,
unfocusedTextColor = Constants.AppColors.OnSurface, unfocusedTextColor = AppColors.OnSurface,
focusedContainerColor = Constants.AppColors.SurfaceVariant, focusedContainerColor = AppColors.SurfaceVariant,
unfocusedContainerColor = Constants.AppColors.SurfaceVariant, unfocusedContainerColor = AppColors.SurfaceVariant,
cursorColor = Constants.AppColors.Primary, cursorColor = AppColors.Primary,
focusedBorderColor = Constants.AppColors.Primary, focusedBorderColor = AppColors.Primary,
unfocusedBorderColor = Color.Transparent unfocusedBorderColor = Color.Transparent
), ),
shape = RoundedCornerShape(12.dp), shape = RoundedCornerShape(12.dp),
@ -123,13 +124,13 @@ fun NoteDialog(
label = { label = {
Text( Text(
"Deskripsi", "Deskripsi",
color = Constants.AppColors.OnSurfaceVariant color = AppColors.OnSurfaceVariant
) )
}, },
placeholder = { placeholder = {
Text( Text(
"Tambahkan deskripsi singkat...", "Tambahkan deskripsi singkat...",
color = Constants.AppColors.OnSurfaceTertiary, color = AppColors.OnSurfaceTertiary,
fontSize = 14.sp fontSize = 14.sp
) )
}, },
@ -137,12 +138,12 @@ fun NoteDialog(
.fillMaxWidth() .fillMaxWidth()
.heightIn(min = 120.dp, max = 200.dp), .heightIn(min = 120.dp, max = 200.dp),
colors = OutlinedTextFieldDefaults.colors( colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Constants.AppColors.OnBackground, focusedTextColor = AppColors.OnBackground,
unfocusedTextColor = Constants.AppColors.OnSurface, unfocusedTextColor = AppColors.OnSurface,
focusedContainerColor = Constants.AppColors.SurfaceVariant, focusedContainerColor = AppColors.SurfaceVariant,
unfocusedContainerColor = Constants.AppColors.SurfaceVariant, unfocusedContainerColor = AppColors.SurfaceVariant,
cursorColor = Constants.AppColors.Primary, cursorColor = AppColors.Primary,
focusedBorderColor = Constants.AppColors.Primary, focusedBorderColor = AppColors.Primary,
unfocusedBorderColor = Color.Transparent unfocusedBorderColor = Color.Transparent
), ),
shape = RoundedCornerShape(12.dp), shape = RoundedCornerShape(12.dp),
@ -163,7 +164,7 @@ fun NoteDialog(
Icon( Icon(
Icons.Default.Delete, Icons.Default.Delete,
contentDescription = "Hapus", contentDescription = "Hapus",
tint = Constants.AppColors.Error tint = AppColors.Error
) )
} }
} }
@ -178,7 +179,7 @@ fun NoteDialog(
) { ) {
Text( Text(
"Batal", "Batal",
color = Constants.AppColors.OnSurfaceVariant, color = AppColors.OnSurfaceVariant,
fontSize = 15.sp fontSize = 15.sp
) )
} }
@ -192,8 +193,8 @@ fun NoteDialog(
}, },
enabled = title.isNotBlank(), enabled = title.isNotBlank(),
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
containerColor = Constants.AppColors.Primary, containerColor = AppColors.Primary,
disabledContainerColor = Constants.AppColors.Primary.copy(alpha = 0.5f) disabledContainerColor = AppColors.Primary.copy(alpha = 0.5f)
), ),
shape = RoundedCornerShape(12.dp), shape = RoundedCornerShape(12.dp),
modifier = Modifier.height(48.dp) modifier = Modifier.height(48.dp)

View File

@ -34,6 +34,7 @@ import com.example.notesai.presentation.screens.ai.components.ChatBubble
import com.example.notesai.presentation.screens.ai.components.ChatHistoryDrawer import com.example.notesai.presentation.screens.ai.components.ChatHistoryDrawer
import com.example.notesai.presentation.screens.ai.components.CompactStatItem import com.example.notesai.presentation.screens.ai.components.CompactStatItem
import com.example.notesai.presentation.screens.ai.components.SuggestionChip import com.example.notesai.presentation.screens.ai.components.SuggestionChip
import com.example.notesai.util.AppColors
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
@ -128,11 +129,11 @@ fun AIHelperScreen(
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(Constants.AppColors.Background) .background(AppColors.Background)
) { ) {
// Top Bar with History Button & Stats // Top Bar with History Button & Stats
Surface( Surface(
color = Constants.AppColors.Surface, color = AppColors.Surface,
shadowElevation = 2.dp shadowElevation = 2.dp
) { ) {
Column( Column(
@ -152,21 +153,21 @@ fun AIHelperScreen(
modifier = Modifier modifier = Modifier
.size(40.dp) .size(40.dp)
.background( .background(
Constants.AppColors.Primary.copy(alpha = 0.1f), AppColors.Primary.copy(alpha = 0.1f),
CircleShape CircleShape
) )
) { ) {
Icon( Icon(
Icons.Default.Menu, Icons.Default.Menu,
contentDescription = "Menu", contentDescription = "Menu",
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(20.dp) modifier = Modifier.size(20.dp)
) )
} }
// Category Badge // Category Badge
Surface( Surface(
color = Constants.AppColors.Primary.copy(alpha = 0.1f), color = AppColors.Primary.copy(alpha = 0.1f),
shape = RoundedCornerShape(Constants.Radius.Medium.dp) shape = RoundedCornerShape(Constants.Radius.Medium.dp)
) { ) {
Row( Row(
@ -180,12 +181,12 @@ fun AIHelperScreen(
Icon( Icon(
Icons.Default.Folder, Icons.Default.Folder,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
Text( Text(
selectedCategory?.name ?: "Semua Kategori", selectedCategory?.name ?: "Semua Kategori",
color = Constants.AppColors.Primary, color = AppColors.Primary,
fontSize = 13.sp, fontSize = 13.sp,
fontWeight = FontWeight.SemiBold fontWeight = FontWeight.SemiBold
) )
@ -197,7 +198,7 @@ fun AIHelperScreen(
Button( Button(
onClick = { startNewChat() }, onClick = { startNewChat() },
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
containerColor = Constants.AppColors.Primary containerColor = AppColors.Primary
), ),
shape = RoundedCornerShape(Constants.Radius.Medium.dp), shape = RoundedCornerShape(Constants.Radius.Medium.dp),
contentPadding = PaddingValues( contentPadding = PaddingValues(
@ -252,7 +253,7 @@ fun AIHelperScreen(
} }
} }
HorizontalDivider(color = Constants.AppColors.Divider) HorizontalDivider(color = AppColors.Divider)
// Chat Area // Chat Area
Box( Box(
@ -273,7 +274,7 @@ fun AIHelperScreen(
modifier = Modifier modifier = Modifier
.size(80.dp) .size(80.dp)
.background( .background(
color = Constants.AppColors.Primary.copy(alpha = 0.1f), color = AppColors.Primary.copy(alpha = 0.1f),
shape = CircleShape shape = CircleShape
), ),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
@ -282,7 +283,7 @@ fun AIHelperScreen(
Icons.Default.AutoAwesome, Icons.Default.AutoAwesome,
contentDescription = null, contentDescription = null,
modifier = Modifier.size(40.dp), modifier = Modifier.size(40.dp),
tint = Constants.AppColors.Primary tint = AppColors.Primary
) )
} }
@ -291,7 +292,7 @@ fun AIHelperScreen(
Text( Text(
"AI Assistant", "AI Assistant",
style = MaterialTheme.typography.headlineMedium, style = MaterialTheme.typography.headlineMedium,
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
@ -300,7 +301,7 @@ fun AIHelperScreen(
Text( Text(
"Tanyakan apa saja tentang catatan Anda", "Tanyakan apa saja tentang catatan Anda",
style = MaterialTheme.typography.bodyLarge, style = MaterialTheme.typography.bodyLarge,
color = Constants.AppColors.OnSurfaceVariant, color = AppColors.OnSurfaceVariant,
textAlign = TextAlign.Center textAlign = TextAlign.Center
) )
@ -315,7 +316,7 @@ fun AIHelperScreen(
Text( Text(
"Contoh pertanyaan:", "Contoh pertanyaan:",
style = MaterialTheme.typography.labelMedium, style = MaterialTheme.typography.labelMedium,
color = Constants.AppColors.OnSurfaceTertiary color = AppColors.OnSurfaceTertiary
) )
SuggestionChip("Analisis catatan saya") { prompt = it } SuggestionChip("Analisis catatan saya") { prompt = it }
SuggestionChip("Buat ringkasan") { prompt = it } SuggestionChip("Buat ringkasan") { prompt = it }
@ -356,7 +357,7 @@ fun AIHelperScreen(
horizontalArrangement = Arrangement.Start horizontalArrangement = Arrangement.Start
) { ) {
Surface( Surface(
color = Constants.AppColors.SurfaceVariant, color = AppColors.SurfaceVariant,
shape = RoundedCornerShape(16.dp) shape = RoundedCornerShape(16.dp)
) { ) {
Row( Row(
@ -366,12 +367,12 @@ fun AIHelperScreen(
) { ) {
CircularProgressIndicator( CircularProgressIndicator(
modifier = Modifier.size(20.dp), modifier = Modifier.size(20.dp),
color = Constants.AppColors.Primary, color = AppColors.Primary,
strokeWidth = 2.dp strokeWidth = 2.dp
) )
Text( Text(
"AI sedang berpikir...", "AI sedang berpikir...",
color = Constants.AppColors.OnSurfaceVariant, color = AppColors.OnSurfaceVariant,
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium
) )
} }
@ -383,7 +384,7 @@ fun AIHelperScreen(
if (errorMessage.isNotEmpty()) { if (errorMessage.isNotEmpty()) {
Surface( Surface(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
color = Constants.AppColors.Error.copy(alpha = 0.1f), color = AppColors.Error.copy(alpha = 0.1f),
shape = RoundedCornerShape(12.dp) shape = RoundedCornerShape(12.dp)
) { ) {
Row( Row(
@ -394,12 +395,12 @@ fun AIHelperScreen(
Icon( Icon(
Icons.Default.Warning, Icons.Default.Warning,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Error, tint = AppColors.Error,
modifier = Modifier.size(20.dp) modifier = Modifier.size(20.dp)
) )
Text( Text(
errorMessage, errorMessage,
color = Constants.AppColors.Error, color = AppColors.Error,
style = MaterialTheme.typography.bodySmall style = MaterialTheme.typography.bodySmall
) )
} }
@ -411,7 +412,7 @@ fun AIHelperScreen(
// Input Area - Minimalist // Input Area - Minimalist
Surface( Surface(
color = Constants.AppColors.Surface, color = AppColors.Surface,
shadowElevation = 8.dp, shadowElevation = 8.dp,
shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp) shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)
) { ) {
@ -428,19 +429,19 @@ fun AIHelperScreen(
placeholder = { placeholder = {
Text( Text(
"Ketik pesan...", "Ketik pesan...",
color = Constants.AppColors.OnSurfaceTertiary color = AppColors.OnSurfaceTertiary
) )
}, },
modifier = Modifier modifier = Modifier
.weight(1f) .weight(1f)
.heightIn(min = 48.dp, max = 120.dp), .heightIn(min = 48.dp, max = 120.dp),
colors = OutlinedTextFieldDefaults.colors( colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Constants.AppColors.OnBackground, focusedTextColor = AppColors.OnBackground,
unfocusedTextColor = Constants.AppColors.OnSurface, unfocusedTextColor = AppColors.OnSurface,
focusedContainerColor = Constants.AppColors.SurfaceVariant, focusedContainerColor = AppColors.SurfaceVariant,
unfocusedContainerColor = Constants.AppColors.SurfaceVariant, unfocusedContainerColor = AppColors.SurfaceVariant,
cursorColor = Constants.AppColors.Primary, cursorColor = AppColors.Primary,
focusedBorderColor = Constants.AppColors.Primary, focusedBorderColor = AppColors.Primary,
unfocusedBorderColor = Color.Transparent unfocusedBorderColor = Color.Transparent
), ),
shape = RoundedCornerShape(24.dp), shape = RoundedCornerShape(24.dp),
@ -501,7 +502,7 @@ fun AIHelperScreen(
} }
} }
}, },
containerColor = Constants.AppColors.Primary, containerColor = AppColors.Primary,
modifier = Modifier.size(48.dp) modifier = Modifier.size(48.dp)
) { ) {
Icon( Icon(

View File

@ -30,6 +30,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.notesai.data.model.ChatMessage import com.example.notesai.data.model.ChatMessage
import com.example.notesai.util.AppColors
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
@ -52,7 +53,7 @@ fun ChatBubble(
modifier = Modifier modifier = Modifier
.size(32.dp) .size(32.dp)
.background( .background(
color = Constants.AppColors.Primary.copy(alpha = 0.1f), color = AppColors.Primary.copy(alpha = 0.1f),
shape = CircleShape shape = CircleShape
), ),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
@ -60,7 +61,7 @@ fun ChatBubble(
Icon( Icon(
Icons.Default.AutoAwesome, Icons.Default.AutoAwesome,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
} }
@ -73,9 +74,9 @@ fun ChatBubble(
) { ) {
Surface( Surface(
color = if (message.isUser) color = if (message.isUser)
Constants.AppColors.Primary AppColors.Primary
else else
Constants.AppColors.SurfaceVariant, AppColors.SurfaceVariant,
shape = RoundedCornerShape( shape = RoundedCornerShape(
topStart = 16.dp, topStart = 16.dp,
topEnd = 16.dp, topEnd = 16.dp,
@ -86,7 +87,7 @@ fun ChatBubble(
Column(modifier = Modifier.padding(12.dp)) { Column(modifier = Modifier.padding(12.dp)) {
Text( Text(
message.message, message.message,
color = if (message.isUser) Color.White else Constants.AppColors.OnSurface, color = if (message.isUser) Color.White else AppColors.OnSurface,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
lineHeight = 20.sp lineHeight = 20.sp
) )
@ -101,7 +102,7 @@ fun ChatBubble(
color = if (message.isUser) color = if (message.isUser)
Color.White.copy(0.7f) Color.White.copy(0.7f)
else else
Constants.AppColors.OnSurfaceTertiary, AppColors.OnSurfaceTertiary,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
fontSize = 11.sp, fontSize = 11.sp,
modifier = Modifier.padding(top = 4.dp) modifier = Modifier.padding(top = 4.dp)
@ -115,7 +116,7 @@ fun ChatBubble(
Icon( Icon(
Icons.Default.ContentCopy, Icons.Default.ContentCopy,
contentDescription = "Copy", contentDescription = "Copy",
tint = Constants.AppColors.OnSurfaceVariant, tint = AppColors.OnSurfaceVariant,
modifier = Modifier.size(14.dp) modifier = Modifier.size(14.dp)
) )
} }
@ -127,7 +128,7 @@ fun ChatBubble(
if (showCopied && !message.isUser) { if (showCopied && !message.isUser) {
Text( Text(
"✓ Disalin", "✓ Disalin",
color = Constants.AppColors.Success, color = AppColors.Success,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
fontSize = 11.sp, fontSize = 11.sp,
modifier = Modifier.padding(top = 4.dp, start = 8.dp) modifier = Modifier.padding(top = 4.dp, start = 8.dp)

View File

@ -19,6 +19,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.notesai.util.AppColors
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
@Composable @Composable
@ -32,7 +33,7 @@ fun CompactStatItem(
horizontalArrangement = Arrangement.spacedBy(6.dp), horizontalArrangement = Arrangement.spacedBy(6.dp),
modifier = Modifier modifier = Modifier
.background( .background(
color = Constants.AppColors.SurfaceVariant, color = AppColors.SurfaceVariant,
shape = RoundedCornerShape(8.dp) shape = RoundedCornerShape(8.dp)
) )
.padding(horizontal = 12.dp, vertical = 8.dp) .padding(horizontal = 12.dp, vertical = 8.dp)
@ -40,21 +41,21 @@ fun CompactStatItem(
Icon( Icon(
icon, icon,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
Column { Column {
Text( Text(
value, value,
style = MaterialTheme.typography.titleMedium, style = MaterialTheme.typography.titleMedium,
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
fontSize = 16.sp fontSize = 16.sp
) )
Text( Text(
label, label,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = Constants.AppColors.OnSurfaceTertiary, color = AppColors.OnSurfaceTertiary,
fontSize = 11.sp fontSize = 11.sp
) )
} }

View File

@ -23,6 +23,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.notesai.util.AppColors
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
@Composable @Composable
@ -32,7 +33,7 @@ fun SuggestionChip(
) { ) {
Surface( Surface(
onClick = { onSelect(text) }, onClick = { onSelect(text) },
color = Constants.AppColors.SurfaceVariant, color = AppColors.SurfaceVariant,
shape = RoundedCornerShape(12.dp) shape = RoundedCornerShape(12.dp)
) { ) {
Row( Row(
@ -43,12 +44,12 @@ fun SuggestionChip(
Icon( Icon(
Icons.Default.Lightbulb, Icons.Default.Lightbulb,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(16.dp) modifier = Modifier.size(16.dp)
) )
Text( Text(
text, text,
color = Constants.AppColors.OnSurface, color = AppColors.OnSurface,
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
fontSize = 14.sp fontSize = 14.sp
) )

View File

@ -22,6 +22,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.notesai.data.model.Category import com.example.notesai.data.model.Category
import com.example.notesai.util.AppColors
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
@Composable @Composable
@ -54,14 +55,14 @@ fun CategoryCard(
title = { title = {
Text( Text(
"Pindahkan ke Sampah?", "Pindahkan ke Sampah?",
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
}, },
text = { text = {
Text( Text(
"Kategori '${category.name}' dan $noteCount catatan di dalamnya akan dipindahkan ke sampah.", "Kategori '${category.name}' dan $noteCount catatan di dalamnya akan dipindahkan ke sampah.",
color = Constants.AppColors.OnSurfaceVariant color = AppColors.OnSurfaceVariant
) )
}, },
confirmButton = { confirmButton = {
@ -71,7 +72,7 @@ fun CategoryCard(
showDeleteConfirm = false showDeleteConfirm = false
}, },
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
containerColor = Constants.AppColors.Error containerColor = AppColors.Error
) )
) { ) {
Text("Hapus", color = Color.White) Text("Hapus", color = Color.White)
@ -79,10 +80,10 @@ fun CategoryCard(
}, },
dismissButton = { dismissButton = {
TextButton(onClick = { showDeleteConfirm = false }) { TextButton(onClick = { showDeleteConfirm = false }) {
Text("Batal", color = Constants.AppColors.OnSurfaceVariant) Text("Batal", color = AppColors.OnSurfaceVariant)
} }
}, },
containerColor = Constants.AppColors.Surface, containerColor = AppColors.Surface,
shape = RoundedCornerShape(Constants.Radius.Large.dp) shape = RoundedCornerShape(Constants.Radius.Large.dp)
) )
} }
@ -107,7 +108,7 @@ fun CategoryCard(
.clickable(onClick = onClick), .clickable(onClick = onClick),
shape = RoundedCornerShape(Constants.Radius.Large.dp), shape = RoundedCornerShape(Constants.Radius.Large.dp),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = Constants.AppColors.Surface containerColor = AppColors.Surface
), ),
elevation = CardDefaults.cardElevation( elevation = CardDefaults.cardElevation(
defaultElevation = Constants.Elevation.Small.dp defaultElevation = Constants.Elevation.Small.dp
@ -167,7 +168,7 @@ fun CategoryCard(
Icon( Icon(
Icons.Default.MoreVert, Icons.Default.MoreVert,
contentDescription = "Menu", contentDescription = "Menu",
tint = Constants.AppColors.OnSurfaceVariant, tint = AppColors.OnSurfaceVariant,
modifier = Modifier.size(20.dp) modifier = Modifier.size(20.dp)
) )
} }
@ -175,7 +176,7 @@ fun CategoryCard(
DropdownMenu( DropdownMenu(
expanded = showMenu, expanded = showMenu,
onDismissRequest = { showMenu = false }, onDismissRequest = { showMenu = false },
modifier = Modifier.background(Constants.AppColors.SurfaceElevated) modifier = Modifier.background(AppColors.SurfaceElevated)
) { ) {
DropdownMenuItem( DropdownMenuItem(
text = { text = {
@ -186,12 +187,12 @@ fun CategoryCard(
Icon( Icon(
Icons.Default.Edit, Icons.Default.Edit,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Primary, tint = AppColors.Primary,
modifier = Modifier.size(18.dp) modifier = Modifier.size(18.dp)
) )
Text( Text(
"Edit Kategori", "Edit Kategori",
color = Constants.AppColors.OnSurface, color = AppColors.OnSurface,
fontSize = 14.sp fontSize = 14.sp
) )
} }
@ -211,12 +212,12 @@ fun CategoryCard(
Icon( Icon(
Icons.Default.Delete, Icons.Default.Delete,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.Error, tint = AppColors.Error,
modifier = Modifier.size(18.dp) modifier = Modifier.size(18.dp)
) )
Text( Text(
"Pindah ke Sampah", "Pindah ke Sampah",
color = Constants.AppColors.OnSurface, color = AppColors.OnSurface,
fontSize = 14.sp fontSize = 14.sp
) )
} }
@ -237,7 +238,7 @@ fun CategoryCard(
category.name, category.name,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
fontSize = 18.sp fontSize = 18.sp
) )
@ -251,13 +252,13 @@ fun CategoryCard(
Icon( Icon(
Icons.Default.Description, Icons.Default.Description,
contentDescription = null, contentDescription = null,
tint = Constants.AppColors.OnSurfaceTertiary, tint = AppColors.OnSurfaceTertiary,
modifier = Modifier.size(14.dp) modifier = Modifier.size(14.dp)
) )
Text( Text(
"$noteCount catatan", "$noteCount catatan",
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = Constants.AppColors.OnSurfaceTertiary, color = AppColors.OnSurfaceTertiary,
fontSize = 13.sp fontSize = 13.sp
) )
} }
@ -275,7 +276,7 @@ fun EditCategoryDialog(
var name by remember { mutableStateOf(category.name) } var name by remember { mutableStateOf(category.name) }
var selectedGradient by remember { var selectedGradient by remember {
mutableStateOf( mutableStateOf(
Constants.AppColors.CategoryColors.indexOfFirst { Constants.CategoryColors.indexOfFirst {
it.first == category.gradientStart && it.second == category.gradientEnd it.first == category.gradientStart && it.second == category.gradientEnd
}.takeIf { it >= 0 } ?: 0 }.takeIf { it >= 0 } ?: 0
) )
@ -283,12 +284,12 @@ fun EditCategoryDialog(
AlertDialog( AlertDialog(
onDismissRequest = onDismiss, onDismissRequest = onDismiss,
containerColor = Constants.AppColors.Surface, containerColor = AppColors.Surface,
shape = RoundedCornerShape(Constants.Radius.Large.dp), shape = RoundedCornerShape(Constants.Radius.Large.dp),
title = { title = {
Text( Text(
"Edit Kategori", "Edit Kategori",
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
fontWeight = FontWeight.Bold fontWeight = FontWeight.Bold
) )
}, },
@ -300,18 +301,18 @@ fun EditCategoryDialog(
label = { label = {
Text( Text(
"Nama Kategori", "Nama Kategori",
color = Constants.AppColors.OnSurfaceVariant color = AppColors.OnSurfaceVariant
) )
}, },
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
colors = OutlinedTextFieldDefaults.colors( colors = OutlinedTextFieldDefaults.colors(
focusedTextColor = Constants.AppColors.OnBackground, focusedTextColor = AppColors.OnBackground,
unfocusedTextColor = Constants.AppColors.OnSurface, unfocusedTextColor = AppColors.OnSurface,
focusedContainerColor = Constants.AppColors.SurfaceVariant, focusedContainerColor = AppColors.SurfaceVariant,
unfocusedContainerColor = Constants.AppColors.SurfaceVariant, unfocusedContainerColor = AppColors.SurfaceVariant,
cursorColor = Constants.AppColors.Primary, cursorColor = AppColors.Primary,
focusedBorderColor = Constants.AppColors.Primary, focusedBorderColor = AppColors.Primary,
unfocusedBorderColor = Constants.AppColors.Border unfocusedBorderColor = AppColors.Border
), ),
shape = RoundedCornerShape(Constants.Radius.Medium.dp) shape = RoundedCornerShape(Constants.Radius.Medium.dp)
) )
@ -321,20 +322,20 @@ fun EditCategoryDialog(
Text( Text(
"Pilih Warna:", "Pilih Warna:",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Constants.AppColors.OnSurface, color = AppColors.OnSurface,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
fontSize = 14.sp fontSize = 14.sp
) )
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
Constants.AppColors.CategoryColors.chunked(4).forEach { row -> Constants.CategoryColors.chunked(4).forEach { row ->
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp) horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
row.forEachIndexed { _, gradient -> row.forEachIndexed { _, gradient ->
val globalIndex = Constants.AppColors.CategoryColors.indexOf(gradient) val globalIndex = Constants.CategoryColors.indexOf(gradient)
val isSelected = selectedGradient == globalIndex val isSelected = selectedGradient == globalIndex
Box( Box(
@ -376,13 +377,13 @@ fun EditCategoryDialog(
Button( Button(
onClick = { onClick = {
if (name.isNotBlank()) { if (name.isNotBlank()) {
val gradient = Constants.AppColors.CategoryColors[selectedGradient] val gradient = Constants.CategoryColors[selectedGradient]
onSave(name, gradient.first, gradient.second) onSave(name, gradient.first, gradient.second)
} }
}, },
enabled = name.isNotBlank(), enabled = name.isNotBlank(),
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
containerColor = Constants.AppColors.Primary containerColor = AppColors.Primary
) )
) { ) {
Text("Simpan", color = Color.White, fontWeight = FontWeight.Bold) Text("Simpan", color = Color.White, fontWeight = FontWeight.Bold)
@ -390,7 +391,7 @@ fun EditCategoryDialog(
}, },
dismissButton = { dismissButton = {
TextButton(onClick = onDismiss) { TextButton(onClick = onDismiss) {
Text("Batal", color = Constants.AppColors.OnSurfaceVariant) Text("Batal", color = AppColors.OnSurfaceVariant)
} }
} }
) )

View File

@ -20,6 +20,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.example.notesai.data.model.Note import com.example.notesai.data.model.Note
import com.example.notesai.util.AppColors
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.* import java.util.*
@ -51,7 +52,7 @@ fun NoteCard(
.combinedClickable(onClick = onClick), .combinedClickable(onClick = onClick),
shape = RoundedCornerShape(16.dp), shape = RoundedCornerShape(16.dp),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = Constants.AppColors.SurfaceVariant containerColor = AppColors.SurfaceVariant
), ),
elevation = CardDefaults.cardElevation( elevation = CardDefaults.cardElevation(
defaultElevation = 2.dp defaultElevation = 2.dp
@ -73,7 +74,7 @@ fun NoteCard(
note.title, note.title,
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleLarge,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = Constants.AppColors.OnBackground, color = AppColors.OnBackground,
modifier = Modifier.weight(1f), modifier = Modifier.weight(1f),
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
@ -88,7 +89,7 @@ fun NoteCard(
Icon( Icon(
if (note.isPinned) Icons.Filled.Star else Icons.Outlined.StarBorder, if (note.isPinned) Icons.Filled.Star else Icons.Outlined.StarBorder,
contentDescription = "Pin", contentDescription = "Pin",
tint = if (note.isPinned) Constants.AppColors.Warning else Constants.AppColors.OnSurfaceVariant, tint = if (note.isPinned) AppColors.Warning else AppColors.OnSurfaceVariant,
modifier = Modifier.size(18.dp) modifier = Modifier.size(18.dp)
) )
} }
@ -103,7 +104,7 @@ fun NoteCard(
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
maxLines = 3, maxLines = 3,
overflow = TextOverflow.Ellipsis, overflow = TextOverflow.Ellipsis,
color = Constants.AppColors.OnSurfaceVariant, color = AppColors.OnSurfaceVariant,
lineHeight = 20.sp, lineHeight = 20.sp,
fontSize = 14.sp fontSize = 14.sp
) )
@ -114,7 +115,7 @@ fun NoteCard(
Text( Text(
"Tidak ada deskripsi", "Tidak ada deskripsi",
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = Constants.AppColors.OnSurfaceVariant.copy(alpha = 0.5f), color = AppColors.OnSurfaceVariant.copy(alpha = 0.5f),
fontStyle = androidx.compose.ui.text.font.FontStyle.Italic, fontStyle = androidx.compose.ui.text.font.FontStyle.Italic,
fontSize = 14.sp fontSize = 14.sp
) )
@ -124,7 +125,7 @@ fun NoteCard(
// Divider // Divider
HorizontalDivider( HorizontalDivider(
color = Constants.AppColors.Divider, color = AppColors.Divider,
thickness = 1.dp thickness = 1.dp
) )
@ -139,7 +140,7 @@ fun NoteCard(
Text( Text(
dateFormat.format(Date(note.timestamp)), dateFormat.format(Date(note.timestamp)),
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = Constants.AppColors.OnSurfaceTertiary, color = AppColors.OnSurfaceTertiary,
fontSize = 12.sp fontSize = 12.sp
) )
} }

View File

@ -1,5 +1,8 @@
package com.example.notesai.util package com.example.notesai.util
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
object Constants { object Constants {
@ -16,56 +19,68 @@ object Constants {
const val MAX_CHAT_PREVIEW_LINES = 2 const val MAX_CHAT_PREVIEW_LINES = 2
const val GRID_COLUMNS = 2 const val GRID_COLUMNS = 2
// NEW MINIMALIST COLOR PALETTE // DARK THEME COLORS
object AppColors { object DarkColors {
// Backgrounds - Neutral Dark val Background = Color(0xFF0A0A0A)
val Background = Color(0xFF0A0A0A) // Almost black val Surface = Color(0xFF141414)
val Surface = Color(0xFF141414) // Dark gray val SurfaceVariant = Color(0xFF1E1E1E)
val SurfaceVariant = Color(0xFF1E1E1E) // Lighter dark gray val SurfaceElevated = Color(0xFF252525)
val SurfaceElevated = Color(0xFF252525) // Elevated surface val Primary = Color(0xFF3B82F6)
val PrimaryVariant = Color(0xFF60A5FA)
// Primary Accent - Subtle Blue (minimalist) val PrimaryContainer = Color(0xFF1E3A8A)
val Primary = Color(0xFF3B82F6) // Modern blue val Secondary = Color(0xFF8B5CF6)
val PrimaryVariant = Color(0xFF60A5FA) // Light blue val SecondaryVariant = Color(0xFFA78BFA)
val PrimaryContainer = Color(0xFF1E3A8A) // Dark blue container val OnBackground = Color(0xFFFFFFFF)
val OnSurface = Color(0xFFE5E5E5)
// Secondary Accent - Minimal use val OnSurfaceVariant = Color(0xFF9CA3AF)
val Secondary = Color(0xFF8B5CF6) // Subtle purple val OnSurfaceTertiary = Color(0xFF6B7280)
val SecondaryVariant = Color(0xFFA78BFA) // Light purple val Success = Color(0xFF10B981)
val Error = Color(0xFFEF4444)
// Text Colors - High contrast for readability val Warning = Color(0xFFFBBF24)
val OnBackground = Color(0xFFFFFFFF) // Pure white val Info = Color(0xFF3B82F6)
val OnSurface = Color(0xFFE5E5E5) // Off white val Border = Color(0xFF2A2A2A)
val OnSurfaceVariant = Color(0xFF9CA3AF) // Gray text val Divider = Color(0xFF1F1F1F)
val OnSurfaceTertiary = Color(0xFF6B7280) // Muted gray
// Functional Colors
val Success = Color(0xFF10B981) // Green
val Error = Color(0xFFEF4444) // Red
val Warning = Color(0xFFFBBF24) // Amber
val Info = Color(0xFF3B82F6) // Blue
// Border & Dividers - Subtle
val Border = Color(0xFF2A2A2A) // Subtle border
val Divider = Color(0xFF1F1F1F) // Very subtle divider
// Overlay & Shadows
val Overlay = Color(0xFF000000).copy(alpha = 0.5f) val Overlay = Color(0xFF000000).copy(alpha = 0.5f)
val Shadow = Color(0xFF000000).copy(alpha = 0.3f) val Shadow = Color(0xFF000000).copy(alpha = 0.3f)
// Category Colors - Minimalist Palette (subtle & muted)
val CategoryColors = listOf(
Pair(0xFF3B82F6L, 0xFF60A5FAL), // Blue gradient
Pair(0xFF8B5CF6L, 0xFFA78BFAL), // Purple gradient
Pair(0xFF10B981L, 0xFF34D399L), // Green gradient
Pair(0xFFF59E0BL, 0xFFFBBF24L), // Amber gradient
Pair(0xFFEF4444L, 0xFFF87171L), // Red gradient
Pair(0xFF06B6D4L, 0xFF22D3EEL), // Cyan gradient
Pair(0xFFEC4899L, 0xFFF472B6L), // Pink gradient
Pair(0xFF6366F1L, 0xFF818CF8L) // Indigo gradient
)
} }
// LIGHT THEME COLORS
object LightColors {
val Background = Color(0xFFF8F9FA)
val Surface = Color(0xFFFFFFFF)
val SurfaceVariant = Color(0xFFF1F3F5)
val SurfaceElevated = Color(0xFFFFFFFF)
val Primary = Color(0xFF3B82F6)
val PrimaryVariant = Color(0xFF2563EB)
val PrimaryContainer = Color(0xFFDCEEFF)
val Secondary = Color(0xFF8B5CF6)
val SecondaryVariant = Color(0xFF7C3AED)
val OnBackground = Color(0xFF1F2937)
val OnSurface = Color(0xFF374151)
val OnSurfaceVariant = Color(0xFF6B7280)
val OnSurfaceTertiary = Color(0xFF9CA3AF)
val Success = Color(0xFF10B981)
val Error = Color(0xFFEF4444)
val Warning = Color(0xFFA16207)
val Info = Color(0xFF3B82F6)
val Border = Color(0xFFE5E7EB)
val Divider = Color(0xFFF3F4F6)
val Overlay = Color(0xFF000000).copy(alpha = 0.3f)
val Shadow = Color(0xFF000000).copy(alpha = 0.1f)
}
// Category Colors - Same for both themes
val CategoryColors = listOf(
Pair(0xFF3B82F6L, 0xFF60A5FAL), // Blue
Pair(0xFF8B5CF6L, 0xFFA78BFAL), // Purple
Pair(0xFF10B981L, 0xFF34D399L), // Green
Pair(0xFFF59E0BL, 0xFFFBBF24L), // Amber
Pair(0xFFEF4444L, 0xFFF87171L), // Red
Pair(0xFF06B6D4L, 0xFF22D3EEL), // Cyan
Pair(0xFFEC4899L, 0xFFF472B6L), // Pink
Pair(0xFF6366F1L, 0xFF818CF8L) // Indigo
)
// Animation Durations // Animation Durations
const val ANIMATION_DURATION_SHORT = 150 const val ANIMATION_DURATION_SHORT = 150
const val ANIMATION_DURATION_MEDIUM = 300 const val ANIMATION_DURATION_MEDIUM = 300
@ -73,7 +88,7 @@ object Constants {
const val FADE_IN_DURATION = 200 const val FADE_IN_DURATION = 200
const val FADE_OUT_DURATION = 200 const val FADE_OUT_DURATION = 200
// Spacing System (8dp grid) // Spacing System
object Spacing { object Spacing {
const val ExtraSmall = 4 const val ExtraSmall = 4
const val Small = 8 const val Small = 8
@ -101,3 +116,78 @@ object Constants {
const val ExtraLarge = 16 const val ExtraLarge = 16
} }
} }
// REACTIVE APP COLORS - Using Compose State
object AppColors {
// Internal state
private var _isDark by mutableStateOf(true)
// Public setter
fun setTheme(isDark: Boolean) {
_isDark = isDark
}
// All colors are now reactive via mutableStateOf
val Background: Color
get() = if (_isDark) Constants.DarkColors.Background else Constants.LightColors.Background
val Surface: Color
get() = if (_isDark) Constants.DarkColors.Surface else Constants.LightColors.Surface
val SurfaceVariant: Color
get() = if (_isDark) Constants.DarkColors.SurfaceVariant else Constants.LightColors.SurfaceVariant
val SurfaceElevated: Color
get() = if (_isDark) Constants.DarkColors.SurfaceElevated else Constants.LightColors.SurfaceElevated
val Primary: Color
get() = if (_isDark) Constants.DarkColors.Primary else Constants.LightColors.Primary
val PrimaryVariant: Color
get() = if (_isDark) Constants.DarkColors.PrimaryVariant else Constants.LightColors.PrimaryVariant
val PrimaryContainer: Color
get() = if (_isDark) Constants.DarkColors.PrimaryContainer else Constants.LightColors.PrimaryContainer
val Secondary: Color
get() = if (_isDark) Constants.DarkColors.Secondary else Constants.LightColors.Secondary
val SecondaryVariant: Color
get() = if (_isDark) Constants.DarkColors.SecondaryVariant else Constants.LightColors.SecondaryVariant
val OnBackground: Color
get() = if (_isDark) Constants.DarkColors.OnBackground else Constants.LightColors.OnBackground
val OnSurface: Color
get() = if (_isDark) Constants.DarkColors.OnSurface else Constants.LightColors.OnSurface
val OnSurfaceVariant: Color
get() = if (_isDark) Constants.DarkColors.OnSurfaceVariant else Constants.LightColors.OnSurfaceVariant
val OnSurfaceTertiary: Color
get() = if (_isDark) Constants.DarkColors.OnSurfaceTertiary else Constants.LightColors.OnSurfaceTertiary
val Success: Color
get() = if (_isDark) Constants.DarkColors.Success else Constants.LightColors.Success
val Error: Color
get() = if (_isDark) Constants.DarkColors.Error else Constants.LightColors.Error
val Warning: Color
get() = if (_isDark) Constants.DarkColors.Warning else Constants.LightColors.Warning
val Info: Color
get() = if (_isDark) Constants.DarkColors.Info else Constants.LightColors.Info
val Border: Color
get() = if (_isDark) Constants.DarkColors.Border else Constants.LightColors.Border
val Divider: Color
get() = if (_isDark) Constants.DarkColors.Divider else Constants.LightColors.Divider
val Overlay: Color
get() = if (_isDark) Constants.DarkColors.Overlay else Constants.LightColors.Overlay
val Shadow: Color
get() = if (_isDark) Constants.DarkColors.Shadow else Constants.LightColors.Shadow
}

View File

@ -12,6 +12,10 @@ constraintlayout = "2.1.4"
uiText = "1.10.0" uiText = "1.10.0"
material3 = "1.4.0" material3 = "1.4.0"
animationCore = "1.10.0" animationCore = "1.10.0"
firebaseAnnotations = "17.0.0"
firebaseFirestoreKtx = "26.0.2"
uiGraphics = "1.10.0"
roomCompiler = "2.8.4"
[libraries] [libraries]
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
@ -25,6 +29,10 @@ androidx-constraintlayout = { group = "androidx.constraintlayout", name = "const
androidx-ui-text = { group = "androidx.compose.ui", name = "ui-text", version.ref = "uiText" } androidx-ui-text = { group = "androidx.compose.ui", name = "ui-text", version.ref = "uiText" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" } androidx-material3 = { group = "androidx.compose.material3", name = "material3", version.ref = "material3" }
androidx-animation-core = { group = "androidx.compose.animation", name = "animation-core", version.ref = "animationCore" } androidx-animation-core = { group = "androidx.compose.animation", name = "animation-core", version.ref = "animationCore" }
firebase-annotations = { group = "com.google.firebase", name = "firebase-annotations", version.ref = "firebaseAnnotations" }
firebase-firestore-ktx = { group = "com.google.firebase", name = "firebase-firestore-ktx", version.ref = "firebaseFirestoreKtx" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics", version.ref = "uiGraphics" }
androidx-room-compiler = { group = "androidx.room", name = "room-compiler", version.ref = "roomCompiler" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }