Moving away from Azure Devops #1

Merged
Janus007 merged 113 commits from refac into master 2026-02-03 00:04:27 +01:00
5 changed files with 526 additions and 43 deletions
Showing only changes of commit fa2eb66fb2 - Show all commits

View file

@ -24,7 +24,7 @@ import {
export class CalendarApp {
private animator!: NavigationAnimator;
private container!: HTMLElement;
private weekOffset = 0;
private dayOffset = 0;
private currentViewId = 'simple';
private workweekPreset: IWorkweekPreset | null = null;
private groupingOverrides: Map<string, string[]> = new Map();
@ -126,13 +126,15 @@ export class CalendarApp {
}
private async handleNavigatePrev(): Promise<void> {
this.weekOffset--;
const step = this.workweekPreset?.periodDays ?? 7;
this.dayOffset -= step;
await this.animator.slide('right', () => this.render());
this.emitStatus('rendered', { viewId: this.currentViewId });
}
private async handleNavigateNext(): Promise<void> {
this.weekOffset++;
const step = this.workweekPreset?.periodDays ?? 7;
this.dayOffset += step;
await this.animator.slide('left', () => this.render());
this.emitStatus('rendered', { viewId: this.currentViewId });
}
@ -159,11 +161,15 @@ export class CalendarApp {
return;
}
// Populate date values based on workweek and offset
// Populate date values based on workweek preset and day offset
const workDays = this.workweekPreset?.workDays || [1, 2, 3, 4, 5];
const dates = this.currentViewId === 'day'
? this.dateService.getWeekDates(this.weekOffset, 1)
: this.dateService.getWorkWeekDates(this.weekOffset, workDays);
const periodDays = this.workweekPreset?.periodDays ?? 7;
// For single-day navigation (periodDays=1), show consecutive days from offset
// For week navigation (periodDays=7), show workDays from the week containing offset
const dates = periodDays === 1
? this.dateService.getDatesFromOffset(this.dayOffset, workDays.length)
: this.dateService.getWorkDaysFromOffset(this.dayOffset, workDays);
// Clone config and apply overrides
const viewConfig: ViewConfig = {

View file

@ -41,21 +41,30 @@ export class DateService {
return new Intl.DateTimeFormat(this.config.locale, { weekday: format }).format(date);
}
getWeekDates(offset = 0, days = 7): string[] {
const monday = this.baseDate.startOf('week').add(1, 'day').add(offset, 'week');
return Array.from({ length: days }, (_, i) =>
monday.add(i, 'day').format('YYYY-MM-DD')
/**
* Get dates starting from a day offset
* @param dayOffset - Day offset from base date
* @param count - Number of consecutive days to return
* @returns Array of date strings in YYYY-MM-DD format
*/
getDatesFromOffset(dayOffset: number, count: number): string[] {
const startDate = this.baseDate.add(dayOffset, 'day');
return Array.from({ length: count }, (_, i) =>
startDate.add(i, 'day').format('YYYY-MM-DD')
);
}
/**
* Get dates for specific weekdays within a week
* @param offset - Week offset from base date (0 = current week)
* Get specific weekdays from the week containing the offset date
* @param dayOffset - Day offset from base date
* @param workDays - Array of ISO weekday numbers (1=Monday, 7=Sunday)
* @returns Array of date strings in YYYY-MM-DD format
*/
getWorkWeekDates(offset: number, workDays: number[]): string[] {
const monday = this.baseDate.startOf('week').add(1, 'day').add(offset, 'week');
getWorkDaysFromOffset(dayOffset: number, workDays: number[]): string[] {
// Get the date at offset, then find its week's Monday
const targetDate = this.baseDate.add(dayOffset, 'day');
const monday = targetDate.startOf('week').add(1, 'day');
return workDays.map(isoDay => {
// ISO: 1=Monday, 7=Sunday → days from Monday: 0-6
const daysFromMonday = isoDay === 7 ? 6 : isoDay - 1;
@ -63,6 +72,15 @@ export class DateService {
});
}
// Legacy methods for backwards compatibility
getWeekDates(weekOffset = 0, days = 7): string[] {
return this.getDatesFromOffset(weekOffset * 7, days);
}
getWorkWeekDates(weekOffset: number, workDays: number[]): string[] {
return this.getWorkDaysFromOffset(weekOffset * 7, workDays);
}
// ============================================
// FORMATTING
// ============================================

View file

@ -17,6 +17,7 @@ export interface IWorkweekPreset {
id: string;
workDays: number[];
label: string;
periodDays: number; // Navigation step in days (1 = day, 7 = week)
}
/**

View file

@ -1,4 +1,262 @@
[
{
"id": "EVT-NOV24-001",
"title": "Herreklipning",
"start": "2025-11-24T09:00:00",
"end": "2025-11-24T09:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N01",
"resourceId": "EMP001",
"customerId": "CUST001",
"syncStatus": "synced",
"metadata": { "color": "blue" }
},
{
"id": "EVT-NOV24-002",
"title": "Balayage",
"start": "2025-11-24T10:00:00",
"end": "2025-11-24T12:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N02",
"resourceId": "EMP002",
"customerId": "CUST002",
"syncStatus": "synced",
"metadata": { "color": "purple" }
},
{
"id": "EVT-NOV24-003",
"title": "Klipning dame",
"start": "2025-11-24T14:00:00",
"end": "2025-11-24T15:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N03",
"resourceId": "EMP003",
"customerId": "CUST003",
"syncStatus": "synced",
"metadata": { "color": "teal" }
},
{
"id": "EVT-NOV25-001",
"title": "Farve behandling",
"start": "2025-11-25T09:00:00",
"end": "2025-11-25T11:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N04",
"resourceId": "EMP001",
"customerId": "CUST004",
"syncStatus": "synced",
"metadata": { "color": "orange" }
},
{
"id": "EVT-NOV25-002",
"title": "Permanent",
"start": "2025-11-25T12:00:00",
"end": "2025-11-25T14:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N05",
"resourceId": "EMP004",
"customerId": "CUST005",
"syncStatus": "synced",
"metadata": { "color": "indigo" }
},
{
"id": "EVT-NOV26-001",
"title": "Highlights",
"start": "2025-11-26T10:00:00",
"end": "2025-11-26T12:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N06",
"resourceId": "EMP002",
"customerId": "CUST006",
"syncStatus": "synced",
"metadata": { "color": "lime" }
},
{
"id": "EVT-NOV26-002",
"title": "Børneklip",
"start": "2025-11-26T14:00:00",
"end": "2025-11-26T14:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N07",
"resourceId": "EMP003",
"customerId": "CUST007",
"syncStatus": "synced",
"metadata": { "color": "cyan" }
},
{
"id": "EVT-NOV27-001",
"title": "Olaplex behandling",
"start": "2025-11-27T09:00:00",
"end": "2025-11-27T10:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N08",
"resourceId": "EMP005",
"customerId": "CUST008",
"syncStatus": "synced",
"metadata": { "color": "pink" }
},
{
"id": "EVT-NOV27-002",
"title": "Skæg trim",
"start": "2025-11-27T11:00:00",
"end": "2025-11-27T11:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N09",
"resourceId": "EMP001",
"customerId": "CUST009",
"syncStatus": "synced",
"metadata": { "color": "teal" }
},
{
"id": "EVT-NOV28-001",
"title": "Extensions",
"start": "2025-11-28T09:00:00",
"end": "2025-11-28T12:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N10",
"resourceId": "EMP002",
"customerId": "CUST010",
"syncStatus": "synced",
"metadata": { "color": "deep-purple" }
},
{
"id": "EVT-DEC01-001",
"title": "Klipning og føn",
"start": "2025-12-01T10:00:00",
"end": "2025-12-01T11:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N11",
"resourceId": "EMP001",
"customerId": "CUST011",
"syncStatus": "synced",
"metadata": { "color": "blue" }
},
{
"id": "EVT-DEC01-002",
"title": "Farve korrektion",
"start": "2025-12-01T13:00:00",
"end": "2025-12-01T16:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N12",
"resourceId": "EMP004",
"customerId": "CUST012",
"syncStatus": "synced",
"metadata": { "color": "magenta" }
},
{
"id": "EVT-DEC02-001",
"title": "Bryllupsfrisure prøve",
"start": "2025-12-02T09:00:00",
"end": "2025-12-02T11:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N13",
"resourceId": "EMP002",
"customerId": "CUST013",
"syncStatus": "synced",
"metadata": { "color": "green" }
},
{
"id": "EVT-DEC02-002",
"title": "Herreklip express",
"start": "2025-12-02T14:00:00",
"end": "2025-12-02T14:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N14",
"resourceId": "EMP003",
"customerId": "CUST014",
"syncStatus": "synced",
"metadata": { "color": "amber" }
},
{
"id": "EVT-DEC03-001",
"title": "Keratin behandling",
"start": "2025-12-03T10:00:00",
"end": "2025-12-03T13:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N15",
"resourceId": "EMP005",
"customerId": "CUST001",
"syncStatus": "synced",
"metadata": { "color": "violet" }
},
{
"id": "EVT-DEC03-002",
"title": "Vask og styling",
"start": "2025-12-03T15:00:00",
"end": "2025-12-03T15:45:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N16",
"resourceId": "EMP001",
"customerId": "CUST002",
"syncStatus": "synced",
"metadata": { "color": "light-green" }
},
{
"id": "EVT-DEC04-001",
"title": "Balayage kort hår",
"start": "2025-12-04T09:00:00",
"end": "2025-12-04T10:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N17",
"resourceId": "EMP002",
"customerId": "CUST003",
"syncStatus": "synced",
"metadata": { "color": "purple" }
},
{
"id": "EVT-DEC04-002",
"title": "Ferie",
"start": "2025-12-04T00:00:00",
"end": "2025-12-05T23:59:59",
"type": "vacation",
"allDay": true,
"resourceId": "EMP004",
"syncStatus": "synced",
"metadata": { "color": "red" }
},
{
"id": "EVT-DEC05-001",
"title": "Dameklip langt hår",
"start": "2025-12-05T11:00:00",
"end": "2025-12-05T12:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N18",
"resourceId": "EMP003",
"customerId": "CUST004",
"syncStatus": "synced",
"metadata": { "color": "pink" }
},
{
"id": "EVT-DEC05-002",
"title": "Highlights delvis",
"start": "2025-12-05T14:00:00",
"end": "2025-12-05T15:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-N19",
"resourceId": "EMP005",
"customerId": "CUST005",
"syncStatus": "synced",
"metadata": { "color": "lime" }
},
{
"id": "EVT-DEC08-001",
"title": "Balayage langt hår",
@ -11,7 +269,7 @@
"resourceId": "EMP001",
"customerId": "CUST001",
"syncStatus": "synced",
"metadata": { "duration": 60, "color": "purple" }
"metadata": { "color": "purple" }
},
{
"id": "EVT-DEC08-002",
@ -25,7 +283,7 @@
"resourceId": "EMP001",
"customerId": "CUST002",
"syncStatus": "synced",
"metadata": { "duration": 90, "color": "pink" }
"metadata": { "color": "pink" }
},
{
"id": "EVT-DEC08-003",
@ -39,7 +297,7 @@
"resourceId": "EMP002",
"customerId": "CUST003",
"syncStatus": "synced",
"metadata": { "duration": 120, "color": "indigo" }
"metadata": { "color": "indigo" }
},
{
"id": "EVT-DEC08-004",
@ -53,7 +311,7 @@
"resourceId": "EMP002",
"customerId": "CUST004",
"syncStatus": "synced",
"metadata": { "duration": 120, "color": "orange" }
"metadata": { "color": "orange" }
},
{
"id": "EVT-DEC09-001",
@ -67,21 +325,19 @@
"resourceId": "EMP003",
"customerId": "CUST005",
"syncStatus": "synced",
"metadata": { "duration": 30, "color": "teal" }
"metadata": { "color": "teal" }
},
{
"id": "EVT-DEC09-002",
"title": "Skæg trimning",
"description": "Skæg trim og styling",
"title": "Kursus",
"description": "Ekstern kursusdag",
"start": "2025-12-09T00:00:00",
"end": "2025-12-10T23:59:59",
"type": "customer",
"type": "blocked",
"allDay": true,
"bookingId": "BOOK-006",
"resourceId": "EMP003",
"customerId": "CUST006",
"syncStatus": "synced",
"metadata": { "duration": 30, "color": "cyan" }
"metadata": { "color": "cyan" }
},
{
"id": "EVT-DEC09-003",
@ -95,7 +351,7 @@
"resourceId": "EMP004",
"customerId": "CUST007",
"syncStatus": "synced",
"metadata": { "duration": 120, "color": "green" }
"metadata": { "color": "green" }
},
{
"id": "EVT-DEC10-001",
@ -109,7 +365,7 @@
"resourceId": "EMP005",
"customerId": "CUST008",
"syncStatus": "synced",
"metadata": { "duration": 120, "color": "lime" }
"metadata": { "color": "lime" }
},
{
"id": "EVT-DEC10-002",
@ -121,7 +377,7 @@
"allDay": false,
"resourceId": "EMP005",
"syncStatus": "synced",
"metadata": { "duration": 30, "color": "amber" }
"metadata": { "color": "amber" }
},
{
"id": "EVT-DEC10-003",
@ -135,7 +391,7 @@
"resourceId": "EMP001",
"customerId": "CUST009",
"syncStatus": "synced",
"metadata": { "duration": 90, "color": "blue" }
"metadata": { "color": "blue" }
},
{
"id": "EVT-DEC11-001",
@ -149,7 +405,7 @@
"resourceId": "EMP002",
"customerId": "CUST010",
"syncStatus": "synced",
"metadata": { "duration": 180, "color": "deep-purple" }
"metadata": { "color": "deep-purple" }
},
{
"id": "EVT-DEC11-002",
@ -163,19 +419,19 @@
"resourceId": "EMP003",
"customerId": "CUST011",
"syncStatus": "synced",
"metadata": { "duration": 30, "color": "light-blue" }
"metadata": { "color": "light-blue" }
},
{
"id": "EVT-DEC11-003",
"title": "Frisør møde",
"description": "Team møde",
"title": "Team møde",
"description": "Morgen briefing",
"start": "2025-12-11T08:00:00",
"end": "2025-12-11T08:30:00",
"type": "meeting",
"allDay": false,
"resourceId": "EMP001",
"syncStatus": "synced",
"metadata": { "duration": 30, "color": "red" }
"metadata": { "color": "red" }
},
{
"id": "EVT-DEC12-001",
@ -189,7 +445,7 @@
"resourceId": "EMP004",
"customerId": "CUST012",
"syncStatus": "synced",
"metadata": { "duration": 180, "color": "violet" }
"metadata": { "color": "violet" }
},
{
"id": "EVT-DEC12-002",
@ -203,7 +459,7 @@
"resourceId": "EMP005",
"customerId": "CUST013",
"syncStatus": "synced",
"metadata": { "duration": 45, "color": "light-green" }
"metadata": { "color": "light-green" }
},
{
"id": "EVT-DEC12-003",
@ -217,6 +473,197 @@
"resourceId": "EMP001",
"customerId": "CUST014",
"syncStatus": "synced",
"metadata": { "duration": 180, "color": "magenta" }
"metadata": { "color": "magenta" }
},
{
"id": "EVT-DEC15-001",
"title": "Juleklipning",
"start": "2025-12-15T09:00:00",
"end": "2025-12-15T10:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F01",
"resourceId": "EMP001",
"customerId": "CUST001",
"syncStatus": "synced",
"metadata": { "color": "red" }
},
{
"id": "EVT-DEC15-002",
"title": "Balayage jul",
"start": "2025-12-15T11:00:00",
"end": "2025-12-15T13:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F02",
"resourceId": "EMP002",
"customerId": "CUST002",
"syncStatus": "synced",
"metadata": { "color": "purple" }
},
{
"id": "EVT-DEC15-003",
"title": "Herreklip",
"start": "2025-12-15T14:00:00",
"end": "2025-12-15T14:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F03",
"resourceId": "EMP003",
"customerId": "CUST003",
"syncStatus": "synced",
"metadata": { "color": "blue" }
},
{
"id": "EVT-DEC16-001",
"title": "Farve og klip",
"start": "2025-12-16T10:00:00",
"end": "2025-12-16T12:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F04",
"resourceId": "EMP004",
"customerId": "CUST004",
"syncStatus": "synced",
"metadata": { "color": "orange" }
},
{
"id": "EVT-DEC16-002",
"title": "Permanent",
"start": "2025-12-16T13:00:00",
"end": "2025-12-16T15:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F05",
"resourceId": "EMP005",
"customerId": "CUST005",
"syncStatus": "synced",
"metadata": { "color": "indigo" }
},
{
"id": "EVT-DEC17-001",
"title": "Julefrokost",
"start": "2025-12-17T12:00:00",
"end": "2025-12-17T14:00:00",
"type": "meeting",
"allDay": false,
"resourceId": "EMP001",
"syncStatus": "synced",
"metadata": { "color": "green" }
},
{
"id": "EVT-DEC17-002",
"title": "Extensions fjernelse",
"start": "2025-12-17T09:00:00",
"end": "2025-12-17T11:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F06",
"resourceId": "EMP002",
"customerId": "CUST006",
"syncStatus": "synced",
"metadata": { "color": "deep-purple" }
},
{
"id": "EVT-DEC18-001",
"title": "Highlights jul",
"start": "2025-12-18T10:00:00",
"end": "2025-12-18T12:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F07",
"resourceId": "EMP001",
"customerId": "CUST007",
"syncStatus": "synced",
"metadata": { "color": "lime" }
},
{
"id": "EVT-DEC18-002",
"title": "Børneklip jul",
"start": "2025-12-18T14:00:00",
"end": "2025-12-18T14:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F08",
"resourceId": "EMP003",
"customerId": "CUST008",
"syncStatus": "synced",
"metadata": { "color": "cyan" }
},
{
"id": "EVT-DEC19-001",
"title": "Festfrisure",
"start": "2025-12-19T09:00:00",
"end": "2025-12-19T10:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F09",
"resourceId": "EMP004",
"customerId": "CUST009",
"syncStatus": "synced",
"metadata": { "color": "pink" }
},
{
"id": "EVT-DEC19-002",
"title": "Julestyling",
"start": "2025-12-19T11:00:00",
"end": "2025-12-19T12:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F10",
"resourceId": "EMP005",
"customerId": "CUST010",
"syncStatus": "synced",
"metadata": { "color": "amber" }
},
{
"id": "EVT-DEC19-003",
"title": "Klipning og farve",
"start": "2025-12-19T14:00:00",
"end": "2025-12-19T16:30:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F11",
"resourceId": "EMP001",
"customerId": "CUST011",
"syncStatus": "synced",
"metadata": { "color": "violet" }
},
{
"id": "EVT-DEC22-001",
"title": "Sidste jul klip",
"start": "2025-12-22T09:00:00",
"end": "2025-12-22T10:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F12",
"resourceId": "EMP002",
"customerId": "CUST012",
"syncStatus": "synced",
"metadata": { "color": "red" }
},
{
"id": "EVT-DEC22-002",
"title": "Juleaften forberedelse",
"start": "2025-12-22T11:00:00",
"end": "2025-12-22T13:00:00",
"type": "customer",
"allDay": false,
"bookingId": "BOOK-F13",
"resourceId": "EMP003",
"customerId": "CUST013",
"syncStatus": "synced",
"metadata": { "color": "green" }
},
{
"id": "EVT-DEC22-003",
"title": "Juleferie start",
"start": "2025-12-22T00:00:00",
"end": "2025-12-27T23:59:59",
"type": "vacation",
"allDay": true,
"resourceId": "EMP004",
"syncStatus": "synced",
"metadata": { "color": "light-blue" }
}
]

View file

@ -6,27 +6,38 @@
"standard": {
"id": "standard",
"workDays": [1, 2, 3, 4, 5],
"label": "Man-Fre"
"label": "Man-Fre",
"periodDays": 7
},
"compressed": {
"id": "compressed",
"workDays": [1, 2, 3],
"label": "Man-Ons"
"label": "Man-Ons",
"periodDays": 7
},
"midweek": {
"id": "midweek",
"workDays": [4, 5],
"label": "Tors-Fre"
"label": "Tors-Fre",
"periodDays": 7
},
"weekend": {
"id": "weekend",
"workDays": [6, 7],
"label": "Weekend"
"label": "Weekend",
"periodDays": 7
},
"fullweek": {
"id": "fullweek",
"workDays": [1, 2, 3, 4, 5, 6, 7],
"label": "Alle dage"
"label": "Alle dage",
"periodDays": 7
},
"day": {
"id": "day",
"workDays": [1],
"label": "Dag",
"periodDays": 1
}
},
"defaultPreset": "standard",