feat(ai): server-side chat history + assistant UX fixes
Server-side, per-user chat history (the thread now survives reload): - new ai_chat_messages table + migration; getChatHistory/appendChatMessages/ clearChatHistory; GET/POST/DELETE /api/admin/ai/history (ai.use-guarded); service + HTTP tests (isolation, ordering, clear, perm, validation). - DashAssistant loads the thread on mount and persists each turn; "Vymazat" clears it. gcTime:0 so each mount re-reads the DB (no stale-cache resurrection); composer gated until history settles + seed flag flipped on submit (no seed/submit race clobbering a freshly-sent turn). UX + correctness fixes from review/feedback: - review cards moved OUT of the scrollable thread into a "Faktury k potvrzení" section so Uložit is always reachable; chat auto-scroll no longer fires on field edits. - chat bubble text colour set on the Typography (GlobalStyles pins `p` to text.secondary, which was overriding the inherited bubble colour → unreadable). - never clear input/staged PDFs on a failed submit (rollback + keep), so a budget error can't wipe staged invoices; roll back the optimistic user turn. - stable attachment ids (no crypto.randomUUID — undefined over plain HTTP). - editable Datum splatnosti + Popis fields; client-side extraction summary. Security: DashProfile clipboard copy now uses an execCommand fallback (works over plain HTTP) and only shows the success toast when the copy actually succeeded — previously it falsely claimed to copy 2FA codes / the TOTP secret. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -19,9 +19,28 @@ export interface ExtractedInvoice {
|
||||
description: string | null;
|
||||
}
|
||||
|
||||
export interface StoredChatMessage {
|
||||
role: "user" | "assistant";
|
||||
content: string;
|
||||
created_at: string;
|
||||
}
|
||||
|
||||
export const aiUsageOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["ai", "usage"],
|
||||
queryFn: () => jsonQuery<AiUsage>("/api/admin/ai/usage"),
|
||||
staleTime: 30_000,
|
||||
});
|
||||
|
||||
export const aiHistoryOptions = () =>
|
||||
queryOptions({
|
||||
queryKey: ["ai", "history"],
|
||||
queryFn: () =>
|
||||
jsonQuery<{ messages: StoredChatMessage[] }>("/api/admin/ai/history"),
|
||||
// Within a mount the thread is seeded once and mutated locally, so don't
|
||||
// refetch on focus (staleTime Infinity). But gcTime 0 drops the cache on
|
||||
// unmount, so every remount re-reads the DB (the source of truth) — this
|
||||
// is what keeps a cleared/appended thread from resurrecting stale messages.
|
||||
staleTime: Infinity,
|
||||
gcTime: 0,
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user