Lul
This commit is contained in:
@@ -6,13 +6,42 @@ import androidx.room.Index
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
enum class FieldType {
|
||||
STRING,
|
||||
PRICE,
|
||||
// Text types
|
||||
TEXT,
|
||||
MULTILINE_TEXT,
|
||||
|
||||
// Number types
|
||||
NUMBER,
|
||||
CURRENCY,
|
||||
PERCENTAGE,
|
||||
|
||||
// Selection types
|
||||
DROPDOWN,
|
||||
AUTOCOMPLETE,
|
||||
CHECKBOX,
|
||||
SWITCH,
|
||||
|
||||
// Link types
|
||||
URL,
|
||||
EMAIL,
|
||||
PHONE,
|
||||
|
||||
// Date/Time types
|
||||
DATE,
|
||||
TIME,
|
||||
DATETIME
|
||||
DATETIME,
|
||||
DURATION,
|
||||
|
||||
// Media types
|
||||
IMAGE,
|
||||
FILE,
|
||||
BARCODE,
|
||||
SIGNATURE,
|
||||
|
||||
// Other types
|
||||
RATING,
|
||||
COLOR,
|
||||
LOCATION
|
||||
}
|
||||
|
||||
@Entity(
|
||||
@@ -31,8 +60,8 @@ data class Field(
|
||||
@PrimaryKey val id: String,
|
||||
val listId: String,
|
||||
val name: String,
|
||||
val fieldType: String = "STRING", // STRING, PRICE, DROPDOWN
|
||||
val fieldOptions: String = "", // JSON string for dropdown options or currency for price
|
||||
val fieldType: String = "TEXT",
|
||||
val fieldOptions: String = "", // JSON string for dropdown options, currency symbol, etc.
|
||||
val order: Int,
|
||||
val createdAt: Long,
|
||||
val updatedAt: Long,
|
||||
@@ -42,7 +71,14 @@ data class Field(
|
||||
return try {
|
||||
FieldType.valueOf(fieldType)
|
||||
} catch (e: Exception) {
|
||||
FieldType.STRING
|
||||
// Handle legacy field types
|
||||
when (fieldType) {
|
||||
"STRING" -> FieldType.TEXT
|
||||
"PRICE" -> FieldType.CURRENCY
|
||||
"AMOUNT" -> FieldType.NUMBER
|
||||
"SIZE" -> FieldType.TEXT
|
||||
else -> FieldType.TEXT
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,10 +91,26 @@ data class Field(
|
||||
}
|
||||
|
||||
fun getCurrency(): String {
|
||||
return if (fieldType == "PRICE" && fieldOptions.isNotBlank()) {
|
||||
return if ((fieldType == "CURRENCY" || fieldType == "PRICE") && fieldOptions.isNotBlank()) {
|
||||
fieldOptions
|
||||
} else {
|
||||
"$"
|
||||
"CHF"
|
||||
}
|
||||
}
|
||||
|
||||
fun getMaxRating(): Int {
|
||||
return if (fieldType == "RATING" && fieldOptions.isNotBlank()) {
|
||||
fieldOptions.toIntOrNull() ?: 5
|
||||
} else {
|
||||
5
|
||||
}
|
||||
}
|
||||
|
||||
fun getAutocompleteOptions(): List<String> {
|
||||
return if (fieldType == "AUTOCOMPLETE" && fieldOptions.isNotBlank()) {
|
||||
fieldOptions.split("|")
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+15
-1
@@ -57,15 +57,29 @@ class SyncRepository(context: Context) {
|
||||
|
||||
// Log details of lists being received
|
||||
syncResponse.lists.forEach { list ->
|
||||
Logger.d("Sync", " Inserting list: ${list.id} - ${list.name} (updated: ${list.updatedAt})")
|
||||
Logger.d("Sync", " Received list: ${list.id} - ${list.name} (updated: ${list.updatedAt}, deleted: ${list.isDeleted})")
|
||||
}
|
||||
|
||||
// Apply server changes to local database
|
||||
Logger.d("Sync", "Inserting ${syncResponse.lists.size} lists into local database")
|
||||
database.listDao().insertLists(syncResponse.lists)
|
||||
|
||||
Logger.d("Sync", "Inserting ${syncResponse.fields.size} fields into local database")
|
||||
database.fieldDao().insertFields(syncResponse.fields)
|
||||
|
||||
Logger.d("Sync", "Inserting ${syncResponse.items.size} items into local database")
|
||||
database.itemDao().insertItems(syncResponse.items)
|
||||
|
||||
Logger.d("Sync", "Inserting ${syncResponse.itemValues.size} item values into local database")
|
||||
database.itemValueDao().insertValues(syncResponse.itemValues)
|
||||
|
||||
// Verify what was actually saved
|
||||
val savedLists = database.listDao().getListsUpdatedSince(0)
|
||||
Logger.d("Sync", "After insert, total lists in DB: ${savedLists.size}")
|
||||
savedLists.forEach { list ->
|
||||
Logger.d("Sync", " DB list: ${list.id} - ${list.name} (deleted: ${list.isDeleted})")
|
||||
}
|
||||
|
||||
// Update last sync timestamp
|
||||
setLastSyncTimestamp(syncResponse.serverTimestamp)
|
||||
|
||||
|
||||
+943
-26
File diff suppressed because it is too large
Load Diff
+16
@@ -91,6 +91,22 @@ class ListDetailViewModel(
|
||||
updatedAt = timestamp
|
||||
)
|
||||
database.fieldDao().insertField(newField)
|
||||
|
||||
// Create empty ItemValue entries for this new field for all existing items
|
||||
val existingItems = _items.value
|
||||
if (existingItems.isNotEmpty()) {
|
||||
val newValues = existingItems.map { itemWithValues ->
|
||||
ItemValue(
|
||||
id = UUID.randomUUID().toString(),
|
||||
itemId = itemWithValues.item.id,
|
||||
fieldId = newField.id,
|
||||
value = "",
|
||||
updatedAt = timestamp
|
||||
)
|
||||
}
|
||||
database.itemValueDao().insertValues(newValues)
|
||||
}
|
||||
|
||||
performSync()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Add
|
||||
import androidx.compose.material.icons.filled.Delete
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.List
|
||||
import androidx.compose.material.icons.filled.Refresh
|
||||
import androidx.compose.material.icons.filled.Settings
|
||||
@@ -39,6 +40,7 @@ fun ListsScreen(
|
||||
|
||||
var showCreateDialog by remember { mutableStateOf(false) }
|
||||
var listToDelete by remember { mutableStateOf<CollabList?>(null) }
|
||||
var listToEdit by remember { mutableStateOf<CollabList?>(null) }
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
@@ -94,6 +96,7 @@ fun ListsScreen(
|
||||
ListItem(
|
||||
list = list,
|
||||
onListClick = { onNavigateToList(list.id) },
|
||||
onEditClick = { listToEdit = list },
|
||||
onDeleteClick = { listToDelete = list }
|
||||
)
|
||||
}
|
||||
@@ -111,6 +114,17 @@ fun ListsScreen(
|
||||
)
|
||||
}
|
||||
|
||||
listToEdit?.let { list ->
|
||||
RenameListDialog(
|
||||
currentName = list.name,
|
||||
onDismiss = { listToEdit = null },
|
||||
onRename = { newName ->
|
||||
viewModel.renameList(list.id, newName)
|
||||
listToEdit = null
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
listToDelete?.let { list ->
|
||||
AlertDialog(
|
||||
onDismissRequest = { listToDelete = null },
|
||||
@@ -139,6 +153,7 @@ fun ListsScreen(
|
||||
fun ListItem(
|
||||
list: CollabList,
|
||||
onListClick: () -> Unit,
|
||||
onEditClick: () -> Unit,
|
||||
onDeleteClick: () -> Unit
|
||||
) {
|
||||
Card(
|
||||
@@ -165,12 +180,21 @@ fun ListItem(
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onDeleteClick) {
|
||||
Icon(
|
||||
Icons.Default.Delete,
|
||||
contentDescription = stringResource(R.string.delete),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
Row {
|
||||
IconButton(onClick = onEditClick) {
|
||||
Icon(
|
||||
Icons.Default.Edit,
|
||||
contentDescription = "Edit",
|
||||
tint = MaterialTheme.colorScheme.primary
|
||||
)
|
||||
}
|
||||
IconButton(onClick = onDeleteClick) {
|
||||
Icon(
|
||||
Icons.Default.Delete,
|
||||
contentDescription = stringResource(R.string.delete),
|
||||
tint = MaterialTheme.colorScheme.error
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -210,6 +234,41 @@ fun CreateListDialog(
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun RenameListDialog(
|
||||
currentName: String,
|
||||
onDismiss: () -> Unit,
|
||||
onRename: (String) -> Unit
|
||||
) {
|
||||
var listName by remember { mutableStateOf(currentName) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
title = { Text("Rename List") },
|
||||
text = {
|
||||
OutlinedTextField(
|
||||
value = listName,
|
||||
onValueChange = { listName = it },
|
||||
label = { Text(stringResource(R.string.list_name)) },
|
||||
singleLine = true
|
||||
)
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = { if (listName.isNotBlank()) onRename(listName.trim()) },
|
||||
enabled = listName.isNotBlank() && listName.trim() != currentName
|
||||
) {
|
||||
Text("Rename")
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = onDismiss) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun formatDate(timestamp: Long): String {
|
||||
val sdf = SimpleDateFormat("MMM dd, yyyy HH:mm", Locale.getDefault())
|
||||
return sdf.format(Date(timestamp))
|
||||
|
||||
@@ -69,6 +69,23 @@ class ListsViewModel(
|
||||
}
|
||||
}
|
||||
|
||||
fun renameList(listId: String, newName: String) {
|
||||
viewModelScope.launch {
|
||||
Logger.i("ListsViewModel", "Renaming list: $listId to $newName")
|
||||
val list = database.listDao().getListById(listId)
|
||||
if (list != null && newName.isNotBlank()) {
|
||||
database.listDao().updateList(
|
||||
list.copy(
|
||||
name = newName.trim(),
|
||||
updatedAt = System.currentTimeMillis()
|
||||
)
|
||||
)
|
||||
// Sync immediately after renaming
|
||||
performSync()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteList(listId: String) {
|
||||
viewModelScope.launch {
|
||||
Logger.i("ListsViewModel", "Deleting list: $listId")
|
||||
|
||||
Reference in New Issue
Block a user