update task and schema module

This commit is contained in:
Marek Lenczewski
2026-04-14 18:15:05 +02:00
parent d576747155
commit 0d028beda8
23 changed files with 227 additions and 59 deletions

1
app/.gitignore vendored
View File

@@ -10,4 +10,3 @@
.cxx
local.properties
app/build
*.jks

View File

@@ -13,8 +13,8 @@ android {
applicationId = "de.haushalt.app"
minSdk = 26
targetSdk = 35
versionCode = 4
versionName = "0.1.0"
versionCode = 5
versionName = "0.1.1"
}
signingConfigs {
@@ -23,6 +23,9 @@ android {
storePassword = "haushalt123"
keyAlias = "haushalt"
keyPassword = "haushalt123"
enableV1Signing = true
enableV2Signing = true
enableV3Signing = true
}
}

View File

@@ -100,6 +100,7 @@ private fun SchemaRow(
schema.repeat.containsKey("2week") -> "2-Wöchentlich"
schema.repeat.containsKey("4week") -> "4-Wöchentlich"
schema.repeat.containsKey("monthly") -> "Monatlich"
schema.repeat.containsKey("days") -> "Mehrere Tage"
else -> ""
}

View File

@@ -5,16 +5,23 @@ import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.ExposedDropdownMenuBox
import androidx.compose.material3.ExposedDropdownMenuDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.MenuAnchorType
import androidx.compose.material3.OutlinedButton
import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Surface
import androidx.compose.material3.Text
@@ -23,10 +30,12 @@ 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.Modifier
import androidx.compose.ui.unit.dp
import de.haushalt.app.data.TaskSchemaStatus
import de.haushalt.app.data.TaskStatus
import de.haushalt.app.ui.task.DatePickerField
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -91,7 +100,7 @@ fun RepeatTypeDropdown(
current: String,
onChange: (String) -> Unit,
) {
val options = listOf("none" to "Keine (Einmalig)", "daily" to "Täglich", "weekly" to "Wöchentlich", "2week" to "2-Wöchentlich", "4week" to "4-Wöchentlich", "monthly" to "Monatlich")
val options = listOf("none" to "Keine (Einmalig)", "daily" to "Täglich", "weekly" to "Wöchentlich", "2week" to "2-Wöchentlich", "4week" to "4-Wöchentlich", "monthly" to "Monatlich", "days" to "Mehrere Tage")
var expanded by remember { mutableStateOf(false) }
ExposedDropdownMenuBox(expanded = expanded, onExpandedChange = { expanded = it }) {
OutlinedTextField(
@@ -172,3 +181,31 @@ fun MonthdaySelector(
}
}
}
@Composable
fun DaysSelector(
days: List<String>,
onChange: (List<String>) -> Unit,
) {
Text("Termine", style = MaterialTheme.typography.bodySmall, color = MaterialTheme.colorScheme.onSurfaceVariant)
days.forEachIndexed { i, day ->
Row(
horizontalArrangement = Arrangement.spacedBy(8.dp),
verticalAlignment = Alignment.CenterVertically,
) {
DatePickerField(
value = day,
onChange = { newVal -> onChange(days.toMutableList().also { it[i] = newVal }) },
label = "Datum",
modifier = Modifier.weight(1f),
)
IconButton(onClick = { onChange(days.toMutableList().also { it.removeAt(i) }) }) {
Icon(Icons.Filled.Delete, contentDescription = "Entfernen")
}
}
}
OutlinedButton(onClick = { onChange(days + "") }) {
Icon(Icons.Filled.Add, contentDescription = null)
Text("Termin")
}
}

View File

@@ -78,6 +78,13 @@ fun SchemaCreateScreen(
)
}
if (viewModel.repeatType == "days") {
DaysSelector(
days = viewModel.days,
onChange = { viewModel.days = it },
)
}
if (viewModel.repeatType == "monthly") {
MonthdaySelector(
selected = viewModel.monthly,
@@ -85,7 +92,7 @@ fun SchemaCreateScreen(
)
}
if (viewModel.repeatType != "none") {
if (viewModel.repeatType !in listOf("none", "days")) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),

View File

@@ -24,6 +24,7 @@ class SchemaCreateViewModel : ViewModel() {
var end by mutableStateOf("")
var weekly by mutableStateOf(List(7) { false })
var monthly by mutableStateOf(List(31) { false })
var days by mutableStateOf(listOf<String>())
var isSubmitting by mutableStateOf(false)
private set
var error by mutableStateOf<String?>(null)
@@ -51,6 +52,7 @@ class SchemaCreateViewModel : ViewModel() {
"2week" -> JsonObject(mapOf("2week" to buildJsonArray { weekly.forEach { add(JsonPrimitive(it)) } }))
"4week" -> JsonObject(mapOf("4week" to buildJsonArray { weekly.forEach { add(JsonPrimitive(it)) } }))
"monthly" -> JsonObject(mapOf("monthly" to buildJsonArray { monthly.forEach { add(JsonPrimitive(it)) } }))
"days" -> JsonObject(mapOf("days" to buildJsonArray { days.filter { it.isNotBlank() }.forEach { add(JsonPrimitive(it)) } }))
else -> null
}

View File

@@ -87,6 +87,13 @@ fun SchemaEditScreen(
)
}
if (viewModel.repeatType == "days") {
DaysSelector(
days = viewModel.days,
onChange = { viewModel.days = it },
)
}
if (viewModel.repeatType == "monthly") {
MonthdaySelector(
selected = viewModel.monthly,
@@ -94,7 +101,7 @@ fun SchemaEditScreen(
)
}
if (viewModel.repeatType != "none") {
if (viewModel.repeatType !in listOf("none", "days")) {
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp),
@@ -125,9 +132,6 @@ fun SchemaEditScreen(
) {
Text("Aktualisieren")
}
OutlinedButton(onClick = { viewModel.reset() }) {
Text("Zurücksetzen")
}
OutlinedButton(onClick = { navController.popBackStack() }) {
Text("Abbrechen")
}

View File

@@ -16,6 +16,7 @@ import kotlinx.serialization.json.JsonPrimitive
import kotlinx.serialization.json.boolean
import kotlinx.serialization.json.buildJsonArray
import kotlinx.serialization.json.jsonArray
import kotlinx.serialization.json.contentOrNull
import kotlinx.serialization.json.jsonPrimitive
class SchemaEditViewModel : ViewModel() {
@@ -28,6 +29,7 @@ class SchemaEditViewModel : ViewModel() {
var end by mutableStateOf("")
var weekly by mutableStateOf(List(7) { false })
var monthly by mutableStateOf(List(31) { false })
var days by mutableStateOf(listOf<String>())
var isLoading by mutableStateOf(false)
private set
var isSubmitting by mutableStateOf(false)
@@ -35,15 +37,12 @@ class SchemaEditViewModel : ViewModel() {
var error by mutableStateOf<String?>(null)
private set
private var original: TaskSchema? = null
fun load(id: Int) {
viewModelScope.launch {
isLoading = true
error = null
try {
val schema = ApiClient.schemaApi.get(id)
original = schema
applySchema(schema)
} catch (e: Exception) {
error = e.message
@@ -68,10 +67,6 @@ class SchemaEditViewModel : ViewModel() {
}
}
fun reset() {
original?.let { applySchema(it) }
}
private fun applySchema(schema: TaskSchema) {
name = schema.name
status = schema.status
@@ -85,31 +80,43 @@ class SchemaEditViewModel : ViewModel() {
repeatType = "none"
weekly = List(7) { false }
monthly = List(31) { false }
days = listOf()
}
schema.repeat.containsKey("daily") -> {
repeatType = "daily"
weekly = List(7) { false }
monthly = List(31) { false }
days = listOf()
}
schema.repeat.containsKey("weekly") -> {
repeatType = "weekly"
weekly = schema.repeat["weekly"]!!.jsonArray.map { it.jsonPrimitive.boolean }
monthly = List(31) { false }
days = listOf()
}
schema.repeat.containsKey("2week") -> {
repeatType = "2week"
weekly = schema.repeat["2week"]!!.jsonArray.map { it.jsonPrimitive.boolean }
monthly = List(31) { false }
days = listOf()
}
schema.repeat.containsKey("4week") -> {
repeatType = "4week"
weekly = schema.repeat["4week"]!!.jsonArray.map { it.jsonPrimitive.boolean }
monthly = List(31) { false }
days = listOf()
}
schema.repeat.containsKey("monthly") -> {
repeatType = "monthly"
weekly = List(7) { false }
monthly = schema.repeat["monthly"]!!.jsonArray.map { it.jsonPrimitive.boolean }
days = listOf()
}
schema.repeat.containsKey("days") -> {
repeatType = "days"
weekly = List(7) { false }
monthly = List(31) { false }
days = schema.repeat["days"]!!.jsonArray.map { it.jsonPrimitive.contentOrNull ?: "" }
}
}
}
@@ -121,6 +128,7 @@ class SchemaEditViewModel : ViewModel() {
"2week" -> JsonObject(mapOf("2week" to buildJsonArray { weekly.forEach { add(JsonPrimitive(it)) } }))
"4week" -> JsonObject(mapOf("4week" to buildJsonArray { weekly.forEach { add(JsonPrimitive(it)) } }))
"monthly" -> JsonObject(mapOf("monthly" to buildJsonArray { monthly.forEach { add(JsonPrimitive(it)) } }))
"days" -> JsonObject(mapOf("days" to buildJsonArray { days.filter { it.isNotBlank() }.forEach { add(JsonPrimitive(it)) } }))
else -> null
}

View File

@@ -69,9 +69,6 @@ fun TaskEditScreen(
) {
Text("Aktualisieren")
}
OutlinedButton(onClick = { viewModel.reset() }) {
Text("Zurücksetzen")
}
OutlinedButton(onClick = { navController.popBackStack() }) {
Text("Abbrechen")
}

View File

@@ -12,8 +12,6 @@ import de.haushalt.app.data.TaskStatus
import kotlinx.coroutines.launch
class TaskEditViewModel : ViewModel() {
private var original: Task? = null
var name by mutableStateOf("")
var date by mutableStateOf("")
var status by mutableStateOf(TaskStatus.active)
@@ -42,7 +40,6 @@ class TaskEditViewModel : ViewModel() {
error = null
try {
val task = ApiClient.taskApi.get(id)
original = task
applyTask(task)
} catch (e: Exception) {
error = e.message
@@ -74,10 +71,6 @@ class TaskEditViewModel : ViewModel() {
}
}
fun reset() {
original?.let { applyTask(it) }
}
private fun applyTask(task: Task) {
name = task.name
date = task.date?.take(10) ?: ""

BIN
app/haushalt.jks Executable file

Binary file not shown.