Some ignored filles was missing

This commit is contained in:
Janus C. H. Knudsen 2026-02-03 00:02:25 +01:00
parent 7db22245e2
commit fd5ab6bc0d
268 changed files with 31970 additions and 4 deletions

View file

@ -0,0 +1,39 @@
import { ICalendarEvent } from '../types/CalendarTypes';
import { Configuration } from '../configurations/CalendarConfig';
/**
* ApiEventRepository
* Handles communication with backend API
*
* Used by SyncManager to send queued operations to the server
* NOT used directly by EventManager (which uses IndexedDBEventRepository)
*
* Future enhancements:
* - SignalR real-time updates
* - Conflict resolution
* - Batch operations
*/
export declare class ApiEventRepository {
private apiEndpoint;
constructor(config: Configuration);
/**
* Send create operation to API
*/
sendCreate(event: ICalendarEvent): Promise<ICalendarEvent>;
/**
* Send update operation to API
*/
sendUpdate(id: string, updates: Partial<ICalendarEvent>): Promise<ICalendarEvent>;
/**
* Send delete operation to API
*/
sendDelete(id: string): Promise<void>;
/**
* Fetch all events from API
*/
fetchAll(): Promise<ICalendarEvent[]>;
/**
* Initialize SignalR connection
* Placeholder for future implementation
*/
initializeSignalR(): Promise<void>;
}

View file

@ -0,0 +1,115 @@
/**
* ApiEventRepository
* Handles communication with backend API
*
* Used by SyncManager to send queued operations to the server
* NOT used directly by EventManager (which uses IndexedDBEventRepository)
*
* Future enhancements:
* - SignalR real-time updates
* - Conflict resolution
* - Batch operations
*/
export class ApiEventRepository {
constructor(config) {
this.apiEndpoint = config.apiEndpoint;
}
/**
* Send create operation to API
*/
async sendCreate(event) {
// TODO: Implement API call
// const response = await fetch(`${this.apiEndpoint}/events`, {
// method: 'POST',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(event)
// });
//
// if (!response.ok) {
// throw new Error(`API create failed: ${response.statusText}`);
// }
//
// return await response.json();
throw new Error('ApiEventRepository.sendCreate not implemented yet');
}
/**
* Send update operation to API
*/
async sendUpdate(id, updates) {
// TODO: Implement API call
// const response = await fetch(`${this.apiEndpoint}/events/${id}`, {
// method: 'PATCH',
// headers: { 'Content-Type': 'application/json' },
// body: JSON.stringify(updates)
// });
//
// if (!response.ok) {
// throw new Error(`API update failed: ${response.statusText}`);
// }
//
// return await response.json();
throw new Error('ApiEventRepository.sendUpdate not implemented yet');
}
/**
* Send delete operation to API
*/
async sendDelete(id) {
// TODO: Implement API call
// const response = await fetch(`${this.apiEndpoint}/events/${id}`, {
// method: 'DELETE'
// });
//
// if (!response.ok) {
// throw new Error(`API delete failed: ${response.statusText}`);
// }
throw new Error('ApiEventRepository.sendDelete not implemented yet');
}
/**
* Fetch all events from API
*/
async fetchAll() {
// TODO: Implement API call
// const response = await fetch(`${this.apiEndpoint}/events`);
//
// if (!response.ok) {
// throw new Error(`API fetch failed: ${response.statusText}`);
// }
//
// return await response.json();
throw new Error('ApiEventRepository.fetchAll not implemented yet');
}
// ========================================
// Future: SignalR Integration
// ========================================
/**
* Initialize SignalR connection
* Placeholder for future implementation
*/
async initializeSignalR() {
// TODO: Setup SignalR connection
// - Connect to hub
// - Register event handlers
// - Handle reconnection
//
// Example:
// const connection = new signalR.HubConnectionBuilder()
// .withUrl(`${this.apiEndpoint}/hubs/calendar`)
// .build();
//
// connection.on('EventCreated', (event: ICalendarEvent) => {
// // Handle remote create
// });
//
// connection.on('EventUpdated', (event: ICalendarEvent) => {
// // Handle remote update
// });
//
// connection.on('EventDeleted', (eventId: string) => {
// // Handle remote delete
// });
//
// await connection.start();
throw new Error('SignalR not implemented yet');
}
}
//# sourceMappingURL=ApiEventRepository.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"ApiEventRepository.js","sourceRoot":"","sources":["../../../src/repositories/ApiEventRepository.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,kBAAkB;IAG7B,YAAY,MAAqB;QAC/B,IAAI,CAAC,WAAW,GAAG,MAAM,CAAC,WAAW,CAAC;IACxC,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,KAAqB;QACpC,2BAA2B;QAC3B,+DAA+D;QAC/D,oBAAoB;QACpB,qDAAqD;QACrD,gCAAgC;QAChC,MAAM;QACN,EAAE;QACF,sBAAsB;QACtB,kEAAkE;QAClE,IAAI;QACJ,EAAE;QACF,gCAAgC;QAEhC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,EAAU,EAAE,OAAgC;QAC3D,2BAA2B;QAC3B,qEAAqE;QACrE,qBAAqB;QACrB,qDAAqD;QACrD,kCAAkC;QAClC,MAAM;QACN,EAAE;QACF,sBAAsB;QACtB,kEAAkE;QAClE,IAAI;QACJ,EAAE;QACF,gCAAgC;QAEhC,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,UAAU,CAAC,EAAU;QACzB,2BAA2B;QAC3B,qEAAqE;QACrE,qBAAqB;QACrB,MAAM;QACN,EAAE;QACF,sBAAsB;QACtB,kEAAkE;QAClE,IAAI;QAEJ,MAAM,IAAI,KAAK,CAAC,mDAAmD,CAAC,CAAC;IACvE,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,QAAQ;QACZ,2BAA2B;QAC3B,8DAA8D;QAC9D,EAAE;QACF,sBAAsB;QACtB,iEAAiE;QACjE,IAAI;QACJ,EAAE;QACF,gCAAgC;QAEhC,MAAM,IAAI,KAAK,CAAC,iDAAiD,CAAC,CAAC;IACrE,CAAC;IAED,2CAA2C;IAC3C,8BAA8B;IAC9B,2CAA2C;IAE3C;;;OAGG;IACH,KAAK,CAAC,iBAAiB;QACrB,iCAAiC;QACjC,mBAAmB;QACnB,4BAA4B;QAC5B,wBAAwB;QACxB,EAAE;QACF,WAAW;QACX,wDAAwD;QACxD,kDAAkD;QAClD,cAAc;QACd,EAAE;QACF,6DAA6D;QAC7D,4BAA4B;QAC5B,MAAM;QACN,EAAE;QACF,6DAA6D;QAC7D,4BAA4B;QAC5B,MAAM;QACN,EAAE;QACF,uDAAuD;QACvD,4BAA4B;QAC5B,MAAM;QACN,EAAE;QACF,4BAA4B;QAE5B,MAAM,IAAI,KAAK,CAAC,6BAA6B,CAAC,CAAC;IACjD,CAAC;CACF"}

View file

@ -0,0 +1,51 @@
import { ICalendarEvent } from '../types/CalendarTypes';
/**
* Update source type
* - 'local': Changes made by the user locally (needs sync)
* - 'remote': Changes from API/SignalR (already synced)
*/
export type UpdateSource = 'local' | 'remote';
/**
* IEventRepository - Interface for event data access
*
* Abstracts the data source for calendar events, allowing easy switching
* between IndexedDB, REST API, GraphQL, or other data sources.
*
* Implementations:
* - IndexedDBEventRepository: Local storage with offline support
* - MockEventRepository: (Legacy) Loads from local JSON file
* - ApiEventRepository: (Future) Loads from backend API
*/
export interface IEventRepository {
/**
* Load all calendar events from the data source
* @returns Promise resolving to array of ICalendarEvent objects
* @throws Error if loading fails
*/
loadEvents(): Promise<ICalendarEvent[]>;
/**
* Create a new event
* @param event - Event to create (without ID, will be generated)
* @param source - Source of the update ('local' or 'remote')
* @returns Promise resolving to the created event with generated ID
* @throws Error if creation fails
*/
createEvent(event: Omit<ICalendarEvent, 'id'>, source?: UpdateSource): Promise<ICalendarEvent>;
/**
* Update an existing event
* @param id - ID of the event to update
* @param updates - Partial event data to update
* @param source - Source of the update ('local' or 'remote')
* @returns Promise resolving to the updated event
* @throws Error if update fails or event not found
*/
updateEvent(id: string, updates: Partial<ICalendarEvent>, source?: UpdateSource): Promise<ICalendarEvent>;
/**
* Delete an event
* @param id - ID of the event to delete
* @param source - Source of the update ('local' or 'remote')
* @returns Promise resolving when deletion is complete
* @throws Error if deletion fails or event not found
*/
deleteEvent(id: string, source?: UpdateSource): Promise<void>;
}

View file

@ -0,0 +1,2 @@
export {};
//# sourceMappingURL=IEventRepository.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"IEventRepository.js","sourceRoot":"","sources":["../../../src/repositories/IEventRepository.ts"],"names":[],"mappings":""}

View file

@ -0,0 +1,47 @@
import { ICalendarEvent } from '../types/CalendarTypes';
import { IEventRepository, UpdateSource } from './IEventRepository';
import { IndexedDBService } from '../storage/IndexedDBService';
import { OperationQueue } from '../storage/OperationQueue';
/**
* IndexedDBEventRepository
* Offline-first repository using IndexedDB as single source of truth
*
* All CRUD operations:
* - Save to IndexedDB immediately (always succeeds)
* - Add to sync queue if source is 'local'
* - Background SyncManager processes queue to sync with API
*/
export declare class IndexedDBEventRepository implements IEventRepository {
private indexedDB;
private queue;
constructor(indexedDB: IndexedDBService, queue: OperationQueue);
/**
* Load all events from IndexedDB
* Ensures IndexedDB is initialized and seeded on first call
*/
loadEvents(): Promise<ICalendarEvent[]>;
/**
* Create a new event
* - Generates ID
* - Saves to IndexedDB
* - Adds to queue if local (needs sync)
*/
createEvent(event: Omit<ICalendarEvent, 'id'>, source?: UpdateSource): Promise<ICalendarEvent>;
/**
* Update an existing event
* - Updates in IndexedDB
* - Adds to queue if local (needs sync)
*/
updateEvent(id: string, updates: Partial<ICalendarEvent>, source?: UpdateSource): Promise<ICalendarEvent>;
/**
* Delete an event
* - Removes from IndexedDB
* - Adds to queue if local (needs sync)
*/
deleteEvent(id: string, source?: UpdateSource): Promise<void>;
/**
* Generate unique event ID
* Format: {timestamp}-{random}
*/
private generateEventId;
}

View file

@ -0,0 +1,127 @@
/**
* IndexedDBEventRepository
* Offline-first repository using IndexedDB as single source of truth
*
* All CRUD operations:
* - Save to IndexedDB immediately (always succeeds)
* - Add to sync queue if source is 'local'
* - Background SyncManager processes queue to sync with API
*/
export class IndexedDBEventRepository {
constructor(indexedDB, queue) {
this.indexedDB = indexedDB;
this.queue = queue;
}
/**
* Load all events from IndexedDB
* Ensures IndexedDB is initialized and seeded on first call
*/
async loadEvents() {
// Lazy initialization on first data load
if (!this.indexedDB.isInitialized()) {
await this.indexedDB.initialize();
await this.indexedDB.seedIfEmpty();
}
return await this.indexedDB.getAllEvents();
}
/**
* Create a new event
* - Generates ID
* - Saves to IndexedDB
* - Adds to queue if local (needs sync)
*/
async createEvent(event, source = 'local') {
// Generate unique ID
const id = this.generateEventId();
// Determine sync status based on source
const syncStatus = source === 'local' ? 'pending' : 'synced';
// Create full event object
const newEvent = {
...event,
id,
syncStatus
};
// Save to IndexedDB
await this.indexedDB.saveEvent(newEvent);
// If local change, add to sync queue
if (source === 'local') {
await this.queue.enqueue({
type: 'create',
eventId: id,
data: newEvent,
timestamp: Date.now(),
retryCount: 0
});
}
return newEvent;
}
/**
* Update an existing event
* - Updates in IndexedDB
* - Adds to queue if local (needs sync)
*/
async updateEvent(id, updates, source = 'local') {
// Get existing event
const existingEvent = await this.indexedDB.getEvent(id);
if (!existingEvent) {
throw new Error(`Event with ID ${id} not found`);
}
// Determine sync status based on source
const syncStatus = source === 'local' ? 'pending' : 'synced';
// Merge updates
const updatedEvent = {
...existingEvent,
...updates,
id, // Ensure ID doesn't change
syncStatus
};
// Save to IndexedDB
await this.indexedDB.saveEvent(updatedEvent);
// If local change, add to sync queue
if (source === 'local') {
await this.queue.enqueue({
type: 'update',
eventId: id,
data: updates,
timestamp: Date.now(),
retryCount: 0
});
}
return updatedEvent;
}
/**
* Delete an event
* - Removes from IndexedDB
* - Adds to queue if local (needs sync)
*/
async deleteEvent(id, source = 'local') {
// Check if event exists
const existingEvent = await this.indexedDB.getEvent(id);
if (!existingEvent) {
throw new Error(`Event with ID ${id} not found`);
}
// If local change, add to sync queue BEFORE deleting
// (so we can send the delete operation to API later)
if (source === 'local') {
await this.queue.enqueue({
type: 'delete',
eventId: id,
data: {}, // No data needed for delete
timestamp: Date.now(),
retryCount: 0
});
}
// Delete from IndexedDB
await this.indexedDB.deleteEvent(id);
}
/**
* Generate unique event ID
* Format: {timestamp}-{random}
*/
generateEventId() {
const timestamp = Date.now();
const random = Math.random().toString(36).substring(2, 9);
return `${timestamp}-${random}`;
}
}
//# sourceMappingURL=IndexedDBEventRepository.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"IndexedDBEventRepository.js","sourceRoot":"","sources":["../../../src/repositories/IndexedDBEventRepository.ts"],"names":[],"mappings":"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"}

View file

@ -0,0 +1,33 @@
import { ICalendarEvent } from '../types/CalendarTypes';
import { IEventRepository, UpdateSource } from './IEventRepository';
/**
* MockEventRepository - Loads event data from local JSON file (LEGACY)
*
* This repository implementation fetches mock event data from a static JSON file.
* DEPRECATED: Use IndexedDBEventRepository for offline-first functionality.
*
* Data Source: data/mock-events.json
*
* NOTE: Create/Update/Delete operations are not supported - throws errors.
* This is intentional to encourage migration to IndexedDBEventRepository.
*/
export declare class MockEventRepository implements IEventRepository {
private readonly dataUrl;
loadEvents(): Promise<ICalendarEvent[]>;
/**
* NOT SUPPORTED - MockEventRepository is read-only
* Use IndexedDBEventRepository instead
*/
createEvent(event: Omit<ICalendarEvent, 'id'>, source?: UpdateSource): Promise<ICalendarEvent>;
/**
* NOT SUPPORTED - MockEventRepository is read-only
* Use IndexedDBEventRepository instead
*/
updateEvent(id: string, updates: Partial<ICalendarEvent>, source?: UpdateSource): Promise<ICalendarEvent>;
/**
* NOT SUPPORTED - MockEventRepository is read-only
* Use IndexedDBEventRepository instead
*/
deleteEvent(id: string, source?: UpdateSource): Promise<void>;
private processCalendarData;
}

View file

@ -0,0 +1,62 @@
/**
* MockEventRepository - Loads event data from local JSON file (LEGACY)
*
* This repository implementation fetches mock event data from a static JSON file.
* DEPRECATED: Use IndexedDBEventRepository for offline-first functionality.
*
* Data Source: data/mock-events.json
*
* NOTE: Create/Update/Delete operations are not supported - throws errors.
* This is intentional to encourage migration to IndexedDBEventRepository.
*/
export class MockEventRepository {
constructor() {
this.dataUrl = 'data/mock-events.json';
}
async loadEvents() {
try {
const response = await fetch(this.dataUrl);
if (!response.ok) {
throw new Error(`Failed to load mock events: ${response.status} ${response.statusText}`);
}
const rawData = await response.json();
return this.processCalendarData(rawData);
}
catch (error) {
console.error('Failed to load event data:', error);
throw error;
}
}
/**
* NOT SUPPORTED - MockEventRepository is read-only
* Use IndexedDBEventRepository instead
*/
async createEvent(event, source) {
throw new Error('MockEventRepository does not support createEvent. Use IndexedDBEventRepository instead.');
}
/**
* NOT SUPPORTED - MockEventRepository is read-only
* Use IndexedDBEventRepository instead
*/
async updateEvent(id, updates, source) {
throw new Error('MockEventRepository does not support updateEvent. Use IndexedDBEventRepository instead.');
}
/**
* NOT SUPPORTED - MockEventRepository is read-only
* Use IndexedDBEventRepository instead
*/
async deleteEvent(id, source) {
throw new Error('MockEventRepository does not support deleteEvent. Use IndexedDBEventRepository instead.');
}
processCalendarData(data) {
return data.map((event) => ({
...event,
start: new Date(event.start),
end: new Date(event.end),
type: event.type,
allDay: event.allDay || false,
syncStatus: 'synced'
}));
}
}
//# sourceMappingURL=MockEventRepository.js.map

View file

@ -0,0 +1 @@
{"version":3,"file":"MockEventRepository.js","sourceRoot":"","sources":["../../../src/repositories/MockEventRepository.ts"],"names":[],"mappings":"AAcA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,mBAAmB;IAAhC;QACmB,YAAO,GAAG,uBAAuB,CAAC;IAqDrD,CAAC;IAnDQ,KAAK,CAAC,UAAU;QACrB,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;YAE3C,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,MAAM,IAAI,KAAK,CAAC,+BAA+B,QAAQ,CAAC,MAAM,IAAI,QAAQ,CAAC,UAAU,EAAE,CAAC,CAAC;YAC3F,CAAC;YAED,MAAM,OAAO,GAAmB,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YAEtD,OAAO,IAAI,CAAC,mBAAmB,CAAC,OAAO,CAAC,CAAC;QAC3C,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,OAAO,CAAC,KAAK,CAAC,4BAA4B,EAAE,KAAK,CAAC,CAAC;YACnD,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW,CAAC,KAAiC,EAAE,MAAqB;QAC/E,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;IAC7G,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,OAAgC,EAAE,MAAqB;QAC1F,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;IAC7G,CAAC;IAED;;;OAGG;IACI,KAAK,CAAC,WAAW,CAAC,EAAU,EAAE,MAAqB;QACxD,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;IAC7G,CAAC;IAEO,mBAAmB,CAAC,IAAoB;QAC9C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,KAAK,EAAkB,EAAE,CAAC,CAAC;YAC1C,GAAG,KAAK;YACR,KAAK,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC;YAC5B,GAAG,EAAE,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC;YACxB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,MAAM,EAAE,KAAK,CAAC,MAAM,IAAI,KAAK;YAC7B,UAAU,EAAE,QAAiB;SAC9B,CAAC,CAAC,CAAC;IACN,CAAC;CACF"}