chris 50680a323f Major overhaul: shared nav, admin improvements, email enhancements, routing fixes
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>
2026-04-14 21:14:06 -04:00

108 lines
4.4 KiB
HTML
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Beach Party Balloons Color Palette Picker</title>
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Autour+One&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bulma@1.0.2/css/bulma.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.7.2/css/all.min.css" integrity="sha512-Evv84Mr4kqVGRNSgIGL/F/aIDqQb7xQ2vcrdIwxfjThSH8CSR7PBEakCr51Ck+w+/U6swU2Im1vVX0SVk9ABhg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sweetalert2@11/dist/sweetalert2.min.css">
<link rel="stylesheet" href="../style.css">
<link rel="stylesheet" href="color.css">
<script src="/nav.js" defer></script>
<link rel="apple-touch-icon" sizes="180x180" href="../assets/favicon/apple-touch-icon.png">
<link rel="icon" type="image/png" sizes="32x32" href="../assets/favicon/favicon-32x32.png">
<link rel="icon" type="image/png" sizes="16x16" href="../assets/favicon/favicon-16x16.png">
<link rel="manifest" href="../assets/favicon/site.webmanifest">
</head>
<body>
<div id="site-nav"></div>
<main class="color-picker-app">
<section class="picker-layout">
<aside id="selected-palette" aria-label="Selected palette">
<div class="palette-header-row">
<div class="palette-title-group">
<h2 class="has-text-dark">Your Palette</h2>
</div>
<div id="palette-controls" aria-label="Palette controls">
<label class="switch" title="Toggle Animations" aria-label="Toggle animations">
<input type="checkbox" id="toggle-animation" checked>
<span class="slider"></span>
</label>
<button id="shuffle-palette" class="palette-control-btn" title="Shuffle Palette" aria-label="Shuffle palette">
<i class="fa-solid fa-shuffle"></i>
</button>
<button id="preset-palettes" class="palette-control-btn" title="Palette Ideas" aria-label="Palette ideas">
<i class="fa-solid fa-palette"></i>
</button>
<button id="share-palette" class="palette-control-btn" title="Share Palette" aria-label="Share palette">
<i class="fa-solid fa-share-nodes"></i>
</button>
<button id="zoom-palette" class="palette-control-btn" title="Zoom In Palette" aria-label="Zoom palette">
<i class="fa-solid fa-magnifying-glass-plus"></i>
</button>
</div>
</div>
<div class="palette-meta-row">
<span id="palette-count" class="palette-count-badge">0 selected</span>
<span class="palette-hint">Tap a balloon below to add it here</span>
</div>
<div id="palette-colors" aria-live="polite"></div>
<div class="palette-footer-row">
<button id="clear-palette" class="has-text-dark">Clear Palette</button>
</div>
</aside>
<section class="library-panel" aria-labelledby="library-heading">
<h2 id="library-heading" class="library-inline-title">Colors</h2>
<div id="color-families"></div>
</section>
</section>
</main>
<div id="site-footer"></div>
<div class="palette-modal-backdrop">
<div class="palette-modal">
<h3>Share Your Palette</h3>
<div id="modal-color-list"></div>
<button id="close-modal">Close</button>
</div>
</div>
<div class="preset-modal-backdrop" aria-hidden="true">
<div class="preset-modal" role="dialog" aria-modal="true" aria-labelledby="preset-modal-title">
<div class="preset-modal-header">
<h3 id="preset-modal-title">Palette Ideas</h3>
<button id="close-preset-modal" type="button" aria-label="Close palette ideas">
<i class="fa-solid fa-xmark"></i>
</button>
</div>
<p class="preset-modal-subtitle">Quick starting points you can tweak after applying.</p>
<div id="preset-palette-list"></div>
</div>
</div>
<div id="zoom-overlay">
<div id="zoom-modal-shell">
<a href="#" id="zoom-close" aria-label="Close zoom view">×</a>
<div id="zoomed-palette-content"></div>
</div>
</div>
<script src="https://cdn.jsdelivr.net/npm/sweetalert2@11"></script>
<script src="script.js"></script>
</body>
</html>