Adds resource-based calendar view mode

Introduces new ResourceColumnDataSource and ResourceHeaderRenderer to support column rendering by resources instead of dates

Enables dynamic calendar mode switching between date and resource views
Updates core managers and services to support async column retrieval
Refactors data source interfaces to use Promise-based methods

Improves calendar flexibility and resource management capabilities
This commit is contained in:
Janus C. H. Knudsen 2025-11-22 19:42:12 +01:00
parent a7d365b186
commit eeaeddeef8
19 changed files with 765 additions and 991 deletions

View file

@ -19,7 +19,7 @@ import { IStore } from './IStore';
*/
export class IndexedDBContext {
private static readonly DB_NAME = 'CalendarDB';
private static readonly DB_VERSION = 3; // Bumped for audit store
private static readonly DB_VERSION = 5; // Bumped to add syncStatus index to resources
static readonly QUEUE_STORE = 'operationQueue';
static readonly SYNC_STATE_STORE = 'syncState';

View file

@ -31,68 +31,25 @@ export class ResourceService extends BaseEntityService<IResource> {
/**
* Get resources by type
*
* @param type - Resource type (person, room, equipment, etc.)
* @returns Array of resources of this type
*/
async getByType(type: string): Promise<IResource[]> {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
const index = store.index('type');
const request = index.getAll(type);
request.onsuccess = () => {
resolve(request.result as IResource[]);
};
request.onerror = () => {
reject(new Error(`Failed to get resources by type ${type}: ${request.error}`));
};
});
const all = await this.getAll();
return all.filter(r => r.type === type);
}
/**
* Get active resources only
*
* @returns Array of active resources (isActive = true)
*/
async getActive(): Promise<IResource[]> {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
const index = store.index('isActive');
const request = index.getAll(IDBKeyRange.only(true));
request.onsuccess = () => {
resolve(request.result as IResource[]);
};
request.onerror = () => {
reject(new Error(`Failed to get active resources: ${request.error}`));
};
});
const all = await this.getAll();
return all.filter(r => r.isActive === true);
}
/**
* Get inactive resources
*
* @returns Array of inactive resources (isActive = false)
*/
async getInactive(): Promise<IResource[]> {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
const index = store.index('isActive');
const request = index.getAll(IDBKeyRange.only(false));
request.onsuccess = () => {
resolve(request.result as IResource[]);
};
request.onerror = () => {
reject(new Error(`Failed to get inactive resources: ${request.error}`));
};
});
const all = await this.getAll();
return all.filter(r => r.isActive === false);
}
}

View file

@ -20,16 +20,7 @@ export class ResourceStore implements IStore {
* @param db - IDBDatabase instance
*/
create(db: IDBDatabase): void {
// Create ObjectStore with 'id' as keyPath
const store = db.createObjectStore(ResourceStore.STORE_NAME, { keyPath: 'id' });
// Index: type (for filtering by resource category)
store.createIndex('type', 'type', { unique: false });
// Index: isActive (for showing/hiding inactive resources)
store.createIndex('isActive', 'isActive', { unique: false });
// Index: syncStatus (for querying by sync status - used by SyncPlugin)
store.createIndex('syncStatus', 'syncStatus', { unique: false });
}
}