feat: implement rules engine and management panel for custom holiday optimization
This commit is contained in:
1 parent
c0549de4dd
commit
08ee84e5c9
20 files changed
+3951
-16847
No files matched your search
@@ -0,0 +1,139 @@
|
||||
import { Injectable, WritableSignal, Injector, inject } from '@angular/core';
|
||||
import { SelectableDayType } from './calendar-service';
|
||||
import { storedSignal } from '../shared/utils/stored-signal';
|
||||
|
||||
export enum RuleType {
|
||||
IF_THEN = 'if_then',
|
||||
TIME_WINDOW = 'time_window',
|
||||
}
|
||||
|
||||
export interface BaseRule {
|
||||
id: string;
|
||||
name: string;
|
||||
type: RuleType;
|
||||
enabled: boolean;
|
||||
}
|
||||
|
||||
export type PlacementStrategy = 'before' | 'after' | 'around' | 'auto';
|
||||
|
||||
export interface IfThenRule extends BaseRule {
|
||||
type: RuleType.IF_THEN;
|
||||
dayOfWeek: number; // 0-6, where 0 is Sunday, 6 is Saturday
|
||||
placeDays: number;
|
||||
placeType: SelectableDayType;
|
||||
placement: PlacementStrategy;
|
||||
rewardDays: number;
|
||||
rewardType: SelectableDayType;
|
||||
}
|
||||
|
||||
export interface TimeWindowRule extends BaseRule {
|
||||
type: RuleType.TIME_WINDOW;
|
||||
startDate: Date;
|
||||
endDate: Date;
|
||||
placeDays: number;
|
||||
placeType: SelectableDayType;
|
||||
}
|
||||
|
||||
export type VacationRule = IfThenRule | TimeWindowRule;
|
||||
|
||||
interface SerializedRule {
|
||||
id: string;
|
||||
name: string;
|
||||
type: RuleType;
|
||||
enabled: boolean;
|
||||
dayOfWeek?: number;
|
||||
placeDays: number;
|
||||
placeType: SelectableDayType;
|
||||
placement?: PlacementStrategy;
|
||||
rewardDays?: number;
|
||||
rewardType?: SelectableDayType;
|
||||
startDate?: string | Date;
|
||||
endDate?: string | Date;
|
||||
}
|
||||
|
||||
@Injectable({
|
||||
providedIn: 'root',
|
||||
})
|
||||
export class RuleService {
|
||||
private readonly injector = inject(Injector);
|
||||
|
||||
private readonly defaultRules: VacationRule[] = [
|
||||
{
|
||||
id: 'default-samedi-malin',
|
||||
name: 'Samedi Malin',
|
||||
type: RuleType.IF_THEN,
|
||||
enabled: true,
|
||||
dayOfWeek: 6, // Saturday
|
||||
placeDays: 2,
|
||||
placeType: SelectableDayType.CP,
|
||||
placement: 'auto',
|
||||
rewardDays: 1,
|
||||
rewardType: SelectableDayType.OTHER,
|
||||
},
|
||||
{
|
||||
id: 'default-christmas',
|
||||
name: 'Vacances de Noël',
|
||||
type: RuleType.TIME_WINDOW,
|
||||
enabled: true,
|
||||
startDate: new Date(new Date().getFullYear(), 11, 20), // Dec 20
|
||||
endDate: new Date(new Date().getFullYear() + 1, 0, 3), // Jan 3 next year
|
||||
placeDays: 5,
|
||||
placeType: SelectableDayType.ANY,
|
||||
},
|
||||
];
|
||||
|
||||
private rawRulesSignal: WritableSignal<SerializedRule[]>;
|
||||
|
||||
constructor() {
|
||||
this.rawRulesSignal = storedSignal<SerializedRule[]>(
|
||||
'vacationRules',
|
||||
this.defaultRules as unknown as SerializedRule[],
|
||||
this.injector,
|
||||
);
|
||||
}
|
||||
|
||||
public get rules(): VacationRule[] {
|
||||
const raw = this.rawRulesSignal();
|
||||
return raw.map((rule) => {
|
||||
if (rule.type === RuleType.TIME_WINDOW) {
|
||||
return {
|
||||
...rule,
|
||||
startDate: new Date(rule.startDate as string),
|
||||
endDate: new Date(rule.endDate as string),
|
||||
} as TimeWindowRule;
|
||||
}
|
||||
return rule as IfThenRule;
|
||||
});
|
||||
}
|
||||
|
||||
public addRule(rule: VacationRule): void {
|
||||
this.rawRulesSignal.update((rules) => [...rules, rule as unknown as SerializedRule]);
|
||||
}
|
||||
|
||||
public updateRule(updatedRule: VacationRule): void {
|
||||
this.rawRulesSignal.update((rules) =>
|
||||
rules.map((rule) =>
|
||||
rule.id === updatedRule.id ? (updatedRule as unknown as SerializedRule) : rule,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public deleteRule(id: string): void {
|
||||
this.rawRulesSignal.update((rules) => rules.filter((rule) => rule.id !== id));
|
||||
}
|
||||
|
||||
public toggleRule(id: string): void {
|
||||
this.rawRulesSignal.update((rules) =>
|
||||
rules.map((rule) => (rule.id === id ? { ...rule, enabled: !rule.enabled } : rule)),
|
||||
);
|
||||
}
|
||||
|
||||
public reorderRules(previousIndex: number, currentIndex: number): void {
|
||||
this.rawRulesSignal.update((rules) => {
|
||||
const updatedRules = [...rules];
|
||||
const [movedRule] = updatedRules.splice(previousIndex, 1);
|
||||
updatedRules.splice(currentIndex, 0, movedRule);
|
||||
return updatedRules;
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user