popertotsandSisyphus 6c16d65476 fix(frontend): Svelte 5 reactivity, each block keys, and path resolution
Add proper keys to #each blocks across 15+ components to fix reordering bugs. Replace new Date() with SvelteDate in reactive contexts. Use $derived for computed values (totalPages). Use resolve() from $app/paths for all internal navigation hrefs. Add ARIA labels and keyboard accessibility to NavBar mobile menu. Remove unused handleRetry from UserPaymentModal.

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 01:08:36 +01:00
2026-05-17 11:03:05 +01:00
2025-10-31 23:11:33 +00:00

Crussell

Nail salon booking platform — Go 1.25 backend + SvelteKit 5 frontend + Docker. Built for a UK sole-trader nail artist.

Features

  • Three booking flows: self-service (customer), walk-in (admin), call-in (admin)
  • Slot reservations: temporary holds prevent double-booking (4 TTL types)
  • Guest accounts: disposable accounts for one-off bookings, GDPR-compliant anonymization
  • Service eligibility: age requirements + patch test validation
  • Deposit system: 3-strike rule for late cancellations, 24-hour threshold
  • Square payments: in-person Terminal + online Web Payments SDK (dev mock + prod stub, build-tagged)
  • Payment methods: Card (Square Terminal/online), cash, gift card — with saved cards, refunds, and webhook handling
  • Payment types: deposit, full, partial, balance, tip — admin can override service prices during payment
  • Scheduling: default hours, holiday overrides, time blockers
  • Loyalty & discounts: stamp-based loyalty, time-based and milestone campaigns
  • Portfolio gallery: S3/R2 storage with tag/category filtering
  • CardDAV sync: profile photos synced to SabreDAV contacts
  • Admin notifications: priority-sorted queue with bell icon, /notifications page, acknowledge flow
  • User notification preferences: per-channel toggles (email, SMS, browser) in account settings
  • Enriched edit requests: side-by-side original vs proposed booking snapshots (time, services, prices, durations, user details) for admin review
  • Admin reschedule modal: full reschedule UI on Today page with real-time available slot lookup, conflict detection, and one-click confirmation
  • Time blockers UI: admin can create/manage one-off and recurring time blockers directly from the Admin dashboard, with overlap detection against existing bookings
  • Today page enhancements: interactive daily calendar grid with visual time blockers, today stats summary, responsive layout improvements
  • Auto-select availability: all booking flows (self-service, admin create, edit request) automatically select the first available date when data loads
  • Shared time slot utilities: extracted common time slot generation, lunch protection, and formatting logic into a reusable module
  • Shared formatting utilities: formatDuration, formatDateTime, formatDate, formatTime, calculateAge in lib/utils/format.ts
  • Referral code registration: new users can enter a 12-character referral code during registration; relationship recorded in user_referrals table
  • Admin schedule page: Google Calendar-style week view at /admin/schedule with drag-scroll, booking details modal, and working hours overlays
  • BookingFlow welcome step: unauthenticated users see a welcome card (Step 0) encouraging login before guest checkout, with clear messaging about lost loyalty/discount benefits
  • Admin login redirect: admins are redirected to /today after login instead of the home page
  • Patch test duration on services: patch_test_duration_hours exposed on Service type; creating a service with duration > 0 auto-creates a patch test record
  • created_by_name on bookings: admin booking details now show who created the booking (admin name)
  • Exceptional scheduling fix: exceptional application queries expand start to Monday of the week, fixing single-day queries missing Monday-based week_start records
  • Portfolio image upload limits: 20MB file size limit with visual feedback (red borders, error text), upload button disabled for oversized files
  • NavBar enhancements: admin links reordered (Today/Schedule promoted), unread notification badge on mobile burger icon, backdrop overlay + slide transition for mobile menu
  • TodayCalendar improvements: fetches working/available hours for full week range instead of single day, closing time indicator on timeline, skips lunch suggestion for short days (5h or less)
  • Lunch protection refinement: shouldApplyLunchProtection() skips lunch protection for days with 5 or fewer working hours
  • formatDateISO utility: new formatDateISO(date) function in lib/utils/format.ts returning YYYY-MM-DD format for API calls, later extracted to shared utilities
  • JWT revocation with JTI: every JWT carries a unique jti claim (UUID v4), in-memory revoked JTI tracking with 5-minute cleanup ticker, POST /api/logout endpoint revokes current token, refresh handler revokes old JTI before issuing replacement
  • Portfolio image deletion fix: extractKey correctly extracts full S3 key path from URLs instead of just the filename, preventing orphaned files in storage
  • Notes validation: all Notes *string fields across booking structs validated with max=1000000 tag (13 fields across 4 files)
  • CharCounter component: reusable grapheme counter using Intl.Segmenter, shows counter only above 750K graphemes, color-coded (green <800K, yellow 800K-950K, red >950K), integrated into 6 booking/admin components
  • loginInProgress rate limiting: switched from map[string]bool to map[string]time.Time with 30-second staleness check, 20-entry cap (returns 429 when full), ticker goroutine cleans up stuck entries
  • Profile picture upload limit: 15MB client-side check before crop dialog in account page
  • Portfolio image upload backend limit: separate portfolioBodyLimit (20MB) applied to /images route, distinct from uploadBodyLimit (15MB) for profile pictures

Project Structure

Crussell/
├─ backend/            # Go 1.25 + chi router API
│  ├─ handlers/        # API handlers (auth, bookings, payments, scheduling, etc.)
│  │  ├─ payments/     # Square payment handlers (terminal, online, refunds, tips)
│  │  └─ webhooks/     # Square webhook handler
│  ├─ internal/        # Internal packages
│  │  └─ square/       # Square client (dev mock + prod stub, build-tagged)
│  └─ testutils/       # Test helpers (fixtures, testdb, JWT)
├─ frontend/           # SvelteKit 5 SPA (static build)
│  └─ src/lib/components/
│     ├─ payments/     # PaymentModal (admin), UserPaymentModal (user)
│     ├─ booking/      # BookingFlow (5-step wizard), DatePicker, TimeSlotPicker, TimeSlotList, SelectedTimeSummary
│     ├─ admin/        # BookingCreateModal, RescheduleModal, TimeBlockers, WeeklySchedule, HolidayHours, etc.
│     ├─ today/        # TodayCalendar (interactive grid), CurrentAppointment, PendingApprovals, TodayStats
│     └─ account/      # EditRequestModal, UserBookingModal
├─ frontend/src/lib/utils/  # Shared utilities (timeSlots.ts, format.ts)
├─ sabredav/           # PHP + Composer for DAV
├─ nginx/              # Nginx reverse-proxy for HTTP & HTTPS
├─ init-scripts/       # PostgreSQL init SQL
├─ compose.yml         # Docker-Compose definition
├─ local-dev-2.sh      # Dev helper using tmux (seeds DB with test data)
└─ README.md

Prerequisites

Tool Version
Docker & Docker Compose >= 20.10
Go >= 1.22
Node >= 18 (npm)
tmux >= 3.0

Getting Started

Docker Compose

cp .env.example .env
# Edit .env — set POSTGRES_*, JWT_SECRET_KEY
docker compose up --build -d
Service URL
Frontend http://localhost
API http://localhost/api
SabreDAV http://localhost/dav

Local Development

chmod +x local-dev-2.sh
./local-dev-2.sh

Launches a tmux session (crussell-dev) with 4 panes: psql console, Go dev server, Svelte dev server, and Rustfs logs. Seeds the database with 20 users, 12 services, 43 bookings, guest accounts, time blockers, and exceptional hours.

Default login (all password: password):

  • Admin: admin@example.com
  • User: user@example.com

Building & Testing

# Backend
cd backend && go build -o bin/backend ./main.go

# Frontend
cd frontend && npm ci && npm run build

# Tests (446/449 passing, 3 skipped)
cd backend && go test -tags "test,dev" ./...

Full Documentation

For detailed user journeys, admin workflows, architecture, API reference, and database schema, see obsidian/Crussell/.

S
Description
Nail salon website with booking system and portfolio
Readme
48 MiB
Languages
Go 64.6%
JavaScript 16.5%
Svelte 14.8%
TypeScript 1.9%
PLpgSQL 1.2%
Other 0.9%