Sets up calendar package with core infrastructure

Adds core calendar package components including:
- Base services for events, resources, and settings
- Calendar app and orchestrator
- Build and bundling configuration
- IndexedDB storage setup

Prepares foundational architecture for calendar functionality
This commit is contained in:
Janus C. H. Knudsen 2026-01-28 15:24:03 +01:00
parent 12e7594f30
commit ceb44446f0
97 changed files with 13858 additions and 1 deletions

View file

@ -0,0 +1,46 @@
import { ICustomer, EntityType, IEventBus } from '../../types/CalendarTypes';
import { CustomerStore } from './CustomerStore';
import { BaseEntityService } from '../../storage/BaseEntityService';
import { IndexedDBContext } from '../../storage/IndexedDBContext';
/**
* CustomerService - CRUD operations for customers in IndexedDB
*/
export class CustomerService extends BaseEntityService<ICustomer> {
readonly storeName = CustomerStore.STORE_NAME;
readonly entityType: EntityType = 'Customer';
constructor(context: IndexedDBContext, eventBus: IEventBus) {
super(context, eventBus);
}
/**
* Search customers by name (case-insensitive contains)
*/
async searchByName(query: string): Promise<ICustomer[]> {
const all = await this.getAll();
const lowerQuery = query.toLowerCase();
return all.filter(c => c.name.toLowerCase().includes(lowerQuery));
}
/**
* Find customer by phone
*/
async getByPhone(phone: string): Promise<ICustomer | null> {
return new Promise((resolve, reject) => {
const transaction = this.db.transaction([this.storeName], 'readonly');
const store = transaction.objectStore(this.storeName);
const index = store.index('phone');
const request = index.get(phone);
request.onsuccess = () => {
const data = request.result;
resolve(data ? (data as ICustomer) : null);
};
request.onerror = () => {
reject(new Error(`Failed to find customer by phone ${phone}: ${request.error}`));
};
});
}
}

View file

@ -0,0 +1,17 @@
import { IStore } from '../../storage/IStore';
/**
* CustomerStore - IndexedDB ObjectStore definition for customers
*/
export class CustomerStore implements IStore {
static readonly STORE_NAME = 'customers';
readonly storeName = CustomerStore.STORE_NAME;
create(db: IDBDatabase): void {
const store = db.createObjectStore(CustomerStore.STORE_NAME, { keyPath: 'id' });
store.createIndex('name', 'name', { unique: false });
store.createIndex('phone', 'phone', { unique: false });
store.createIndex('syncStatus', 'syncStatus', { unique: false });
}
}

View file

@ -0,0 +1,18 @@
export { CustomerService } from './CustomerService';
export { CustomerStore } from './CustomerStore';
export type { ICustomer } from '../../types/CalendarTypes';
// DI registration helper
import type { Builder } from '@novadi/core';
import { IStore } from '../../storage/IStore';
import { IEntityService } from '../../storage/IEntityService';
import type { ICustomer, ISync } from '../../types/CalendarTypes';
import { CustomerStore } from './CustomerStore';
import { CustomerService } from './CustomerService';
export function registerCustomers(builder: Builder): void {
builder.registerType(CustomerStore).as<IStore>();
builder.registerType(CustomerService).as<IEntityService<ICustomer>>();
builder.registerType(CustomerService).as<IEntityService<ISync>>();
builder.registerType(CustomerService).as<CustomerService>();
}