From d0cd8701c7d403a473dc8bfa60f6492c60075e96 Mon Sep 17 00:00:00 2001 From: nyk <93952610+0xNyk@users.noreply.github.com> Date: Tue, 17 Mar 2026 13:52:34 +0700 Subject: [PATCH] feat: add Dunk It button animation with CSS transitions (#427) Add a CSS-only Dunk It button to task cards with a 4-phase state machine (idle, success, error, dismissing). Uses inline CSS transitions for animations with no external dependencies. Supersedes #373 --- messages/ar.json | 3 +- messages/de.json | 3 +- messages/en.json | 3 +- messages/es.json | 3 +- messages/fr.json | 3 +- messages/ja.json | 3 +- messages/ko.json | 3 +- messages/pt.json | 3 +- messages/ru.json | 3 +- messages/zh.json | 3 +- src/components/panels/task-board-panel.tsx | 54 ++++++++++++++++++++++ 11 files changed, 74 insertions(+), 10 deletions(-) diff --git a/messages/ar.json b/messages/ar.json index 57d4313..c68202e 100644 --- a/messages/ar.json +++ b/messages/ar.json @@ -670,7 +670,8 @@ "reviewNotesPlaceholder": "ملاحظات المراجعة...", "submit": "إرسال", "loadingTranscript": "جارٍ تحميل النص...", - "noSessionMessages": "لا توجد رسائل جلسة" + "noSessionMessages": "لا توجد رسائل جلسة", + "dunkIt": "أنجزها" }, "cronManagement": { "title": "إدارة Cron", diff --git a/messages/de.json b/messages/de.json index e81446c..50bcace 100644 --- a/messages/de.json +++ b/messages/de.json @@ -670,7 +670,8 @@ "reviewNotesPlaceholder": "Prüfnotizen...", "submit": "Einreichen", "loadingTranscript": "Transkript laden...", - "noSessionMessages": "Keine Sitzungsnachrichten" + "noSessionMessages": "Keine Sitzungsnachrichten", + "dunkIt": "Erledigen" }, "cronManagement": { "title": "Cron-Verwaltung", diff --git a/messages/en.json b/messages/en.json index 24c8d18..6fe09a9 100644 --- a/messages/en.json +++ b/messages/en.json @@ -819,7 +819,8 @@ "reviewNotesPlaceholder": "Review notes...", "submit": "Submit", "loadingTranscript": "Loading transcript...", - "noSessionMessages": "No session messages" + "noSessionMessages": "No session messages", + "dunkIt": "Dunk It" }, "cronManagement": { "title": "Cron Management", diff --git a/messages/es.json b/messages/es.json index 93c5783..9bbfc0f 100644 --- a/messages/es.json +++ b/messages/es.json @@ -670,7 +670,8 @@ "reviewNotesPlaceholder": "Notas de revisión...", "submit": "Enviar", "loadingTranscript": "Cargando transcripción...", - "noSessionMessages": "Sin mensajes de sesión" + "noSessionMessages": "Sin mensajes de sesión", + "dunkIt": "Completar" }, "cronManagement": { "title": "Gestión de cron", diff --git a/messages/fr.json b/messages/fr.json index 53fb875..2663690 100644 --- a/messages/fr.json +++ b/messages/fr.json @@ -670,7 +670,8 @@ "reviewNotesPlaceholder": "Notes de révision...", "submit": "Soumettre", "loadingTranscript": "Chargement de la transcription...", - "noSessionMessages": "Aucun message de session" + "noSessionMessages": "Aucun message de session", + "dunkIt": "Terminer" }, "cronManagement": { "title": "Gestion des crons", diff --git a/messages/ja.json b/messages/ja.json index 3b08c64..8b8ad28 100644 --- a/messages/ja.json +++ b/messages/ja.json @@ -670,7 +670,8 @@ "reviewNotesPlaceholder": "レビューノート...", "submit": "送信", "loadingTranscript": "トランスクリプトを読み込み中...", - "noSessionMessages": "セッションメッセージなし" + "noSessionMessages": "セッションメッセージなし", + "dunkIt": "完了" }, "cronManagement": { "title": "Cron管理", diff --git a/messages/ko.json b/messages/ko.json index efa8edf..4d97499 100644 --- a/messages/ko.json +++ b/messages/ko.json @@ -670,7 +670,8 @@ "reviewNotesPlaceholder": "검토 메모...", "submit": "제출", "loadingTranscript": "기록 로딩 중...", - "noSessionMessages": "세션 메시지 없음" + "noSessionMessages": "세션 메시지 없음", + "dunkIt": "완료" }, "cronManagement": { "title": "크론 관리", diff --git a/messages/pt.json b/messages/pt.json index 4c000df..e9bb472 100644 --- a/messages/pt.json +++ b/messages/pt.json @@ -670,7 +670,8 @@ "reviewNotesPlaceholder": "Notas de revisão...", "submit": "Enviar", "loadingTranscript": "Carregando transcrição...", - "noSessionMessages": "Sem mensagens de sessão" + "noSessionMessages": "Sem mensagens de sessão", + "dunkIt": "Concluir" }, "cronManagement": { "title": "Gerenciamento de cron", diff --git a/messages/ru.json b/messages/ru.json index 31b526a..80fb30b 100644 --- a/messages/ru.json +++ b/messages/ru.json @@ -670,7 +670,8 @@ "reviewNotesPlaceholder": "Заметки проверки...", "submit": "Отправить", "loadingTranscript": "Загрузка транскрипта...", - "noSessionMessages": "Нет сообщений сессии" + "noSessionMessages": "Нет сообщений сессии", + "dunkIt": "Завершить" }, "cronManagement": { "title": "Управление cron", diff --git a/messages/zh.json b/messages/zh.json index 61632d7..0258bb1 100644 --- a/messages/zh.json +++ b/messages/zh.json @@ -908,7 +908,8 @@ "reviewNotesPlaceholder": "审查备注...", "submit": "提交", "loadingTranscript": "加载记录中...", - "noSessionMessages": "无会话消息" + "noSessionMessages": "无会话消息", + "dunkIt": "完成" }, "cronManagement": { "title": "定时任务管理", diff --git a/src/components/panels/task-board-panel.tsx b/src/components/panels/task-board-panel.tsx index 5367f1e..a7a27d9 100644 --- a/src/components/panels/task-board-panel.tsx +++ b/src/components/panels/task-board-panel.tsx @@ -310,6 +310,57 @@ function MentionTextarea({ ) } +type DunkPhase = 'idle' | 'success' | 'error' | 'dismissing' + +function DunkItButton({ taskId, onDunked }: { taskId: number; onDunked: (id: number) => void }) { + const t = useTranslations('taskBoard') + const [phase, setPhase] = useState('idle') + + const handleClick = async (e: React.MouseEvent) => { + e.stopPropagation() + if (phase !== 'idle') return + try { + const res = await fetch(`/api/tasks/${taskId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ status: 'done' }), + }) + if (!res.ok) throw new Error('Failed') + setPhase('success') + setTimeout(() => { + setPhase('dismissing') + setTimeout(() => onDunked(taskId), 400) + }, 600) + } catch { + setPhase('error') + setTimeout(() => setPhase('idle'), 1500) + } + } + + return ( + + ) +} + interface SpawnFormData { task: string model: string @@ -990,6 +1041,9 @@ export function TaskBoardPanel() { )}
+ {task.status !== 'done' && ( + fetchData()} /> + )}