feat(ui): checklist report types with emoji icons; improved card styling and 3D panic button
This commit is contained in:
parent
97d37a43ad
commit
f0d6074b55
3
.idea/copilot.data.migration.agent.xml
generated
3
.idea/copilot.data.migration.agent.xml
generated
@ -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
18
.idea/copilotDiffState.xml
generated
Normal file
File diff suppressed because one or more lines are too long
@ -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()
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user