feat: add "Retry All Errors" batch reanalyze button

Backend:
- POST /api/messages/reanalyze-batch — bulk reset error messages to pending
- messages.repository.reanalyzeErrorBatch() — scoped by guildId/channelId/messageIds
- messages.service.reanalyzeErrorBatch() — validation layer

Frontend:
- reanalyzeErrorBatch() API client function
- useMessages.reanalyzeAllErrors() — optimistic state + batch call
- MessagesPanel: destructive button visible when error count > 0
- Shows confirmation text with queued count

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
MythEclipse
2026-06-03 00:17:51 +07:00
co-authored by Claude Opus 4.8
parent d81aaf2b4a
commit 6da8a32c9b
7 changed files with 156 additions and 3 deletions
@@ -1,6 +1,6 @@
import { useCallback, useRef, useState } from "react";
import type { MessageRecord } from "../../../shared/api/client";
import { listMessages, reanalyzeMessage } from "../../../shared/api/client";
import { listMessages, reanalyzeErrorBatch, reanalyzeMessage } from "../../../shared/api/client";
const PAGE_SIZE = 100;
@@ -91,6 +91,29 @@ export function useMessages() {
await reanalyzeMessage(id);
}, []);
const reanalyzeAllErrors = useCallback(
async (): Promise<number> => {
// Optimistically mark all error messages as pending
setMessages((prev) =>
prev.map((message) =>
message.ai_status === "error"
? {
...message,
ai_status: "pending" as const,
ai_error: null,
ai_analysis: null,
}
: message,
),
);
const { count } = await reanalyzeErrorBatch({
guildId: currentGuild.current ?? undefined,
});
return count;
},
[],
);
return {
messages,
setMessages,
@@ -99,6 +122,7 @@ export function useMessages() {
error,
fetchMessages,
reanalyze,
reanalyzeAllErrors,
loadMore,
hasMore,
};