Commit Graph
278 Commits
Author SHA1 Message Date
popertotsandSisyphus 44895a3697 feat(admin): add business settings API and routing for gift card config
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-11 22:08:21 +01:00
popertotsandSisyphus 30ded0bb95 feat(payments): add gift card system v2 with management, expiry, inventory, audit log
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-11 22:08:18 +01:00
popertots 6756723c60 saved cards store 2026-06-07 12:38:22 +01:00
popertots 75cca8b158 fixes 2026-06-06 15:25:58 +01:00
popertots 59e7dd9fdc fixes 2026-06-06 15:23:10 +01:00
popertots 9c6eeeb426 Fixes 2026-06-06 15:18:37 +01:00
popertots b274d2c47b fixes for giftcards and phone input 2026-06-06 15:11:01 +01:00
popertots 4e174a6123 Fixes for giftcards and phone input 2026-06-06 15:10:48 +01:00
popertots 5258f90b06 Add giftcard management 2026-06-06 14:26:42 +01:00
popertots 310e6aaa80 Add giftcard management 2026-06-06 14:26:33 +01:00
popertots 44c9d423ec fix: restore cardNum definition in addCard
- Re-added 'const cardNum = newCardNumber.replace(/\s/g, '');' inside addCard
- Log actual error to console in catch block of addCard
2026-06-05 21:15:05 +01:00
popertots d2dea38b01 fix: replace crypto.randomUUID with generateIdempotencyKey
- Changed purchase flow to use generateIdempotencyKey() which uses secure
  window.crypto.getRandomValues with a math fallback, avoiding secure-context blocks on HTTP
- Log actual error to console in both Buy and Redeem catch blocks
2026-06-05 21:11:47 +01:00
popertots 883fd3076a fix: disable pay button unless full valid card is entered
- Added isBuyExpiryValid and isBuyCardValid reactive derivations
- Disabled Buy Gift Card button unless card is fully valid (Luhn checked number, future expiry, complete CVC)
- Verified clean compilation with zero warnings
2026-06-05 21:07:13 +01:00
popertots 0d4a74bd4a feat: caret preservation and Luhn validation in card inputs
- Added generic formatAndPreserveCursor() helper on frontend to track
  and restore selection caret position during dynamic input sanitization
- Applied to all card inputs, gift card code inputs, and expiry inputs
- Added Luhn validation (isValidLuhn) for saved cards and gift cards
- Rebuilt payments test DB and got 100% green tests
2026-06-05 21:05:36 +01:00
popertots fd41ca2920 chore: add catch-all for Go test binaries in .gitignore 2026-06-05 16:37:37 +01:00
popertots f4a6033715 feat: financial data retention & aggregation system
Add CleanupExpiredFinancialRecords to enforce HMRC + Limitation Act
compliance (7-year retention, 1-year post-anonymization buffer).

- financial_aggregates table: monthly totals by payment method/type (no PII)
- CleanupExpiredFinancialRecords(): aggregates expired payments/refunds,
  deletes granular records, idempotent via ON CONFLICT DO UPDATE
- Wired into GET /api/availability alongside existing cleanup functions
- 8 tests: 7yr expiry, 1yr buffer, 9yr override, aggregation totals,
  idempotency, active user protection, both-thresholds elapsed, refunds
- testdb.go: financial_aggregates in drop-order and truncate lists
- README + Technical Manual updated
2026-06-05 16:37:04 +01:00
popertotsandSisyphus 06efdfdac0 chore(obsidian): update workspace state
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-05 15:40:51 +01:00
popertotsandSisyphus 69420298bd feat(frontend): add booking stats summary section to GDPR page
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-05 15:40:46 +01:00
popertotsandSisyphus 5cc3e25a2c fix(db): update FK constraints to SET NULL and expand delete_user anonymization
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-05 15:40:43 +01:00
popertotsandSisyphus f88854e5c2 feat(scheduling): add expired loyalty cleanup and extend guest anonymization
Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-05 15:40:38 +01:00
popertotsandSisyphus 6cc65341a0 docs: update README and Obsidian docs with GDPR compliance system
Update README feature list with GDPR compliance system details. Update Overview.md Data & Compliance section with full Article 15 SAR details. Update Technical Manual: add /gdpr route, gdpr_export.go handler, GET /api/user/gdpr-export endpoint, update Key Functions table, update Guest User System section. Update Future Work backlog: mark #7 GDPR export as complete, update dependency map and Phase 3 execution order. Update test count: 446/449 -> 576/579.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-05 12:13:34 +01:00
popertotsandSisyphus 36ad38f670 feat(frontend): add /gdpr page with PDF export and JSON download
New /gdpr route: skeleton loading state, 2s polling for async export, styled report cards/tables matching /schedule and /account styling. 16 data sections with conditional rendering (empty sections hidden). VAT breakdowns hidden until any payment has non-zero VAT. Deposits Required hidden when 0. PDF export via window.print() with print CSS hiding navbar and verification banner. Raw JSON download button. Export My Data button added to Account page under Change Password. Vite worker format set to 'es' for WASM encoder compatibility.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-05 12:13:07 +01:00
popertotsandSisyphus b460c2ec20 fix(scheduling): extend AnonymizeStaleGuestAccounts with additional field scrubbing
AnonymizeStaleGuestAccounts now additionally scrubs profile_pic_url (NULL), referral_code (NULL), notes (NULL), and data_retention_consent (FALSE) for stale guest accounts. Previously only scrubbed name, email, phone, and date_of_birth. Ensures comprehensive PII removal for guests with no active/pending bookings and last booking over 6 months old.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-05 12:12:38 +01:00
popertotsandSisyphus 40840c0458 feat(user): extend DeleteAccountHandler with external system scrubbing
DeleteAccountHandler now scrubs external systems BEFORE SQL-level anonymization. S3: deletes profile picture from S3/R2 (profiles/{userID}.jpg) when s3.Client is configured. Square: iterates user's saved cards and calls DeleteCardOnFile for each non-deleted card when payments.SquareClient is configured. Both run in background goroutines (non-blocking, best-effort with warning logs). CardDAV contact deletion remains as existing best-effort goroutine.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-05 12:12:12 +01:00
popertotsandSisyphus f58a4bd2a9 feat(user): add async GDPR export endpoint with 12h cache and 25 tests
Add GET /api/user/gdpr-export handler (GetGDPRExportHandler) with async background generation — navigation away doesn't cancel. 12h in-memory cache with 5-minute cleanup ticker. Three cache states: MISS (triggers generation), HIT (returns cached data), GENERATING (concurrent request while building). Route registered without RequireVerified middleware so unverified users can export. 25 tests covering: auth rejection, cache miss/hit/generating/expired states, anonymize_user child table scrubbing (social logins, saved cards, verification codes, time blockers, edit notes, notification prefs), export_all_user_data 16-section export, AnonymizeStaleGuestAccounts field scrubbing.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-05 12:11:45 +01:00
popertotsandSisyphus bf0571fe84 fix(db): anonymize_user NOT NULL constraint fix + export_all_user_data extensions
Fix anonymize_user() setting exp_month/exp_year to NULL on NOT NULL columns — use placeholder values (1/2000) matching pattern for other NOT NULL columns. Extend export_all_user_data() with 9 new sections: saved_cards, refunds, social_logins, loyalty_redemptions, booking_discounts, edit_requests, affiliate_payouts, verification_codes, forgiven_no_shows. Add total_price to booking objects (sums COALESCE override prices). LEFT JOIN loyalty_redemptions to bookings for booking_date/booking_services. LEFT JOIN booking_discounts to discount_campaigns for campaign_name. Simplify referrals to referrer_name/referred_name using u.fn.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-05 12:11:17 +01:00
popertots 6d4bc4d637 feat(loyalty-discount): implement loyalty and discount system
- Add discount campaign management and validation logic
- Update booking handlers with discount application flow
- Add customer relationship endpoints for loyalty tracking
- Update frontend modals (booking, approval, payment, reschedule)
- Add DiscountsManagement and loyalty reference documentation
- Update dev scripts and database init for discount tables
- Clean up completed plan files
2026-06-04 23:13:02 +01:00
popertots 79b23a3cf7 New Plan 2026-06-04 21:12:21 +01:00
popertotsandSisyphus 5e02b90fdc docs: update route references for /admin/notifications move
Updated Technical Manual and Future Work backlog to reflect /notifications → /admin/notifications move and /manage removal.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 20:09:30 +01:00
popertotsandSisyphus f379e5c09a refactor(routes): move notifications to /admin/notifications, remove /manage
Moved /notifications to /admin/notifications to match admin-only access pattern. Updated NavBar bell icon and mobile menu links. Removed /manage route (leftover prototyping). Pre-hydration redirect already in place for admin-only guard.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 20:08:34 +01:00
popertotsandSisyphus 24b1823a5d fix(routes): add pre-hydration auth redirects to all protected pages
Replaced slow $effect-based auth checks with synchronous IIFE scripts in <svelte:head> that run during HTML parsing, before Svelte hydration. Covers: /today, /admin, /admin/schedule, /manage, /notifications (admin-only), and /account (auth-required). All redirect via window.location.replace() with no page flash.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 20:04:54 +01:00
popertotsandSisyphus 413b28b759 fix(login): redirect authenticated users before hydration
Uses a raw IIFE script in <svelte:head> that runs synchronously during HTML parsing, before Svelte hydration. Checks localStorage JWT and calls window.location.replace() for an instant redirect with no page flash.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 19:50:53 +01:00
popertotsandSisyphus ae1bc0f91a fix(login): redirect authenticated users before page render
Replaced -based redirect with +page.ts load function. Checks localStorage token directly and throws redirect(302) before the component mounts, preventing any flash of the login page.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 19:48:08 +01:00
popertotsandSisyphus f083da6345 fix(login): redirect authenticated users away from login page
Authenticated users visiting /login are immediately redirected: admins to /today, all others to /.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 19:46:36 +01:00
popertotsandSisyphus 8a1da43131 test(auth): add referral code case-insensitivity tests
Tests lowercase, uppercase, mixed case, and all caps referral codes against a lowercase stored code. All 4 sub-tests pass, confirming the strings.ToLower fix in RegisterHandler.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 12:05:20 +01:00
popertotsandSisyphus 7d1b90a5be fix(auth): lowercase referral code before validation and lookup
Defense-in-depth: referral codes are generated as hex (lowercase only) by the DB. The frontend already lowercases on input, but direct API calls with uppercase would fail the DB lookup. Normalize to lowercase on the backend to prevent capslock situations.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 12:02:39 +01:00
popertotsandSisyphus 486173c5ff fix(user): profile handler and main router updates
Minor adjustments to profile handler imports and main router configuration.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 11:25:41 +01:00
popertotsandSisyphus 068ae79462 chore: update dev seed data with loyalty stamps
Primary test user (user@example.com) now has 4 loyalty stamps in seed data for testing the redesigned stamp card.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 01:09:33 +01:00
popertotsandSisyphus db78b5186e chore: update S3 dev configuration with AWS SDK v2
S3 dev implementation now uses AWS SDK v2 for RustFS local storage. Prod stub remains placeholder. Both implement Uploader interface with Upload, Download, Delete, GetURL methods.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 01:09:15 +01:00
popertotsandSisyphus 490274f466 docs: update README and Obsidian documentation
Document multi-format image pipeline, MapLibre GL map components, WASM encoder workers, loyalty stamp redesign, admin role restrictions, and Svelte 5 improvements across README, Overview, and Technical Manual.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 01:09:00 +01:00
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
popertotsandSisyphus c76c3f52f7 feat(ui): loyalty stamp redesign and admin account restrictions
Redesigned fuchsia-themed loyalty stamp card with procedural SVG flower-petal stamps (unique shape per slot via sine-wave perturbation), star cutout mask, hover animations, and gradient background. Admin accounts now see simplified account page with History, Referral, Danger Zone tabs and loyalty stamp card hidden.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 01:07:43 +01:00
popertotsandSisyphus 4c26db81db feat(frontend): add MapLibre GL map components
Reusable Svelte map component library wrapping MapLibre GL JS: Map, MapMarker, MapControls, MapPopup, MapRoute, MapClusterLayer, MapArc, and supporting components. Features theme auto-detection, controlled/uncontrolled viewport, drag support, and context API. useMap hook provides reactive access. Integrated on contact page for salon location display.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 01:07:25 +01:00
popertotsandSisyphus 584c02ac6c feat(frontend): ImageVariant component and WASM encoder workers
Add client-side multi-format image encoding via Web Workers using @jsquash and @discourse/jxl libraries. ImageVariant Svelte component renders <picture> elements with format-aware fallback. Vite configured for WASM asset inclusion and encoder dependency exclusion. Portfolio page updated to use multi-format URLs.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 01:07:00 +01:00
popertotsandSisyphus 0da7498ad7 feat(images): multi-format portfolio pipeline (AVIF/WebP/JPEG/JXL)
Backend now stores AVIF, WebP, JPEG, and optional JXL variants for both full-size and thumbnail images. Database schema extended with 7 new columns. Image validation supports AVIF and JXL magic bytes. Comprehensive test coverage for all format URL fields and magic byte detection. Legacy single-URL images remain backward-compatible.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-06-04 01:06:33 +01:00
popertots bffb984ebb feat(auth,security,scheduling): JWT revocation, S3 fix, notes validation, docs, tests
- JWT revocation with JTI (UUID v4): in-memory tracking, POST /api/logout,
  refresh handler revokes old JTI, RequireAuth rejects revoked tokens
- Fix extractKey for S3 portfolio deletion: extracts full key path from URLs
  instead of just filename, preventing orphaned storage files
- Notes validation: max=1000000 on all 13 Notes fields across 4 booking structs
- CharCounter: grapheme-aware counter (Intl.Segmenter), threshold 750K,
  color-coded, integrated into 6 booking/admin components
- loginInProgress: timestamp-based tracking, 30s staleness, 20-entry cap (429),
  ticker cleanup for stuck entries
- Profile picture 15MB client-side limit, portfolio 20MB backend limit
- Exceptional scheduling: expand query start to Monday of week
- TodayCalendar: week-range fetching, closing time indicator, short-day lunch skip
- NavBar: link reorder, mobile burger badge, slide transition, backdrop
- ImageUpload: 20MB limit with visual feedback
- formatDateISO: shared YYYY-MM-DD utility, shouldApplyLunchProtection helper
- Update README.md and all Obsidian docs (Overview, Technical, Admin, Future Work)
- Add 28 new tests: JWT (11), auth handlers (7), portfolio extractKey (5),
  notes validation (5). go build + go vet clean with test,dev tags
2026-06-03 11:17:41 +01:00
popertots 169d7dc6e3 feat(scheduling,admin,today): week-range queries, file size limits, mobile nav UX
- Expand exceptional application query to Monday of start week
- Add 20MB file size limit with visual feedback in ImageUpload
- Reorder admin nav links, add burger badge, slide transition + backdrop
- Fetch week-range working/available hours, add closing time indicator
- Skip lunch protection for days <= 5h via shouldApplyLunchProtection
- Extract formatDateISO to shared utils
2026-06-03 10:22:16 +01:00
popertotsandSisyphus b1847b4cfc fix(payments,portfolio,scheduling): add ID validation hardening
Add validators.IsValidID() checks on URL param IDs to return 404 instead of 400 for invalid IDs. Add offset cap and query length limit in portfolio images handler.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-31 18:49:16 +01:00
popertotsandSisyphus 1d8247e655 fix(auth,services,profile): add request struct validation
Add validate tags and validators.Validate.Struct() calls on request structs for auth verification, service creation, and user patch test endpoints.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-31 18:49:12 +01:00
popertotsandSisyphus 577cceb304 fix(bookings): add request struct validation and fix patch test notice logic
Add validators.Validate.Struct() calls across booking handlers. Fix patch test notice period check to compare against booking start time (not current time) and fix expiry check similarly. Update test to match new error message.

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

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
2026-05-31 18:49:07 +01:00