// Tillstånd för Guidad Felsökning (MVP). // Persistens: localStorage. I produktionsversionen ersätts detta av ett // API-first-backend med samma händelsemodell — append-only-loggen gör // synkronisering (inkl. offline-läge) konfliktfri. import { create } from "zustand"; import { persist } from "zustand/middleware"; import type { Arende, Handelse, LoggPost, Objekt } from "./domain"; import { nyLoggPost } from "./domain"; import { felbeskrivning } from "./projektioner"; import { GENERISK_METODIK, metodikForId, valjMetodik, type Metodik } from "./metodik"; interface FelsokningState { anvandare: string; arenden: Record; nastaNummer: number; sattAnvandare: (namn: string) => void; skapaArende: (objekt: Objekt, felbeskrivningText: string, metodikId?: string) => string; laggTill: (arendeId: string, handelse: Handelse) => void; // Används endast av synken: ersätter listan med den ihopflätade versionen. // Semantiken är fortfarande append-only — flätningen lägger bara till. sammanfoga: (arendeId: string, handelser: LoggPost[]) => void; // Lägger in ett färdigbyggt ärende (demoärende, importerat ärende). laggInArende: (arende: Arende) => void; } function nyDelningskod(): string { return Array.from(crypto.getRandomValues(new Uint8Array(12)), (b) => "abcdefghijklmnopqrstuvwxyz0123456789"[b % 36], ).join(""); } export const useFelsokning = create()( persist( (set, get) => ({ anvandare: "", arenden: {}, nastaNummer: 1, sattAnvandare: (namn) => set({ anvandare: namn.trim() }), skapaArende: (objekt, felbeskrivningText, metodikId) => { const { anvandare, nastaNummer } = get(); const id = `arende-${Date.now().toString(36)}`; const arende: Arende = { id, nummer: nastaNummer, skapad: new Date().toISOString(), delningskod: nyDelningskod(), metodikId, handelser: [ nyLoggPost(anvandare, { typ: "objekt_identifierat", objekt }), nyLoggPost(anvandare, { typ: "felbeskrivning", text: felbeskrivningText }), ], }; set((s) => ({ arenden: { ...s.arenden, [id]: arende }, nastaNummer: s.nastaNummer + 1, })); return id; }, laggTill: (arendeId, handelse) => { const { anvandare } = get(); set((s) => { const arende = s.arenden[arendeId]; if (!arende) return s; return { arenden: { ...s.arenden, [arendeId]: { ...arende, handelser: [...arende.handelser, nyLoggPost(anvandare, handelse)], }, }, }; }); }, sammanfoga: (arendeId, handelser) => { set((s) => { const arende = s.arenden[arendeId]; if (!arende || handelser.length === arende.handelser.length) return s; return { arenden: { ...s.arenden, [arendeId]: { ...arende, handelser } }, }; }); }, laggInArende: (arende) => { set((s) => ({ arenden: { ...s.arenden, [arende.id]: arende }, nastaNummer: Math.max(s.nastaNummer, arende.nummer + 1), })); }, }), { name: "guidad-felsokning" }, ), ); export function metodikForArende(arende: Arende | undefined): Metodik { if (!arende) return GENERISK_METODIK; if (arende.metodikId) return metodikForId(arende.metodikId); const text = felbeskrivning(arende); return text ? valjMetodik(text) : GENERISK_METODIK; }