style: fix svelte/no-useless-mustaches — unwrap string literals from mustache expressions

This commit is contained in:
2026-06-25 14:31:49 +01:00
parent 766b5b7dbd
commit 0dff41a4ee
5 changed files with 7 additions and 493 deletions
@@ -1,486 +0,0 @@
# Timezone + Transaction Migration — Completion Plan
## Workflow per Issue (MANDATORY)
For EVERY issue in this plan, follow this exact sequence:
```
Step 1: IMPLEMENT the fix (delegate to subagent)
Step 2: WAIT for subagent to complete
Step 3: VERIFY — Read the changed files, run lsp_diagnostics, run go build
Step 4: CODE REVIEW — Manually inspect the diff. Check:
- Does the fix match the intended pattern?
- Are there edge cases the fix misses?
- Does it follow existing codebase conventions?
Step 5: ADD BACKEND TESTS for the fix (delegate to subagent)
Step 6: WAIT for test subagent to complete
Step 7: VERIFY tests — lsp_diagnostics, go build, go test on the relevant package
Step 8: UPDATE todo — mark issue complete
Step 9: MOVE TO next issue
```
**NO parallel execution across issues.** Each issue is a lock-step sequence.
## Ground Rules
- **NO `git checkout`**, `git stash`, `git reset`, `git restore`, or any destructive git commands.
- **NO `rm -rf`**, mass file deletion, or destructive filesystem operations.
- **Do NOT commit** unless explicitly asked.
- **Do NOT create new files** unless the plan explicitly says to. Prefer editing existing files.
- **Stage only intended files** if you do stage anything.
- **Every delegation prompt** must include the 6-section structure:
1. TASK — atomic, specific goal
2. EXPECTED OUTCOME — concrete deliverables
3. REQUIRED TOOLS — explicit whitelist
4. MUST DO — exhaustive requirements
5. MUST NOT DO — forbidden actions
6. CONTEXT — file paths, existing patterns, constraints
---
## Issue 1: Fix `calculateAge()` in format.ts
**File:** `/home/popertots/Crussell/frontend/src/lib/utils/format.ts`
**Lines:** ~92-101
**Bug:** Uses browser-local `getFullYear()/getMonth()/getDate()` for age calculation.
### Step 1 — Implement fix
Delegate to a `deep` or `unspecified-high` agent. Use Read + Edit tools only.
**Prompt:**
```
TASK: Fix calculateAge() function in /home/popertots/Crussell/frontend/src/lib/utils/format.ts to use Europe/London timezone.
EXPECTED OUTCOME: calculateAge() returns correct age for all UK users regardless of browser timezone. Function signature, return type, and all callers remain unchanged.
REQUIRED TOOLS: Read, Edit
MUST DO:
- Read lines 85-105 of the file to see the current implementation
- Parse the DOB string directly (split by '-') to get year/month/day
- Get today's London date using toLocaleDateString('en-CA', { timeZone: 'Europe/London' })
- Compute age: difference in years, subtract 1 if birthday hasn't occurred yet in London time
- Return integer (years)
MUST NOT DO:
- Do not change function signature or how callers use it
- Do not import external date libraries
- Do not modify any other function in format.ts
CONTEXT: Used during registration to validate minimum age (16+). DOB comes from form as "YYYY-MM-DD" string. UK-only app.
```
### Step 3 — Verify
Read the changed function. Confirm:
- DOB is parsed by string split (not via `new Date(dob)`)
- London date uses `toLocaleDateString('en-CA', { timeZone: 'Europe/London' })`
- Age computation handles month/day comparison correctly
- Run `lsp_diagnostics` on format.ts
### Step 5 — Add backend tests (skip — this is frontend-only code)
---
## Issue 2: Fix 4 display components missing `timeZone: 'Europe/London'`
### Step 1 — Implement fix
Delegate to a `visual-engineering` or `deep` agent. Read each file, then edit.
**Files to fix:**
1. `/home/popertots/Crussell/frontend/src/lib/components/admin/CustomServicesManagement.svelte` — line ~289
2. `/home/popertots/Crussell/frontend/src/lib/components/admin/GiftCardsManagement.svelte` — line ~752
3. `/home/popertots/Crussell/frontend/src/routes/gdpr/+page.svelte` — lines ~262 and ~268
**Prompt:**
```
TASK: Add timeZone: 'Europe/London' to toLocaleDateString/toLocaleString calls in 4 locations across 3 frontend files.
EXPECTED OUTCOME: All date displays use Europe/London timezone so that UTC timestamps near midnight display the correct London date.
REQUIRED TOOLS: Read, Edit, Grep
MUST DO:
- Read each file at the specified line to understand the context
- Determine if the value being formatted is a datetime (needs timezone) or date-only (safe as-is)
- Add timeZone: 'Europe/London' to the options object where needed
- The call sites are:
1. CustomServicesManagement.svelte:~289 — likely a toLocaleDateString for expiry/schedule display
2. GiftCardsManagement.svelte:~752 — gift card date display
3. gdpr/+page.svelte:~262 — some GDPR-related date
4. gdpr/+page.svelte:~268 — some GDPR-related date
MUST NOT DO:
- Do not change any logic other than adding the timeZone option
- Do not modify any CSS or HTML structure
CONTEXT: UK-only booking platform. UTC timestamps near midnight display wrong London date without explicit timeZone option.
```
### Step 3 — Verify
Read each changed file. Confirm the `timeZone: 'Europe/London'` was added correctly.
Run `lsp_diagnostics` on each changed file (if the LSP supports Svelte).
### Step 5 — Add backend tests (skip — these are frontend-only changes)
---
## Issue 3: Fix remaining `toISOString().split('T')[0]` calls
### Step 1 — Implement fix
Delegate to a `deep` or `visual-engineering` agent.
**Files:**
1. `/home/popertots/Crussell/frontend/src/routes/login/+page.svelte` — line ~491
2. `/home/popertots/Crussell/frontend/src/routes/gdpr/+page.svelte` — line ~467
**Prompt:**
```
TASK: Replace 2 remaining toISOString().split('T')[0] calls with toLocaleDateString('en-CA', { timeZone: 'Europe/London' }) in the frontend.
EXPECTED OUTCOME: No more toISOString().split('T')[0] patterns remain in the frontend. Date-only strings are produced via London-timezone-aware formatting.
REQUIRED TOOLS: Read, Edit
MUST DO:
- Read login/+page.svelte around line 491 — replace toISOString().split('T')[0] with toLocaleDateString('en-CA', { timeZone: 'Europe/London' })
- Read gdpr/+page.svelte around line 467 — same replacement
- Verify the context: login uses the value as a max date attribute; gdpr uses it in a JSON filename
MUST NOT DO:
- Do not change any surrounding logic
- Do not remove the +00:00 or any other API communication toISOString() calls that were confirmed safe
CONTEXT: Both are cosmetic — login date input max value and GDPR JSON filename. But keeping them consistent prevents future timezone bugs.
```
### Step 3 — Verify
Read both changed files. Confirm the replacement intent (date-only, not API communication).
### Step 5 — Add backend tests (skip — frontend-only changes)
---
## Issue 4: Fix 3 genuine payment bugs
**Background:** The reference pattern is `ProcessPendingSquareRefunds` in `refunds.go`. It inserts records with `status='pending'` inside the transaction, commits, THEN calls the Square API. The schema already supports this: `payment_status` ENUM has `'pending'`, and all tables default to it.
### Step 1 — Fix Issue 4a: CreateTipPayment Square-BEFORE-tx
**File:** `/home/popertots/Crussell/backend/handlers/payments/handlers.go`
**Lines:** ~1700-1752
**Prompt:** (delegate to `unspecified-high` agent)
```
TASK: Fix CreateTipPayment handler in /home/popertots/Crussell/backend/handlers/payments/handlers.go so the Square API call happens AFTER the DB transaction commit, not before.
EXPECTED OUTCOME: Square CreatePayment is called only after the DB transaction commits successfully. If the transaction fails, Square is never called. No money lost.
REQUIRED TOOLS: Read, Edit
MUST DO:
- Read lines 1690-1760 to understand the current flow
- Restructure so: start tx → INSERT payment with status='pending' and square_payment_id=NULL → tx.Commit() → call SquareClient.CreatePayment() → if success, UPDATE payment SET status='completed', square_payment_id=$1
- Keep the same idempotency key pattern
- Keep the same error logging
- The payment record already has a `status` column (payment_status ENUM, defaults to 'pending')
MUST NOT DO:
- Do not change the function signature
- Do not change any other handler in the file
- Do not remove the advisory unlock or any existing cleanup
CONTEXT: Currently Square is called at line ~1711 BEFORE the tx starts at ~1732. If tx.Commit() fails, the Square charge is orphaned with no recovery path.
```
### Step 3 — Code Review for 4a
Read the changed handler. Verify:
- tx begins FIRST
- Payment record inserted BEFORE Square call with `status='pending'`
- `tx.Commit()` happens BEFORE `SquareClient.CreatePayment()`
- Square result updates the record to `'completed'`
- Error handling covers: tx.Commit() fails (return 500, no Square charge), Square fails (leave as 'pending' for manual retry)
- `go build ./handlers/payments/` passes
### Step 5 — Add backend tests for 4a
Delegate to subagent:
```
TASK: Add test coverage for the CreateTipPayment fix in handlers/payments/payments_test.go.
EXPECTED OUTCOME: A test that verifies when CreateTipPayment's tx.Commit() fails, the Square API is never called, and when tx.Commit() succeeds, Square IS called.
REQUIRED TOOLS: Read, Edit, Grep
MUST DO:
- Read the existing test patterns in payments_test.go (mock Square client, test HTTP handlers)
- Create a test that:
1. Sets up a booking with a tip amount
2. Mocks SquareClient.CreatePayment to succeed
3. Calls CreateTipPayment handler
4. Asserts the payment record exists with status='completed'
- Create another test that:
1. Sets up a booking with a tip amount
2. Mocks the DB to fail on commit (use test context cancellation or tx rollback trigger)
3. Verifies Square.CreatePayment was NOT called
4. Verifies 500 status is returned
MUST NOT DO:
- Do not modify production code (only add tests)
- Do not use external mocking frameworks (use existing mock patterns)
- Follow existing test file conventions
CONTEXT: Payment tests use mock square client. Test fixtures create bookings. Test transactions use testutils.SetupTestTx(t).
```
### Step 7 — Verify tests for 4a
Run `go test ./handlers/payments/ -run TestCreateTipPayment -v -count=1` — confirm it passes.
---
### Step 1 — Fix Issue 4b: BuyGiftCard Square-BEFORE-tx
**File:** `/home/popertots/Crussell/backend/handlers/payments/giftcards.go`
**Lines:** ~860-1044
**Prompt:** (delegate to `unspecified-high` agent)
```
TASK: Fix BuyGiftCard handler in giftcards.go so the Square API call happens AFTER the DB transaction commit.
EXPECTED OUTCOME: Square CreatePayment is called only after DB transaction commits successfully. If commit fails, Square is never called.
REQUIRED TOOLS: Read, Edit
MUST DO:
- Read lines 840-1060 of giftcards.go
- Restructure: start tx → INSERT gift_card with status='pending' → INSERT payment with status='pending' → tx.Commit() → squareClient.CreatePayment() → if success, UPDATE gift_card SET status='active' AND UPDATE payment SET status='completed'
- Remove the Square API calls from before the tx (lines ~860-906)
- Keep the idempotency check at lines ~846-855
- The gift_cards table uses the same payment_status ENUM pattern
MUST NOT DO:
- Do not change the handler signature
- Do not change gift card value calculations or VAT
- Do not modify the CreateCardOnFileRaw call (it's for card tokenization, not charging)
CONTEXT: Currently Square CreatePayment is called at line ~901 before the tx starts at line ~908. Schema supports pending→completed pattern.
```
### Step 3 — Code Review for 4b
Read the changed handler. Verify the pending→completed pattern is correct.
`go build ./handlers/payments/` passes.
### Step 5 — Add backend tests for 4b
Delegate to subagent following existing gift card test patterns.
### Step 7 — Verify tests for 4b
`go test ./handlers/payments/ -run TestBuyGiftCard -v -count=1` passes.
---
### Step 1 — Fix Issue 4c: CreateTillSale saved_card/online_square inside-tx
**File:** `/home/popertots/Crussell/backend/handlers/payments/till.go`
**Lines:** ~112-420
**Prompt:** (delegate to `unspecified-high` agent)
```
TASK: Fix CreateTillSale handler in till.go so Square API calls for saved_card and online_square payment methods happen AFTER the DB transaction commit.
EXPECTED OUTCOME: Square CreatePayment for saved_card and online_square methods is called only after tx.Commit(). If commit fails, Square is never called. Card_machine, cash, and on_the_house methods remain unchanged.
REQUIRED TOOLS: Read, Edit
MUST DO:
- Read till.go lines 100-430 fully
- Understand the 4 payment methods: cash (no Square), card_machine (checkout only — keep as is), saved_card (Square Payment), online_square (Square card tokenize + Payment), on_the_house (no Square)
- For saved_card and online_square:
- Insert the till_sale INSIDE the tx with status='pending' (saleStatus = "pending")
- Insert/update gift_card records INSIDE the tx as currently done
- Commit the tx
- THEN call SquareClient.CreatePayment()
- On Square success, update the till_sale status to 'completed' with the square_payment_id
- On Square failure, leave till_sale as 'pending' for manual retry
- For card_machine: keep the existing pattern (checkout created inside tx, reconciled by GetTillCheckoutStatus)
- For cash/on_the_house: keep as-is (no Square call)
MUST NOT DO:
- Do not change the function signature or request/response types
- Do not change the VAT calculation logic
- Do not modify card_machine, cash, or on_the_house flows
- Do not remove the gift card creation/topup logic from inside the tx (it should stay there)
CONTEXT: The till_sales table has a `status payment_status DEFAULT 'pending'` column. The gift_card_transactions table tracks gift card operations. The existing card_machine flow already uses saleStatus = "pending".
```
### Step 3 — Code Review for 4c
Read the changed handler. Verify:
- saved_card flow uses pending→commit→Square→completed
- online_square flow uses pending→commit→Square→completed
- card_machine flow unchanged
- cash/on_the_house flows unchanged
- `go build ./handlers/payments/` passes
### Step 5 — Add backend tests for 4c
Delegate to subagent to add tests for the till sale flows.
### Step 7 — Verify tests for 4c
`go test ./handlers/payments/ -run TestCreateTillSale -v -count=1` passes.
---
## Issue 5: Migrate all `time.Now()` → `clock.Now()` in test files
**Strategy:** Process by Go package directory, one directory at a time.
**Order:**
1. `handlers/today/` (already clean — 0 replacements, just verify)
2. `handlers/admin/` (4 files, ~67 replacements)
3. `handlers/scheduling/` (2 files, ~54 replacements)
4. `handlers/payments/` (4 files, ~25 replacements)
5. `handlers/auth/` + `auth/` (2 files, ~12 replacements)
6. `handlers/user/` (1 file, ~5 replacements)
7. `handlers/notifications/` (1 file, ~4 replacements)
8. `mw/` (1 file, ~4 replacements)
9. `handlers/bookings/` (7 files, ~232 replacements — save the largest for last)
**For EACH directory:**
### Step 1 — Implement migrate
Delegate to an `unspecified-high` agent.
**Prompt template:**
```
TASK: Replace all time.Now() calls with clock.Now() in test files in /home/popertots/Crussell/backend/handlers/[PACKAGE]/.
EXPECTED OUTCOME: Every test file in the package uses clock.Now() instead of time.Now(). Import of "crussell/clock" is added where missing. Build passes.
REQUIRED TOOLS: Read, Edit
MUST DO:
- For EACH *_test.go file in the directory:
1. Read the file to find all time.Now() calls
2. If the file doesn't already import "crussell/clock", add it to the imports
3. Replace EVERY occurrence of time.Now() with clock.Now()
4. Handle special cases:
- time.Now().UTC() → clock.Now() (clock.Now() already returns UTC)
- time.Now().In(londonLocation) → clock.Now().In(londonLocation) (time.Now().In() → clock.Now().In())
- time.Now().Add(...) → clock.Now().Add(...) (bulk replace works)
- time.Now().UnixNano() → clock.Now().UnixNano() (same)
- time.Now().Truncate(...) → clock.Now().Truncate(...) (same)
5. Save each file after editing
- Run go build ./handlers/[PACKAGE]/... to verify it compiles
SPECIAL CASE for handlers/bookings/bookings_test.go:
- The nextWorkingHour() helper function at ~line 48 uses time.Now() with .Hour() for wall-clock logic
- Fix: replace time.Now() with clock.Now().In(londonLocation) so the wall-clock check uses London time
- Then change the return statement to use .UTC() before returning
MUST NOT DO:
- Do not modify production code files (only *_test.go files)
- Do not modify files outside the specified directory
- Do not add "crussell/clock" to production code files
- Do not remove any existing imports
CONTEXT: clock.Now() returns time.Now().UTC(). All production code already uses clock.Now(). The test files were left behind in the previous migration.
```
### Step 3 — Verify
Check each changed file:
- Import of "crussell/clock" is present
- `time.Now()` is fully replaced (no lingering occurrences)
- `go build ./handlers/[PACKAGE]/...` passes
- `go vet ./handlers/[PACKAGE]/...` passes
### Step 5 — Add backend tests (not needed for this issue — it IS test migration)
### Step 9 — Move to next package directory
---
## Issue 6: Add autumn DST regression tests
### Step 1 — Add tests (one file at a time)
**Test file 1:** `/home/popertots/Crussell/backend/handlers/scheduling/scheduling_test.go`
**Tests to add:**
- `TestScheduling_DST_AutumnBack_BookingAt0130BST` — booking at 01:30 BST (00:30 UTC) on Oct 25 spans transition
- `TestScheduling_DST_AutumnBack_BookingAt0130GMT` — booking at 01:30 GMT (01:30 UTC) on Oct 25
Delegate to subagent:
```
TASK: Add autumn DST regression tests to scheduling_test.go.
EXPECTED OUTCOME: Two new test functions covering bookings at the repeated hour on autumn DST day.
REQUIRED TOOLS: Read, Edit
MUST DO:
- Read the existing test TestScheduling_DST_BlockerOnSpringForward (~line 2580) for pattern reference
- Read scheduling_test.go helpers: resetTestData, makeAdminAvailableHoursRequest, findDayByDate, slotExists
- Add TestScheduling_DST_AutumnBack_BookingAt0130BST:
- Date: 2026-10-25 (Oct 25, 2026 = last Sunday of October, BST→GMT transition)
- Create exceptional hours for Sunday (weekday 6) 09:00-17:00
- Create a booking at 01:30 BST = 00:30 UTC (spans DST transition)
- Query available hours for 2026-10-25
- Assert: the 00:30 slot is blocked (booking starts)
- Add TestScheduling_DST_AutumnBack_BookingAt0130GMT:
- Same setup, but booking at 01:30 GMT = 01:30 UTC
- Query available hours
- Assert: the 01:30 slot is blocked
MUST NOT DO:
- Do not modify production code
- Do not modify existing tests
- Use time.Date with londonLocation for explicit timezone construction
CONTEXT: The londonLocation variable is available in scheduling_test.go (package-level). Oct 25, 2026 is the autumn DST date. BST ends at 02:00 BST → 01:00 GMT. The duplicated hour is 01:00-02:00 wall-clock time (occurs twice: first as BST, then as GMT).
```
### Step 3 — Verify
Read the added test functions. `go build ./handlers/scheduling/` passes.
### Step 5 — Add more tests (next file)
**Test file 2:** `/home/popertots/Crussell/backend/handlers/today/today_test.go`
**Test to add:**
- `TestFindWeekSummaryRange_AutumnDST` — London-aligned boundaries for Oct 25
**Test file 3:** `/home/popertots/Crussell/backend/handlers/bookings/admin_reserve_test.go`
**Test to add:**
- `TestAdminReserveSlot_AutumnDST_ClosingBoundary` — closing time on autumn DST day
**Test file 4:** `/home/popertots/Crussell/backend/handlers/admin/bookings_test.go`
**Test to add:**
- `TestAdminBookings_CountMatchesData_AutumnDST` — count query at autumn boundary
Each gets its own subagent delegation, implemented and verified sequentially.
---
## Issue 7: Align `timeSlots.ts` buffer to 60 minutes
**File:** `/home/popertots/Crussell/frontend/src/lib/utils/timeSlots.ts`
**Line:** ~201
### Step 1 — Implement fix
Simple one-line change. Can do directly or delegate.
**Fix:** Change `Math.ceil((currentMinutes + 15) / 15) * 15` to `Math.ceil((currentMinutes + 60) / 15) * 15`.
### Step 3 — Verify
Read the changed line. Confirm the correct line was changed.
### Step 5 — Add backend tests (skip — frontend-only)
---
## Completion Verification
After all 7 issues are complete:
1. `cd /home/popertots/Crussell/backend && go build ./...` — must pass with zero errors
2. `cd /home/popertots/Crussell/backend && go vet ./...` — must pass with zero errors
3. `cd /home/popertots/Crussell/backend && go test ./...` — must pass (or note pre-existing failures)
4. Report on each issue: completed, any deviations, any test failures
@@ -336,7 +336,7 @@
{settings.website_url}
</a>
{:else}
{'\u2014'}
{/if}
</p>
</div>
@@ -877,10 +877,10 @@
minute: '2-digit',
hour12: true
})}}
{' · '}
·
{formatDuration(booking.duration_minutes)}
{#if booking.services?.length}
{' · '}
·
{booking.services.join(', ')}
{/if}
</div>
@@ -96,8 +96,8 @@
</div>
<div class="mt-1 text-sm text-emerald-700">
{selectedDate} at {selectedTime}
{' — '}
{endTime}
{' ('}{duration} min)
({duration} min)
</div>
</div>
@@ -1034,9 +1034,9 @@
minute: '2-digit',
hour12: true
})}
{' · '}{formatDuration(booking.duration_minutes)}
· {formatDuration(booking.duration_minutes)}
{#if booking.services?.length}
{' · '}{booking.services.join(', ')}{/if}
· {booking.services.join(', ')}{/if}
</div>
</div>
<div class="mt-1.5 flex gap-2">