chore: commit pending changes before custom services implementation

This commit is contained in:
2026-06-13 11:27:47 +01:00
parent 45814a4040
commit d77f330bda
4 changed files with 77 additions and 53 deletions
+1
View File
@@ -64,6 +64,7 @@
"@zxcvbn-ts/language-common": "^3.0.4", "@zxcvbn-ts/language-common": "^3.0.4",
"@zxcvbn-ts/language-en": "^3.0.2", "@zxcvbn-ts/language-en": "^3.0.2",
"cropperjs": "^1.6.2", "cropperjs": "^1.6.2",
"heic2any": "^0.0.4",
"maplibre-gl": "^5.24.0" "maplibre-gl": "^5.24.0"
} }
} }
@@ -4,6 +4,7 @@
import FileDropZone from '$lib/components/ui/file-drop-zone.svelte'; import FileDropZone from '$lib/components/ui/file-drop-zone.svelte';
import { authStore } from '$lib/stores/auth.svelte'; import { authStore } from '$lib/stores/auth.svelte';
import * as AlertDialog from '$lib/components/ui/alert-dialog'; import * as AlertDialog from '$lib/components/ui/alert-dialog';
import heic2any from 'heic2any';
// =============== Image Upload =============== // =============== Image Upload ===============
let uploading = $state(false); let uploading = $state(false);
@@ -28,9 +29,60 @@
let hasOversizedFiles = $derived(uploadFiles.some(isFileTooBig)); let hasOversizedFiles = $derived(uploadFiles.some(isFileTooBig));
function handleFilesDropped(files: File[]) { function isHeicFile(file: File): boolean {
uploadFiles = files; const name = file.name.toLowerCase();
generatePreviews(files); return name.endsWith('.heic') || name.endsWith('.heif');
}
/**
* Check if the browser can natively decode a HEIC file.
* Safari (macOS/iOS) and Chrome-on-Android have native HEIC support,
* which preserves HDR metadata and color profiles better than any conversion.
*/
function supportsNativeHeicDecode(file: File): Promise<boolean> {
return new Promise((resolve) => {
const img = new Image();
const url = URL.createObjectURL(file);
img.onload = () => { URL.revokeObjectURL(url); resolve(true); };
img.onerror = () => { URL.revokeObjectURL(url); resolve(false); };
img.src = url;
});
}
async function convertHeicToPng(file: File): Promise<File> {
const result = await heic2any({ blob: file, toType: 'image/png' });
const blob = Array.isArray(result) ? result[0] : result;
const pngName = file.name.replace(/\.(heic|heif)$/i, '.png');
return new File([blob], pngName, { type: 'image/png' });
}
async function handleFilesDropped(files: File[]) {
const converted = await Promise.all(
files.map(async (file) => {
if (!isHeicFile(file)) return file;
// Try native browser HEIC decode first (Safari, Chrome w/ HEVC)
// This preserves HDR metadata & color profiles at full quality
try {
const nativeOk = await supportsNativeHeicDecode(file);
if (nativeOk) return file;
} catch {
// Fall through to heic2any
}
// Fallback: convert to PNG via heic2any (libheif WASM)
// PNG is lossless, libheif handles ICC profiles correctly,
// and the downstream canvas pipeline is 8-bit anyway.
try {
return await convertHeicToPng(file);
} catch (e) {
console.error('HEIC conversion failed for', file.name, e);
return file;
}
})
);
uploadFiles = converted;
generatePreviews(converted);
} }
/** Generate scaled previews for each file */ /** Generate scaled previews for each file */
+4 -5
View File
@@ -13,12 +13,12 @@
"state": { "state": {
"type": "markdown", "type": "markdown",
"state": { "state": {
"file": "Crussell/Technical Manual.md", "file": "Crussell/Future Work - Gap Backlog.md",
"mode": "source", "mode": "source",
"source": false "source": false
}, },
"icon": "lucide-file", "icon": "lucide-file",
"title": "Technical Manual" "title": "Future Work - Gap Backlog"
} }
} }
] ]
@@ -78,8 +78,7 @@
} }
], ],
"direction": "horizontal", "direction": "horizontal",
"width": 300, "width": 300
"collapsed": true
}, },
"right": { "right": {
"id": "2750d7726f904ef3", "id": "2750d7726f904ef3",
@@ -172,10 +171,10 @@
}, },
"active": "6776d739ee18449c", "active": "6776d739ee18449c",
"lastOpenFiles": [ "lastOpenFiles": [
"Crussell/Technical Manual.md",
"Crussell/Future Work - Gap Backlog.md", "Crussell/Future Work - Gap Backlog.md",
"Crussell/Loyalty & Discount System Reference.md", "Crussell/Loyalty & Discount System Reference.md",
"Crussell/Overview.md", "Crussell/Overview.md",
"Crussell/Technical Manual.md",
"Crussell/User Manual.md", "Crussell/User Manual.md",
"Crussell/Admin Manual.md", "Crussell/Admin Manual.md",
"Crussell/Test Implementation Plan.md", "Crussell/Test Implementation Plan.md",
+1 -29
View File
@@ -39,7 +39,7 @@ These are blockers: missing functionality that prevents daily operations, legal
These improve the experience or add features, but the business can operate without them. These improve the experience or add features, but the business can operate without them.
| # | Gap | Effort | Area | Notes | | # | Gap | Effort | Area | Notes |
|---|---|---|---|---| | --- | ---------------------------------------- | -------- | ---------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| 12 | **One-off custom services** | M (1-2d) | Full-stack | Admin can't create single-use services. Every custom job (bridal party, special request) must be added to the permanent catalog. | | 12 | **One-off custom services** | M (1-2d) | Full-stack | Admin can't create single-use services. Every custom job (bridal party, special request) must be added to the permanent catalog. |
| 13 | **One-off exceptional hours** | M (1d) | Full-stack | Single-day overrides (dentist appointment, afternoon off) require creating a full exceptional group. Time blockers handle unavailable periods; one-off *open* hours (e.g., "open Sunday 2pm-5pm") still need simplification. | | 13 | **One-off exceptional hours** | M (1d) | Full-stack | Single-day overrides (dentist appointment, afternoon off) require creating a full exceptional group. Time blockers handle unavailable periods; one-off *open* hours (e.g., "open Sunday 2pm-5pm") still need simplification. |
| 14 | **Referral system UI** | M (1-2d) | Full-stack | Backend complete — registration validates codes, relationships recorded. Users can't see their referral code or track uses. Admin can't manage referral campaigns. | | 14 | **Referral system UI** | M (1-2d) | Full-stack | Backend complete — registration validates codes, relationships recorded. Users can't see their referral code or track uses. Admin can't manage referral campaigns. |
@@ -78,31 +78,3 @@ E10 Sentry → error tracking, 5xx alerting
``` ```
All local (MVP + Stretch) items have zero external dependencies. All local (MVP + Stretch) items have zero external dependencies.
---
## Notes on Removed Items
The following items were completed and removed from this document:
- Delete account endpoint, GDPR data export, delete_guest_user SQL function, GDPR anonymization
- Walk-in guest booking, walk-in slot blocking, reservation transitions
- ApprovalModal decline/cancel, admin notification panel, admin schedule page
- BookingFlow welcome step, shared format utilities, patch_test_duration_hours
- created_by_name on bookings, admin login redirect, customer relationship view
- Admin UI for edit requests, booking rescheduling (user + admin)
- Auto lunch protection, health check endpoint, graceful shutdown
- HSTS + Referrer-Policy headers, console.log cleanup, alert prototypes
- Idempotency keys for bookings, loyalty stamp redemption + discount system
- Test DB optimization, image optimization, image deletion fix
- User notification preferences UI, payment integration (Square), deposit payments
- Tips UI, gift card system (backend + admin UI), referral code registration
- CharCounter, loginInProgress rate limiting, profile picture limits, formatDateISO
- Portfolio upload limits, JWT revocation, MapLibre GL, NavBar enhancements
- TodayCalendar improvements, Svelte 5 $derived, SvelteDate, $app/paths resolve
- Vite WASM support, portfolio multi-format images, loyalty stamp redesign
- Admin role restrictions, account restrictions, BookingsByCreatedRange, enriched edit requests
- Email check endpoint, PhoneInput, business settings API, gift card audit log
- Gift card expiry, idle account cleanup, inventory cards, expired balance recovery
- Till sale idempotency, VAT on till sales, test infrastructure upgrades
- Financial data retention, gift card transaction audit log