From 6eeba1bba27d1576711c639e4c4ef9bf0568b495 Mon Sep 17 00:00:00 2001 From: b-mob-deck Date: Mon, 13 Jul 2026 07:28:46 +0000 Subject: [PATCH] =?UTF-8?q?feat(deck):=20=D0=BD=D0=B0=D0=B7=D0=BD=D0=B0?= =?UTF-8?q?=D1=87=D0=B8=D1=82=D1=8C/=D1=81=D0=BD=D1=8F=D1=82=D1=8C=20?= =?UTF-8?q?=D1=81=D0=B5=D0=B1=D1=8F=20=D0=BD=D0=B0=20=D0=BA=D0=B0=D1=80?= =?UTF-8?q?=D1=82=D0=BE=D1=87=D0=BA=D0=B5=20(P1=20diff=20=E2=80=94=20?= =?UTF-8?q?=D0=B8=D1=81=D0=BF=D0=BE=D0=BB=D0=BD=D0=B8=D1=82=D0=B5=D0=BB?= =?UTF-8?q?=D0=B8=20=D0=B1=D1=8B=D0=BB=D0=B8=20read-only)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit DeckRepository.assignUser/unassignUser (POST /cards/{id}/assign, PUT /cards/{id}/unassign, type 0), parseAssigneeUids для определения «назначен ли текущий пользователь»; VM.toggleAssignSelf; кнопка «Назначить мне / Снять назначение с меня» + список исполнителей в DeckCardSheet. Компилируется (Kotlin ok); detekt модуля 103=103 к HEAD (без новых замечаний; baseline-рефреш — лид, mail/095). --- .../f7cloud/feature/deck/DeckCardSheet.kt | 20 +++++++++++++ .../f7cloud/feature/deck/DeckRepository.kt | 29 +++++++++++++++++++ .../f7cloud/feature/deck/DeckScreen.kt | 2 ++ .../f7cloud/feature/deck/DeckViewModel.kt | 8 +++++ 4 files changed, 59 insertions(+) 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()) }