Refactor settings model to separate record-based approach
Restructures tenant settings to use individual records instead of a single document Decouples settings sections into separate typed interfaces with unique IDs Modifies data loading and service methods to support new record-based settings Updates mock data and repository to align with new settings structure
This commit is contained in:
parent
b2c81dc163
commit
9f360237cf
5 changed files with 97 additions and 69 deletions
|
|
@ -38,7 +38,7 @@ import { DepartmentStore } from './storage/departments/DepartmentStore';
|
|||
import { DepartmentService } from './storage/departments/DepartmentService';
|
||||
import { SettingsStore } from './storage/settings/SettingsStore';
|
||||
import { SettingsService } from './storage/settings/SettingsService';
|
||||
import { ITenantSettings } from './types/SettingsTypes';
|
||||
import { TenantSetting } from './types/SettingsTypes';
|
||||
import { ViewConfigStore } from './storage/viewconfigs/ViewConfigStore';
|
||||
import { ViewConfigService } from './storage/viewconfigs/ViewConfigService';
|
||||
import { ViewConfig } from './core/ViewConfig';
|
||||
|
|
@ -150,7 +150,7 @@ export function createV2Container(): Container {
|
|||
builder.registerType(DepartmentService).as<IEntityService<ISync>>();
|
||||
builder.registerType(DepartmentService).as<DepartmentService>();
|
||||
|
||||
builder.registerType(SettingsService).as<IEntityService<ITenantSettings>>();
|
||||
builder.registerType(SettingsService).as<IEntityService<TenantSetting>>();
|
||||
builder.registerType(SettingsService).as<IEntityService<ISync>>();
|
||||
builder.registerType(SettingsService).as<SettingsService>();
|
||||
|
||||
|
|
@ -180,7 +180,7 @@ export function createV2Container(): Container {
|
|||
builder.registerType(MockDepartmentRepository).as<IApiRepository<IDepartment>>();
|
||||
builder.registerType(MockDepartmentRepository).as<IApiRepository<ISync>>();
|
||||
|
||||
builder.registerType(MockSettingsRepository).as<IApiRepository<ITenantSettings>>();
|
||||
builder.registerType(MockSettingsRepository).as<IApiRepository<TenantSetting>>();
|
||||
builder.registerType(MockSettingsRepository).as<IApiRepository<ISync>>();
|
||||
|
||||
builder.registerType(MockViewConfigRepository).as<IApiRepository<ViewConfig>>();
|
||||
|
|
|
|||
|
|
@ -1,18 +1,18 @@
|
|||
import { EntityType } from '../types/CalendarTypes';
|
||||
import { ITenantSettings } from '../types/SettingsTypes';
|
||||
import { TenantSetting } from '../types/SettingsTypes';
|
||||
import { IApiRepository } from './IApiRepository';
|
||||
|
||||
/**
|
||||
* MockSettingsRepository - Loads tenant settings from local JSON file
|
||||
*
|
||||
* Settings is a single document, but we wrap it in an array to match
|
||||
* the IApiRepository interface used by DataSeeder.
|
||||
* Settings are stored as separate records per section (workweek, grid, etc.).
|
||||
* The JSON file is already an array of TenantSetting records.
|
||||
*/
|
||||
export class MockSettingsRepository implements IApiRepository<ITenantSettings> {
|
||||
export class MockSettingsRepository implements IApiRepository<TenantSetting> {
|
||||
public readonly entityType: EntityType = 'Settings';
|
||||
private readonly dataUrl = 'data/tenant-settings.json';
|
||||
|
||||
public async fetchAll(): Promise<ITenantSettings[]> {
|
||||
public async fetchAll(): Promise<TenantSetting[]> {
|
||||
try {
|
||||
const response = await fetch(this.dataUrl);
|
||||
|
||||
|
|
@ -20,24 +20,23 @@ export class MockSettingsRepository implements IApiRepository<ITenantSettings> {
|
|||
throw new Error(`Failed to load tenant settings: ${response.status} ${response.statusText}`);
|
||||
}
|
||||
|
||||
const rawData = await response.json();
|
||||
// Ensure syncStatus is set
|
||||
const settings: ITenantSettings = {
|
||||
...rawData,
|
||||
syncStatus: rawData.syncStatus || 'synced'
|
||||
};
|
||||
return [settings];
|
||||
const settings: TenantSetting[] = await response.json();
|
||||
// Ensure syncStatus is set on each record
|
||||
return settings.map(s => ({
|
||||
...s,
|
||||
syncStatus: s.syncStatus || 'synced'
|
||||
}));
|
||||
} catch (error) {
|
||||
console.error('Failed to load tenant settings:', error);
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
public async sendCreate(_settings: ITenantSettings): Promise<ITenantSettings> {
|
||||
public async sendCreate(_settings: TenantSetting): Promise<TenantSetting> {
|
||||
throw new Error('MockSettingsRepository does not support sendCreate. Mock data is read-only.');
|
||||
}
|
||||
|
||||
public async sendUpdate(_id: string, _updates: Partial<ITenantSettings>): Promise<ITenantSettings> {
|
||||
public async sendUpdate(_id: string, _updates: Partial<TenantSetting>): Promise<TenantSetting> {
|
||||
throw new Error('MockSettingsRepository does not support sendUpdate. Mock data is read-only.');
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,21 +1,24 @@
|
|||
import { EntityType, IEventBus } from '../../types/CalendarTypes';
|
||||
import { ITenantSettings, IWorkweekPreset } from '../../types/SettingsTypes';
|
||||
import {
|
||||
TenantSetting,
|
||||
IWorkweekSettings,
|
||||
IGridSettings,
|
||||
ITimeFormatSettings,
|
||||
IViewSettings,
|
||||
IWorkweekPreset,
|
||||
SettingsIds
|
||||
} from '../../types/SettingsTypes';
|
||||
import { SettingsStore } from './SettingsStore';
|
||||
import { BaseEntityService } from '../BaseEntityService';
|
||||
import { IndexedDBContext } from '../IndexedDBContext';
|
||||
|
||||
/**
|
||||
* Default settings ID - single document per tenant
|
||||
*/
|
||||
const TENANT_SETTINGS_ID = 'tenant-settings';
|
||||
|
||||
/**
|
||||
* SettingsService - CRUD operations for tenant settings
|
||||
*
|
||||
* Settings are stored as a single document with sections.
|
||||
* This service provides convenience methods for accessing specific sections.
|
||||
* Settings are stored as separate records per section.
|
||||
* This service provides typed methods for accessing specific settings.
|
||||
*/
|
||||
export class SettingsService extends BaseEntityService<ITenantSettings> {
|
||||
export class SettingsService extends BaseEntityService<TenantSetting> {
|
||||
readonly storeName = SettingsStore.STORE_NAME;
|
||||
readonly entityType: EntityType = 'Settings';
|
||||
|
||||
|
|
@ -24,37 +27,57 @@ export class SettingsService extends BaseEntityService<ITenantSettings> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Get the tenant settings document
|
||||
* Returns null if not yet loaded from backend
|
||||
* Get workweek settings
|
||||
*/
|
||||
async getSettings(): Promise<ITenantSettings | null> {
|
||||
return this.get(TENANT_SETTINGS_ID);
|
||||
async getWorkweekSettings(): Promise<IWorkweekSettings | null> {
|
||||
return this.get(SettingsIds.WORKWEEK) as Promise<IWorkweekSettings | null>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get grid settings
|
||||
*/
|
||||
async getGridSettings(): Promise<IGridSettings | null> {
|
||||
return this.get(SettingsIds.GRID) as Promise<IGridSettings | null>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get time format settings
|
||||
*/
|
||||
async getTimeFormatSettings(): Promise<ITimeFormatSettings | null> {
|
||||
return this.get(SettingsIds.TIME_FORMAT) as Promise<ITimeFormatSettings | null>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get view settings
|
||||
*/
|
||||
async getViewSettings(): Promise<IViewSettings | null> {
|
||||
return this.get(SettingsIds.VIEWS) as Promise<IViewSettings | null>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get workweek preset by ID
|
||||
*/
|
||||
async getWorkweekPreset(presetId: string): Promise<IWorkweekPreset | null> {
|
||||
const settings = await this.getSettings();
|
||||
const settings = await this.getWorkweekSettings();
|
||||
if (!settings) return null;
|
||||
return settings.workweek.presets[presetId] || null;
|
||||
return settings.presets[presetId] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the default workweek preset
|
||||
*/
|
||||
async getDefaultWorkweekPreset(): Promise<IWorkweekPreset | null> {
|
||||
const settings = await this.getSettings();
|
||||
const settings = await this.getWorkweekSettings();
|
||||
if (!settings) return null;
|
||||
return settings.workweek.presets[settings.workweek.defaultPreset] || null;
|
||||
return settings.presets[settings.defaultPreset] || null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available workweek presets
|
||||
*/
|
||||
async getWorkweekPresets(): Promise<IWorkweekPreset[]> {
|
||||
const settings = await this.getSettings();
|
||||
const settings = await this.getWorkweekSettings();
|
||||
if (!settings) return [];
|
||||
return Object.values(settings.workweek.presets);
|
||||
return Object.values(settings.presets);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
*
|
||||
* Settings are tenant-specific configuration that comes from the backend
|
||||
* and is stored in IndexedDB for offline access.
|
||||
*
|
||||
* Each settings section is stored as a separate record with its own id.
|
||||
*/
|
||||
|
||||
import { ISync } from './CalendarTypes';
|
||||
|
|
@ -18,18 +20,20 @@ export interface IWorkweekPreset {
|
|||
}
|
||||
|
||||
/**
|
||||
* Workweek settings section
|
||||
* Workweek settings - stored as separate record
|
||||
*/
|
||||
export interface IWorkweekSettings {
|
||||
export interface IWorkweekSettings extends ISync {
|
||||
id: 'workweek';
|
||||
presets: Record<string, IWorkweekPreset>;
|
||||
defaultPreset: string;
|
||||
firstDayOfWeek: number; // ISO: 1=Monday
|
||||
}
|
||||
|
||||
/**
|
||||
* Grid display settings section
|
||||
* Grid display settings - stored as separate record
|
||||
*/
|
||||
export interface IGridSettings {
|
||||
export interface IGridSettings extends ISync {
|
||||
id: 'grid';
|
||||
dayStartHour: number;
|
||||
dayEndHour: number;
|
||||
workStartHour: number;
|
||||
|
|
@ -39,34 +43,35 @@ export interface IGridSettings {
|
|||
}
|
||||
|
||||
/**
|
||||
* Time format settings section
|
||||
* Time format settings - stored as separate record
|
||||
*/
|
||||
export interface ITimeFormatSettings {
|
||||
export interface ITimeFormatSettings extends ISync {
|
||||
id: 'timeFormat';
|
||||
timezone: string;
|
||||
locale: string;
|
||||
use24HourFormat: boolean;
|
||||
}
|
||||
|
||||
/**
|
||||
* View settings section
|
||||
* View settings - stored as separate record
|
||||
*/
|
||||
export interface IViewSettings {
|
||||
export interface IViewSettings extends ISync {
|
||||
id: 'views';
|
||||
availableViews: string[];
|
||||
defaultView: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* ITenantSettings - Complete tenant configuration
|
||||
*
|
||||
* Single document stored in IndexedDB 'settings' store.
|
||||
* Sections can be extended as needed without schema changes.
|
||||
* Union type for all tenant settings records
|
||||
*/
|
||||
export interface ITenantSettings extends ISync {
|
||||
id: string;
|
||||
lastModified?: string;
|
||||
export type TenantSetting = IWorkweekSettings | IGridSettings | ITimeFormatSettings | IViewSettings;
|
||||
|
||||
workweek: IWorkweekSettings;
|
||||
grid: IGridSettings;
|
||||
timeFormat: ITimeFormatSettings;
|
||||
views: IViewSettings;
|
||||
}
|
||||
/**
|
||||
* Settings IDs as const for type safety
|
||||
*/
|
||||
export const SettingsIds = {
|
||||
WORKWEEK: 'workweek',
|
||||
GRID: 'grid',
|
||||
TIME_FORMAT: 'timeFormat',
|
||||
VIEWS: 'views'
|
||||
} as const;
|
||||
|
|
|
|||
|
|
@ -1,9 +1,7 @@
|
|||
{
|
||||
"id": "tenant-settings",
|
||||
[
|
||||
{
|
||||
"id": "workweek",
|
||||
"syncStatus": "synced",
|
||||
"lastModified": "2025-12-15T10:00:00Z",
|
||||
|
||||
"workweek": {
|
||||
"presets": {
|
||||
"standard": {
|
||||
"id": "standard",
|
||||
|
|
@ -34,8 +32,9 @@
|
|||
"defaultPreset": "standard",
|
||||
"firstDayOfWeek": 1
|
||||
},
|
||||
|
||||
"grid": {
|
||||
{
|
||||
"id": "grid",
|
||||
"syncStatus": "synced",
|
||||
"dayStartHour": 6,
|
||||
"dayEndHour": 22,
|
||||
"workStartHour": 8,
|
||||
|
|
@ -43,15 +42,17 @@
|
|||
"hourHeight": 80,
|
||||
"snapInterval": 15
|
||||
},
|
||||
|
||||
"timeFormat": {
|
||||
{
|
||||
"id": "timeFormat",
|
||||
"syncStatus": "synced",
|
||||
"timezone": "Europe/Copenhagen",
|
||||
"locale": "da-DK",
|
||||
"use24HourFormat": true
|
||||
},
|
||||
|
||||
"views": {
|
||||
{
|
||||
"id": "views",
|
||||
"syncStatus": "synced",
|
||||
"availableViews": ["simple", "resource", "team", "department"],
|
||||
"defaultView": "simple"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue