import type { IFutureSignalProvider, FutureSignalFilters } from './IFutureSignalProvider' import type { FutureSignal } from '../domain/futureSignal' import type { ReviewStatus } from '../domain/enums' import { mockFutureSignals } from '../mock-data/futureSignals' import { TEAM_STORAGE_KEYS, loadVersioned, persistVersioned } from './teamPersistence' // Mutation overrides — applied on top of the live mockFutureSignals import. // Reading directly from mockFutureSignals (not a copied store) ensures that // Vite HMR changes to the mock data are always reflected immediately. const mutationOverrides = new Map>() /** * Leads aus Rechercheläufen (Runde 10, §2.12). * * Sie liegen in derselben Ablage wie der Seed und werden von `resolve()` in * dieselbe Liste gegeben — es gibt bewusst keinen zweiten Bestand, den Nora * separat abfragen müsste. Getrennt ist nur die Persistenz: der Seed steht im * Code, die erzeugten Leads im Local Storage, damit ein Reload sie behält. */ let researchSignals: FutureSignal[] = loadVersioned(TEAM_STORAGE_KEYS.RESEARCH_LEADS) ?? [] function resolve(): FutureSignal[] { // Erzeugte Leads zuerst: der jüngste Lauf steht oben, wo man ihn sucht. return [...researchSignals, ...mockFutureSignals].map(s => { const override = mutationOverrides.get(s.id) return override ? { ...s, ...override } : s }) } export const MockupFutureSignalProvider: IFutureSignalProvider = { async getAll(filters?: FutureSignalFilters) { let results = resolve() if (filters?.signalType) results = results.filter(s => s.signalType === filters.signalType) if (filters?.minProbability !== undefined) results = results.filter(s => s.probability >= filters.minProbability!) if (filters?.organizationId) results = results.filter(s => s.organizationId === filters.organizationId) if (filters?.isVerified !== undefined) results = results.filter(s => s.isVerified === filters.isVerified) if (filters?.sensitivityLevel) results = results.filter(s => s.sensitivityLevel === filters.sensitivityLevel) if (filters?.reviewStatus) results = results.filter(s => (s.reviewStatus ?? 'UNREVIEWED') === filters.reviewStatus) if (filters?.maxTimeHorizonMonths !== undefined) results = results.filter(s => s.timeHorizonMonths <= filters.maxTimeHorizonMonths!) return results.sort((a, b) => b.probability - a.probability) }, async getById(id) { return resolve().find(s => s.id === id) ?? null }, async getByProperty(propertyId) { return resolve().filter(s => s.propertyId === propertyId) }, async verify(id, verifiedBy) { const existing = mutationOverrides.get(id) ?? {} mutationOverrides.set(id, { ...existing, isVerified: true, verifiedBy, verifiedAt: new Date().toISOString(), updatedAt: new Date().toISOString() }) return resolve().find(s => s.id === id)! }, async updateReviewStatus(id, status: ReviewStatus) { const existing = mutationOverrides.get(id) ?? {} mutationOverrides.set(id, { ...existing, reviewStatus: status, updatedAt: new Date().toISOString() }) return resolve().find(s => s.id === id)! }, /** * Leads eines Recherchelaufs aufnehmen. * * Gleiche Kennung heisst gleicher Sachverhalt: Ein zweiter Lauf über * denselben Artikel ersetzt den bestehenden Eintrag, statt ihn zu * verdoppeln. Sonst wüchse die Liste bei jedem Knopfdruck um Dubletten, und * niemand könnte mehr sagen, welcher Stand gilt. */ async addResearchSignals(signals: FutureSignal[]) { const bekannt = new Map(researchSignals.map(s => [s.id, s])) for (const s of signals) bekannt.set(s.id, s) researchSignals = [...bekannt.values()].sort((a, b) => b.createdAt.localeCompare(a.createdAt)) persistVersioned(TEAM_STORAGE_KEYS.RESEARCH_LEADS, researchSignals) return researchSignals.length }, }