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
This commit is contained in:
Janus C. H. Knudsen 2025-11-16 19:56:31 +01:00
parent 6174dc895e
commit 88cccb3456
6 changed files with 990 additions and 1 deletions

View file

@ -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
*/

View file

@ -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
*/