feat(deck): назначить/снять себя на карточке (P1 diff — исполнители были read-only)
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).
This commit is contained in:
@@ -55,10 +55,12 @@ fun DeckCardSheet(
|
||||
stacks: List<DeckStack>,
|
||||
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)
|
||||
|
||||
@@ -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<DeckLabel> {
|
||||
@@ -126,6 +127,21 @@ class DeckRepository {
|
||||
return out
|
||||
}
|
||||
|
||||
/** uid'ы назначенных пользователей (type 0 — user) — для «назначен ли текущий пользователь». */
|
||||
private fun parseAssigneeUids(array: JSONArray?): List<String> {
|
||||
if (array == null) return emptyList()
|
||||
val out = mutableListOf<String>()
|
||||
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<DeckLabel> = emptyList(),
|
||||
val assignees: List<String> = emptyList(),
|
||||
val assigneeUids: List<String> = emptyList(),
|
||||
)
|
||||
|
||||
data class DeckBoardDetail(
|
||||
|
||||
@@ -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) },
|
||||
|
||||
@@ -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()) }
|
||||
|
||||
Reference in New Issue
Block a user