Upload via GUI (4909 Dateien)

This commit is contained in:
2026-10-26 21:31:35 +00:00
parent 1d5cb064a4
commit a322212ef5
3 changed files with 31 additions and 12 deletions
@@ -227,9 +227,11 @@ class TicketRepository(
status: String?,
assignedStaffId: Int?,
updateAssignment: Boolean,
resolutionCode: String? = null,
): TicketSummary {
val request = linkedMapOf<String, Any?>("number" to number)
status?.let { request["status"] = it }
resolutionCode?.let { request["resolutionCode"] = it }
if (updateAssignment) request["assignedStaffId"] = assignedStaffId
return requireApi().updateTicket(request).ticket.toDomain()
}
@@ -264,8 +266,8 @@ class TicketRepository(
)
}
suspend fun addMessage(number: String, type: String, message: String, setStatus: String?, signature: String? = null) {
requireApi().addMessage(MessageRequest(number, type, message, setStatus, signature))
suspend fun addMessage(number: String, type: String, message: String, setStatus: String?, signature: String? = null, resolutionCode: String? = null) {
requireApi().addMessage(MessageRequest(number, type, message, setStatus, signature, resolutionCode))
}
suspend fun cannedResponses(): List<CannedResponse> =
@@ -5365,8 +5365,8 @@ private fun TicketDetailScreen(
state: DeskHubState,
onBack: () -> Unit,
onRetry: () -> Unit,
onUpdate: (String, Int?) -> Unit,
onMessage: (String, String, String?, String?, () -> Unit) -> Unit,
onUpdate: (String, Int?, String?) -> Unit,
onMessage: (String, String, String?, String?, String?, () -> Unit) -> Unit,
onSpamAction: (Boolean) -> Unit,
onSnooze: (String?) -> Unit = {},
onFollow: (Boolean) -> Unit = {},
@@ -5421,8 +5421,8 @@ private fun TicketDetailContent(
cannedResponses: List<de.deskhub.staff.domain.model.CannedResponse>,
mutationLoading: Boolean,
mutationError: String?,
onUpdate: (String, Int?) -> Unit,
onMessage: (String, String, String?, String?, () -> Unit) -> Unit,
onUpdate: (String, Int?, String?) -> Unit,
onMessage: (String, String, String?, String?, String?, () -> Unit) -> Unit,
onSpamAction: (Boolean) -> Unit,
onSnooze: (String?) -> Unit = {},
onFollow: (Boolean) -> Unit = {},
@@ -5437,6 +5437,8 @@ private fun TicketDetailContent(
var messageType by rememberSaveable(ticket.summary.number) { mutableStateOf("reply") }
var message by rememberSaveable(ticket.summary.number) { mutableStateOf("") }
var replyStatus by rememberSaveable(ticket.summary.number) { mutableStateOf("answered") }
var resolutionCode by rememberSaveable(ticket.summary.number) { mutableStateOf("") }
val resolutionChoices = listOf("fixed" to "Fehler behoben", "guidance" to "Anleitung", "duplicate" to "Doppelt", "not_reproducible" to "Nicht reproduzierbar", "no_action" to "Keine Maßnahme", "spam" to "Spam", "automated" to "Automatisch", "other" to "Sonstiges")
// Signatur beim Antworten: "own", "dept" oder "none" (Voreinstellung vom Server)
var signatureChoice by rememberSaveable(ticket.summary.number) { mutableStateOf(ticket.replySignatures?.default ?: "none") }
// Textbausteine: nur die der Abteilung dieses Tickets und die für alle Abteilungen (ältere Server liefern alle)
@@ -5726,14 +5728,21 @@ private fun TicketDetailContent(
}
}
Button(
onClick = { onUpdate(selectedStatus, selectedAgent) },
onClick = { onUpdate(selectedStatus, selectedAgent, resolutionCode.takeIf { selectedStatus in setOf("resolved", "closed") }) },
modifier = Modifier.fillMaxWidth(),
shape = MaterialTheme.shapes.medium,
enabled = !mutationLoading &&
(selectedStatus != ticket.summary.status || selectedAgent != ticket.summary.assignedStaff?.id),
(selectedStatus != ticket.summary.status || selectedAgent != ticket.summary.assignedStaff?.id) &&
(selectedStatus !in setOf("resolved", "closed") || selectedStatus == ticket.summary.status || resolutionCode.isNotBlank()),
) {
Text(if (mutationLoading) "Speichert …" else "Änderungen speichern")
}
if (selectedStatus in setOf("resolved", "closed")) {
var reasonExpanded by remember { mutableStateOf(false) }
SelectionField(label = "Abschlussgrund", value = resolutionChoices.firstOrNull { it.first == resolutionCode }?.second ?: "Bitte auswählen", expanded = reasonExpanded, onExpand = { reasonExpanded = true }, onDismiss = { reasonExpanded = false }) {
resolutionChoices.forEach { (code, label) -> DropdownMenuItem(text = { Text(label) }, onClick = { resolutionCode = code; reasonExpanded = false }) }
}
}
}
}
}
@@ -5886,6 +5895,12 @@ private fun TicketDetailContent(
)
}
}
if (replyStatus in setOf("resolved", "closed")) {
var reasonExpanded by remember { mutableStateOf(false) }
SelectionField(label = "Abschlussgrund", value = resolutionChoices.firstOrNull { it.first == resolutionCode }?.second ?: "Bitte auswählen", expanded = reasonExpanded, onExpand = { reasonExpanded = true }, onDismiss = { reasonExpanded = false }) {
resolutionChoices.forEach { (code, label) -> DropdownMenuItem(text = { Text(label) }, onClick = { resolutionCode = code; reasonExpanded = false }) }
}
}
}
mutationError?.let { ErrorText(it) }
Button(
@@ -5895,13 +5910,14 @@ private fun TicketDetailContent(
message,
replyStatus.takeIf { messageType == "reply" },
signatureChoice.takeIf { messageType == "reply" && ticket.replySignatures != null },
resolutionCode.takeIf { messageType == "reply" && replyStatus in setOf("resolved", "closed") },
) { message = "" }
},
modifier = Modifier
.fillMaxWidth()
.height(48.dp),
shape = MaterialTheme.shapes.medium,
enabled = !mutationLoading && message.isNotBlank() &&
enabled = !mutationLoading && message.isNotBlank() && (messageType != "reply" || replyStatus !in setOf("resolved", "closed") || resolutionCode.isNotBlank()) &&
((messageType == "reply" && canReply && !chatLockedByOther) || (messageType == "note" && canNote)),
) {
if (!mutationLoading) {
@@ -530,7 +530,7 @@ class DeskHubViewModel(
}
}
fun updateTicket(status: String, assignedStaffId: Int?) {
fun updateTicket(status: String, assignedStaffId: Int?, resolutionCode: String?) {
val ticket = state.value.selectedTicket ?: return
viewModelScope.launch {
mutableState.update { it.copy(mutationLoading = true, mutationError = null) }
@@ -541,6 +541,7 @@ class DeskHubViewModel(
status = status.takeIf { it != ticket.summary.status },
assignedStaffId = assignedStaffId,
updateAssignment = updateAssignment,
resolutionCode = resolutionCode,
)
}.onSuccess {
mutableState.update { it.copy(mutationLoading = false) }
@@ -720,11 +721,11 @@ class DeskHubViewModel(
}
}
fun addMessage(type: String, message: String, setStatus: String?, signature: String?, onSuccess: () -> Unit) {
fun addMessage(type: String, message: String, setStatus: String?, signature: String?, resolutionCode: String?, onSuccess: () -> Unit) {
val number = state.value.selectedTicket?.summary?.number ?: return
viewModelScope.launch {
mutableState.update { it.copy(mutationLoading = true, mutationError = null) }
runCatching { repository.addMessage(number, type, message, setStatus, signature) }
runCatching { repository.addMessage(number, type, message, setStatus, signature, resolutionCode) }
.onSuccess {
mutableState.update { it.copy(mutationLoading = false) }
onSuccess()