Menyesuaikan UI/UX Design pada StarredNotesScreen.kt dan TopBar pada seluruh komponen Halaman

This commit is contained in:
202310715297 RAIHAN ARIQ MUZAKKI 2025-12-18 16:58:33 +07:00
parent b264f87b14
commit 7a67943800
5 changed files with 181 additions and 133 deletions

View File

@ -199,13 +199,9 @@ fun NotesApp() {
"trash" -> "Sampah" "trash" -> "Sampah"
else -> "AI Notes" else -> "AI Notes"
}, },
showBackButton = (selectedCategory != null && currentScreen == "main") || currentScreen == "starred", showBackButton = (selectedCategory != null && currentScreen == "main"),
onBackClick = { onBackClick = {
if (currentScreen == "starred") { selectedCategory = null
currentScreen = "main"
} else {
selectedCategory = null
}
}, },
onMenuClick = { drawerState = !drawerState }, onMenuClick = { drawerState = !drawerState },
onSearchClick = { showSearch = !showSearch }, onSearchClick = { showSearch = !showSearch },
@ -354,6 +350,16 @@ fun NotesApp() {
it it
} }
} }
},
onNoteEdit = { note ->
editingNote = note
showNoteDialog = true
},
onNoteDelete = { note ->
notes = notes.map {
if (it.id == note.id) it.copy(isDeleted = true)
else it
}
} }
) )
@ -392,8 +398,6 @@ fun NotesApp() {
fullScreenNote = note fullScreenNote = note
showFullScreenNote = true showFullScreenNote = true
}, },
onMenuClick = { drawerState = true },
onBack = { currentScreen = "main" },
onUnpin = { note -> onUnpin = { note ->
notes = notes.map { notes = notes.map {
if (it.id == note.id) it.copy(isPinned = false) if (it.id == note.id) it.copy(isPinned = false)

View File

@ -4,6 +4,7 @@ import androidx.compose.animation.*
import androidx.compose.animation.core.* import androidx.compose.animation.core.*
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
@ -14,6 +15,7 @@ import androidx.compose.material3.*
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.draw.shadow import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
@ -34,113 +36,141 @@ fun ModernTopBar(
onSearchQueryChange: (String) -> Unit, onSearchQueryChange: (String) -> Unit,
showSearch: Boolean showSearch: Boolean
) { ) {
// Smooth transition for search bar // Floating Top Bar with same style as Bottom Bar
AnimatedContent( Box(
targetState = showSearch, modifier = Modifier
transitionSpec = { .fillMaxWidth()
fadeIn(animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM)) togetherWith .padding(horizontal = 16.dp, vertical = 8.dp)
fadeOut(animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM)) ) {
}, Surface(
label = "topbar" modifier = Modifier
) { isSearching -> .fillMaxWidth()
if (isSearching) { .shadow(
// Search Mode - Minimalist elevation = Constants.Elevation.Large.dp,
Surface( shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp)
modifier = Modifier ),
.fillMaxWidth() color = AppColors.SurfaceElevated,
.shadow(Constants.Elevation.Small.dp), shape = RoundedCornerShape(Constants.Radius.ExtraLarge.dp)
color = AppColors.Surface ) {
) { // Smooth transition for search bar
Row( AnimatedContent(
modifier = Modifier targetState = showSearch,
.fillMaxWidth() transitionSpec = {
.padding(horizontal = Constants.Spacing.Medium.dp, vertical = Constants.Spacing.Small.dp), fadeIn(animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM)) togetherWith
verticalAlignment = Alignment.CenterVertically fadeOut(animationSpec = tween(Constants.ANIMATION_DURATION_MEDIUM))
) { },
// Search TextField label = "topbar"
TextField( ) { isSearching ->
value = searchQuery, if (isSearching) {
onValueChange = onSearchQueryChange, // Search Mode
placeholder = { Row(
Text(
"Cari catatan atau kategori...",
color = AppColors.OnSurfaceVariant,
fontSize = 15.sp
)
},
colors = TextFieldDefaults.colors(
focusedContainerColor = AppColors.SurfaceVariant,
unfocusedContainerColor = AppColors.SurfaceVariant,
focusedTextColor = AppColors.OnBackground,
unfocusedTextColor = AppColors.OnSurface,
cursorColor = AppColors.Primary,
focusedIndicatorColor = Color.Transparent,
unfocusedIndicatorColor = Color.Transparent
),
modifier = Modifier modifier = Modifier
.weight(1f) .fillMaxWidth()
.heightIn(min = 48.dp), .padding(horizontal = 12.dp, vertical = 8.dp),
shape = RoundedCornerShape(Constants.Radius.Medium.dp), verticalAlignment = Alignment.CenterVertically,
singleLine = true, horizontalArrangement = Arrangement.spacedBy(8.dp)
textStyle = LocalTextStyle.current.copy(fontSize = 15.sp)
)
Spacer(modifier = Modifier.width(Constants.Spacing.Small.dp))
// Close Search Button
IconButton(
onClick = {
onSearchQueryChange("")
onSearchClick()
}
) { ) {
Icon( // Search TextField
Icons.Default.Close, OutlinedTextField(
contentDescription = "Close Search", value = searchQuery,
tint = AppColors.OnSurfaceVariant onValueChange = onSearchQueryChange,
placeholder = {
Text(
"Cari...",
color = AppColors.OnSurfaceVariant,
fontSize = 15.sp
)
},
colors = OutlinedTextFieldDefaults.colors(
focusedContainerColor = AppColors.SurfaceVariant,
unfocusedContainerColor = AppColors.SurfaceVariant,
focusedTextColor = AppColors.OnBackground,
unfocusedTextColor = AppColors.OnSurface,
cursorColor = AppColors.Primary,
focusedBorderColor = AppColors.Primary,
unfocusedBorderColor = Color.Transparent
),
modifier = Modifier
.weight(1f)
.heightIn(min = 48.dp),
shape = RoundedCornerShape(Constants.Radius.Large.dp),
singleLine = true,
textStyle = LocalTextStyle.current.copy(fontSize = 15.sp)
) )
}
}
}
} else {
// Normal Mode - Minimalist
Surface(
modifier = Modifier
.fillMaxWidth()
.shadow(Constants.Elevation.Small.dp),
color = AppColors.Surface
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = Constants.Spacing.Small.dp, vertical = Constants.Spacing.Small.dp),
verticalAlignment = Alignment.CenterVertically
) {
// Back/Menu Button
IconButton(onClick = if (showBackButton) onBackClick else onMenuClick) {
Icon(
if (showBackButton) Icons.AutoMirrored.Filled.ArrowBack else Icons.Default.Menu,
contentDescription = if (showBackButton) "Back" else "Menu",
tint = AppColors.OnSurface
)
}
// Title // Close Search Button
Text( IconButton(
title, onClick = {
fontWeight = FontWeight.Bold, onSearchQueryChange("")
fontSize = 20.sp, onSearchClick()
color = AppColors.OnBackground, },
modifier = Modifier.weight(1f) modifier = Modifier
) .size(40.dp)
.clip(CircleShape)
.background(AppColors.SurfaceVariant)
) {
Icon(
Icons.Default.Close,
contentDescription = "Close Search",
tint = AppColors.OnSurfaceVariant,
modifier = Modifier.size(20.dp)
)
}
}
} else {
// Normal Mode
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
// Back/Menu Button
IconButton(
onClick = if (showBackButton) onBackClick else onMenuClick,
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(
if (showBackButton) AppColors.SurfaceVariant
else AppColors.Primary.copy(alpha = 0.1f)
)
) {
Icon(
if (showBackButton) Icons.AutoMirrored.Filled.ArrowBack else Icons.Default.Menu,
contentDescription = if (showBackButton) "Back" else "Menu",
tint = if (showBackButton) AppColors.OnSurface else AppColors.Primary,
modifier = Modifier.size(20.dp)
)
}
// Search Button // Title
IconButton(onClick = onSearchClick) { Text(
Icon( title,
Icons.Default.Search, fontWeight = FontWeight.Bold,
contentDescription = "Search", fontSize = 18.sp,
tint = AppColors.OnSurfaceVariant color = AppColors.OnBackground,
modifier = Modifier
.weight(1f)
.padding(horizontal = 12.dp)
) )
// Search Button
IconButton(
onClick = onSearchClick,
modifier = Modifier
.size(40.dp)
.clip(CircleShape)
.background(AppColors.SurfaceVariant)
) {
Icon(
Icons.Default.Search,
contentDescription = "Search",
tint = AppColors.OnSurfaceVariant,
modifier = Modifier.size(20.dp)
)
}
} }
} }
} }

View File

@ -14,6 +14,7 @@ import androidx.compose.material3.*
import androidx.compose.runtime.* import androidx.compose.runtime.*
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.shadow
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalClipboardManager import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@ -22,6 +23,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
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.config.APIKey
import com.example.notesai.data.local.DataStoreManager import com.example.notesai.data.local.DataStoreManager
import com.example.notesai.data.model.* import com.example.notesai.data.model.*
import com.example.notesai.util.Constants import com.example.notesai.util.Constants
@ -35,7 +37,6 @@ 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 import com.example.notesai.util.AppColors
import com.example.notesai.config.APIKey
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable

View File

@ -27,7 +27,9 @@ fun MainScreen(
onNoteClick: (Note) -> Unit, onNoteClick: (Note) -> Unit,
onPinToggle: (Note) -> Unit, onPinToggle: (Note) -> Unit,
onCategoryDelete: (Category) -> Unit, onCategoryDelete: (Category) -> Unit,
onCategoryEdit: (Category, String, Long, Long) -> Unit onCategoryEdit: (Category, String, Long, Long) -> Unit,
onNoteEdit: (Note) -> Unit = {},
onNoteDelete: (Note) -> Unit = {}
) { ) {
Column(modifier = Modifier.fillMaxSize()) { Column(modifier = Modifier.fillMaxSize()) {
if (selectedCategory == null) { if (selectedCategory == null) {
@ -120,7 +122,9 @@ fun MainScreen(
NoteCard( NoteCard(
note = note, note = note,
onClick = { onNoteClick(note) }, onClick = { onNoteClick(note) },
onPinClick = { onPinToggle(note) } onPinClick = { onPinToggle(note) },
onEdit = { onNoteEdit(note) },
onDelete = { onNoteDelete(note) }
) )
} }
} }

View File

@ -1,13 +1,16 @@
package com.example.notesai.presentation.screens.starred package com.example.notesai.presentation.screens.starred
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Star import androidx.compose.material.icons.filled.Star
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.example.notesai.presentation.components.EmptyState import com.example.notesai.presentation.components.EmptyState
import com.example.notesai.presentation.screens.starred.components.StarredNoteCard import com.example.notesai.presentation.screens.starred.components.StarredNoteCard
@ -20,31 +23,37 @@ fun StarredNotesScreen(
notes: List<Note>, notes: List<Note>,
categories: List<Category>, categories: List<Category>,
onNoteClick: (Note) -> Unit, onNoteClick: (Note) -> Unit,
onMenuClick: () -> Unit,
onBack: () -> Unit,
onUnpin: (Note) -> Unit onUnpin: (Note) -> Unit
) { ) {
val starredNotes = notes.filter { it.isPinned && !it.isArchived && !it.isDeleted } val starredNotes = notes.filter { it.isPinned && !it.isArchived && !it.isDeleted }
.sortedByDescending { it.timestamp }
if (starredNotes.isEmpty()) { Column(modifier = Modifier.fillMaxSize()) {
EmptyState( if (starredNotes.isEmpty()) {
icon = Icons.Default.Star, EmptyState(
message = "Belum ada catatan berbintang", icon = Icons.Default.Star,
subtitle = "Catatan yang ditandai berbintang akan muncul di sini" message = "Belum ada catatan berbintang",
) subtitle = "Catatan yang ditandai berbintang akan muncul di sini"
} else { )
LazyColumn( } else {
contentPadding = PaddingValues(16.dp), LazyColumn(
verticalArrangement = Arrangement.spacedBy(12.dp) contentPadding = PaddingValues(
) { start = 16.dp,
items(starredNotes) { note -> end = 16.dp,
val category = categories.find { it.id == note.categoryId } top = 16.dp,
StarredNoteCard( bottom = 100.dp // Extra space untuk bottom bar
note = note, ),
categoryName = category?.name ?: "Unknown", verticalArrangement = Arrangement.spacedBy(12.dp)
onClick = { onNoteClick(note) }, ) {
onUnpin = { onUnpin(note) } items(starredNotes) { note ->
) val category = categories.find { it.id == note.categoryId }
StarredNoteCard(
note = note,
categoryName = category?.name ?: "Unknown",
onClick = { onNoteClick(note) },
onUnpin = { onUnpin(note) }
)
}
} }
} }
} }