feat: 10 quick wins — account deletion, health check, debug cleanup, UX polish, graceful shutdown
- backend/handlers/user/account.go: Wire DELETE /api/user/account to call anonymize_user() for registered users and delete_guest_user() for guests, with CardDAV contact cleanup - backend/handlers/user/profile_test.go: Add TestAccount_DeleteGuest and enhance TestAccount_Delete to verify anonymization results - backend/main.go: Add GET /api/health endpoint with DB ping and S3 status check; add HSTS and Referrer-Policy security headers; replace http.ListenAndServe with http.Server + graceful SIGTERM/SIGINT shutdown - frontend/routes/+layout.svelte: Replace alert() with toast notifications for email verification flow - frontend/routes/login/+page.svelte: Replace alert() with toast.info for social login prototype buttons - frontend/booking/BookingFlow.svelte: Remove 2 console.log debug calls; add cancellation policy note in Step 3; add timezone policy comment - frontend/ImageUpload.svelte: Comment out debug console.log - init-scripts/init-script.sql: Add delete_guest_user() SQL function - docs: Update README.md and Obsidian notes to reflect completed items
This commit is contained in:
@@ -440,13 +440,14 @@
|
||||
fd.append('tags', tags.join(','));
|
||||
}
|
||||
|
||||
// Debug: log what we're sending
|
||||
/* Debug: log what we're sending
|
||||
console.log(`Uploading ${fileKey}:`, {
|
||||
baseName,
|
||||
thumbName,
|
||||
thumbMimeType: thumbBlob.type,
|
||||
fullMimeType: resizedBlob.type
|
||||
});
|
||||
*/
|
||||
/* -------- 5. Call the API ------------------------------------- */
|
||||
uploadStatus[fileKey] = 'Uploading...';
|
||||
const response = await fetch('/api/portfolio/images', {
|
||||
|
||||
@@ -5,6 +5,11 @@
|
||||
import { Label } from '$lib/components/ui/label/index.js';
|
||||
import { Textarea } from '$lib/components/ui/textarea/index.js';
|
||||
import { Separator } from '$lib/components/ui/separator/index.js';
|
||||
// INTENTIONAL: We use the browser's local timezone (getLocalTimeZone) because Crussell is a UK-only
|
||||
// salon app. All customers are physically in the UK and book UK appointment slots. We do NOT
|
||||
// auto-adjust for international timezones — the slot time shown is the actual UK salon time.
|
||||
// Cloudflare geo-blocking prevents non-UK access. BST/GMT transitions are handled manually by
|
||||
// staff adjusting working hours; the app does not need timezone-aware scheduling logic.
|
||||
import { CalendarDate, getLocalTimeZone, type DateValue } from '@internationalized/date';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { toast } from 'svelte-sonner';
|
||||
@@ -856,8 +861,6 @@
|
||||
requestBody.user_id = guestUserId;
|
||||
}
|
||||
|
||||
console.log('Submitting booking:', requestBody);
|
||||
|
||||
const headers: Record<string, string> = { 'Content-Type': 'application/json' };
|
||||
if (authStore.currentToken) {
|
||||
headers['Authorization'] = `Bearer ${authStore.currentToken}`;
|
||||
@@ -871,8 +874,7 @@
|
||||
|
||||
if (response.ok) {
|
||||
const booking = await response.json();
|
||||
console.log('Booking created:', booking);
|
||||
|
||||
|
||||
// Show success message with booking details
|
||||
const bookingDateStr = new Date(booking.start_time).toLocaleDateString('en-GB', {
|
||||
weekday: 'long',
|
||||
@@ -1186,6 +1188,10 @@
|
||||
By booking, you agree to our Terms & Conditions and Privacy Policy. We'll send you
|
||||
appointment reminders via email and/or SMS.
|
||||
</p>
|
||||
<p class="mt-2 text-xs text-gray-500">
|
||||
<strong>Cancellation Policy:</strong> Free cancellation up to 24 hours before your
|
||||
appointment. Cancellations within 24 hours may incur a deposit penalty.
|
||||
</p>
|
||||
</div>
|
||||
</Card.Content>
|
||||
<Card.Footer class="flex justify-between">
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import favicon from '$lib/assets/favicon.svg';
|
||||
import NavBar from '$lib/components/layout/NavBar.svelte';
|
||||
import { Toaster } from '$lib/components/ui/sonner/index.js';
|
||||
import { toast } from 'svelte-sonner';
|
||||
import { authStore } from '$lib/stores/auth.svelte';
|
||||
import { onMount } from 'svelte';
|
||||
|
||||
@@ -30,15 +31,16 @@
|
||||
|
||||
// Verify email address
|
||||
async function verify_email() {
|
||||
alert('Verifying email address...');
|
||||
const loadingToast = toast.loading('Verifying email address...');
|
||||
const response = await fetch('/api/verify-email', {
|
||||
method: 'POST'
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
toast.success('Email verified successfully!', { id: loadingToast });
|
||||
window.location.reload();
|
||||
} else {
|
||||
alert('Failed to verify email address');
|
||||
toast.error('Failed to verify email address', { id: loadingToast });
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -253,7 +253,7 @@
|
||||
}
|
||||
|
||||
function handleSocialLogin(provider: string) {
|
||||
alert(`${provider} login clicked! (This is just a prototype)`);
|
||||
toast.info(`${provider} login coming soon`);
|
||||
}
|
||||
|
||||
// when password changes, re-compute strength
|
||||
|
||||
Reference in New Issue
Block a user