med-det-samme/app/src/main/java/no/naiv/meddetsamme/ui/ItemEditScreen.kt
Ole-Morten Duesund 608d84236b Rx-expiry ongoing alert (7-day lead), configurable morning check, Felleskatalogen links
The daily digest splits in two: low stock stays an auto-cancel
notification re-posted each morning while below the per-item limit;
prescription renewal becomes a separate ONGOING notification starting
7 days before rx expiry — survives 'Clear all', swipe-dismissable on
Android 14+, returns each morning until the rx date is updated, and
cancels itself once renewed (post-or-cancel in SupplyCheckReceiver).
Check time is now configurable (Innstillinger → Varsler, M3 TimePicker,
default 10:00); same PendingIntent so re-arming replaces. 'Slå opp i
Felleskatalogen' buttons on the item editor and the med editor's
summary card open the browser — decision #8 clarified: the ban covers
Felleskatalogen as a data source, not human-facing links.

Emulator-verified: both notifications post with correct flags
(ONGOING_EVENT vs AUTO_CANCEL), auto-clear after renewal/restock,
alarm follows the configured time (10:00 → 07:30), FK button opens
Chrome.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-11 16:21:25 +02:00

135 lines
5.2 KiB
Kotlin

package no.naiv.meddetsamme.ui
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.AlertDialog
import androidx.compose.material3.Button
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import no.naiv.meddetsamme.R
import no.naiv.meddetsamme.data.MedDatabase
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ItemEditScreen(nav: Nav, itemId: Long?) {
val context = LocalContext.current
val db = remember { MedDatabase.get(context) }
val scope = rememberCoroutineScope()
val form = remember { ItemFormState() }
var confirmDelete by remember { mutableStateOf(false) }
var deleteBlocked by remember { mutableStateOf(false) }
LaunchedEffect(itemId) {
itemId?.let { id -> db.inventoryDao().getById(id)?.let(form::loadFrom) }
}
Scaffold(
topBar = {
TopAppBar(
title = { Text(if (itemId == null) "Ny lagervare" else "Rediger lagervare") },
navigationIcon = {
IconButton(onClick = { nav.pop() }) {
Icon(painterResource(R.drawable.ic_back), contentDescription = "Tilbake")
}
},
)
},
) { padding ->
Column(
modifier = Modifier
.fillMaxSize()
.padding(padding)
.padding(horizontal = 16.dp)
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(10.dp),
) {
InventoryItemForm(form)
if (form.name.isNotBlank()) {
TextButton(
onClick = { context.startActivity(felleskatalogenIntent(form.name)) },
modifier = Modifier
.fillMaxWidth()
.semantics { contentDescription = "Slå opp ${form.name} i Felleskatalogen. Åpner nettleseren." },
) { Text("Slå opp i Felleskatalogen ↗") }
}
Button(
onClick = {
val item = form.buildItem(itemId ?: 0) ?: return@Button
scope.launch {
if (itemId == null) db.inventoryDao().insert(item) else db.inventoryDao().update(item)
nav.pop()
}
},
modifier = Modifier.fillMaxWidth(),
) { Text("Lagre") }
if (itemId != null) {
TextButton(
onClick = {
scope.launch {
if (db.inventoryDao().referencingMedCount(itemId) > 0) {
deleteBlocked = true
} else {
confirmDelete = true
}
}
},
modifier = Modifier.fillMaxWidth(),
) { Text("Slett vare", color = MaterialTheme.colorScheme.error) }
}
}
}
if (deleteBlocked) {
AlertDialog(
onDismissRequest = { deleteBlocked = false },
title = { Text("Varen er i bruk") },
text = { Text("En medisin bruker denne varen. Slett eller avslutt medisinen først.") },
confirmButton = { TextButton(onClick = { deleteBlocked = false }) { Text("OK") } },
)
}
if (confirmDelete) {
AlertDialog(
onDismissRequest = { confirmDelete = false },
title = { Text("Slette ${form.name.ifBlank { "varen" }}?") },
text = { Text("Dette kan ikke angres.") },
confirmButton = {
TextButton(onClick = {
confirmDelete = false
scope.launch {
itemId?.let { db.inventoryDao().delete(it) }
nav.pop()
}
}) { Text("Slett", color = MaterialTheme.colorScheme.error) }
},
dismissButton = { TextButton(onClick = { confirmDelete = false }) { Text("Avbryt") } },
)
}
}