230 Commits

Author SHA1 Message Date
f0b60f123d Fix store closure message rendering in dark mode
Force explicit light-mode colors with inline styles so Bulma's dark
mode cannot override the closure banner text and background.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-09 21:05:13 -04:00
781f990541 Add store kill switch to admin panel and estore
Admin panel shows a prominent open/closed toggle above the tabs. When
closed, the shop displays a branded closure message and the checkout API
returns 503. The closure state persists in data/store-status.json.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 00:52:31 -04:00
2e5f253580 Polish: meta/OG tags, JSON-LD, 404 pages, typo fix
- Unique title + meta description on every main-site page
- OpenGraph + Twitter card tags sitewide (hero image on homepage, logo elsewhere)
- LocalBusiness JSON-LD on homepage for Google rich results
- Custom 404 page on main-site (branded, links home + contact)
- Custom not-found page on estore
- Fix typo: "Delivery avalable" → "Delivery available"

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 00:47:26 -04:00
0d57760df1 Contact form: set min date to today on event date picker 2026-06-07 00:35:32 -04:00
77318fb477 Contact form: split name into first/last, add ntfy notification
- First and last name are now separate required fields
- Server combines them into a full name for emails
- Sends a push notification to NTFY_URL on new inquiry (fire-and-forget)
- NTFY_URL env var wired through docker-compose

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-07 00:32:06 -04:00
cd18bd3937 Contact form: add emoji to subject, show event date instead of type 2026-06-07 00:27:50 -04:00
d026bc8217 Fix balloon animation: use Web Animations API instead of CSS custom properties in keyframes 2026-06-06 21:38:40 -04:00
75b20e6ca2 Add debug logging to easter egg trigger 2026-06-06 21:29:51 -04:00
181195dbbc Fix easter egg trigger: use touchstart on mobile, 5 taps in 3s
click events are unreliable on mobile due to scroll handling. Use
touchstart (fires immediately) for mobile and click for desktop with
deduplication. Lower threshold to 5 taps, widen window to 3 seconds.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:23:05 -04:00
066364d2b7 Fix easter egg trigger: 7 quick taps on non-interactive area
Long press on the logo link was unreliable. Switch to detecting 7 rapid
taps on any non-link/button area within 2 seconds — works on mobile and
desktop without conflicting with navigation.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:15:26 -04:00
2002d7f35a Add balloon easter egg — long press logo to trigger
Hold the logo for 0.6s (works on mobile touch and desktop mouse) to launch
22 balloon silhouettes floating up from the bottom. Balloons drift with a
slight sway, have a shine highlight, and naturally disappear under the navbar.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:07:06 -04:00
5900ce817e Fix dark mode: add data-theme=light to all main-site HTML pages
Bulma 1.0 follows system dark mode preference if no theme is set.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 21:01:38 -04:00
6a2bf1f30b Fix nginx port: bind to 3000 instead of 80 for NPM compatibility
NPM proxies beachpartyballoons.com → host:3000. Binding to 80 conflicts
with NPM which owns that port.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 20:59:04 -04:00
0ec3766447 Wire SMTP env vars into main-site container, document in .env.example
Contact form needs SMTP_HOST/PORT/SECURE/USER/PASS and CONTACT_TO passed
through docker-compose. Added to main-site environment block and documented
in .env.example.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 20:37:50 -04:00
aee1f10179 Sync native contact form to main-site, replace iframe
Replaces the third-party iframe form on both the homepage and contact page
with the self-hosted form: drag-and-drop photo upload, honeypot, rate
limiting, inline validation, auto-reply email. Adds multer/sharp/nodemailer
dependencies and the /api/contact endpoint to server.js.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 20:32:05 -04:00
fca6e8da0a Add fulfillment state controls to admin orders panel
Replace single Mark Complete button with contextual In progress / Ready /
Complete buttons based on current fulfillment state. Adds a general
/api/admin/orders/[orderId]/status endpoint that handles all transitions.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 20:22:23 -04:00
ca8773d3c3 Fix order complete: update fulfillment state alongside order state
Square requires all fulfillments to be COMPLETED before the order can be
marked COMPLETED — include fulfillment state in the same updateOrder call.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 20:08:54 -04:00
ef38c42e17 Add Orders tab to admin panel for managing online orders
Fetches open orders from Square filtered by source=online-shop metadata.
Each order shows customer, fulfillment time/address, items, and total with
a Mark Complete button that updates the order state in Square directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 20:03:52 -04:00
02e49ba41b Fix checkout: block false slots when calendar down, add booking request fallback
- Change fulfillment state from RESERVED to PROPOSED (Square rejects RESERVED)
- Return 503 from slots API when CalDAV is unreachable instead of serving empty
  busy blocks that made all time slots appear falsely available
- Add BookingRequestPanel and /api/booking-request endpoint: when the calendar
  server is down, customers can submit their order and preferred time; server
  emails info@beachpartyballoons.com and sends a confirmation to the customer

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-05 19:58:22 -04:00
a49075b167 Fix Apple Pay verification file — serve directly from nginx
With basePath=/shop the Next.js app can't serve /.well-known/ at the
domain root. Mount the file into the nginx container and serve it
directly instead.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-29 06:47:06 -04:00
7d5f74a79e Serve Apple Pay verification file via nginx
Move domain association file to estore/public/.well-known/ so Next.js
serves it, and add a /.well-known/ location block in nginx so Apple's
servers can reach it at the domain root.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 17:50:17 -04:00
92ab3a5633 Add Google Pay and Apple Pay support; fix catalog category filter
- PaymentForm now initialises Google Pay and Apple Pay via Square's Web
  Payments SDK alongside the existing card form; wallet buttons appear
  above the card with an "or pay with card" divider when available
- Apple Pay domain verification file added to public/.well-known/
- square.ts: fix online-category filter to show all items when the
  category doesn't exist; support multi-category display per item

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-28 17:01:29 -04:00
53a2ca03e7 fix: force Bulma light theme on admin page 2026-05-21 12:07:22 -04:00
55055ae9bc fix: light theme for all admin modals 2026-05-21 12:02:56 -04:00
1435964f6f feat: drag-to-select and full-header click in admin gallery
- Click anywhere on the top bar of a photo card to toggle selection
  (replaces the tiny checkbox)
- Drag across the grid to rubber-band select multiple photos at once
- Selected cards show a blue ring + tinted header + solid checkmark icon
- Cards swept during drag show a green ring preview before releasing
- Fixed innerHTML += perf issue (now builds all cards then sets once)
- Thumbnails used in grid so page loads faster

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 11:49:31 -04:00
7fce1632be feat: editable tag presets, next/prev modal nav, needs-tagging filter
- Restructured presets as single-tag accumulators (click multiple to build up tags)
- Added 6 new tags: bridal-shower, cocktail, signature, indoor, outdoor, mitzvah
- Fixed organic/garland alias conflict
- Presets stored in data/presets.json with full CRUD API (add, edit, delete from admin)
- Edit modal shows photo thumbnail, prev/next navigation, preset buttons
- Keyboard shortcuts: Ctrl+Enter to save, arrow keys to navigate, Esc to close
- "Needs tagging" filter in manage view shows only uncategorized/low-tag photos

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 11:42:32 -04:00
0e4461e957 fix: reseed sets createdAt from filename timestamp or file mtime
Photos reseeded from disk now sort by their original upload time
instead of all getting the same insertion timestamp.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-21 11:29:11 -04:00
633f1e2380 fix: reseed script now skips -sm and -md variant files
Previously all .webp files were indexed including thumbnails and medium
variants, causing each photo to appear three times in the gallery.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:48:06 -04:00
2723a6d954 feat: replace text watermark with BPB logo SVG overlay on gallery uploads
Loads bpb-watermark.svg at startup, converts black fills to semi-transparent
white (35% opacity), and composites it centered at 45% image width over every
uploaded photo. Removes the old diagonal "BEACH PARTY BALLOONS" text overlay.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 15:39:03 -04:00
92cf44e5f5 fix: resolve gallery CORS failure and simplify API routing
- gallery backend: replace origin whitelist with wildcard CORS — NPMplus
  was stripping the Allow-Origin header; wildcard passes through reliably
  and is appropriate for a public photo gallery
- gallery.js: hardcode photobackend.beachpartyballoons.com as the API base
  (NPMplus already routes this subdomain) and remove dead port fallbacks
- nginx.conf: add /photos and /uploads proxy routes to gallery-backend
  (kept for direct-nginx access; NPMplus handles external traffic)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 14:53:13 -04:00
4a135a7919 fix: route gallery API through nginx, send Square receipts, unblock order completion
- nginx: add /photos and /uploads proxy routes to gallery-backend so the
  browser can reach the gallery API without needing direct port access
- gallery.js: drop hardcoded port/subdomain fallbacks; use same-origin path
  via the new nginx routes
- square.ts: pass buyerEmailAddress to createPayment so Square auto-sends
  a payment receipt to the customer on capture
- square.ts: create fulfillments in RESERVED state (was PROPOSED) so staff
  can mark orders complete/filled directly from the Square dashboard
- CartDrawer: merge Custom Vinyl into the Shape Balloon line item (one fewer
  Square line item per vinyl order); show modifier price deltas in cart

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-20 14:32:00 -04:00
bb878c2a8a fix: apply 6.35% CT Sales Tax to all order line items
Tax was $0 on first production order — catalog items don't have tax
configured in Square Dashboard. Apply it programmatically via an
ad-hoc LINE_ITEM scoped tax on every line item. Delivery remains
untaxed (service charge taxable: false).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-11 15:46:36 -04:00
5777788127 fix: point all Plausible scripts to self-hosted metrics instance
Replace plausible.io with metrics.beachpartyballoons.com across all
main-site pages and estore layout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 13:15:41 -04:00
8799892341 fix: add Plausible window initializer to estore layout
Matches the pattern used on all main-site pages.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 10:40:54 -04:00
973808088e revert: restore product image background to white
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-10 10:34:24 -04:00
f45a1f807f fix: make product image rounded corners visible
border-radius was already set but invisible (white image on white background).
Changing the image background to a warm off-white makes the 12px rounded
corners show against the surrounding area.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 22:07:12 -04:00
e8240e383a fix: increase Square card form height to show postal code field
89px only fit two rows; postal code (third row) was clipped by the container.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 13:24:23 -04:00
bbf08e4267 fix: hide subtotal line when it equals the total (no delivery/tax)
Subtotal was always shown alongside Total even for pickup orders with
no additional charges, making both lines identical. Now the breakdown
(Subtotal / Delivery / Tax) only appears when there are fees beyond
the item total. Applies to both customer and store alert emails.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 13:16:33 -04:00
8e283a4dff fix: update Green Tea hex to #b2ddc3, remove Pastel Magenta
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 10:50:16 -04:00
5643153a05 feat: add Magenta (#a01357) to Pinks & Reds color family
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 10:41:50 -04:00
07ae012aa3 feat: add Green Tea (#8a9f7f) to Greens color family
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-09 10:39:34 -04:00
ed5db69a90 fix: replace placeholder phone number in privacy policy
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 10:44:17 -04:00
e2af78ff55 fix: nginx waits for estore healthcheck before starting
Nginx resolves upstream hostnames at boot time; if estore isn't
registered in Docker DNS yet it crashes in a restart loop.
Using service_healthy lets nginx wait until the Next.js app
passes its healthcheck before nginx attempts to start.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 10:36:04 -04:00
57cc5840b9 feat: obfuscate email with click-to-reveal across all pages
Replace all bare info@ occurrences with a click-to-reveal pattern:
- New EmailLink React component (base64 decode on click, never in DOM pre-click)
- privacy, terms, refund pages use EmailLink
- contact/index.html uses a vanilla JS button with the same pattern
- PaymentForm mailto builder uses atob() to keep email out of source literals

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 10:23:50 -04:00
3330c47af2 fix: secure admin API endpoints with Bearer token auth
- main-site/server.js: add requireAuth middleware to POST /api/update-status
- gallery-backend/routes/photos.js: add requireAuth to upload, delete, and update routes
- admin/admin.js: send Authorization: Bearer header on all mutating requests (fetch + XHR upload); handle 401 on update-status and photo save
- docker-compose.yml: pass ADMIN_PASSWORD to gallery-backend; remove MongoDB public port mapping (27017:27017)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 08:30:58 -04:00
c40db43c04 fix: replace literal \u2014 escape with em dash in notes placeholder
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 07:57:27 -04:00
f969e5d242 feat: configurable booking lead time in admin (default 48h)
Adds leadTimeHours to HoursConfig. Slot generation, calendar minDate,
and pickup disabled-date precomputation all read from the config.
Admin hours page has a new input to adjust it without a redeploy.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-08 07:48:13 -04:00
134705792c fix: pass OSRM_URL from root .env into estore container
env_file only reads estore/.env; the root .env value wasn't reaching
the container. Wiring it through compose environment: fixes this.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 16:40:53 -04:00
c240ec4ce6 fix: disable Next.js fetch cache for OSRM requests
next:{revalidate:86400} was caching the OSRM URL in Next.js's on-disk
data cache, so old localhost:5002 requests replayed even after the env
var was updated to osrm:5000. Drive-time lookups need live responses.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 16:19:50 -04:00
5bebd51ac4 fix: log OSRM failure reason and URL instead of silent fallback
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-05 15:15:40 -04:00