fix(chat,todo): show sent chat messages immediately + kanban drag-and-drop reorder

チャット送信の即時表示とKanbanのDnD並べ替えを修正。

- chat: 送信成功時に返却メッセージを楽観的にローカルへ先頭追加(SSE到着前でも即表示、idで重複排除)。setSending(false) をレスポンス処理後に移動し二重送信窓を縮小。
- todo: TodoService.reorderItems(projectId, columnId, itemIds[]) を追加(1トランザクションでカラム内を再採番、メンバーシップ+同プロジェクト検証、活動ログ/SSEは1件)。POST /todos/items/reorder を追加。
- KanbanBoard: カード単位のドロップターゲット(ポインタYで前/後挿入)+カラム空き領域は末尾追加。楽観的にorderIndex/columnIdを更新しrenderをorderIndexでソート(即時反映)、失敗時はエラー表示+再取得で巻き戻し。ドラッグ直後の誤クリック(ダイアログ誘起)をdragEndタイムスタンプで抑制。空の並べ替えはSSE/ログを出さない。
This commit is contained in:
Ken Yasue
2026-06-25 13:06:05 +02:00
parent 2588c0247b
commit 03cf27a148
6 changed files with 441 additions and 126 deletions

View File

@ -81,8 +81,19 @@ export function ChatWindow({ projectId, userName }: ChatWindowProps) {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ body: input, fileIds }),
});
setSending(false);
if (res.ok) {
// 送信したメッセージを即座に表示(SSE到着前でも見えるように楽観追加)。
// SSEの chat.message.created は id で重複排除する。
const data = (await res.json().catch(() => null)) as {
message?: ChatMessageWithAttachments;
} | null;
if (data?.message) {
setMessages((prev) =>
prev.some((m) => m.id === data.message!.id)
? prev
: [data.message!, ...prev]
);
}
setInput('');
pickerRef.current?.clear();
} else {
@ -91,6 +102,7 @@ export function ChatWindow({ projectId, userName }: ChatWindowProps) {
} | null;
setError(b?.error?.message ?? '送信に失敗しました');
}
setSending(false);
}
return (