Download button exports item-overrides, delivery-rates, categories-display,
occasions, hours, and vinyl-config as a single JSON file. Restore button
applies a previously downloaded backup (skips vinyl-config to avoid
overwriting it). Both accessible from the admin header.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add force-dynamic to /api/catalog so Next.js never serves a
stale cached route response to the shop
- Add invalidateCatalogCache() to catalog-cache lib to drop the
30s in-process memory cache on demand
- Call invalidateCatalogCache() after every admin PATCH/DELETE on
an item so override saves are reflected on the very next shop
request (no 30s delay)
Refresh from Square already updated the shared disk + memory cache;
force-dynamic ensures the shop route handler actually runs each time.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Category selector replaced with checkboxes — items can now be
assigned to multiple categories directly in admin (not just Square).
Each category shows a "Square" label if it came from the Square
assignment. Saves as categoriesOverride[] (array of category names).
- categoriesOverride takes precedence over old categoryOverride in the
catalog route; old overrides still work as fallback.
- Requires-delivery toggle and custom rate fields were already in the
code but needed container rebuild to appear — no logic change.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
If a "Mothers Day" or "Graduation" occasion is active and its
squareCategorySlug matches a product category, suppress the duplicate
regular category tab so it doesn't appear twice in the bar.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Items can now belong to multiple Square categories and appear in all
matching tabs (e.g. a Mother's Day balloon also shows under Easter).
Also fixes new items not appearing when the Square account has no
"online" category — previously this caused zero items to load; now
it falls back to showing all items.
Changes:
- CatalogItem gains categories[] + categoryLabels[] (multi-category)
- square.ts collects all non-skip categories per item; "online" filter
is now optional (show all if category doesn't exist in Square)
- catalog/route.ts propagates categoryOverride into categories[0]
- FeaturedProducts: tabs and filter use the full categories array
- Admin CategoryDisplayEditor sees all categories from multi-cat items
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add featured to ItemOverride so it can be set per-item in admin
- Catalog API applies the override and sorts featured items before
non-featured (within each group, sortOrder still applies)
- ProductCard shows a teal Featured badge on the image when featured
and not sold out
- Admin item editor has a ⭐ Featured checkbox beside Hidden
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Fix Chrome Rose Gold hex (#B76E79 → #C17F87) so it no longer
conflicts with Classic Rose Gold; image still used for display
- ScrollToTop hides when cart drawer is open and uses z-index 98
(below the drawer); uses drawerOpen from CartContext
- Search now switches to All tab automatically so results span every
item, not just the active category
- Add sendAdminErrorAlert() to notify.ts; checkout route emails
admin@beachpartyballoons.com on unexpected server errors and on
critical calendar-write failures; card decline errors are not
forwarded (customers can self-resolve those)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Add useLockBodyScroll hook (sets overflow:hidden on body, restores on
unmount) and apply it to ColorPicker, AdminColorFilter, WelcomeModal,
and GuidedTour. CartDrawer uses an inline effect keyed on drawerOpen
since it is always mounted.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add disabledColors field to ItemOverride and CatalogItem
- Propagate through catalog API applyOverrides
- ColorPicker filters disabled colors out before showing to customers
- New AdminColorFilter modal: same collapsible family layout and balloon
swatches as the customer view; click to hide/show individual colors;
Enable all / Disable all shortcuts; badge shows count of hidden colors
- Button appears in the color limits section for color-enabled items
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
A botched sed command stripped the first import line from every admin
route file, breaking NextRequest/NextResponse references. Restored all
imports and added export const dynamic = 'force-dynamic' to all admin
GET handlers so Next.js 14 never serves a stale cached response after
a save — this was the root cause of changes appearing not to save.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Image-based colors (chrome/metallic) have a balloon silhouette against
a transparent bg, so cover was fitting the whole image including
whitespace. 220% zooms into the center where the finish actually is.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
ColorPicker.tsx was constructing image URLs with the old /color-picker/
prefix. globals.css had the same for the balloon-mask.svg SVG mask.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Add ScrollToTop component matching main site's green Top button
(appears after 130px scroll, same styling and font)
- Fix main-site server.js: JS/CSS now use max-age=3600 + must-revalidate
instead of 30d immutable — changes reach users within 1 hour instead
of being stuck in browser cache for a month
- Images/fonts keep 30d immutable (safe, as they are content-addressed)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Tour now switches to the All tab and clears search on start, ensuring
the 11" Latex product is always visible and the exit overlay works
- data-tour="first-card" now targets the 11" Latex item by name instead
of whichever card happens to be first in the filtered list
- Modal header title now truncates with ellipsis so the X close button
is never pushed off screen by a long product name
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
quote was non-null after entering a delivery address, so the delivery
fee row showed even after switching back to pickup.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Dockerfile: create /app/data owned by nextjs before USER switch so fresh
deployments work without manual chown. Existing servers need:
sudo chown -R 1001:1001 estore/data
- nav.js: fix footer legal links to point to /shop/privacy|terms|refund
(pages live in estore, not main site)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>