/** * Flock 登录态「常用货件」:按客户维度存浏览器 localStorage,供搜索快速回填。 */ export const FLOCK_SAVED_FREIGHT_MAX = 40; export type FlockSavedFreightPreset = { id: string; /** 列表/搜索显示名 */ name: string; description: string; quantity: string; packagingType: string; lengthIn: string; widthIn: string; heightIn: string; totalWeightLb: string; freightClass: string; stackable: boolean; turnable: boolean; updatedAt: string; }; export type FlockSavedFreightSource = { description: string; quantity: string; packagingType: string; lengthIn: string; widthIn: string; heightIn: string; totalWeightLb: string; freightClass: string; stackable: boolean; turnable: boolean; }; export function flockSavedFreightStorageKey(customerId: string): string { const id = customerId.trim() || "anon"; return `chajia.flock.savedFreight.v1:${id}`; } function isPreset(v: unknown): v is FlockSavedFreightPreset { if (!v || typeof v !== "object") return false; const o = v as Record; return ( typeof o.id === "string" && typeof o.name === "string" && typeof o.description === "string" ); } export function parseFlockSavedFreightList( raw: string | null, ): FlockSavedFreightPreset[] { if (!raw) return []; try { const data = JSON.parse(raw) as unknown; if (!Array.isArray(data)) return []; return data.filter(isPreset).slice(0, FLOCK_SAVED_FREIGHT_MAX); } catch { return []; } } export function loadFlockSavedFreight( customerId: string, storage: Pick = globalThis.localStorage, ): FlockSavedFreightPreset[] { try { return parseFlockSavedFreightList( storage.getItem(flockSavedFreightStorageKey(customerId)), ); } catch { return []; } } export function persistFlockSavedFreight( customerId: string, list: FlockSavedFreightPreset[], storage: Pick = globalThis.localStorage, ): void { const trimmed = list.slice(0, FLOCK_SAVED_FREIGHT_MAX); storage.setItem( flockSavedFreightStorageKey(customerId), JSON.stringify(trimmed), ); } export function filterFlockSavedFreight( list: FlockSavedFreightPreset[], query: string, ): FlockSavedFreightPreset[] { const q = query.trim().toLowerCase(); if (!q) return list; return list.filter( (p) => p.name.toLowerCase().includes(q) || p.description.toLowerCase().includes(q), ); } /** 同名(忽略大小写)覆盖;新条目插到最前 */ export function upsertFlockSavedFreight( list: FlockSavedFreightPreset[], preset: FlockSavedFreightPreset, ): FlockSavedFreightPreset[] { const nameKey = preset.name.trim().toLowerCase(); const without = list.filter( (p) => p.id !== preset.id && p.name.trim().toLowerCase() !== nameKey, ); return [preset, ...without].slice(0, FLOCK_SAVED_FREIGHT_MAX); } export function removeFlockSavedFreight( list: FlockSavedFreightPreset[], id: string, ): FlockSavedFreightPreset[] { return list.filter((p) => p.id !== id); } export function buildFlockSavedFreightPreset( source: FlockSavedFreightSource, name: string, id = `sf-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`, ): FlockSavedFreightPreset | null { const desc = source.description.trim(); const displayName = name.trim() || desc; if (!displayName && !desc) return null; return { id, name: displayName || desc, description: desc || displayName, quantity: source.quantity, packagingType: source.packagingType, lengthIn: source.lengthIn, widthIn: source.widthIn, heightIn: source.heightIn, totalWeightLb: source.totalWeightLb, freightClass: source.freightClass, stackable: Boolean(source.stackable), turnable: Boolean(source.turnable), updatedAt: new Date().toISOString(), }; }