Files
plantillas-proyectos/frontend/src/lib/csv-import-pending.ts
2026-04-24 09:58:22 -06:00

151 lines
4.7 KiB
TypeScript

import { browser } from '$app/environment';
import type { CsvImportProfile, CsvImportTab } from '$lib/csv-import-session';
const VALID_PROFILES: CsvImportProfile[] = [
'customs_brokers',
'clients_providers',
'exchange_rates',
'pedimentos',
'material_classes',
'vehicles',
'drivers',
'trailers',
'transporters',
'part_numbers',
'boms',
'exportacion',
'imports'
];
const VALID_TABS: CsvImportTab[] = ['catalogos', 'transportes', 'importacion', 'exportacion'];
export const CSV_IMPORT_PENDING_KEY = 'anexo76_csv_pending_v1';
export const CSV_IMPORT_PENDING_CHANGED = 'csvImportPendingChanged';
const MAX_ITEMS = 20;
export interface CsvImportPendingEntry {
companyId: number;
jobId: string;
profile: CsvImportProfile;
activeModelTarget: string | null;
activeTab: CsvImportTab;
label?: string | null;
savedAt: string;
totalRows?: number;
validRows?: number;
}
interface PendingStoreV1 {
v: 1;
items: CsvImportPendingEntry[];
}
function notifyPendingChanged() {
if (browser) {
window.dispatchEvent(new CustomEvent(CSV_IMPORT_PENDING_CHANGED));
}
}
function parseStore(raw: string | null): PendingStoreV1 {
if (!raw) return { v: 1, items: [] };
try {
const data = JSON.parse(raw) as Partial<PendingStoreV1>;
if (data.v !== 1 || !Array.isArray(data.items)) return { v: 1, items: [] };
return { v: 1, items: data.items.filter(isValidEntry) };
} catch {
return { v: 1, items: [] };
}
}
function isValidEntry(x: unknown): x is CsvImportPendingEntry {
if (!x || typeof x !== 'object') return false;
const o = x as Record<string, unknown>;
return (
typeof o.companyId === 'number' &&
typeof o.jobId === 'string' &&
typeof o.profile === 'string' &&
VALID_PROFILES.includes(o.profile as CsvImportProfile) &&
typeof o.savedAt === 'string' &&
(o.activeModelTarget === null || typeof o.activeModelTarget === 'string') &&
typeof o.activeTab === 'string' &&
VALID_TABS.includes(o.activeTab as CsvImportTab)
);
}
function writeStore(store: PendingStoreV1, notify = true) {
if (!browser) return;
try {
localStorage.setItem(CSV_IMPORT_PENDING_KEY, JSON.stringify(store));
if (notify) notifyPendingChanged();
} catch {
//
}
}
export function readCsvImportPendingStore(): PendingStoreV1 {
if (!browser) return { v: 1, items: [] };
return parseStore(localStorage.getItem(CSV_IMPORT_PENDING_KEY));
}
export function listCsvImportPendingForCompany(companyId: number | undefined): CsvImportPendingEntry[] {
if (companyId === undefined) return [];
const { items } = readCsvImportPendingStore();
return items
.filter((i) => i.companyId === companyId)
.sort((a, b) => (a.savedAt < b.savedAt ? 1 : -1));
}
export function countCsvImportPendingForCompany(companyId: number | undefined): number {
return listCsvImportPendingForCompany(companyId).length;
}
/** Inserta o actualiza por jobId; mantiene como máximo MAX_ITEMS (más recientes primero). */
export function upsertCsvImportPending(partial: Omit<CsvImportPendingEntry, 'savedAt'> & { savedAt?: string }): void {
if (!browser) return;
const store = readCsvImportPendingStore();
const now = partial.savedAt ?? new Date().toISOString();
const next: CsvImportPendingEntry = {
companyId: partial.companyId,
jobId: partial.jobId,
profile: partial.profile,
activeModelTarget: partial.activeModelTarget,
activeTab: partial.activeTab,
savedAt: now,
...(partial.label !== undefined && partial.label !== null && String(partial.label).trim()
? { label: String(partial.label).trim() }
: {}),
...(typeof partial.totalRows === 'number' ? { totalRows: partial.totalRows } : {}),
...(typeof partial.validRows === 'number' ? { validRows: partial.validRows } : {})
};
const without = store.items.filter((i) => i.jobId !== next.jobId);
const merged = [next, ...without].sort((a, b) => (a.savedAt < b.savedAt ? 1 : -1)).slice(0, MAX_ITEMS);
writeStore({ v: 1, items: merged });
}
export function removeCsvImportPending(jobId: string): void {
if (!browser) return;
const store = readCsvImportPendingStore();
const filtered = store.items.filter((i) => i.jobId !== jobId);
if (filtered.length === store.items.length) return;
writeStore({ v: 1, items: filtered });
}
export function updateCsvImportPendingSnapshot(
jobId: string,
patch: Pick<CsvImportPendingEntry, 'totalRows' | 'validRows'>
): void {
if (!browser) return;
const store = readCsvImportPendingStore();
const idx = store.items.findIndex((i) => i.jobId === jobId);
if (idx < 0) return;
const cur = store.items[idx];
store.items[idx] = {
...cur,
...(typeof patch.totalRows === 'number' ? { totalRows: patch.totalRows } : {}),
...(typeof patch.validRows === 'number' ? { validRows: patch.validRows } : {})
};
writeStore({ v: 1, items: [...store.items] }, false);
}