feat(ui): checklist report types with emoji icons; improved card styling and 3D panic button

This commit is contained in:
Rakha adi 2025-11-19 21:30:38 +07:00
parent 97d37a43ad
commit f0d6074b55
3 changed files with 90 additions and 33 deletions

View File

@ -13,6 +13,9 @@
<option value="f607f271-4885-4c4e-91da-e2caf2abd67a" /> <option value="f607f271-4885-4c4e-91da-e2caf2abd67a" />
<option value="39a0d9c4-a68f-4050-bb14-eda17dc695db" /> <option value="39a0d9c4-a68f-4050-bb14-eda17dc695db" />
<option value="94d90579-6a6d-44a9-8b93-de694c0c38ef" /> <option value="94d90579-6a6d-44a9-8b93-de694c0c38ef" />
<option value="fe272d92-3acf-42e8-bd88-66dc00800c3f" />
<option value="ea6afe5a-c296-461b-a4a7-49209b3d7937" />
<option value="2b41e5bf-4d90-46d8-8fc8-7b41c3f788e1" />
</set> </set>
</value> </value>
</entry> </entry>

18
.idea/copilotDiffState.xml generated Normal file

File diff suppressed because one or more lines are too long

View File

@ -46,54 +46,89 @@ fun MyApp() {
// dialogMessage akan menampung hasil callback dari sendNotification; null = tidak tampil // dialogMessage akan menampung hasil callback dari sendNotification; null = tidak tampil
var dialogMessage by remember { mutableStateOf<String?>(null) } var dialogMessage by remember { mutableStateOf<String?>(null) }
// Report options (checkbox-style) // theme color for panic
val reportOptions = listOf("Kebakaran", "Banjir", "Gempa Bumi", "Huru Hara/Demostrasi", "Lainnya") val panicColor = Color(0xFFB71C1C)
val checkedMap = remember { mutableStateMapOf<String, Boolean>().apply { reportOptions.forEach { put(it, false) } } }
// Report options (checkbox-style) with icons (emoji from ntfy emoji list)
val reportOptions = listOf(
"Kebakaran" to "🔥",
"Banjir" to "🌊",
"Gempa Bumi" to "🌍",
"Huru Hara/Demostrasi" to "📣",
"Lainnya" to "📝"
)
val checkedMap = remember { mutableStateMapOf<String, Boolean>().apply { reportOptions.forEach { put(it.first, false) } } }
var otherNote by remember { mutableStateOf("") } var otherNote by remember { mutableStateOf("") }
// UI // UI
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.padding(16.dp), .padding(18.dp),
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
// Checklist area similar to the wireframe // Checklist area styled as card
Surface( Surface(
tonalElevation = 2.dp, color = Color(0xFFFFEBEE), // soft red/pink background to match panic theme
shape = RoundedCornerShape(8.dp), tonalElevation = 4.dp,
shape = RoundedCornerShape(10.dp),
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth(0.92f)
.padding(horizontal = 24.dp) .padding(horizontal = 12.dp)
) { ) {
Column(modifier = Modifier.padding(12.dp)) { Column(modifier = Modifier.padding(14.dp)) {
Text(text = "Terjadi Kondisi Darurat", fontWeight = FontWeight.SemiBold) Text(
Spacer(modifier = Modifier.height(8.dp)) text = "Terjadi Kondisi Darurat",
fontWeight = FontWeight.SemiBold,
fontSize = 18.sp,
color = panicColor
)
// Each option as a row with checkbox Spacer(modifier = Modifier.height(10.dp))
reportOptions.forEach { option ->
// Each option as a row with icon, label and checkbox aligned nicely
reportOptions.forEach { (label, icon) ->
Row( Row(
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = 4.dp) .padding(vertical = 6.dp)
.toggleable( .toggleable(
value = checkedMap[option] ?: false, value = checkedMap[label] ?: false,
onValueChange = { checked -> checkedMap[option] = checked } onValueChange = { checked -> checkedMap[label] = checked }
) )
) { ) {
// icon circle
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.size(34.dp)
.background(color = Color.White.copy(alpha = 0.9f), shape = CircleShape)
.shadow(1.dp, shape = CircleShape)
) {
Text(text = icon, fontSize = 18.sp)
}
Spacer(modifier = Modifier.width(12.dp))
Column(modifier = Modifier.weight(1f)) {
Text(text = label, fontSize = 16.sp)
}
Checkbox( Checkbox(
checked = checkedMap[option] ?: false, checked = checkedMap[label] ?: false,
onCheckedChange = { checked -> checkedMap[option] = checked } onCheckedChange = { checked -> checkedMap[label] = checked },
colors = CheckboxDefaults.colors(
checkedColor = panicColor,
uncheckedColor = Color.DarkGray
)
) )
Spacer(modifier = Modifier.width(8.dp))
Text(text = option)
} }
} }
// Additional notes field — show always (wireframe shows a note field)
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
OutlinedTextField( OutlinedTextField(
value = otherNote, value = otherNote,
onValueChange = { otherNote = it }, onValueChange = { otherNote = it },
@ -101,7 +136,8 @@ fun MyApp() {
placeholder = { Text("Catatan tambahan ...") }, placeholder = { Text("Catatan tambahan ...") },
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 8.dp) .heightIn(min = 56.dp, max = 140.dp),
maxLines = 4
) )
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
@ -110,7 +146,7 @@ fun MyApp() {
if ((checkedMap["Lainnya"] == true) && otherNote.isBlank()) { if ((checkedMap["Lainnya"] == true) && otherNote.isBlank()) {
Text( Text(
text = "Catatan wajib jika Anda memilih 'Lainnya'", text = "Catatan wajib jika Anda memilih 'Lainnya'",
color = Color(0xFFB71C1C), color = panicColor,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
modifier = Modifier.padding(top = 6.dp) modifier = Modifier.padding(top = 6.dp)
) )
@ -118,12 +154,12 @@ fun MyApp() {
} }
} }
Spacer(modifier = Modifier.height(18.dp)) Spacer(modifier = Modifier.height(20.dp))
// Keep the existing nice PanicButton unchanged // Keep the existing nice PanicButton unchanged (slightly smaller spacing)
PanicButton(onClick = { PanicButton(onClick = {
// Validation: at least one option selected // Validation: at least one option selected
val selected = reportOptions.filter { checkedMap[it] == true } val selected = reportOptions.map { it.first }.filter { checkedMap[it] == true }
if (selected.isEmpty()) { if (selected.isEmpty()) {
dialogMessage = "Pilih minimal satu jenis laporan sebelum mengirim." dialogMessage = "Pilih minimal satu jenis laporan sebelum mengirim."
return@PanicButton return@PanicButton
@ -139,15 +175,15 @@ fun MyApp() {
append(selected.joinToString(", ")) append(selected.joinToString(", "))
append("\n") append("\n")
append("Keterangan: ") append("Keterangan: ")
if (selected.contains("Lainnya")) append(otherNote.trim()) else append("-") if (otherNote.isNotBlank()) append(otherNote.trim()) else append("-")
append("\n") append("\n")
append("Pengirim: Satrio Putra Wardani 202310715307") append("Pengirim: Rakha adi saputro 202310715083")
} }
sendNotification(message) { response -> dialogMessage = response } sendNotification(message) { response -> dialogMessage = response }
}) })
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(14.dp))
// dialog hasil // dialog hasil
if (dialogMessage != null) { if (dialogMessage != null) {
@ -174,7 +210,7 @@ fun MyApp() {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.background(color = Color(0xFFB71C1C)) .background(color = panicColor)
.padding(vertical = 12.dp), .padding(vertical = 12.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
@ -206,7 +242,7 @@ fun MyApp() {
) { ) {
Button( Button(
onClick = { dialogMessage = null }, onClick = { dialogMessage = null },
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFFB71C1C)) colors = ButtonDefaults.buttonColors(containerColor = panicColor)
) { ) {
Text(text = "OK", color = Color.White) Text(text = "OK", color = Color.White)
} }
@ -333,4 +369,4 @@ fun sendNotification(message: String, onResult: (String) -> Unit) {
onResult("Error: ${e.message}") onResult("Error: ${e.message}")
} }
}.start() }.start()
} }