1// Single-source-of-truth cart for sign-ups, shared across the ServingOpps page, 2// the upcoming sign-up details page, and anything else that needs it. 3// 4// State is a Vue reactive array, so consumers that import `items` get automatic 5// reactivity without prop-drilling â mutating via the helpers below updates every 6// view bound to it. 7// 8// Persistence: every mutation is mirrored to localStorage under STORAGE_KEY. 9// On module load, the array hydrates from storage so the cart survives reloads 10// and navigation between sign-up pages. 11 12const { reactive, watch } = Vue; 13 14const STORAGE_KEY = 'saddleback.signups.cart'; 15 16function loadFromStorage() { 17 try { 18 const raw = localStorage.getItem(STORAGE_KEY); 19 if (!raw) return []; 20 const parsed = JSON.parse(raw); 21 return Array.isArray(parsed) ? parsed : []; 22 } catch (err) { 23 console.warn('[CartStore] Failed to parse cart from localStorage; starting empty.', err); 24 return []; 25 } 26} 27 28function saveToStorage(snapshot) { 29 try { 30 localStorage.setItem(STORAGE_KEY, JSON.stringify(snapshot)); 31 } catch (err) { 32 // Storage may be full or disabled (private mode, etc.). Cart still works in-memory. 33 console.warn('[CartStore] Failed to save cart to localStorage.', err); 34 } 35} 36 37// Composite key â id + locationId + scheduleId uniquely identifies a sign-up. 38// Same key the map drawer and list item already use for `isInCart` / `isSignedUp`. 39function oppKey(opp) { 40 const id = opp?.id ?? 0; 41 const locationId = opp?.location?.id ?? 0; 42 const scheduleId = opp?.schedules?.[0]?.id ?? 0; 43 return `${id}-${locationId}-${scheduleId}`; 44} 45 46const items = reactive(loadFromStorage()); 47 48// Persist whenever the array shape changes (push / splice / replace). 49// `deep: true` also catches mutations inside individual entries â overkill for 50// our usage (we only push/splice) but cheap and future-proof. 51watch( 52 items, 53 (snapshot) => saveToStorage(snapshot), 54 { deep: true } 55); 56 57function isInCart(opp) { 58 if (!opp) return false; 59 const key = oppKey(opp); 60 return items.some((item) => oppKey(item) === key); 61} 62 63function addItem(opp) { 64 if (!opp || isInCart(opp)) return false; 65 items.push(opp); 66 return true; 67} 68 69function removeItem(opp) { 70 if (!opp) return false; 71 const key = oppKey(opp); 72 const idx = items.findIndex((item) => oppKey(item) === key); 73 if (idx === -1) return false; 74 items.splice(idx, 1); 75 return true; 76} 77 78function clearCart() { 79 items.splice(0, items.length); 80} 81 82export default { 83 items, 84 oppKey, 85 isInCart, 86 addItem, 87 removeItem, 88 clearCart, 89};
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.