87 lines
2.7 KiB
TypeScript
87 lines
2.7 KiB
TypeScript
import { create } from 'zustand';
|
|
import { apiFetch } from '../lib/api';
|
|
|
|
let historyLoaded = false;
|
|
|
|
export type Message = {
|
|
id: string;
|
|
role: 'user' | 'assistant';
|
|
content: string;
|
|
isError?: boolean;
|
|
feedbackSaved?: boolean;
|
|
};
|
|
|
|
type CoachState = {
|
|
messages: Message[];
|
|
thinking: boolean;
|
|
historyLoaded: boolean;
|
|
/** True sobald der Welcome-Back-Call in dieser App-Session geantwortet hat
|
|
* — verhindert dass jede Tab-Rückkehr eine neue Lyra-Begrüßung anhängt. */
|
|
welcomeBackShownThisSession: boolean;
|
|
|
|
loadHistory: () => Promise<void>;
|
|
clearHistory: () => Promise<void>;
|
|
sendMessage: (content: string, locale: string) => Promise<{ message: string; feedbackSaved?: boolean }>;
|
|
prependMessage: (msg: Message) => void;
|
|
pushMessage: (msg: Message) => void;
|
|
markFeedbackSaved: (id: string) => void;
|
|
setThinking: (v: boolean) => void;
|
|
setWelcomeBackShown: (v: boolean) => void;
|
|
reset: () => void;
|
|
};
|
|
|
|
export const useCoachStore = create<CoachState>((set, get) => ({
|
|
messages: [],
|
|
thinking: false,
|
|
historyLoaded: false,
|
|
welcomeBackShownThisSession: false,
|
|
|
|
loadHistory: async () => {
|
|
if (historyLoaded) {
|
|
set({ historyLoaded: true });
|
|
return;
|
|
}
|
|
historyLoaded = true;
|
|
const res = await apiFetch<{ messages: Array<{ role: 'user' | 'assistant'; content: string }> }>(
|
|
'/api/coach/history'
|
|
);
|
|
set({
|
|
messages: res.messages?.length
|
|
? res.messages.map((m, i) => ({ id: i.toString(), role: m.role, content: m.content }))
|
|
: [],
|
|
historyLoaded: true,
|
|
});
|
|
},
|
|
|
|
clearHistory: async () => {
|
|
await apiFetch('/api/coach/history', { method: 'DELETE' }).catch(() => null);
|
|
historyLoaded = false;
|
|
set({ messages: [], historyLoaded: false, welcomeBackShownThisSession: false });
|
|
},
|
|
|
|
sendMessage: async (content, locale) => {
|
|
const { messages } = get();
|
|
const res = await apiFetch<{ message: string; feedbackSaved?: boolean }>('/api/coach/message', {
|
|
method: 'POST',
|
|
body: {
|
|
messages: messages.filter((m) => !m.isError).map((m) => ({ role: m.role, content: m.content })),
|
|
locale,
|
|
},
|
|
});
|
|
return res;
|
|
},
|
|
|
|
prependMessage: (msg) => set((s) => ({ messages: [msg, ...s.messages] })),
|
|
pushMessage: (msg) => set((s) => ({ messages: [...s.messages, msg] })),
|
|
markFeedbackSaved: (id) =>
|
|
set((s) => ({
|
|
messages: s.messages.map((m) => (m.id === id ? { ...m, feedbackSaved: true } : m)),
|
|
})),
|
|
setThinking: (v) => set({ thinking: v }),
|
|
setWelcomeBackShown: (v) => set({ welcomeBackShownThisSession: v }),
|
|
reset: () => {
|
|
historyLoaded = false;
|
|
set({ messages: [], thinking: false, historyLoaded: false, welcomeBackShownThisSession: false });
|
|
},
|
|
}));
|