import { getDB } from '$lib/data/db'; import { getAllItems, createItem as dbCreateItem, updateItem as dbUpdateItem, removeItem as dbRemoveItem } from '$lib/data/items'; import { getAllLocations, seedDefaultLocations } from '$lib/data/locations'; import { createSighting } from '$lib/data/sightings'; import { generateItemId, generateSightingId } from '$lib/utils/id'; import { getConfidence } from '$lib/utils/confidence'; import { buildCheckOutUpdate, buildCheckInUpdate } from '$lib/utils/custody'; import type { Item, Location, Sighting, CheckOutReason } from '$lib/types'; class InventoryStore { items = $state([]); locations = $state([]); loading = $state(true); checkedOutItems = $derived( this.items.filter((i) => i.custodyState === 'checked-out') ); overdueItems = $derived( this.items.filter( (i) => i.custodyState === 'checked-out' && i.checkedOutSince && Date.now() - new Date(i.checkedOutSince).getTime() > 7 * 86400000 ) ); lowStockItems = $derived( this.items.filter( (i) => i.currentQuantity != null && i.lowThreshold != null && i.currentQuantity <= i.lowThreshold ) ); async loadAll() { await seedDefaultLocations(); this.items = await getAllItems(); this.locations = await getAllLocations(); this.loading = false; } async createItem(data: Omit): Promise { const now = new Date().toISOString(); const shortId = generateItemId(); const item: Item = { ...data, shortId, barcodeFormat: 'qr', barcodeUri: `https://haus.toph.so/${shortId}`, createdAt: now, updatedAt: now, }; await dbCreateItem(item); this.items = [...this.items, item]; return item; } async updateItem(shortId: string, updates: Partial): Promise { const updated = await dbUpdateItem(shortId, updates); if (updated) { this.items = this.items.map((i) => (i.shortId === shortId ? updated : i)); } return updated; } async removeItem(shortId: string): Promise { await dbRemoveItem(shortId); this.items = this.items.filter((i) => i.shortId !== shortId); } async checkOut( itemId: string, reason: CheckOutReason, fromLocationId: string, toLocationId: string | null = null, note: string = '' ): Promise { const updates = buildCheckOutUpdate(reason, fromLocationId, toLocationId, note); await this.updateItem(itemId, updates); } async checkIn(itemId: string, returnToLocationId: string): Promise { const updates = buildCheckInUpdate(returnToLocationId); await this.updateItem(itemId, updates); } async recordSighting( itemId: string, locationId: string, type: 'scan' | 'manual' | 'camera-detect' | 'audit-verify' = 'scan' ): Promise { const now = new Date().toISOString(); const sighting: Sighting = { id: generateSightingId(), itemId, locationId, timestamp: now, sightingType: type, confidence: 'confirmed', notes: '', createdBy: null, }; await createSighting(sighting); await this.updateItem(itemId, { lastSeenAt: locationId, lastSeenTimestamp: now, locationConfidence: 'confirmed', }); } getItemsByLocation(locationId: string): Item[] { return this.items.filter((i) => i.lastSeenAt === locationId); } getLocationTree(): Map { const tree = new Map(); for (const loc of this.locations) { const children = tree.get(loc.parentId) ?? []; children.push(loc); tree.set(loc.parentId, children); } return tree; } } export const inventory = new InventoryStore();