diff --git a/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckCardSheet.kt b/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckCardSheet.kt index 5bd6181..59a3a7d 100644 --- a/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckCardSheet.kt +++ b/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckCardSheet.kt @@ -55,10 +55,12 @@ fun DeckCardSheet( stacks: List, canEdit: Boolean, busy: Boolean, + assignedToMe: Boolean, onDismiss: () -> Unit, onToggleDone: () -> Unit, onSave: (title: String, description: String, duedate: String?) -> Unit, onToggleLabel: (DeckLabel) -> Unit, + onToggleAssignSelf: () -> Unit, onMove: (targetStackId: Int) -> Unit, onArchive: () -> Unit, onDelete: () -> Unit, @@ -146,6 +148,24 @@ fun DeckCardSheet( } } + // Исполнители + Text("Исполнители", style = MaterialTheme.typography.bodyMedium, color = F7Colors.TextSecondary) + if (card.assignees.isNotEmpty()) { + Text( + card.assignees.joinToString(", "), + style = MaterialTheme.typography.bodyMedium, + color = F7Colors.TextPrimary, + ) + } + if (canEdit) { + F7SecondaryButton( + text = if (assignedToMe) "Снять назначение с меня" else "Назначить мне", + onClick = onToggleAssignSelf, + enabled = !busy, + modifier = Modifier.fillMaxWidth(), + ) + } + // Переместить в колонку if (canEdit && stacks.size > 1) { Text("Колонка", style = MaterialTheme.typography.bodyMedium, color = F7Colors.TextSecondary) diff --git a/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckRepository.kt b/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckRepository.kt index 984900e..27196c7 100644 --- a/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckRepository.kt +++ b/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckRepository.kt @@ -99,6 +99,7 @@ class DeckRepository { stackId = stackId, labels = parseLabels(card.optJSONArray("labels")), assignees = parseAssignees(card.optJSONArray("assignedUsers")), + assigneeUids = parseAssigneeUids(card.optJSONArray("assignedUsers")), ) private fun parseLabels(array: JSONArray?): List { @@ -126,6 +127,21 @@ class DeckRepository { return out } + /** uid'ы назначенных пользователей (type 0 — user) — для «назначен ли текущий пользователь». */ + private fun parseAssigneeUids(array: JSONArray?): List { + if (array == null) return emptyList() + val out = mutableListOf() + for (i in 0 until array.length()) { + val a = array.optJSONObject(i) + if (a != null && a.optInt("type", 0) == 0) { + val uid = a.optJSONObject("participant")?.optString("uid")?.takeIf { it.isNotBlank() } + ?: a.optString("participant").takeIf { it.isNotBlank() } + if (!uid.isNullOrBlank()) out += uid + } + } + return out + } + // --- Запись --- fun createCard(session: AuthSession, stackId: Int, title: String): DeckCard { @@ -179,6 +195,18 @@ class DeckRepository { sendJson(session, "DELETE", "${apiBase(session)}/cards/$cardId/label/$labelId", null) } + /** Назначить пользователя (type 0) на карточку. */ + fun assignUser(session: AuthSession, cardId: Int, uid: String) { + val body = JSONObject().put("userId", uid).put("type", 0) + sendJson(session, "POST", "${apiBase(session)}/cards/$cardId/assign", body) + } + + /** Снять назначение пользователя (type 0) с карточки. */ + fun unassignUser(session: AuthSession, cardId: Int, uid: String) { + val body = JSONObject().put("userId", uid).put("type", 0) + sendJson(session, "PUT", "${apiBase(session)}/cards/$cardId/unassign", body) + } + fun createStack(session: AuthSession, boardId: Int, title: String): DeckStack { val body = JSONObject().put("title", title).put("boardId", boardId).put("order", 999) val json = sendJson(session, "POST", "${apiBase(session)}/stacks", body) as? JSONObject @@ -258,6 +286,7 @@ data class DeckCard( val stackId: Int = 0, val labels: List = emptyList(), val assignees: List = emptyList(), + val assigneeUids: List = emptyList(), ) data class DeckBoardDetail( diff --git a/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckScreen.kt b/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckScreen.kt index c67b566..d285697 100644 --- a/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckScreen.kt +++ b/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckScreen.kt @@ -135,10 +135,12 @@ fun DeckScreen( stacks = state.boardDetail?.stacks.orEmpty(), canEdit = state.boardDetail?.canEdit ?: false, busy = state.busy, + assignedToMe = card.assigneeUids.contains(session.username), onDismiss = vm::closeCard, onToggleDone = { vm.toggleDone(session, card) }, onSave = { title, desc, due -> vm.updateCard(session, card, title, desc, due) }, onToggleLabel = { vm.toggleLabel(session, card, it) }, + onToggleAssignSelf = { vm.toggleAssignSelf(session, card) }, onMove = { vm.moveCard(session, card, it) }, onArchive = { vm.archiveCard(session, card) }, onDelete = { vm.deleteCard(session, card) }, diff --git a/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckViewModel.kt b/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckViewModel.kt index c9515e2..1668b40 100644 --- a/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckViewModel.kt +++ b/feature/deck/src/main/java/ru/forbion/f7cloud/feature/deck/DeckViewModel.kt @@ -111,6 +111,14 @@ class DeckViewModel( } } + fun toggleAssignSelf(session: AuthSession, card: DeckCard) = mutate(session) { + if (card.assigneeUids.contains(session.username)) { + repository.unassignUser(session, card.id, session.username) + } else { + repository.assignUser(session, card.id, session.username) + } + } + fun createStack(session: AuthSession, title: String) { val boardId = _state.value.selectedBoardId ?: return mutate(session) { repository.createStack(session, boardId, title.trim()) }