balloon-shop/estore/src/lib/overrides.ts
chris 0ea1b98a1f feat: required delivery toggle with custom rates per item
Items can now be marked as "requires delivery" in admin — these items
cannot be picked up and must be delivered (and struck).

- Admin item editor: "Requires delivery" checkbox + custom base/per-mile
  rate fields that appear when the toggle is on
- ProductCard: "Delivery & setup required" note on the card
- CartDrawer: pickup toggle is hidden and replaced with an explanation
  when any cart item requires delivery; the quote call passes the
  item's custom rate override (highest base + highest per-mile wins
  when multiple requires-delivery items are in the cart)
- delivery-quote API: accepts optional rateOverride to apply per-item
  pricing on top of the inferred tier

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-18 09:31:29 -04:00

63 lines
2.2 KiB
TypeScript

import { readFileSync, existsSync } from 'fs'
import path from 'path'
import { atomicWriteJSON } from './file-utils'
export interface ItemOverride {
hidden?: boolean
featured?: 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
/** Color names that are hidden from the customer picker for this item. */
disabledColors?: string[]
/** Unit label for the quantity field, e.g. "ft". When set, the quantity control shows "X ft". */
quantityUnit?: string
/** When true, pickup is not offered — item must be delivered. */
requiresDelivery?: boolean
/** Override delivery base charge in cents for this item (replaces the tier default). */
deliveryBaseOverride?: number | null
/** Override per-mile rate in cents for this item (replaces the tier default). */
deliveryPerMileOverride?: number | null
}
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)
}