Penyesuaian Migrasi (import Library), Fix Bug Aplikasi Crash, Menambahkan Fitur edit dan hapus pada kategori
This commit is contained in:
parent
63b10a3e1c
commit
3f84068d72
4
.idea/deploymentTargetSelector.xml
generated
4
.idea/deploymentTargetSelector.xml
generated
@ -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-12T17:42:15.072692700Z">
|
<DropdownSelection timestamp="2025-12-13T07:41:36.634314200Z">
|
||||||
<Target type="DEFAULT_BOOT">
|
<Target type="DEFAULT_BOOT">
|
||||||
<handle>
|
<handle>
|
||||||
<DeviceId pluginId="LocalEmulator" identifier="path=C:\Users\dendi\.android\avd\Medium_Phone.avd" />
|
<DeviceId pluginId="PhysicalDevice" identifier="serial=RR8T103A6JZ" />
|
||||||
</handle>
|
</handle>
|
||||||
</Target>
|
</Target>
|
||||||
</DropdownSelection>
|
</DropdownSelection>
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
package com.example.notesai
|
package com.example.notesai
|
||||||
|
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.activity.compose.setContent
|
import androidx.activity.compose.setContent
|
||||||
@ -11,12 +12,10 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.*
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
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.draw.shadow
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
@ -31,37 +30,13 @@ import com.example.notesai.presentation.screens.ai.AIHelperScreen
|
|||||||
import com.example.notesai.presentation.screens.archive.ArchiveScreen
|
import com.example.notesai.presentation.screens.archive.ArchiveScreen
|
||||||
import com.example.notesai.presentation.screens.main.MainScreen
|
import com.example.notesai.presentation.screens.main.MainScreen
|
||||||
import com.example.notesai.presentation.screens.note.EditableFullScreenNoteView
|
import com.example.notesai.presentation.screens.note.EditableFullScreenNoteView
|
||||||
import com.example.notesai.presentation.screens.starred.components.StarredNotesScreen
|
import com.example.notesai.presentation.screens.starred.StarredNotesScreen
|
||||||
import com.example.notesai.presentation.screens.trash.components.TrashScreen
|
import com.example.notesai.presentation.screens.trash.TrashScreen
|
||||||
|
import com.example.notesai.data.model.Note
|
||||||
|
import com.example.notesai.data.model.Category
|
||||||
|
import com.example.notesai.util.updateWhere
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
// Data Classes
|
|
||||||
data class Category(
|
|
||||||
val id: String = UUID.randomUUID().toString(),
|
|
||||||
val name: String,
|
|
||||||
val gradientStart: Long,
|
|
||||||
val gradientEnd: Long,
|
|
||||||
val timestamp: Long = System.currentTimeMillis()
|
|
||||||
)
|
|
||||||
|
|
||||||
data class Note(
|
|
||||||
val id: String = UUID.randomUUID().toString(),
|
|
||||||
val categoryId: String,
|
|
||||||
val title: String,
|
|
||||||
val content: String,
|
|
||||||
val timestamp: Long = System.currentTimeMillis(),
|
|
||||||
val isArchived: Boolean = false,
|
|
||||||
val isDeleted: Boolean = false,
|
|
||||||
val isPinned: Boolean = false
|
|
||||||
)
|
|
||||||
|
|
||||||
data class ChatMessage(
|
|
||||||
val id: String = UUID.randomUUID().toString(),
|
|
||||||
val message: String,
|
|
||||||
val isUser: Boolean,
|
|
||||||
val timestamp: Long = System.currentTimeMillis()
|
|
||||||
)
|
|
||||||
|
|
||||||
class MainActivity : ComponentActivity() {
|
class MainActivity : ComponentActivity() {
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
@ -300,6 +275,19 @@ fun NotesApp() {
|
|||||||
categories = categories.filter { it.id != category.id }
|
categories = categories.filter { it.id != category.id }
|
||||||
notes = notes.filter { it.categoryId != category.id }
|
notes = notes.filter { it.categoryId != category.id }
|
||||||
selectedCategory = null
|
selectedCategory = null
|
||||||
|
},
|
||||||
|
onCategoryEdit = { category, newName, newGradientStart, newGradientEnd ->
|
||||||
|
categories = categories.updateWhere(
|
||||||
|
predicate = { it.id == category.id },
|
||||||
|
transform = {
|
||||||
|
it.copy(
|
||||||
|
name = newName,
|
||||||
|
gradientStart = newGradientStart,
|
||||||
|
gradientEnd = newGradientEnd,
|
||||||
|
timestamp = System.currentTimeMillis()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
"starred" -> StarredNotesScreen(
|
"starred" -> StarredNotesScreen(
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
package com.example.notesai.config
|
package com.example.notesai.config
|
||||||
|
|
||||||
object APIKey {
|
object APIKey {
|
||||||
const val GEMINI_API_KEY = "MY_GEMINI_KEY"
|
const val GEMINI_API_KEY = "AIzaSyBzC64RXsNtSERlts_FSd8HXKEpkLdT7-8"
|
||||||
}
|
}
|
||||||
@ -9,8 +9,8 @@ import androidx.datastore.preferences.core.edit
|
|||||||
import androidx.datastore.preferences.core.emptyPreferences
|
import androidx.datastore.preferences.core.emptyPreferences
|
||||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||||
import androidx.datastore.preferences.preferencesDataStore
|
import androidx.datastore.preferences.preferencesDataStore
|
||||||
import com.example.notesai.Category
|
import com.example.notesai.data.model.Note
|
||||||
import com.example.notesai.Note
|
import com.example.notesai.data.model.Category
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
import kotlinx.coroutines.flow.catch
|
import kotlinx.coroutines.flow.catch
|
||||||
import kotlinx.coroutines.flow.map
|
import kotlinx.coroutines.flow.map
|
||||||
|
|||||||
@ -1,9 +0,0 @@
|
|||||||
// File: data/local/PreferencesKeys.kt
|
|
||||||
package com.example.notesai.data.local
|
|
||||||
|
|
||||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
|
||||||
|
|
||||||
object PreferencesKeys {
|
|
||||||
val CATEGORIES_KEY = stringPreferencesKey("categories")
|
|
||||||
val NOTES_KEY = stringPreferencesKey("notes")
|
|
||||||
}
|
|
||||||
@ -1,67 +0,0 @@
|
|||||||
// File: data/model/SerializableModels.kt
|
|
||||||
package com.example.notesai.data.model
|
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
@Serializable
|
|
||||||
data class SerializableCategory(
|
|
||||||
val id: String,
|
|
||||||
val name: String,
|
|
||||||
val gradientStart: Long,
|
|
||||||
val gradientEnd: Long,
|
|
||||||
val timestamp: Long
|
|
||||||
)
|
|
||||||
|
|
||||||
@SuppressLint("UnsafeOptInUsageError")
|
|
||||||
@Serializable
|
|
||||||
data class SerializableNote(
|
|
||||||
val id: String,
|
|
||||||
val categoryId: String,
|
|
||||||
val title: String,
|
|
||||||
val content: String,
|
|
||||||
val timestamp: Long,
|
|
||||||
val isArchived: Boolean,
|
|
||||||
val isDeleted: Boolean,
|
|
||||||
val isPinned: Boolean
|
|
||||||
)
|
|
||||||
|
|
||||||
// Extension functions untuk konversi
|
|
||||||
fun Category.toSerializable() = SerializableCategory(
|
|
||||||
id = id,
|
|
||||||
name = name,
|
|
||||||
gradientStart = gradientStart,
|
|
||||||
gradientEnd = gradientEnd,
|
|
||||||
timestamp = timestamp
|
|
||||||
)
|
|
||||||
|
|
||||||
fun SerializableCategory.toCategory() = Category(
|
|
||||||
id = id,
|
|
||||||
name = name,
|
|
||||||
gradientStart = gradientStart,
|
|
||||||
gradientEnd = gradientEnd,
|
|
||||||
timestamp = timestamp
|
|
||||||
)
|
|
||||||
|
|
||||||
fun Note.toSerializable() = SerializableNote(
|
|
||||||
id = id,
|
|
||||||
categoryId = categoryId,
|
|
||||||
title = title,
|
|
||||||
content = content,
|
|
||||||
timestamp = timestamp,
|
|
||||||
isArchived = isArchived,
|
|
||||||
isDeleted = isDeleted,
|
|
||||||
isPinned = isPinned
|
|
||||||
)
|
|
||||||
|
|
||||||
fun SerializableNote.toNote() = Note(
|
|
||||||
id = id,
|
|
||||||
categoryId = categoryId,
|
|
||||||
title = title,
|
|
||||||
content = content,
|
|
||||||
timestamp = timestamp,
|
|
||||||
isArchived = isArchived,
|
|
||||||
isDeleted = isDeleted,
|
|
||||||
isPinned = isPinned
|
|
||||||
)
|
|
||||||
@ -25,7 +25,7 @@ import androidx.compose.ui.graphics.Brush
|
|||||||
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
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.notesai.Note
|
import com.example.notesai.data.model.Note
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun NoteDialog(
|
fun NoteDialog(
|
||||||
|
|||||||
@ -53,9 +53,9 @@ import androidx.compose.ui.text.AnnotatedString
|
|||||||
import androidx.compose.ui.text.font.FontWeight
|
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 com.example.notesai.Category
|
import com.example.notesai.data.model.Note
|
||||||
import com.example.notesai.ChatMessage
|
import com.example.notesai.data.model.ChatMessage
|
||||||
import com.example.notesai.Note
|
import com.example.notesai.data.model.Category
|
||||||
import com.example.notesai.config.APIKey
|
import com.example.notesai.config.APIKey
|
||||||
import com.example.notesai.presentation.screens.ai.components.ChatBubble
|
import com.example.notesai.presentation.screens.ai.components.ChatBubble
|
||||||
import com.example.notesai.presentation.screens.ai.components.CompactStatItem
|
import com.example.notesai.presentation.screens.ai.components.CompactStatItem
|
||||||
|
|||||||
@ -23,7 +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.ChatMessage
|
import com.example.notesai.data.model.ChatMessage
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|||||||
@ -8,8 +8,8 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.Archive
|
import androidx.compose.material.icons.filled.Archive
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.notesai.Category
|
import com.example.notesai.data.model.Note
|
||||||
import com.example.notesai.Note
|
import com.example.notesai.data.model.Category
|
||||||
import com.example.notesai.presentation.components.EmptyState
|
import com.example.notesai.presentation.components.EmptyState
|
||||||
import com.example.notesai.presentation.screens.archive.components.ArchiveNoteCard
|
import com.example.notesai.presentation.screens.archive.components.ArchiveNoteCard
|
||||||
|
|
||||||
|
|||||||
@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier
|
|||||||
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
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.notesai.Note
|
import com.example.notesai.data.model.Note
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun ArchiveNoteCard(
|
fun ArchiveNoteCard(
|
||||||
|
|||||||
@ -1,10 +1,7 @@
|
|||||||
|
// File: presentation/screens/main/MainScreen.kt
|
||||||
package com.example.notesai.presentation.screens.main
|
package com.example.notesai.presentation.screens.main
|
||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
|
||||||
import androidx.compose.foundation.layout.fillMaxSize
|
|
||||||
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
|
import androidx.compose.foundation.lazy.staggeredgrid.LazyVerticalStaggeredGrid
|
||||||
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
import androidx.compose.foundation.lazy.staggeredgrid.StaggeredGridCells
|
||||||
import androidx.compose.foundation.lazy.staggeredgrid.items
|
import androidx.compose.foundation.lazy.staggeredgrid.items
|
||||||
@ -15,12 +12,12 @@ import androidx.compose.material.icons.filled.Search
|
|||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.notesai.Category
|
import com.example.notesai.data.model.Category
|
||||||
import com.example.notesai.Note
|
import com.example.notesai.data.model.Note
|
||||||
import com.example.notesai.presentation.components.EmptyState
|
import com.example.notesai.presentation.components.EmptyState
|
||||||
import com.example.notesai.presentation.screens.main.components.CategoryCard
|
import com.example.notesai.presentation.screens.main.components.CategoryCard
|
||||||
|
import com.example.notesai.presentation.screens.main.components.NoteCard
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun MainScreen(
|
fun MainScreen(
|
||||||
categories: List<Category>,
|
categories: List<Category>,
|
||||||
@ -30,7 +27,8 @@ fun MainScreen(
|
|||||||
onCategoryClick: (Category) -> Unit,
|
onCategoryClick: (Category) -> Unit,
|
||||||
onNoteClick: (Note) -> Unit,
|
onNoteClick: (Note) -> Unit,
|
||||||
onPinToggle: (Note) -> Unit,
|
onPinToggle: (Note) -> Unit,
|
||||||
onCategoryDelete: (Category) -> Unit
|
onCategoryDelete: (Category) -> Unit,
|
||||||
|
onCategoryEdit: (Category, String, Long, Long) -> Unit // Parameter baru
|
||||||
) {
|
) {
|
||||||
Column(modifier = Modifier.fillMaxSize()) {
|
Column(modifier = Modifier.fillMaxSize()) {
|
||||||
if (selectedCategory == null) {
|
if (selectedCategory == null) {
|
||||||
@ -68,10 +66,15 @@ fun MainScreen(
|
|||||||
items(filteredCategories) { category ->
|
items(filteredCategories) { category ->
|
||||||
CategoryCard(
|
CategoryCard(
|
||||||
category = category,
|
category = category,
|
||||||
noteCount = notes.count { it.categoryId == category.id && !it.isDeleted && !it.isArchived },
|
noteCount = notes.count {
|
||||||
|
it.categoryId == category.id &&
|
||||||
|
!it.isDeleted &&
|
||||||
|
!it.isArchived
|
||||||
|
},
|
||||||
onClick = { onCategoryClick(category) },
|
onClick = { onCategoryClick(category) },
|
||||||
onDelete = {
|
onDelete = { onCategoryDelete(category) },
|
||||||
onCategoryDelete(category)
|
onEdit = { name, gradientStart, gradientEnd ->
|
||||||
|
onCategoryEdit(category, name, gradientStart, gradientEnd)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -108,16 +111,11 @@ fun MainScreen(
|
|||||||
NoteCard(
|
NoteCard(
|
||||||
note = note,
|
note = note,
|
||||||
onClick = { onNoteClick(note) },
|
onClick = { onNoteClick(note) },
|
||||||
onPinClick = { onPinToggle(note) },
|
onPinClick = { onPinToggle(note) }
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
@Composable
|
|
||||||
fun NoteCard(note: Note, onClick: () -> Unit, onPinClick: () -> Unit) {
|
|
||||||
TODO("Not yet implemented")
|
|
||||||
}
|
}
|
||||||
@ -1,58 +1,44 @@
|
|||||||
package com.example.notesai.presentation.screens.main.components
|
package com.example.notesai.presentation.screens.main.components
|
||||||
|
|
||||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.layout.Column
|
|
||||||
import androidx.compose.foundation.layout.Spacer
|
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
|
||||||
import androidx.compose.foundation.layout.height
|
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.size
|
|
||||||
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.filled.Close
|
import androidx.compose.material.icons.filled.*
|
||||||
import androidx.compose.material.icons.filled.Folder
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.material3.AlertDialog
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.material3.Button
|
|
||||||
import androidx.compose.material3.ButtonDefaults
|
|
||||||
import androidx.compose.material3.Card
|
|
||||||
import androidx.compose.material3.CardDefaults
|
|
||||||
import androidx.compose.material3.Icon
|
|
||||||
import androidx.compose.material3.IconButton
|
|
||||||
import androidx.compose.material3.MaterialTheme
|
|
||||||
import androidx.compose.material3.Text
|
|
||||||
import androidx.compose.runtime.Composable
|
|
||||||
import androidx.compose.runtime.getValue
|
|
||||||
import androidx.compose.runtime.mutableStateOf
|
|
||||||
import androidx.compose.runtime.remember
|
|
||||||
import androidx.compose.runtime.setValue
|
|
||||||
import androidx.compose.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.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
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
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.notesai.Category
|
import com.example.notesai.data.model.Category
|
||||||
|
|
||||||
@OptIn(ExperimentalFoundationApi::class)
|
|
||||||
@Composable
|
@Composable
|
||||||
fun CategoryCard(
|
fun CategoryCard(
|
||||||
category: Category,
|
category: Category,
|
||||||
noteCount: Int,
|
noteCount: Int,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
onDelete: () -> Unit = {}
|
onDelete: () -> Unit = {},
|
||||||
|
onEdit: (String, Long, Long) -> Unit = { _, _, _ -> }
|
||||||
) {
|
) {
|
||||||
var showDeleteConfirm by remember { mutableStateOf(false) }
|
var showDeleteConfirm by remember { mutableStateOf(false) }
|
||||||
|
var showEditDialog by remember { mutableStateOf(false) }
|
||||||
|
var showMenu by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
// Delete confirmation dialog
|
// Delete Confirmation Dialog
|
||||||
if (showDeleteConfirm) {
|
if (showDeleteConfirm) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = { showDeleteConfirm = false },
|
onDismissRequest = { showDeleteConfirm = false },
|
||||||
title = { Text("Hapus Kategori?", color = Color.White) },
|
title = { Text("Pindahkan ke Sampah?", color = Color.White) },
|
||||||
text = {
|
text = {
|
||||||
Text("Kategori '${category.name}' dan semua catatan di dalamnya akan dihapus. Tindakan ini tidak dapat dibatalkan.", color = Color.White)
|
Text(
|
||||||
|
"Kategori '${category.name}' dan semua catatan di dalamnya akan dipindahkan ke sampah.",
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
},
|
},
|
||||||
confirmButton = {
|
confirmButton = {
|
||||||
Button(
|
Button(
|
||||||
@ -81,6 +67,18 @@ fun CategoryCard(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Edit Dialog
|
||||||
|
if (showEditDialog) {
|
||||||
|
EditCategoryDialog(
|
||||||
|
category = category,
|
||||||
|
onDismiss = { showEditDialog = false },
|
||||||
|
onSave = { name, gradientStart, gradientEnd ->
|
||||||
|
onEdit(name, gradientStart, gradientEnd)
|
||||||
|
showEditDialog = false
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
@ -124,22 +122,199 @@ fun CategoryCard(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Delete button di top-right corner
|
// Menu Button (Titik Tiga)
|
||||||
IconButton(
|
Box(
|
||||||
onClick = {
|
modifier = Modifier.align(Alignment.TopEnd)
|
||||||
showDeleteConfirm = true
|
|
||||||
},
|
|
||||||
modifier = Modifier
|
|
||||||
.align(Alignment.TopEnd)
|
|
||||||
.size(40.dp)
|
|
||||||
) {
|
) {
|
||||||
Icon(
|
IconButton(
|
||||||
Icons.Default.Close,
|
onClick = { showMenu = true }
|
||||||
contentDescription = "Hapus kategori",
|
) {
|
||||||
tint = Color.White.copy(0.7f),
|
Icon(
|
||||||
modifier = Modifier.size(20.dp)
|
Icons.Default.MoreVert,
|
||||||
)
|
contentDescription = "Menu",
|
||||||
|
tint = Color.White.copy(0.9f)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
DropdownMenu(
|
||||||
|
expanded = showMenu,
|
||||||
|
onDismissRequest = { showMenu = false },
|
||||||
|
modifier = Modifier.background(Color(0xFF1E293B))
|
||||||
|
) {
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Edit,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = Color(0xFF6366F1),
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
Text("Edit Kategori", color = Color.White)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
showMenu = false
|
||||||
|
showEditDialog = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
DropdownMenuItem(
|
||||||
|
text = {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Delete,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = Color(0xFFEF4444),
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
Text("Pindah ke Sampah", color = Color.White)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onClick = {
|
||||||
|
showMenu = false
|
||||||
|
showDeleteConfirm = true
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun EditCategoryDialog(
|
||||||
|
category: Category,
|
||||||
|
onDismiss: () -> Unit,
|
||||||
|
onSave: (String, Long, Long) -> Unit
|
||||||
|
) {
|
||||||
|
val gradients = listOf(
|
||||||
|
Pair(0xFF6366F1L, 0xFFA855F7L),
|
||||||
|
Pair(0xFFEC4899L, 0xFFF59E0BL),
|
||||||
|
Pair(0xFF8B5CF6L, 0xFFEC4899L),
|
||||||
|
Pair(0xFF06B6D4L, 0xFF3B82F6L),
|
||||||
|
Pair(0xFF10B981L, 0xFF059669L),
|
||||||
|
Pair(0xFFF59E0BL, 0xFFEF4444L),
|
||||||
|
Pair(0xFF6366F1L, 0xFF8B5CF6L),
|
||||||
|
Pair(0xFFEF4444L, 0xFFDC2626L)
|
||||||
|
)
|
||||||
|
var name by remember { mutableStateOf(category.name) }
|
||||||
|
var selectedGradient by remember {
|
||||||
|
mutableStateOf(
|
||||||
|
gradients.indexOfFirst {
|
||||||
|
it.first == category.gradientStart && it.second == category.gradientEnd
|
||||||
|
}.takeIf { it >= 0 } ?: 0
|
||||||
|
)
|
||||||
|
}
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = onDismiss,
|
||||||
|
containerColor = Color(0xFF1E293B),
|
||||||
|
title = {
|
||||||
|
Text(
|
||||||
|
"Edit Kategori",
|
||||||
|
color = Color.White,
|
||||||
|
fontWeight = FontWeight.Bold
|
||||||
|
)
|
||||||
|
},
|
||||||
|
text = {
|
||||||
|
Column {
|
||||||
|
OutlinedTextField(
|
||||||
|
value = name,
|
||||||
|
onValueChange = { name = it },
|
||||||
|
label = { Text("Nama Kategori", color = Color(0xFF94A3B8)) },
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = TextFieldDefaults.colors(
|
||||||
|
focusedTextColor = Color.White,
|
||||||
|
unfocusedTextColor = Color.White,
|
||||||
|
focusedContainerColor = Color(0xFF334155),
|
||||||
|
unfocusedContainerColor = Color(0xFF334155),
|
||||||
|
cursorColor = Color(0xFFA855F7),
|
||||||
|
focusedIndicatorColor = Color(0xFFA855F7),
|
||||||
|
unfocusedIndicatorColor = Color(0xFF64748B)
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(12.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(20.dp))
|
||||||
|
Text(
|
||||||
|
"Pilih Gradient:",
|
||||||
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
|
color = Color.White,
|
||||||
|
fontWeight = FontWeight.SemiBold
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
|
gradients.chunked(4).forEach { row ->
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
row.forEachIndexed { _, gradient ->
|
||||||
|
val globalIndex = gradients.indexOf(gradient)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.weight(1f)
|
||||||
|
.aspectRatio(1f)
|
||||||
|
.clip(RoundedCornerShape(12.dp))
|
||||||
|
.background(
|
||||||
|
brush = Brush.linearGradient(
|
||||||
|
colors = listOf(
|
||||||
|
Color(gradient.first),
|
||||||
|
Color(gradient.second)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.clickable { selectedGradient = globalIndex },
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
if (selectedGradient == globalIndex) {
|
||||||
|
Icon(
|
||||||
|
Icons.Default.Check,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = Color.White,
|
||||||
|
modifier = Modifier.size(24.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
confirmButton = {
|
||||||
|
Button(
|
||||||
|
onClick = {
|
||||||
|
if (name.isNotBlank()) {
|
||||||
|
val gradient = gradients[selectedGradient]
|
||||||
|
onSave(name, gradient.first, gradient.second)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
enabled = name.isNotBlank(),
|
||||||
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = Color.Transparent
|
||||||
|
),
|
||||||
|
modifier = Modifier.background(
|
||||||
|
brush = Brush.linearGradient(
|
||||||
|
colors = listOf(Color(0xFF6366F1), Color(0xFFA855F7))
|
||||||
|
),
|
||||||
|
shape = RoundedCornerShape(8.dp)
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text("Simpan", color = Color.White, fontWeight = FontWeight.Bold)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = onDismiss) {
|
||||||
|
Text("Batal", color = Color(0xFF94A3B8))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
@ -16,7 +16,6 @@ import androidx.compose.material.icons.filled.Star
|
|||||||
import androidx.compose.material.icons.outlined.StarBorder
|
import androidx.compose.material.icons.outlined.StarBorder
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
import androidx.compose.material3.Divider
|
|
||||||
import androidx.compose.material3.HorizontalDivider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.material3.IconButton
|
import androidx.compose.material3.IconButton
|
||||||
@ -30,8 +29,7 @@ import androidx.compose.ui.text.font.FontWeight
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
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.Note
|
import com.example.notesai.data.model.Note
|
||||||
import com.example.notesai.util.Constants.AppColors.Divider
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
@ -112,7 +110,7 @@ fun NoteCard(
|
|||||||
|
|
||||||
Spacer(modifier = Modifier.height(12.dp))
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
|
||||||
Divider(
|
HorizontalDivider(
|
||||||
color = Color(0xFF334155),
|
color = Color(0xFF334155),
|
||||||
thickness = 1.dp
|
thickness = 1.dp
|
||||||
)
|
)
|
||||||
|
|||||||
@ -39,8 +39,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.Note
|
import com.example.notesai.data.model.Note
|
||||||
import com.example.notesai.util.Constants.AppColors.Divider
|
|
||||||
import java.text.SimpleDateFormat
|
import java.text.SimpleDateFormat
|
||||||
import java.util.Date
|
import java.util.Date
|
||||||
import java.util.Locale
|
import java.util.Locale
|
||||||
|
|||||||
@ -1,4 +1,4 @@
|
|||||||
package com.example.notesai.presentation.screens.starred.components
|
package com.example.notesai.presentation.screens.starred
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
@ -9,10 +9,10 @@ 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.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.notesai.Category
|
|
||||||
import com.example.notesai.Note
|
|
||||||
import com.example.notesai.presentation.components.EmptyState
|
import com.example.notesai.presentation.components.EmptyState
|
||||||
import com.example.notesai.presentation.screens.starred.StarredNoteCard
|
import com.example.notesai.presentation.screens.starred.components.StarredNoteCard
|
||||||
|
import com.example.notesai.data.model.Note
|
||||||
|
import com.example.notesai.data.model.Category
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.example.notesai.presentation.screens.starred
|
package com.example.notesai.presentation.screens.starred.components
|
||||||
|
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@ -28,7 +28,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.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.notesai.Note
|
import com.example.notesai.data.model.Note
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun StarredNoteCard(
|
fun StarredNoteCard(
|
||||||
@ -1,4 +1,4 @@
|
|||||||
package com.example.notesai.presentation.screens.trash.components
|
package com.example.notesai.presentation.screens.trash
|
||||||
|
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.PaddingValues
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
@ -8,9 +8,10 @@ import androidx.compose.material.icons.Icons
|
|||||||
import androidx.compose.material.icons.filled.Delete
|
import androidx.compose.material.icons.filled.Delete
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.notesai.Category
|
|
||||||
import com.example.notesai.Note
|
|
||||||
import com.example.notesai.presentation.components.EmptyState
|
import com.example.notesai.presentation.components.EmptyState
|
||||||
|
import com.example.notesai.presentation.screens.trash.components.TrashNoteCard
|
||||||
|
import com.example.notesai.data.model.Note
|
||||||
|
import com.example.notesai.data.model.Category
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TrashScreen(
|
fun TrashScreen(
|
||||||
@ -24,7 +24,7 @@ import androidx.compose.ui.Modifier
|
|||||||
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
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import com.example.notesai.Note
|
import com.example.notesai.data.model.Note
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
fun TrashNoteCard(
|
fun TrashNoteCard(
|
||||||
|
|||||||
@ -24,4 +24,8 @@ fun <T> List<T>.replaceWhere(predicate: (T) -> Boolean, transform: (T) -> T): Li
|
|||||||
|
|
||||||
fun <T> List<T>.removeWhere(predicate: (T) -> Boolean): List<T> {
|
fun <T> List<T>.removeWhere(predicate: (T) -> Boolean): List<T> {
|
||||||
return this.filter { !predicate(it) }
|
return this.filter { !predicate(it) }
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> List<T>.updateWhere(predicate: (T) -> Boolean, transform: (T) -> T): List<T> {
|
||||||
|
return this.map { if (predicate(it)) transform(it) else it }
|
||||||
}
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user