From 88cccb34564bd661b3b730d1338430230510df06 Mon Sep 17 00:00:00 2001 From: "Janus C. H. Knudsen" Date: Sun, 16 Nov 2025 19:56:31 +0100 Subject: [PATCH] Enhances IndexedDB service with booking and resource support Updates IndexedDB database schema to version 2 Adds support for bookings, customers, and resources stores Includes new serialization methods for booking data Extends events store with additional indexes for improved querying --- src/storage/IndexedDBService.ts | 70 ++++- src/types/BookingTypes.ts | 1 + wwwroot/data/bookings.json | 306 ++++++++++++++++++++ wwwroot/data/customers.json | 49 ++++ wwwroot/data/events.json | 485 ++++++++++++++++++++++++++++++++ wwwroot/data/resources.json | 80 ++++++ 6 files changed, 990 insertions(+), 1 deletion(-) create mode 100644 wwwroot/data/bookings.json create mode 100644 wwwroot/data/customers.json create mode 100644 wwwroot/data/events.json create mode 100644 wwwroot/data/resources.json diff --git a/src/storage/IndexedDBService.ts b/src/storage/IndexedDBService.ts index 20d7293..6a9546f 100644 --- a/src/storage/IndexedDBService.ts +++ b/src/storage/IndexedDBService.ts @@ -1,4 +1,5 @@ import { ICalendarEvent } from '../types/CalendarTypes'; +import { IBooking } from '../types/BookingTypes'; /** * Operation for the sync queue @@ -18,10 +19,13 @@ export interface IQueueOperation { */ export class IndexedDBService { private static readonly DB_NAME = 'CalendarDB'; - private static readonly DB_VERSION = 1; + private static readonly DB_VERSION = 2; private static readonly EVENTS_STORE = 'events'; private static readonly QUEUE_STORE = 'operationQueue'; private static readonly SYNC_STATE_STORE = 'syncState'; + private static readonly BOOKINGS_STORE = 'bookings'; + private static readonly CUSTOMERS_STORE = 'customers'; + private static readonly RESOURCES_STORE = 'resources'; private db: IDBDatabase | null = null; private initialized: boolean = false; @@ -45,6 +49,7 @@ export class IndexedDBService { request.onupgradeneeded = (event) => { const db = (event.target as IDBOpenDBRequest).result; + const oldVersion = (event as IDBVersionChangeEvent).oldVersion; // Create events store if (!db.objectStoreNames.contains(IndexedDBService.EVENTS_STORE)) { @@ -52,6 +57,27 @@ export class IndexedDBService { eventsStore.createIndex('start', 'start', { unique: false }); eventsStore.createIndex('end', 'end', { unique: false }); eventsStore.createIndex('syncStatus', 'syncStatus', { unique: false }); + eventsStore.createIndex('resourceId', 'resourceId', { unique: false }); + eventsStore.createIndex('customerId', 'customerId', { unique: false }); + eventsStore.createIndex('bookingId', 'bookingId', { unique: false }); + eventsStore.createIndex('startEnd', ['start', 'end'], { unique: false }); + } else if (oldVersion < 2) { + // Upgrade from version 1: Add new indexes to existing events store + const transaction = (event.target as IDBOpenDBRequest).transaction!; + const eventsStore = transaction.objectStore(IndexedDBService.EVENTS_STORE); + + if (!eventsStore.indexNames.contains('resourceId')) { + eventsStore.createIndex('resourceId', 'resourceId', { unique: false }); + } + if (!eventsStore.indexNames.contains('customerId')) { + eventsStore.createIndex('customerId', 'customerId', { unique: false }); + } + if (!eventsStore.indexNames.contains('bookingId')) { + eventsStore.createIndex('bookingId', 'bookingId', { unique: false }); + } + if (!eventsStore.indexNames.contains('startEnd')) { + eventsStore.createIndex('startEnd', ['start', 'end'], { unique: false }); + } } // Create operation queue store @@ -64,6 +90,28 @@ export class IndexedDBService { if (!db.objectStoreNames.contains(IndexedDBService.SYNC_STATE_STORE)) { db.createObjectStore(IndexedDBService.SYNC_STATE_STORE, { keyPath: 'key' }); } + + // Create bookings store (v2) + if (!db.objectStoreNames.contains(IndexedDBService.BOOKINGS_STORE)) { + const bookingsStore = db.createObjectStore(IndexedDBService.BOOKINGS_STORE, { keyPath: 'id' }); + bookingsStore.createIndex('customerId', 'customerId', { unique: false }); + bookingsStore.createIndex('status', 'status', { unique: false }); + bookingsStore.createIndex('createdAt', 'createdAt', { unique: false }); + } + + // Create customers store (v2) + if (!db.objectStoreNames.contains(IndexedDBService.CUSTOMERS_STORE)) { + const customersStore = db.createObjectStore(IndexedDBService.CUSTOMERS_STORE, { keyPath: 'id' }); + customersStore.createIndex('name', 'name', { unique: false }); + customersStore.createIndex('phone', 'phone', { unique: false }); + } + + // Create resources store (v2) + if (!db.objectStoreNames.contains(IndexedDBService.RESOURCES_STORE)) { + const resourcesStore = db.createObjectStore(IndexedDBService.RESOURCES_STORE, { keyPath: 'id' }); + resourcesStore.createIndex('type', 'type', { unique: false }); + resourcesStore.createIndex('isActive', 'isActive', { unique: false }); + } }; }); } @@ -334,6 +382,26 @@ export class IndexedDBService { }; } + /** + * Serialize booking for IndexedDB storage (convert Dates to ISO strings) + */ + private serializeBooking(booking: IBooking): any { + return { + ...booking, + createdAt: booking.createdAt instanceof Date ? booking.createdAt.toISOString() : booking.createdAt + }; + } + + /** + * Deserialize booking from IndexedDB (convert ISO strings to Dates) + */ + private deserializeBooking(booking: any): IBooking { + return { + ...booking, + createdAt: typeof booking.createdAt === 'string' ? new Date(booking.createdAt) : booking.createdAt + }; + } + /** * Close database connection */ diff --git a/src/types/BookingTypes.ts b/src/types/BookingTypes.ts index 17bac5e..7509fd5 100644 --- a/src/types/BookingTypes.ts +++ b/src/types/BookingTypes.ts @@ -14,6 +14,7 @@ * - One service can be performed by one resource * - Multiple services can be performed by different resources * - Example: Hårvask by Student, Bundfarve by Master (same booking, 2 resources) + * - Equal-split: Two services with same type but different resources (e.g., "Bryllupsfrisure Del 1" by Karina, "Bryllupsfrisure Del 2" by Nanna) * * Matches backend Booking table structure */ diff --git a/wwwroot/data/bookings.json b/wwwroot/data/bookings.json new file mode 100644 index 0000000..a4c0eec --- /dev/null +++ b/wwwroot/data/bookings.json @@ -0,0 +1,306 @@ +[ + { + "id": "BOOK001", + "customerId": "CUST001", + "status": "arrived", + "createdAt": "2025-08-05T08:00:00Z", + "services": [ + { + "serviceId": "SRV001", + "serviceName": "Klipning og styling", + "baseDuration": 60, + "basePrice": 500, + "customPrice": 500, + "resourceId": "EMP001" + } + ], + "totalPrice": 500, + "notes": "Kunde ønsker lidt kortere" + }, + { + "id": "BOOK002", + "customerId": "CUST002", + "status": "paid", + "createdAt": "2025-08-05T09:00:00Z", + "services": [ + { + "serviceId": "SRV002", + "serviceName": "Hårvask", + "baseDuration": 30, + "basePrice": 100, + "customPrice": 100, + "resourceId": "STUDENT001" + }, + { + "serviceId": "SRV003", + "serviceName": "Bundfarve", + "baseDuration": 90, + "basePrice": 800, + "customPrice": 800, + "resourceId": "EMP001" + } + ], + "totalPrice": 900, + "notes": "Split booking: Elev laver hårvask, master laver farve" + }, + { + "id": "BOOK003", + "customerId": "CUST003", + "status": "created", + "createdAt": "2025-08-05T07:00:00Z", + "services": [ + { + "serviceId": "SRV004A", + "serviceName": "Bryllupsfrisure - Del 1", + "baseDuration": 60, + "basePrice": 750, + "customPrice": 750, + "resourceId": "EMP001" + }, + { + "serviceId": "SRV004B", + "serviceName": "Bryllupsfrisure - Del 2", + "baseDuration": 60, + "basePrice": 750, + "customPrice": 750, + "resourceId": "EMP002" + } + ], + "totalPrice": 1500, + "notes": "Equal-split: To master stylister arbejder sammen" + }, + { + "id": "BOOK004", + "customerId": "CUST004", + "status": "arrived", + "createdAt": "2025-08-05T10:00:00Z", + "services": [ + { + "serviceId": "SRV005", + "serviceName": "Herreklipning", + "baseDuration": 30, + "basePrice": 350, + "customPrice": 350, + "resourceId": "EMP003" + } + ], + "totalPrice": 350 + }, + { + "id": "BOOK005", + "customerId": "CUST005", + "status": "paid", + "createdAt": "2025-08-05T11:00:00Z", + "services": [ + { + "serviceId": "SRV006", + "serviceName": "Balayage langt hår", + "baseDuration": 120, + "basePrice": 1200, + "customPrice": 1200, + "resourceId": "EMP002" + } + ], + "totalPrice": 1200, + "notes": "Kunde ønsker naturlig blond tone" + }, + { + "id": "BOOK006", + "customerId": "CUST006", + "status": "created", + "createdAt": "2025-08-06T08:00:00Z", + "services": [ + { + "serviceId": "SRV007", + "serviceName": "Permanent", + "baseDuration": 90, + "basePrice": 900, + "customPrice": 900, + "resourceId": "EMP004" + } + ], + "totalPrice": 900 + }, + { + "id": "BOOK007", + "customerId": "CUST007", + "status": "arrived", + "createdAt": "2025-08-06T09:00:00Z", + "services": [ + { + "serviceId": "SRV008", + "serviceName": "Highlights", + "baseDuration": 90, + "basePrice": 850, + "customPrice": 850, + "resourceId": "EMP001" + }, + { + "serviceId": "SRV009", + "serviceName": "Styling", + "baseDuration": 30, + "basePrice": 200, + "customPrice": 200, + "resourceId": "EMP001" + } + ], + "totalPrice": 1050, + "notes": "Highlights + styling samme stylist" + }, + { + "id": "BOOK008", + "customerId": "CUST008", + "status": "paid", + "createdAt": "2025-08-06T10:00:00Z", + "services": [ + { + "serviceId": "SRV010", + "serviceName": "Klipning", + "baseDuration": 45, + "basePrice": 450, + "customPrice": 450, + "resourceId": "EMP004" + } + ], + "totalPrice": 450 + }, + { + "id": "BOOK009", + "customerId": "CUST001", + "status": "created", + "createdAt": "2025-08-07T08:00:00Z", + "services": [ + { + "serviceId": "SRV011", + "serviceName": "Farve behandling", + "baseDuration": 120, + "basePrice": 950, + "customPrice": 950, + "resourceId": "EMP002" + } + ], + "totalPrice": 950 + }, + { + "id": "BOOK010", + "customerId": "CUST002", + "status": "arrived", + "createdAt": "2025-08-07T09:00:00Z", + "services": [ + { + "serviceId": "SRV012", + "serviceName": "Skæg trimning", + "baseDuration": 20, + "basePrice": 200, + "customPrice": 200, + "resourceId": "EMP003" + } + ], + "totalPrice": 200 + }, + { + "id": "BOOK011", + "customerId": "CUST003", + "status": "paid", + "createdAt": "2025-08-07T10:00:00Z", + "services": [ + { + "serviceId": "SRV002", + "serviceName": "Hårvask", + "baseDuration": 30, + "basePrice": 100, + "customPrice": 100, + "resourceId": "STUDENT002" + }, + { + "serviceId": "SRV013", + "serviceName": "Ombré", + "baseDuration": 100, + "basePrice": 1100, + "customPrice": 1100, + "resourceId": "EMP002" + } + ], + "totalPrice": 1200, + "notes": "Split booking: Student hårvask, master ombré" + }, + { + "id": "BOOK012", + "customerId": "CUST004", + "status": "created", + "createdAt": "2025-08-08T08:00:00Z", + "services": [ + { + "serviceId": "SRV014", + "serviceName": "Føntørring", + "baseDuration": 30, + "basePrice": 250, + "customPrice": 250, + "resourceId": "STUDENT001" + } + ], + "totalPrice": 250 + }, + { + "id": "BOOK013", + "customerId": "CUST005", + "status": "arrived", + "createdAt": "2025-08-08T09:00:00Z", + "services": [ + { + "serviceId": "SRV015", + "serviceName": "Opsætning", + "baseDuration": 60, + "basePrice": 700, + "customPrice": 700, + "resourceId": "EMP004" + } + ], + "totalPrice": 700, + "notes": "Fest opsætning" + }, + { + "id": "BOOK014", + "customerId": "CUST006", + "status": "created", + "createdAt": "2025-08-09T08:00:00Z", + "services": [ + { + "serviceId": "SRV016A", + "serviceName": "Ekstensions - Del 1", + "baseDuration": 90, + "basePrice": 1250, + "customPrice": 1250, + "resourceId": "EMP001" + }, + { + "serviceId": "SRV016B", + "serviceName": "Ekstensions - Del 2", + "baseDuration": 90, + "basePrice": 1250, + "customPrice": 1250, + "resourceId": "EMP004" + } + ], + "totalPrice": 2500, + "notes": "Equal-split: To stylister arbejder sammen om extensions" + }, + { + "id": "BOOK015", + "customerId": "CUST007", + "status": "noshow", + "createdAt": "2025-08-09T09:00:00Z", + "services": [ + { + "serviceId": "SRV001", + "serviceName": "Klipning og styling", + "baseDuration": 60, + "basePrice": 500, + "customPrice": 500, + "resourceId": "EMP002" + } + ], + "totalPrice": 500, + "notes": "Kunde mødte ikke op" + } +] diff --git a/wwwroot/data/customers.json b/wwwroot/data/customers.json new file mode 100644 index 0000000..28997bf --- /dev/null +++ b/wwwroot/data/customers.json @@ -0,0 +1,49 @@ +[ + { + "id": "CUST001", + "name": "Sofie Nielsen", + "phone": "+45 23 45 67 89", + "email": "sofie.nielsen@email.dk" + }, + { + "id": "CUST002", + "name": "Emma Andersen", + "phone": "+45 31 24 56 78", + "email": "emma.andersen@email.dk" + }, + { + "id": "CUST003", + "name": "Freja Christensen", + "phone": "+45 42 67 89 12", + "email": "freja.christensen@email.dk" + }, + { + "id": "CUST004", + "name": "Laura Pedersen", + "phone": "+45 51 98 76 54" + }, + { + "id": "CUST005", + "name": "Ida Larsen", + "phone": "+45 29 87 65 43", + "email": "ida.larsen@email.dk" + }, + { + "id": "CUST006", + "name": "Caroline Jensen", + "phone": "+45 38 76 54 32", + "email": "caroline.jensen@email.dk" + }, + { + "id": "CUST007", + "name": "Mathilde Hansen", + "phone": "+45 47 65 43 21", + "email": "mathilde.hansen@email.dk" + }, + { + "id": "CUST008", + "name": "Olivia Sørensen", + "phone": "+45 56 54 32 10", + "email": "olivia.sorensen@email.dk" + } +] diff --git a/wwwroot/data/events.json b/wwwroot/data/events.json new file mode 100644 index 0000000..498cbe5 --- /dev/null +++ b/wwwroot/data/events.json @@ -0,0 +1,485 @@ +[ + { + "id": "EVT001", + "title": "Sofie Nielsen - Klipning og styling", + "start": "2025-08-05T10:00:00Z", + "end": "2025-08-05T11:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK001", + "resourceId": "EMP001", + "customerId": "CUST001" + }, + { + "id": "EVT002", + "title": "Emma Andersen - Hårvask", + "start": "2025-08-05T11:00:00Z", + "end": "2025-08-05T11:30:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK002", + "resourceId": "STUDENT001", + "customerId": "CUST002" + }, + { + "id": "EVT003", + "title": "Emma Andersen - Bundfarve", + "start": "2025-08-05T11:30:00Z", + "end": "2025-08-05T13:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK002", + "resourceId": "EMP001", + "customerId": "CUST002" + }, + { + "id": "EVT004", + "title": "Freja Christensen - Bryllupsfrisure (Camilla)", + "start": "2025-08-05T08:00:00Z", + "end": "2025-08-05T10:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK003", + "resourceId": "EMP001", + "customerId": "CUST003", + "metadata": { + "note": "To stylister arbejder sammen" + } + }, + { + "id": "EVT005", + "title": "Freja Christensen - Bryllupsfrisure (Isabella)", + "start": "2025-08-05T08:00:00Z", + "end": "2025-08-05T10:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK003", + "resourceId": "EMP002", + "customerId": "CUST003", + "metadata": { + "note": "To stylister arbejder sammen" + } + }, + { + "id": "EVT006", + "title": "Laura Pedersen - Herreklipning", + "start": "2025-08-05T11:00:00Z", + "end": "2025-08-05T11:30:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK004", + "resourceId": "EMP003", + "customerId": "CUST004" + }, + { + "id": "EVT007", + "title": "Ida Larsen - Balayage langt hår", + "start": "2025-08-05T13:00:00Z", + "end": "2025-08-05T15:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK005", + "resourceId": "EMP002", + "customerId": "CUST005" + }, + { + "id": "EVT008", + "title": "Frokostpause", + "start": "2025-08-05T12:00:00Z", + "end": "2025-08-05T12:30:00Z", + "type": "break", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP003" + }, + { + "id": "EVT009", + "title": "Caroline Jensen - Permanent", + "start": "2025-08-06T09:00:00Z", + "end": "2025-08-06T10:30:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK006", + "resourceId": "EMP004", + "customerId": "CUST006" + }, + { + "id": "EVT010", + "title": "Mathilde Hansen - Highlights", + "start": "2025-08-06T10:00:00Z", + "end": "2025-08-06T11:30:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK007", + "resourceId": "EMP001", + "customerId": "CUST007" + }, + { + "id": "EVT011", + "title": "Mathilde Hansen - Styling", + "start": "2025-08-06T11:30:00Z", + "end": "2025-08-06T12:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK007", + "resourceId": "EMP001", + "customerId": "CUST007" + }, + { + "id": "EVT012", + "title": "Olivia Sørensen - Klipning", + "start": "2025-08-06T13:00:00Z", + "end": "2025-08-06T13:45:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK008", + "resourceId": "EMP004", + "customerId": "CUST008" + }, + { + "id": "EVT013", + "title": "Team møde - Salgsmål", + "start": "2025-08-06T08:00:00Z", + "end": "2025-08-06T08:30:00Z", + "type": "meeting", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP001", + "metadata": { + "attendees": ["EMP001", "EMP002", "EMP003", "EMP004"] + } + }, + { + "id": "EVT014", + "title": "Frokostpause", + "start": "2025-08-06T12:00:00Z", + "end": "2025-08-06T12:30:00Z", + "type": "break", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP002" + }, + { + "id": "EVT015", + "title": "Sofie Nielsen - Farve behandling", + "start": "2025-08-07T10:00:00Z", + "end": "2025-08-07T12:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK009", + "resourceId": "EMP002", + "customerId": "CUST001" + }, + { + "id": "EVT016", + "title": "Emma Andersen - Skæg trimning", + "start": "2025-08-07T09:00:00Z", + "end": "2025-08-07T09:20:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK010", + "resourceId": "EMP003", + "customerId": "CUST002" + }, + { + "id": "EVT017", + "title": "Freja Christensen - Hårvask", + "start": "2025-08-07T11:00:00Z", + "end": "2025-08-07T11:30:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK011", + "resourceId": "STUDENT002", + "customerId": "CUST003" + }, + { + "id": "EVT018", + "title": "Freja Christensen - Ombré", + "start": "2025-08-07T11:30:00Z", + "end": "2025-08-07T13:10:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK011", + "resourceId": "EMP002", + "customerId": "CUST003" + }, + { + "id": "EVT019", + "title": "Frokostpause", + "start": "2025-08-07T12:00:00Z", + "end": "2025-08-07T12:30:00Z", + "type": "break", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP001" + }, + { + "id": "EVT020", + "title": "Laura Pedersen - Føntørring", + "start": "2025-08-08T09:00:00Z", + "end": "2025-08-08T09:30:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK012", + "resourceId": "STUDENT001", + "customerId": "CUST004" + }, + { + "id": "EVT021", + "title": "Ida Larsen - Opsætning", + "start": "2025-08-08T10:00:00Z", + "end": "2025-08-08T11:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK013", + "resourceId": "EMP004", + "customerId": "CUST005" + }, + { + "id": "EVT022", + "title": "Produktleverance møde", + "start": "2025-08-08T08:00:00Z", + "end": "2025-08-08T08:30:00Z", + "type": "meeting", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP001", + "metadata": { + "attendees": ["EMP001", "EMP004"] + } + }, + { + "id": "EVT023", + "title": "Frokostpause", + "start": "2025-08-08T12:00:00Z", + "end": "2025-08-08T12:30:00Z", + "type": "break", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP004" + }, + { + "id": "EVT024", + "title": "Caroline Jensen - Ekstensions (Camilla)", + "start": "2025-08-09T09:00:00Z", + "end": "2025-08-09T12:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK014", + "resourceId": "EMP001", + "customerId": "CUST006", + "metadata": { + "note": "To stylister arbejder sammen" + } + }, + { + "id": "EVT025", + "title": "Caroline Jensen - Ekstensions (Viktor)", + "start": "2025-08-09T09:00:00Z", + "end": "2025-08-09T12:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK014", + "resourceId": "EMP004", + "customerId": "CUST006", + "metadata": { + "note": "To stylister arbejder sammen" + } + }, + { + "id": "EVT026", + "title": "Mathilde Hansen - Klipning og styling", + "start": "2025-08-09T10:00:00Z", + "end": "2025-08-09T11:00:00Z", + "type": "customer", + "allDay": false, + "syncStatus": "synced", + "bookingId": "BOOK015", + "resourceId": "EMP002", + "customerId": "CUST007", + "metadata": { + "note": "NOSHOW - kunde mødte ikke op" + } + }, + { + "id": "EVT027", + "title": "Ferie - Spanien", + "start": "2025-08-10T00:00:00Z", + "end": "2025-08-17T23:59:59Z", + "type": "vacation", + "allDay": true, + "syncStatus": "synced", + "resourceId": "EMP003", + "metadata": { + "destination": "Mallorca" + } + }, + { + "id": "EVT028", + "title": "Frokostpause", + "start": "2025-08-09T12:00:00Z", + "end": "2025-08-09T12:30:00Z", + "type": "break", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP002" + }, + { + "id": "EVT029", + "title": "Kaffepause", + "start": "2025-08-05T14:00:00Z", + "end": "2025-08-05T14:15:00Z", + "type": "break", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP004" + }, + { + "id": "EVT030", + "title": "Kursus - Nye farvningsteknikker", + "start": "2025-08-11T09:00:00Z", + "end": "2025-08-11T16:00:00Z", + "type": "meeting", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP001", + "metadata": { + "location": "København", + "type": "external_course" + } + }, + { + "id": "EVT031", + "title": "Supervision - Elev", + "start": "2025-08-05T15:00:00Z", + "end": "2025-08-05T15:30:00Z", + "type": "meeting", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP001", + "metadata": { + "attendees": ["EMP001", "STUDENT001"] + } + }, + { + "id": "EVT032", + "title": "Aftensmad pause", + "start": "2025-08-06T17:00:00Z", + "end": "2025-08-06T17:30:00Z", + "type": "break", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP001" + }, + { + "id": "EVT033", + "title": "Supervision - Elev", + "start": "2025-08-07T15:00:00Z", + "end": "2025-08-07T15:30:00Z", + "type": "meeting", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP002", + "metadata": { + "attendees": ["EMP002", "STUDENT002"] + } + }, + { + "id": "EVT034", + "title": "Rengøring af arbejdsstation", + "start": "2025-08-08T16:00:00Z", + "end": "2025-08-08T16:30:00Z", + "type": "blocked", + "allDay": false, + "syncStatus": "synced", + "resourceId": "STUDENT001" + }, + { + "id": "EVT035", + "title": "Rengøring af arbejdsstation", + "start": "2025-08-08T16:00:00Z", + "end": "2025-08-08T16:30:00Z", + "type": "blocked", + "allDay": false, + "syncStatus": "synced", + "resourceId": "STUDENT002" + }, + { + "id": "EVT036", + "title": "Leverandør møde", + "start": "2025-08-09T14:00:00Z", + "end": "2025-08-09T15:00:00Z", + "type": "meeting", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP004", + "metadata": { + "attendees": ["EMP004"] + } + }, + { + "id": "EVT037", + "title": "Sygedag", + "start": "2025-08-12T00:00:00Z", + "end": "2025-08-12T23:59:59Z", + "type": "vacation", + "allDay": true, + "syncStatus": "synced", + "resourceId": "STUDENT001", + "metadata": { + "reason": "sick_leave" + } + }, + { + "id": "EVT038", + "title": "Frokostpause", + "start": "2025-08-05T12:00:00Z", + "end": "2025-08-05T12:30:00Z", + "type": "break", + "allDay": false, + "syncStatus": "synced", + "resourceId": "STUDENT001" + }, + { + "id": "EVT039", + "title": "Frokostpause", + "start": "2025-08-05T12:00:00Z", + "end": "2025-08-05T12:30:00Z", + "type": "break", + "allDay": false, + "syncStatus": "synced", + "resourceId": "STUDENT002" + }, + { + "id": "EVT040", + "title": "Morgen briefing", + "start": "2025-08-05T08:30:00Z", + "end": "2025-08-05T08:45:00Z", + "type": "meeting", + "allDay": false, + "syncStatus": "synced", + "resourceId": "EMP004", + "metadata": { + "attendees": ["EMP001", "EMP002", "EMP003", "EMP004", "STUDENT001", "STUDENT002"] + } + } +] diff --git a/wwwroot/data/resources.json b/wwwroot/data/resources.json new file mode 100644 index 0000000..3600c0a --- /dev/null +++ b/wwwroot/data/resources.json @@ -0,0 +1,80 @@ +[ + { + "id": "EMP001", + "name": "camilla.jensen", + "displayName": "Camilla Jensen", + "type": "person", + "avatarUrl": "/avatars/camilla.jpg", + "color": "#9c27b0", + "isActive": true, + "metadata": { + "role": "master stylist", + "specialties": ["balayage", "color", "bridal"] + } + }, + { + "id": "EMP002", + "name": "isabella.hansen", + "displayName": "Isabella Hansen", + "type": "person", + "avatarUrl": "/avatars/isabella.jpg", + "color": "#e91e63", + "isActive": true, + "metadata": { + "role": "master stylist", + "specialties": ["highlights", "ombre", "styling"] + } + }, + { + "id": "EMP003", + "name": "alexander.nielsen", + "displayName": "Alexander Nielsen", + "type": "person", + "avatarUrl": "/avatars/alexander.jpg", + "color": "#3f51b5", + "isActive": true, + "metadata": { + "role": "master stylist", + "specialties": ["men's cuts", "beard", "fade"] + } + }, + { + "id": "EMP004", + "name": "viktor.andersen", + "displayName": "Viktor Andersen", + "type": "person", + "avatarUrl": "/avatars/viktor.jpg", + "color": "#009688", + "isActive": true, + "metadata": { + "role": "stylist", + "specialties": ["cuts", "styling", "perms"] + } + }, + { + "id": "STUDENT001", + "name": "line.pedersen", + "displayName": "Line Pedersen (Elev)", + "type": "person", + "avatarUrl": "/avatars/line.jpg", + "color": "#8bc34a", + "isActive": true, + "metadata": { + "role": "student", + "specialties": ["wash", "blow-dry", "basic cuts"] + } + }, + { + "id": "STUDENT002", + "name": "mads.larsen", + "displayName": "Mads Larsen (Elev)", + "type": "person", + "avatarUrl": "/avatars/mads.jpg", + "color": "#ff9800", + "isActive": true, + "metadata": { + "role": "student", + "specialties": ["wash", "styling assistance"] + } + } +]