'use client'; import { useState, useEffect, useCallback } from 'react'; import { useStorage } from '@/core/storage'; import { v4 as uuid } from 'uuid'; import type { RegistryEntry, RegistryDirection, RegistryStatus, DocumentType, TrackedDeadline, DeadlineResolution } from '../types'; import { getAllEntries, saveEntry, deleteEntry, generateRegistryNumber } from '../services/registry-service'; import { createTrackedDeadline, resolveDeadline as resolveDeadlineFn } from '../services/deadline-service'; import { getDeadlineType } from '../services/deadline-catalog'; export interface RegistryFilters { search: string; direction: RegistryDirection | 'all'; status: RegistryStatus | 'all'; documentType: DocumentType | 'all'; company: string; } export function useRegistry() { const storage = useStorage('registratura'); const [entries, setEntries] = useState([]); const [loading, setLoading] = useState(true); const [filters, setFilters] = useState({ search: '', direction: 'all', status: 'all', documentType: 'all', company: 'all', }); const refresh = useCallback(async () => { setLoading(true); const items = await getAllEntries(storage); setEntries(items); setLoading(false); }, [storage]); // eslint-disable-next-line react-hooks/set-state-in-effect useEffect(() => { refresh(); }, [refresh]); const addEntry = useCallback(async (data: Omit) => { const now = new Date().toISOString(); const number = generateRegistryNumber(data.company, data.date, entries); const entry: RegistryEntry = { ...data, id: uuid(), number, createdAt: now, updatedAt: now, }; await saveEntry(storage, entry); await refresh(); return entry; }, [storage, refresh, entries]); const updateEntry = useCallback(async (id: string, updates: Partial) => { const existing = entries.find((e) => e.id === id); if (!existing) return; const updated: RegistryEntry = { ...existing, ...updates, id: existing.id, number: existing.number, createdAt: existing.createdAt, updatedAt: new Date().toISOString(), }; await saveEntry(storage, updated); await refresh(); }, [storage, refresh, entries]); const removeEntry = useCallback(async (id: string) => { await deleteEntry(storage, id); await refresh(); }, [storage, refresh]); /** Close an entry and optionally its linked entries */ const closeEntry = useCallback(async (id: string, closeLinked: boolean) => { const entry = entries.find((e) => e.id === id); if (!entry) return; await updateEntry(id, { status: 'inchis' }); const linked = entry.linkedEntryIds ?? []; if (closeLinked && linked.length > 0) { for (const linkedId of linked) { const linked = entries.find((e) => e.id === linkedId); if (linked && linked.status !== 'inchis') { const updatedLinked: RegistryEntry = { ...linked, status: 'inchis', updatedAt: new Date().toISOString(), }; await saveEntry(storage, updatedLinked); } } await refresh(); } }, [entries, updateEntry, storage, refresh]); const updateFilter = useCallback((key: K, value: RegistryFilters[K]) => { setFilters((prev) => ({ ...prev, [key]: value })); }, []); // ── Deadline operations ── const addDeadline = useCallback(async (entryId: string, typeId: string, startDate: string, chainParentId?: string) => { const entry = entries.find((e) => e.id === entryId); if (!entry) return null; const tracked = createTrackedDeadline(typeId, startDate, chainParentId); if (!tracked) return null; const existing = entry.trackedDeadlines ?? []; const updated: RegistryEntry = { ...entry, trackedDeadlines: [...existing, tracked], updatedAt: new Date().toISOString(), }; await saveEntry(storage, updated); await refresh(); return tracked; }, [entries, storage, refresh]); const resolveDeadline = useCallback(async ( entryId: string, deadlineId: string, resolution: DeadlineResolution, note?: string, ): Promise => { const entry = entries.find((e) => e.id === entryId); if (!entry) return null; const deadlines = entry.trackedDeadlines ?? []; const idx = deadlines.findIndex((d) => d.id === deadlineId); if (idx === -1) return null; const dl = deadlines[idx]; if (!dl) return null; const resolved = resolveDeadlineFn(dl, resolution, note); const updatedDeadlines = [...deadlines]; updatedDeadlines[idx] = resolved; const updated: RegistryEntry = { ...entry, trackedDeadlines: updatedDeadlines, updatedAt: new Date().toISOString(), }; await saveEntry(storage, updated); // If the resolved deadline has a chain, automatically check for the next type const def = getDeadlineType(dl.typeId); await refresh(); if (def?.chainNextTypeId && (resolution === 'completed' || resolution === 'aprobat-tacit')) { return resolved; } return resolved; }, [entries, storage, refresh]); const removeDeadline = useCallback(async (entryId: string, deadlineId: string) => { const entry = entries.find((e) => e.id === entryId); if (!entry) return; const deadlines = entry.trackedDeadlines ?? []; const updated: RegistryEntry = { ...entry, trackedDeadlines: deadlines.filter((d) => d.id !== deadlineId), updatedAt: new Date().toISOString(), }; await saveEntry(storage, updated); await refresh(); }, [entries, storage, refresh]); const filteredEntries = entries.filter((entry) => { if (filters.direction !== 'all' && entry.direction !== filters.direction) return false; if (filters.status !== 'all' && entry.status !== filters.status) return false; if (filters.documentType !== 'all' && entry.documentType !== filters.documentType) return false; if (filters.company !== 'all' && entry.company !== filters.company) return false; if (filters.search) { const q = filters.search.toLowerCase(); return ( entry.subject.toLowerCase().includes(q) || entry.sender.toLowerCase().includes(q) || entry.recipient.toLowerCase().includes(q) || entry.number.toLowerCase().includes(q) ); } return true; }); return { entries: filteredEntries, allEntries: entries, loading, filters, updateFilter, addEntry, updateEntry, removeEntry, closeEntry, addDeadline, resolveDeadline, removeDeadline, refresh, }; }