diff --git a/.env.example b/.env.example index 52900f5..79bc536 100644 --- a/.env.example +++ b/.env.example @@ -45,10 +45,12 @@ SQUARE_WEBHOOK_NOTIFICATION_URL= # Frontend (public — safe for the browser). Square Web Payments SDK: # VITE_SQUARE_APPLICATION_ID — client-side application ID (sandbox IDs start with "sandbox-") # VITE_SQUARE_LOCATION_ID — Square location ID -# VITE_SQUARE_ENVIRONMENT — 'sandbox' | 'production' (optional; derived from the app ID prefix when omitted) +# VITE_SQUARE_ENVIRONMENT — 'mock' | 'sandbox' | 'production'. Local dev: 'mock' renders the +# frontend's built-in mock card form (tokens only; pairs with +# SQUARE_ENVIRONMENT=mock above). NEVER set 'mock' in production. VITE_SQUARE_APPLICATION_ID= VITE_SQUARE_LOCATION_ID= -VITE_SQUARE_ENVIRONMENT= +VITE_SQUARE_ENVIRONMENT=mock # Test Database (separate from main DB) # Used by testutils/testdb for running tests without corrupting dev data diff --git a/frontend/src/lib/components/admin/ApprovalModal.svelte b/frontend/src/lib/components/admin/ApprovalModal.svelte index 2353936..737600b 100644 --- a/frontend/src/lib/components/admin/ApprovalModal.svelte +++ b/frontend/src/lib/components/admin/ApprovalModal.svelte @@ -335,7 +335,9 @@ onApproved(); } else { const text = await response.text(); - toast.error('Failed to confirm: ' + sanitizeText(extractErrorMessage(text)), { id: loadingToast }); + toast.error('Failed to confirm: ' + sanitizeText(extractErrorMessage(text)), { + id: loadingToast + }); } } catch { toast.error('Network error confirming booking', { id: loadingToast }); @@ -363,7 +365,9 @@ onApproved(); } else { const text = await response.text(); - toast.error('Failed to decline: ' + sanitizeText(extractErrorMessage(text)), { id: loadingToast }); + toast.error('Failed to decline: ' + sanitizeText(extractErrorMessage(text)), { + id: loadingToast + }); } } catch { toast.error('Network error declining booking', { id: loadingToast }); diff --git a/frontend/src/lib/components/admin/ServicesManagement.svelte b/frontend/src/lib/components/admin/ServicesManagement.svelte index fe5e84f..7eb28ca 100644 --- a/frontend/src/lib/components/admin/ServicesManagement.svelte +++ b/frontend/src/lib/components/admin/ServicesManagement.svelte @@ -279,7 +279,9 @@ toast.error(`Validation error: ${extractErrorMessage(errorText)}`, { id: loadingToast }); } else { const errorText = await response.text(); - toast.error(`Failed to create service: ${extractErrorMessage(errorText)}`, { id: loadingToast }); + toast.error(`Failed to create service: ${extractErrorMessage(errorText)}`, { + id: loadingToast + }); } } catch (err) { console.error('Error creating service:', err); diff --git a/frontend/src/lib/components/admin/TimeBlockers.svelte b/frontend/src/lib/components/admin/TimeBlockers.svelte index 2550bdf..eb1a849 100644 --- a/frontend/src/lib/components/admin/TimeBlockers.svelte +++ b/frontend/src/lib/components/admin/TimeBlockers.svelte @@ -448,7 +448,9 @@ await fetchBlockers(); } else { const text = await response.text(); - toast.error('Failed to create: ' + sanitizeText(extractErrorMessage(text)), { id: loadingToast }); + toast.error('Failed to create: ' + sanitizeText(extractErrorMessage(text)), { + id: loadingToast + }); } } catch (err) { console.error('Error creating time blocker:', err); @@ -475,7 +477,9 @@ await fetchBlockers(); } else { const text = await response.text(); - toast.error('Failed to delete: ' + sanitizeText(extractErrorMessage(text)), { id: loadingToast }); + toast.error('Failed to delete: ' + sanitizeText(extractErrorMessage(text)), { + id: loadingToast + }); } } catch (err) { console.error('Error deleting time blocker:', err); @@ -872,7 +876,12 @@ ? '' : 's'} with this slot - diff --git a/frontend/src/lib/components/layout/ContactCard.svelte b/frontend/src/lib/components/layout/ContactCard.svelte index 74be7da..58a73ad 100644 --- a/frontend/src/lib/components/layout/ContactCard.svelte +++ b/frontend/src/lib/components/layout/ContactCard.svelte @@ -23,16 +23,14 @@ type AvailabilityState = 'sleeping' | 'with_client' | 'busy' | 'prepping' | 'available'; - const STATUS_MAP: Record< - AvailabilityState, - { label: string; color: 'green' | 'amber' | 'red' } - > = { - sleeping: { label: 'Unable to take calls right now', color: 'red' }, - with_client: { label: 'With a client', color: 'red' }, - busy: { label: 'Busy', color: 'amber' }, - prepping: { label: 'Prepping for a client', color: 'amber' }, - available: { label: 'Available to contact', color: 'green' } - }; + const STATUS_MAP: Record = + { + sleeping: { label: 'Unable to take calls right now', color: 'red' }, + with_client: { label: 'With a client', color: 'red' }, + busy: { label: 'Busy', color: 'amber' }, + prepping: { label: 'Prepping for a client', color: 'amber' }, + available: { label: 'Available to contact', color: 'green' } + }; let availabilityState = $state(null); @@ -59,9 +57,7 @@ } } - let contactStatus = $derived( - availabilityState ? STATUS_MAP[availabilityState] : null - ); + let contactStatus = $derived(availabilityState ? STATUS_MAP[availabilityState] : null);
@@ -91,8 +87,8 @@ class="font-medium" class:text-green-700={contactStatus.color === 'green'} class:text-amber-700={contactStatus.color === 'amber'} - class:text-red-700={contactStatus.color === 'red'} - >{contactStatus.label} + class:text-red-700={contactStatus.color === 'red'}>{contactStatus.label}
{/if} diff --git a/frontend/src/lib/components/layout/PortfolioCarousel.svelte b/frontend/src/lib/components/layout/PortfolioCarousel.svelte index 6ec473e..b986c14 100644 --- a/frontend/src/lib/components/layout/PortfolioCarousel.svelte +++ b/frontend/src/lib/components/layout/PortfolioCarousel.svelte @@ -38,7 +38,9 @@
-

Our Portfolio

+

+ Our Portfolio +

diff --git a/frontend/src/lib/components/payments/MockCardForm.svelte b/frontend/src/lib/components/payments/MockCardForm.svelte new file mode 100644 index 0000000..95f0067 --- /dev/null +++ b/frontend/src/lib/components/payments/MockCardForm.svelte @@ -0,0 +1,253 @@ + + + +
+
+ +
+ onCardNumberInput(e)} + placeholder="1234 5678 9012 3456" + inputmode="numeric" + autocomplete="cc-number" + {disabled} + class="{inputClasses} pr-14" + aria-invalid={cardNumberError !== ''} + aria-describedby={cardNumberError ? `${cardNumberId}-error` : undefined} + /> + {#if detectedBrand !== ''} +
+ +
+ {/if} +
+ {#if cardNumberError} +

{cardNumberError}

+ {/if} +
+ +
+
+ + onExpiryInput(e)} + placeholder="MM/YY" + inputmode="numeric" + autocomplete="cc-exp" + {disabled} + class={inputClasses} + aria-invalid={expiryError !== ''} + aria-describedby={expiryError ? `${expiryId}-error` : undefined} + /> + {#if expiryError} +

{expiryError}

+ {/if} +
+
+ + onCvcInput(e)} + placeholder="123" + inputmode="numeric" + autocomplete="cc-csc" + {disabled} + class={inputClasses} + aria-invalid={cvcError !== ''} + aria-describedby={cvcError ? `${cvcId}-error` : undefined} + /> + {#if cvcError} +

{cvcError}

+ {/if} +
+
+ +
+ + +
+
diff --git a/frontend/src/lib/components/payments/SquareCardInput.svelte b/frontend/src/lib/components/payments/SquareCardInput.svelte index 4fff33d..b3c11e0 100644 --- a/frontend/src/lib/components/payments/SquareCardInput.svelte +++ b/frontend/src/lib/components/payments/SquareCardInput.svelte @@ -1,7 +1,8 @@ -{#if initError === 'not_configured'} +{#if isSquareMock()} + {#await import('./MockCardForm.svelte')} +
Loading card form...
+ {:then { default: MockCardFormCtor }} + + {/await} +{:else if initError === 'not_configured'} {:else if initError === 'init_failed'}
-

Our Services

-
-
-

Hands

-

- Gel polish, nail art, and natural nail care — finished with a soothing hand massage. -

+

Our Services

+
+
+

Hands

+

+ Gel polish, nail art, and natural nail care — finished with a soothing hand massage. +

+
+
+

Feet

+

+ Luxurious pedicures with exfoliation, cuticle care, and a long-lasting polish finish. +

+
+
+

Brows

+

+ Expert shaping, tinting, and lamination for brows that frame your face perfectly. +

+
+
+

Wax

+

+ Gentle waxing for face, body, and brows — smooth results with minimal discomfort. +

+
-
-

Feet

-

- Luxurious pedicures with exfoliation, cuticle care, and a long-lasting polish finish. -

-
-
-

Brows

-

- Expert shaping, tinting, and lamination for brows that frame your face perfectly. -

-
-
-

Wax

-

- Gentle waxing for face, body, and brows — smooth results with minimal discomfort. -

-
-
@@ -169,8 +169,8 @@
-

Opening Hours

- +

Opening Hours

+
@@ -178,5 +178,3 @@

Ready to Treat Yourself?

- - diff --git a/frontend/src/routes/admin/schedule/+page.svelte b/frontend/src/routes/admin/schedule/+page.svelte index bd8be22..051fc85 100644 --- a/frontend/src/routes/admin/schedule/+page.svelte +++ b/frontend/src/routes/admin/schedule/+page.svelte @@ -766,7 +766,11 @@ {/if} - fetchWeekData()} /> + fetchWeekData()} + /> {/if}