Navigation & layout - Replace per-page hardcoded nav/footer with shared nav.js (client-side injection) - Add nginx reverse proxy back to docker-compose for clean localhost routing - Rename /color-picker/ to /color/ across nav, directory, and references eStore admin - Add variation hiding controls (mirrors existing modifier hiding) - Add delivery rate editor (base fee + per-mile per tier, persisted to data/) - Fix all missing BASE prefix on fetch calls (admin PATCH/DELETE, availability, slots, colors) - Mount estore/data/ as a Docker volume so admin config survives rebuilds Booking & calendar - Set pickup calendar events to TRANSPARENT (free) so they don't block delivery slots - Skip CANCELLED events in busy-time calculation - Re-check slot availability at checkout before charging (409 on conflict) Phone & email validation - Auto-format phone as (XXX) XXX-XXXX as user types - Require exactly 10 digits; tighten email regex Confirmation emails (store alert + customer) - Full item detail per line: name, price, add-ons, colors, note - Charges breakdown: subtotal, delivery fee, tax, total - Delivery window: simplified M/D/YY h:mm – h:mm AM/PM format - .ics calendar attachment on customer confirmation Delivery rates - Extract configurable rates to delivery-rates.ts (server-only, no fs in client bundle) - calcDelivery() accepts optional rates param; delivery-quote route passes configured rates Content - Change all "40+ latex colors" references to "70+" Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
54 lines
1.7 KiB
TypeScript
54 lines
1.7 KiB
TypeScript
import { readFileSync, existsSync } from 'fs'
|
|
import path from 'path'
|
|
import { atomicWriteJSON } from './file-utils'
|
|
|
|
export interface ItemOverride {
|
|
hidden?: boolean
|
|
categoryOverride?: string
|
|
categoryLabelOverride?: string
|
|
sortOrder?: number
|
|
showColors?: boolean
|
|
hiddenModifierIds?: string[]
|
|
hiddenVariationIds?: string[]
|
|
descriptionOverride?: string
|
|
/** Per-modifier minimum selections override. Key = modifier list ID, value = min count. */
|
|
modifierMinSelected?: Record<string, number>
|
|
/** Minimum latex colors the customer must pick (default 1 when showColors=true). */
|
|
colorMin?: number
|
|
/** Maximum latex colors the customer can pick (null = unlimited). */
|
|
colorMax?: number
|
|
/** Extra charge in cents added per chrome color selected (0 = no per-color surcharge). */
|
|
chromeSurchargePerColor?: number
|
|
/** Unit label for the quantity field, e.g. "ft". When set, the quantity control shows "X ft". */
|
|
quantityUnit?: string
|
|
}
|
|
|
|
export type OverridesMap = Record<string, ItemOverride>
|
|
|
|
const OVERRIDES_PATH = path.join(process.cwd(), 'data', 'item-overrides.json')
|
|
|
|
export function readOverrides(): OverridesMap {
|
|
if (!existsSync(OVERRIDES_PATH)) return {}
|
|
try {
|
|
return JSON.parse(readFileSync(OVERRIDES_PATH, 'utf-8'))
|
|
} catch {
|
|
return {}
|
|
}
|
|
}
|
|
|
|
export function writeOverrides(overrides: OverridesMap): void {
|
|
atomicWriteJSON(OVERRIDES_PATH, overrides)
|
|
}
|
|
|
|
export function setOverride(itemId: string, patch: Partial<ItemOverride>): void {
|
|
const all = readOverrides()
|
|
all[itemId] = { ...(all[itemId] ?? {}), ...patch }
|
|
writeOverrides(all)
|
|
}
|
|
|
|
export function clearOverride(itemId: string): void {
|
|
const all = readOverrides()
|
|
delete all[itemId]
|
|
writeOverrides(all)
|
|
}
|