Files
ArchiTools/src/modules/address-book/hooks/use-contacts.ts
Marius Tarau f0b878cf00 fix: guard against undefined fields when loading old localStorage data
Old entries in localStorage lack new fields (department, role, contactPersons,
linkedEntryIds, attachments, versions, placeholders, ipAddress, vendor, model).
Add null-coalescing guards to prevent client-side crashes on property access.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-18 06:49:08 +02:00

88 lines
3.0 KiB
TypeScript

'use client';
import { useState, useEffect, useCallback } from 'react';
import { useStorage } from '@/core/storage';
import { v4 as uuid } from 'uuid';
import type { AddressContact, ContactType } from '../types';
const PREFIX = 'contact:';
export interface ContactFilters {
search: string;
type: ContactType | 'all';
}
export function useContacts() {
const storage = useStorage('address-book');
const [contacts, setContacts] = useState<AddressContact[]>([]);
const [loading, setLoading] = useState(true);
const [filters, setFilters] = useState<ContactFilters>({ search: '', type: 'all' });
const refresh = useCallback(async () => {
setLoading(true);
const keys = await storage.list();
const results: AddressContact[] = [];
for (const key of keys) {
if (key.startsWith(PREFIX)) {
const item = await storage.get<AddressContact>(key);
if (item) results.push(item);
}
}
results.sort((a, b) => a.name.localeCompare(b.name));
setContacts(results);
setLoading(false);
}, [storage]);
// eslint-disable-next-line react-hooks/set-state-in-effect
useEffect(() => { refresh(); }, [refresh]);
const addContact = useCallback(async (data: Omit<AddressContact, 'id' | 'createdAt' | 'updatedAt'>) => {
const now = new Date().toISOString();
const contact: AddressContact = { ...data, id: uuid(), createdAt: now, updatedAt: now };
await storage.set(`${PREFIX}${contact.id}`, contact);
await refresh();
return contact;
}, [storage, refresh]);
const updateContact = useCallback(async (id: string, updates: Partial<AddressContact>) => {
const existing = contacts.find((c) => c.id === id);
if (!existing) return;
const updated: AddressContact = {
...existing,
...updates,
id: existing.id,
createdAt: existing.createdAt,
updatedAt: new Date().toISOString(),
};
await storage.set(`${PREFIX}${id}`, updated);
await refresh();
}, [storage, refresh, contacts]);
const removeContact = useCallback(async (id: string) => {
await storage.delete(`${PREFIX}${id}`);
await refresh();
}, [storage, refresh]);
const updateFilter = useCallback(<K extends keyof ContactFilters>(key: K, value: ContactFilters[K]) => {
setFilters((prev) => ({ ...prev, [key]: value }));
}, []);
const filteredContacts = contacts.filter((c) => {
if (filters.type !== 'all' && c.type !== filters.type) return false;
if (filters.search) {
const q = filters.search.toLowerCase();
return (
c.name.toLowerCase().includes(q) ||
c.company.toLowerCase().includes(q) ||
c.email.toLowerCase().includes(q) ||
c.phone.includes(q) ||
(c.department ?? '').toLowerCase().includes(q) ||
(c.role ?? '').toLowerCase().includes(q)
);
}
return true;
});
return { contacts: filteredContacts, allContacts: contacts, loading, filters, updateFilter, addContact, updateContact, removeContact, refresh };
}