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>
This commit is contained in:
@@ -174,7 +174,7 @@ function ContactCard({ contact, onEdit, onDelete }: {
|
||||
<Globe className="h-3 w-3 shrink-0" /><span className="truncate">{contact.website}</span>
|
||||
</div>
|
||||
)}
|
||||
{contact.contactPersons.length > 0 && (
|
||||
{(contact.contactPersons ?? []).length > 0 && (
|
||||
<div className="mt-1 border-t pt-1">
|
||||
<p className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider mb-1">
|
||||
Persoane de contact ({contact.contactPersons.length})
|
||||
|
||||
@@ -76,8 +76,8 @@ export function useContacts() {
|
||||
c.company.toLowerCase().includes(q) ||
|
||||
c.email.toLowerCase().includes(q) ||
|
||||
c.phone.includes(q) ||
|
||||
c.department.toLowerCase().includes(q) ||
|
||||
c.role.toLowerCase().includes(q)
|
||||
(c.department ?? '').toLowerCase().includes(q) ||
|
||||
(c.role ?? '').toLowerCase().includes(q)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -157,8 +157,8 @@ export function DigitalSignaturesModule() {
|
||||
{asset.usageNotes && (
|
||||
<p className="text-xs text-muted-foreground line-clamp-1">Note: {asset.usageNotes}</p>
|
||||
)}
|
||||
{asset.versions.length > 0 && (
|
||||
<p className="text-xs text-muted-foreground">Versiuni: {asset.versions.length + 1}</p>
|
||||
{(asset.versions ?? []).length > 0 && (
|
||||
<p className="text-xs text-muted-foreground">Versiuni: {(asset.versions ?? []).length + 1}</p>
|
||||
)}
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
@@ -60,7 +60,7 @@ export function useSignatures() {
|
||||
const existing = assets.find((a) => a.id === assetId);
|
||||
if (!existing) return;
|
||||
const version: AssetVersion = { id: uuid(), imageUrl, notes, createdAt: new Date().toISOString() };
|
||||
const updatedVersions = [...existing.versions, version];
|
||||
const updatedVersions = [...(existing.versions ?? []), version];
|
||||
await updateAsset(assetId, { imageUrl, versions: updatedVersions });
|
||||
}, [assets, updateAsset]);
|
||||
|
||||
|
||||
@@ -79,9 +79,9 @@ export function useInventory() {
|
||||
item.name.toLowerCase().includes(q) ||
|
||||
item.serialNumber.toLowerCase().includes(q) ||
|
||||
item.assignedTo.toLowerCase().includes(q) ||
|
||||
item.ipAddress.toLowerCase().includes(q) ||
|
||||
item.vendor.toLowerCase().includes(q) ||
|
||||
item.model.toLowerCase().includes(q)
|
||||
(item.ipAddress ?? '').toLowerCase().includes(q) ||
|
||||
(item.vendor ?? '').toLowerCase().includes(q) ||
|
||||
(item.model ?? '').toLowerCase().includes(q)
|
||||
);
|
||||
}
|
||||
return true;
|
||||
|
||||
@@ -50,7 +50,7 @@ export function RegistraturaModule() {
|
||||
|
||||
const handleCloseRequest = (id: string) => {
|
||||
const entry = allEntries.find((e) => e.id === id);
|
||||
if (entry && entry.linkedEntryIds.length > 0) {
|
||||
if (entry && (entry.linkedEntryIds ?? []).length > 0) {
|
||||
setClosingId(id);
|
||||
} else {
|
||||
closeEntry(id, false);
|
||||
@@ -158,7 +158,7 @@ export function RegistraturaModule() {
|
||||
</DialogHeader>
|
||||
<div className="py-2">
|
||||
<p className="text-sm">
|
||||
Această înregistrare are {closingEntry?.linkedEntryIds.length ?? 0} înregistrări legate.
|
||||
Această înregistrare are {closingEntry?.linkedEntryIds?.length ?? 0} înregistrări legate.
|
||||
Vrei să le închizi și pe acestea?
|
||||
</p>
|
||||
</div>
|
||||
|
||||
@@ -69,7 +69,7 @@ export function RegistryTable({ entries, loading, onEdit, onDelete, onClose }: R
|
||||
</thead>
|
||||
<tbody>
|
||||
{entries.map((entry) => {
|
||||
const overdueDays = entry.status === 'deschis' ? getOverdueDays(entry.deadline) : null;
|
||||
const overdueDays = (entry.status === 'deschis' || !entry.status) ? getOverdueDays(entry.deadline) : null;
|
||||
const isOverdue = overdueDays !== null && overdueDays > 0;
|
||||
return (
|
||||
<tr
|
||||
@@ -86,16 +86,16 @@ export function RegistryTable({ entries, loading, onEdit, onDelete, onClose }: R
|
||||
variant={entry.direction === 'intrat' ? 'default' : 'secondary'}
|
||||
className="text-xs"
|
||||
>
|
||||
{DIRECTION_LABELS[entry.direction]}
|
||||
{DIRECTION_LABELS[entry.direction] ?? entry.direction ?? '—'}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-3 py-2 text-xs">{DOC_TYPE_LABELS[entry.documentType]}</td>
|
||||
<td className="px-3 py-2 text-xs">{DOC_TYPE_LABELS[entry.documentType] ?? entry.documentType ?? '—'}</td>
|
||||
<td className="px-3 py-2 max-w-[200px] truncate">
|
||||
{entry.subject}
|
||||
{entry.linkedEntryIds.length > 0 && (
|
||||
{(entry.linkedEntryIds ?? []).length > 0 && (
|
||||
<Link2 className="ml-1 inline h-3 w-3 text-muted-foreground" />
|
||||
)}
|
||||
{entry.attachments.length > 0 && (
|
||||
{(entry.attachments ?? []).length > 0 && (
|
||||
<Badge variant="outline" className="ml-1 text-[10px] px-1">
|
||||
{entry.attachments.length} fișiere
|
||||
</Badge>
|
||||
|
||||
@@ -76,8 +76,9 @@ export function useRegistry() {
|
||||
const entry = entries.find((e) => e.id === id);
|
||||
if (!entry) return;
|
||||
await updateEntry(id, { status: 'inchis' });
|
||||
if (closeLinked && entry.linkedEntryIds.length > 0) {
|
||||
for (const linkedId of entry.linkedEntryIds) {
|
||||
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 = {
|
||||
|
||||
@@ -124,9 +124,9 @@ export function WordTemplatesModule() {
|
||||
{tpl.clonedFrom && <Badge variant="secondary" className="text-[10px]">Clonă</Badge>}
|
||||
</div>
|
||||
{/* Placeholders display */}
|
||||
{tpl.placeholders.length > 0 && (
|
||||
{(tpl.placeholders ?? []).length > 0 && (
|
||||
<div className="mt-1.5 flex flex-wrap gap-1">
|
||||
{tpl.placeholders.map((p) => (
|
||||
{(tpl.placeholders ?? []).map((p) => (
|
||||
<span key={p} className="rounded bg-muted px-1 py-0.5 font-mono text-[10px] text-muted-foreground">{`{{${p}}}`}</span>
|
||||
))}
|
||||
</div>
|
||||
@@ -181,7 +181,7 @@ function TemplateForm({ initial, onSubmit, onCancel }: {
|
||||
const [fileUrl, setFileUrl] = useState(initial?.fileUrl ?? '');
|
||||
const [company, setCompany] = useState<CompanyId>(initial?.company ?? 'beletage');
|
||||
const [version, setVersion] = useState(initial?.version ?? '1.0.0');
|
||||
const [placeholdersText, setPlaceholdersText] = useState(initial?.placeholders.join(', ') ?? '');
|
||||
const [placeholdersText, setPlaceholdersText] = useState((initial?.placeholders ?? []).join(', '));
|
||||
|
||||
return (
|
||||
<form onSubmit={(e) => {
|
||||
|
||||
Reference in New Issue
Block a user