@weaver-apps/calendar 0.3.0 → 0.3.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/{weaver-apps-calendar-calendar-gateway-DUo797bn.mjs → weaver-apps-calendar-calendar-gateway-DqiL9VbI.mjs} +14 -2
- package/fesm2022/weaver-apps-calendar-calendar-gateway-DqiL9VbI.mjs.map +1 -0
- package/fesm2022/{weaver-apps-calendar-calendar-list-page-dgF-nnQF.mjs → weaver-apps-calendar-calendar-list-page-CF8odTAv.mjs} +3 -3
- package/fesm2022/{weaver-apps-calendar-calendar-list-page-dgF-nnQF.mjs.map → weaver-apps-calendar-calendar-list-page-CF8odTAv.mjs.map} +1 -1
- package/fesm2022/{weaver-apps-calendar-calendar-page-DpdU_Vfy.mjs → weaver-apps-calendar-calendar-page-nhRq6se2.mjs} +64 -39
- package/fesm2022/weaver-apps-calendar-calendar-page-nhRq6se2.mjs.map +1 -0
- package/fesm2022/weaver-apps-calendar-calendar-paths-AFikgbRs.mjs +8 -0
- package/fesm2022/weaver-apps-calendar-calendar-paths-AFikgbRs.mjs.map +1 -0
- package/fesm2022/weaver-apps-calendar.mjs +4 -4
- package/i18n/weaver-apps-calendar/de.json +2 -1
- package/i18n/weaver-apps-calendar/en.json +2 -1
- package/package.json +2 -2
- package/fesm2022/weaver-apps-calendar-calendar-gateway-DUo797bn.mjs.map +0 -1
- package/fesm2022/weaver-apps-calendar-calendar-page-DpdU_Vfy.mjs.map +0 -1
- package/fesm2022/weaver-apps-calendar-calendar-paths-qzIR-lDU.mjs +0 -19
- package/fesm2022/weaver-apps-calendar-calendar-paths-qzIR-lDU.mjs.map +0 -1
|
@@ -16,6 +16,18 @@ var CalendarHost = class {
|
|
|
16
16
|
}
|
|
17
17
|
};
|
|
18
18
|
const calendarHost = new CalendarHost();
|
|
19
|
+
const calendarPluginId = "weaver-apps-calendar";
|
|
20
|
+
const calendarContributionIds = {
|
|
21
|
+
calendars: `${calendarPluginId}.calendars`,
|
|
22
|
+
calendar: `${calendarPluginId}.calendar`,
|
|
23
|
+
rail: `${calendarPluginId}.rail`
|
|
24
|
+
};
|
|
25
|
+
const calendarIconNames = {
|
|
26
|
+
calendar: `${calendarPluginId}.calendar`,
|
|
27
|
+
previous: `${calendarPluginId}.previous`,
|
|
28
|
+
next: `${calendarPluginId}.next`,
|
|
29
|
+
cancel: `${calendarPluginId}.cancel`
|
|
30
|
+
};
|
|
19
31
|
var CalendarGatewayError = class extends Error {
|
|
20
32
|
status;
|
|
21
33
|
constructor(status) {
|
|
@@ -30,6 +42,6 @@ function provideCalendarGateway(gateway) {
|
|
|
30
42
|
useValue: gateway
|
|
31
43
|
};
|
|
32
44
|
}
|
|
33
|
-
export {
|
|
45
|
+
export { calendarIconNames as a, calendarContributionIds as i, CalendarGatewayError as n, calendarPluginId as o, provideCalendarGateway as r, calendarHost as s, CALENDAR_GATEWAY as t };
|
|
34
46
|
|
|
35
|
-
//# sourceMappingURL=weaver-apps-calendar-calendar-gateway-
|
|
47
|
+
//# sourceMappingURL=weaver-apps-calendar-calendar-gateway-DqiL9VbI.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weaver-apps-calendar-calendar-gateway-DqiL9VbI.mjs","names":[],"sources":["../../../../libs/calendar/src/lib/plugin/calendar-host.ts","../../../../libs/calendar/src/lib/plugin/calendar-ids.ts","../../../../libs/calendar/src/lib/gateway/calendar-gateway.ts"],"sourcesContent":["import type { PluginContext } from '@loomweaver/plugin-sdk';\n\ntype HostContext = Pick<PluginContext, 'ui' | 'navigateContent'>;\n\nclass CalendarHost {\n private ctx?: HostContext;\n\n bind(ctx: HostContext): void {\n this.ctx = ctx;\n }\n\n unbind(): void {\n this.ctx = undefined;\n }\n\n get ui(): HostContext['ui'] {\n if (!this.ctx) {\n throw new Error('The calendar plugin is not active.');\n }\n return this.ctx.ui;\n }\n\n navigate(path: string): void {\n this.ctx?.navigateContent(path);\n }\n}\n\nexport const calendarHost = new CalendarHost();\n","export const calendarPluginId = 'weaver-apps-calendar';\n\nexport const calendarContributionIds = {\n calendars: `${calendarPluginId}.calendars`,\n calendar: `${calendarPluginId}.calendar`,\n rail: `${calendarPluginId}.rail`,\n} as const;\n\nexport const calendarIconNames = {\n calendar: `${calendarPluginId}.calendar`,\n previous: `${calendarPluginId}.previous`,\n next: `${calendarPluginId}.next`,\n cancel: `${calendarPluginId}.cancel`,\n} as const;\n","import { InjectionToken, Provider } from '@angular/core';\nimport type { AppointmentRequest, AppointmentResponse, AppointmentStatus, CalendarResponse } from '../api/models';\n\nexport interface CalendarRange {\n readonly from: Date;\n readonly until: Date;\n}\n\nexport interface ConflictingAppointment {\n readonly id: string;\n readonly start: string;\n readonly end: string;\n}\n\nexport type AppointmentOutcome =\n | { readonly kind: 'saved'; readonly appointment: AppointmentResponse }\n | { readonly kind: 'conflict'; readonly conflicts: readonly ConflictingAppointment[] }\n | { readonly kind: 'closed' }\n | { readonly kind: 'changed' }\n | { readonly kind: 'invalid' }\n | { readonly kind: 'missing' };\n\nexport interface CalendarGateway {\n calendars(): Promise<readonly CalendarResponse[]>;\n calendar(calendarId: string): Promise<CalendarResponse | null>;\n saveCalendar(calendarId: string, name: string): Promise<CalendarResponse>;\n appointments(calendarId: string, range: CalendarRange, contact?: string, signal?: AbortSignal): Promise<readonly AppointmentResponse[]>;\n book(calendarId: string, request: AppointmentRequest): Promise<AppointmentOutcome>;\n update(calendarId: string, appointmentId: string, request: AppointmentRequest): Promise<AppointmentOutcome>;\n cancel(calendarId: string, appointmentId: string, reason: string | null): Promise<AppointmentOutcome>;\n setStatus(calendarId: string, appointmentId: string, status: AppointmentStatus): Promise<AppointmentOutcome>;\n remove(calendarId: string, appointmentId: string): Promise<void>;\n}\n\nexport class CalendarGatewayError extends Error {\n constructor(readonly status: number) {\n super(`The calendar answered ${status}.`);\n }\n}\n\nexport const CALENDAR_GATEWAY = new InjectionToken<CalendarGateway>('weaver-apps.calendar.gateway');\n\nexport function provideCalendarGateway(gateway: CalendarGateway): Provider {\n return { provide: CALENDAR_GATEWAY, useValue: gateway };\n}\n"],"mappings":";AAIA,IAAM,eAAN,MAAkB;CACR;CAER,KAAK,KAAgB;EACnB,KAAK,MAAM;CACb;CAEA,SAAM;EACJ,KAAK,MAAM;CACb;CAEA,IAAI,KAAE;EACJ,IAAI,CAAC,KAAK,KACR,MAAM,IAAI,MAAM,oCAAoC;EAEtD,OAAO,KAAK,IAAI;CAClB;CAEA,SAAS,MAAY;EACnB,KAAK,KAAK,gBAAgB,IAAI;CAChC;AACD;AAED,MAAa,eAAe,IAAI,aAAY;AC3B5C,MAAa,mBAAmB;AAEhC,MAAa,0BAA0B;CACrC,WAAW,GAAG,iBAAgB;CAC9B,UAAU,GAAG,iBAAgB;CAC7B,MAAM,GAAG,iBAAgB;AACjB;AAEV,MAAa,oBAAoB;CAC/B,UAAU,GAAG,iBAAgB;CAC7B,UAAU,GAAG,iBAAgB;CAC7B,MAAM,GAAG,iBAAgB;CACzB,QAAQ,GAAG,iBAAgB;AACnB;ACqBV,IAAa,uBAAb,cAA0C,MAAK;CACxB;CAArB,YAAqB,QAAc;EACjC,MAAM,yBAAyB,OAAM,EAAG;EADrB,KAAA,SAAA;CAErB;AACD;AAED,MAAa,mBAAmB,IAAI,eAAgC,8BAA8B;AAElG,SAAgB,uBAAuB,SAAwB;CAC7D,OAAO;EAAE,SAAS;EAAkB,UAAU;CAAO;AACvD"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as calendarPath } from "./weaver-apps-calendar-calendar-paths-
|
|
1
|
+
import { s as calendarHost, t as CALENDAR_GATEWAY } from "./weaver-apps-calendar-calendar-gateway-DqiL9VbI.mjs";
|
|
2
|
+
import { n as calendarPath } from "./weaver-apps-calendar-calendar-paths-AFikgbRs.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
4
|
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, resource } from "@angular/core";
|
|
5
5
|
import { TranslocoPipe } from "@jsverse/transloco";
|
|
@@ -71,4 +71,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
71
71
|
});
|
|
72
72
|
export { CalendarListPage };
|
|
73
73
|
|
|
74
|
-
//# sourceMappingURL=weaver-apps-calendar-calendar-list-page-
|
|
74
|
+
//# sourceMappingURL=weaver-apps-calendar-calendar-list-page-CF8odTAv.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weaver-apps-calendar-calendar-list-page-
|
|
1
|
+
{"version":3,"file":"weaver-apps-calendar-calendar-list-page-CF8odTAv.mjs","names":[],"sources":["../../../../libs/calendar/src/lib/views/calendar-list-page.ts","../../../../libs/calendar/src/lib/views/calendar-list-page.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, inject, resource } from '@angular/core';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport { CALENDAR_GATEWAY } from '../gateway/calendar-gateway';\nimport { calendarHost } from '../plugin/calendar-host';\nimport { calendarPath } from '../plugin/calendar-paths';\n\n@Component({\n selector: 'wa-calendar-list-page',\n imports: [TranslocoPipe],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './calendar-list-page.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CalendarListPage {\n private readonly gateway = inject(CALENDAR_GATEWAY);\n\n protected readonly calendars = resource({ loader: () => this.gateway.calendars() });\n\n protected open(calendarId: string): void {\n calendarHost.navigate(calendarPath(calendarId));\n }\n\n protected async create(): Promise<void> {\n const name = await calendarHost.ui.prompt({\n title: 'weaver-apps-calendar.calendars.create',\n message: 'weaver-apps-calendar.calendars.createMessage',\n confirmLabel: 'weaver-apps-calendar.calendars.createConfirm',\n });\n if (!name?.trim()) {\n return;\n }\n try {\n const calendar = await this.gateway.saveCalendar(crypto.randomUUID(), name.trim());\n this.open(calendar.id);\n } catch {\n calendarHost.ui.toast({ message: 'weaver-apps-calendar.errors.create', kind: 'error' });\n }\n }\n}\n","<div class=\"flex h-full flex-col gap-4 overflow-auto p-6\">\n <div class=\"flex items-center justify-between gap-4\">\n <h2 class=\"text-lg font-semibold text-content\">{{ 'weaver-apps-calendar.calendars.title' | transloco }}</h2>\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"create()\">{{ 'weaver-apps-calendar.calendars.create' | transloco }}</button>\n </div>\n @if (calendars.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-calendar.errors.load' | transloco }}</p>\n } @else if (calendars.hasValue()) {\n <ul role=\"list\" class=\"divide-y divide-border rounded-xl border border-border bg-surface\">\n @for (calendar of calendars.value(); track calendar.id) {\n <li>\n <button type=\"button\" class=\"flex w-full items-center gap-3 px-4 py-3 text-left text-sm text-content hover:bg-surface-overlay\" (click)=\"open(calendar.id)\">\n <lw-icon name=\"weaver-apps-calendar.calendar\" size=\"1.25rem\"></lw-icon>\n <span>{{ calendar.name }}</span>\n </button>\n </li>\n } @empty {\n <li class=\"px-4 py-8 text-center text-sm text-content-faint\">{{ 'weaver-apps-calendar.calendars.empty' | transloco }}</li>\n }\n </ul>\n }\n</div>\n"],"mappings":";;;;;AAaA,IAAa,mBAAb,MAAa,iBAAgB;CACV,UAAU,OAAO,gBAAgB;CAE/B,YAAY,SAAQ;EAAA,GAAA,YAAA,EAAA,WAAA,YAAA,+BAAA,CAAA;EAAG,cAAc,KAAK,QAAQ,UAAS;CAAE,CAAA;CAEtE,KAAK,YAAkB;EAC/B,aAAa,SAAS,aAAa,UAAU,CAAC;CAChD;CAEU,MAAM,SAAM;EACpB,MAAM,OAAO,MAAM,aAAa,GAAG,OAAO;GACxC,OAAO;GACP,SAAS;GACT,cAAc;EACf,CAAA;EACD,IAAI,CAAC,MAAM,KAAI,GACb;EAEF,IAAI;GACF,MAAM,WAAW,MAAM,KAAK,QAAQ,aAAa,OAAO,WAAU,GAAI,KAAK,KAAI,CAAE;GACjF,KAAK,KAAK,SAAS,EAAE;EACvB,QAAQ;GACN,aAAa,GAAG,MAAM;IAAE,SAAS;IAAsC,MAAM;GAAO,CAAE;EACxF;CACF;CAxBW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAgB,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAhB,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAgB,cAAA;EAAA,UAAA;EAAA,UAAA;EAAA,UCb7B;EAsBA,cAAA,CAAA;GAAA,MAAA;GAAA,MDdY;GAAa,MAAA;EAAA,CAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAKZ,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAgB,YAAA,CAAA;EAP5B,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAuB,SACxB,CAAC,aAAa;GAAC,SACf,CAAC,sBAAsB;GAAC,iBAEhB,wBAAwB;GAAM,UAAA;EAAA,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { a as calendarIconNames, s as calendarHost, t as CALENDAR_GATEWAY } from "./weaver-apps-calendar-calendar-gateway-DqiL9VbI.mjs";
|
|
2
2
|
import * as i0 from "@angular/core";
|
|
3
3
|
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, DestroyRef, computed, effect, inject, input, output, resource, signal } from "@angular/core";
|
|
4
4
|
import { TranslocoPipe, TranslocoService, translateObjectSignal } from "@jsverse/transloco";
|
|
@@ -6,7 +6,7 @@ import { toSignal } from "@angular/core/rxjs-interop";
|
|
|
6
6
|
import { ActivatedRoute } from "@angular/router";
|
|
7
7
|
import { map } from "rxjs";
|
|
8
8
|
import { DialogRef } from "@loomweaver/plugin-sdk";
|
|
9
|
-
import { DataGrid, ListFilter } from "@weaver-apps/data-grid";
|
|
9
|
+
import { DataGrid, ListFilter, openOnRow } from "@weaver-apps/data-grid";
|
|
10
10
|
const daysPerWeek = 7;
|
|
11
11
|
const sunday = 0;
|
|
12
12
|
const daysFromSundayToMonday = 6;
|
|
@@ -238,6 +238,26 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
238
238
|
}]
|
|
239
239
|
}]
|
|
240
240
|
});
|
|
241
|
+
function appointmentMenu(appointment, runs) {
|
|
242
|
+
const cancellable = appointment.status !== AppointmentStatus.cancelled;
|
|
243
|
+
return [
|
|
244
|
+
{
|
|
245
|
+
label: "weaver-apps-calendar.actions.edit",
|
|
246
|
+
icon: "edit",
|
|
247
|
+
run: runs.edit
|
|
248
|
+
},
|
|
249
|
+
...cancellable ? [{
|
|
250
|
+
label: "weaver-apps-calendar.actions.cancel",
|
|
251
|
+
icon: calendarIconNames.cancel,
|
|
252
|
+
run: runs.cancel
|
|
253
|
+
}] : [],
|
|
254
|
+
{
|
|
255
|
+
label: "weaver-apps-calendar.actions.delete",
|
|
256
|
+
icon: "trash",
|
|
257
|
+
run: runs.remove
|
|
258
|
+
}
|
|
259
|
+
];
|
|
260
|
+
}
|
|
241
261
|
var AppointmentCell = class AppointmentCell {
|
|
242
262
|
params = null;
|
|
243
263
|
locale = signal("en", ...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
|
|
@@ -275,18 +295,12 @@ var AppointmentCell = class AppointmentCell {
|
|
|
275
295
|
this.agInit(params);
|
|
276
296
|
return true;
|
|
277
297
|
}
|
|
278
|
-
|
|
279
|
-
this.hand(this.params?.edit);
|
|
280
|
-
}
|
|
281
|
-
cancel() {
|
|
282
|
-
this.hand(this.params?.cancel);
|
|
283
|
-
}
|
|
284
|
-
remove() {
|
|
285
|
-
this.hand(this.params?.remove);
|
|
286
|
-
}
|
|
287
|
-
hand(to) {
|
|
298
|
+
openActions(event) {
|
|
288
299
|
const appointment = this.appointment();
|
|
289
|
-
if (appointment)
|
|
300
|
+
if (appointment) this.params?.actions({
|
|
301
|
+
appointment,
|
|
302
|
+
...menuAnchor(event)
|
|
303
|
+
});
|
|
290
304
|
}
|
|
291
305
|
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
292
306
|
minVersion: "12.0.0",
|
|
@@ -303,7 +317,7 @@ var AppointmentCell = class AppointmentCell {
|
|
|
303
317
|
isStandalone: true,
|
|
304
318
|
selector: "wa-appointment-cell",
|
|
305
319
|
ngImport: i0,
|
|
306
|
-
template: "@if (appointment(); as appointment) {\n @switch (kind()) {\n @case ('when') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ when().day }}</span>\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ when().hours }}</span>\n }\n @case ('title') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ appointment.title }}</span>\n }\n @case ('contact') {\n <span [class.line-through]=\"cancelled()\">{{ appointment.contactName ?? '–' }}</span>\n @if (appointment.contactPhone) {\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.contactPhone }}</span>\n }\n }\n @case ('location') {\n <span class=\"text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.location ?? '–' }}</span>\n }\n @case ('status') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"appointment-status\">{{ 'weaver-apps-calendar.status.' + appointment.status | transloco }}</span>\n }\n @case ('actions') {\n <
|
|
320
|
+
template: "@if (appointment(); as appointment) {\n @switch (kind()) {\n @case ('when') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ when().day }}</span>\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ when().hours }}</span>\n }\n @case ('title') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ appointment.title }}</span>\n }\n @case ('contact') {\n <span [class.line-through]=\"cancelled()\">{{ appointment.contactName ?? '–' }}</span>\n @if (appointment.contactPhone) {\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.contactPhone }}</span>\n }\n }\n @case ('location') {\n <span class=\"text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.location ?? '–' }}</span>\n }\n @case ('status') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"appointment-status\">{{ 'weaver-apps-calendar.status.' + appointment.status | transloco }}</span>\n }\n @case ('actions') {\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.actions.more' | transloco\" (click)=\"openActions($event)\">\n <lw-icon name=\"more\" size=\"1rem\"></lw-icon>\n </button>\n }\n }\n}\n",
|
|
307
321
|
dependencies: [{
|
|
308
322
|
kind: "pipe",
|
|
309
323
|
type: TranslocoPipe,
|
|
@@ -324,10 +338,21 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
324
338
|
imports: [TranslocoPipe],
|
|
325
339
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
326
340
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
327
|
-
template: "@if (appointment(); as appointment) {\n @switch (kind()) {\n @case ('when') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ when().day }}</span>\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ when().hours }}</span>\n }\n @case ('title') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ appointment.title }}</span>\n }\n @case ('contact') {\n <span [class.line-through]=\"cancelled()\">{{ appointment.contactName ?? '–' }}</span>\n @if (appointment.contactPhone) {\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.contactPhone }}</span>\n }\n }\n @case ('location') {\n <span class=\"text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.location ?? '–' }}</span>\n }\n @case ('status') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"appointment-status\">{{ 'weaver-apps-calendar.status.' + appointment.status | transloco }}</span>\n }\n @case ('actions') {\n <
|
|
341
|
+
template: "@if (appointment(); as appointment) {\n @switch (kind()) {\n @case ('when') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ when().day }}</span>\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ when().hours }}</span>\n }\n @case ('title') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ appointment.title }}</span>\n }\n @case ('contact') {\n <span [class.line-through]=\"cancelled()\">{{ appointment.contactName ?? '–' }}</span>\n @if (appointment.contactPhone) {\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.contactPhone }}</span>\n }\n }\n @case ('location') {\n <span class=\"text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.location ?? '–' }}</span>\n }\n @case ('status') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"appointment-status\">{{ 'weaver-apps-calendar.status.' + appointment.status | transloco }}</span>\n }\n @case ('actions') {\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.actions.more' | transloco\" (click)=\"openActions($event)\">\n <lw-icon name=\"more\" size=\"1rem\"></lw-icon>\n </button>\n }\n }\n}\n"
|
|
328
342
|
}]
|
|
329
343
|
}]
|
|
330
344
|
});
|
|
345
|
+
function menuAnchor(event) {
|
|
346
|
+
if (event.detail > 0) return {
|
|
347
|
+
x: event.clientX,
|
|
348
|
+
y: event.clientY
|
|
349
|
+
};
|
|
350
|
+
const bounds = event.currentTarget.getBoundingClientRect();
|
|
351
|
+
return {
|
|
352
|
+
x: bounds.left,
|
|
353
|
+
y: bounds.bottom
|
|
354
|
+
};
|
|
355
|
+
}
|
|
331
356
|
const appointmentColumn = {
|
|
332
357
|
when: "start",
|
|
333
358
|
title: "title",
|
|
@@ -336,7 +361,7 @@ const appointmentColumn = {
|
|
|
336
361
|
status: "status",
|
|
337
362
|
actions: "actions"
|
|
338
363
|
};
|
|
339
|
-
const actionsWidthPx =
|
|
364
|
+
const actionsWidthPx = 72;
|
|
340
365
|
const whenMinWidthPx = 230;
|
|
341
366
|
const textMinWidthPx = 110;
|
|
342
367
|
const locationMinWidthPx = 90;
|
|
@@ -354,8 +379,7 @@ var AppointmentTable = class AppointmentTable {
|
|
|
354
379
|
appointments = input.required(...ngDevMode ? [{ debugName: "appointments" }] : /* istanbul ignore next */ []);
|
|
355
380
|
locale = input.required(...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
|
|
356
381
|
editAppointment = output();
|
|
357
|
-
|
|
358
|
-
deleteAppointment = output();
|
|
382
|
+
appointmentActions = output();
|
|
359
383
|
translated = computed(() => typeof this.table() === "object" && typeof this.table()["title"] === "string", ...ngDevMode ? [{ debugName: "translated" }] : /* istanbul ignore next */ []);
|
|
360
384
|
empty = computed(() => ({ noRowsToShow: this.table()["empty"] ?? "" }), ...ngDevMode ? [{ debugName: "empty" }] : /* istanbul ignore next */ []);
|
|
361
385
|
options = computed(() => ({
|
|
@@ -371,7 +395,8 @@ var AppointmentTable = class AppointmentTable {
|
|
|
371
395
|
colId: appointmentColumn.when,
|
|
372
396
|
sort: "asc"
|
|
373
397
|
}] } },
|
|
374
|
-
rowClassRules: { "text-content-faint": (params) => params.data?.status === AppointmentStatus.cancelled }
|
|
398
|
+
rowClassRules: { "text-content-faint": (params) => params.data?.status === AppointmentStatus.cancelled },
|
|
399
|
+
...openOnRow((appointment) => this.editAppointment.emit(appointment), { except: [appointmentColumn.actions] })
|
|
375
400
|
}), ...ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []);
|
|
376
401
|
constructor() {
|
|
377
402
|
effect(() => {
|
|
@@ -426,7 +451,8 @@ var AppointmentTable = class AppointmentTable {
|
|
|
426
451
|
{
|
|
427
452
|
...this.cell(appointmentColumn.actions, "actions", ""),
|
|
428
453
|
width: actionsWidthPx,
|
|
429
|
-
resizable: false
|
|
454
|
+
resizable: false,
|
|
455
|
+
pinned: "right"
|
|
430
456
|
}
|
|
431
457
|
];
|
|
432
458
|
}
|
|
@@ -438,9 +464,7 @@ var AppointmentTable = class AppointmentTable {
|
|
|
438
464
|
cellRendererParams: {
|
|
439
465
|
kind,
|
|
440
466
|
locale: () => this.locale(),
|
|
441
|
-
|
|
442
|
-
cancel: (appointment) => this.cancelAppointment.emit(appointment),
|
|
443
|
-
remove: (appointment) => this.deleteAppointment.emit(appointment)
|
|
467
|
+
actions: (actions) => this.appointmentActions.emit(actions)
|
|
444
468
|
}
|
|
445
469
|
};
|
|
446
470
|
}
|
|
@@ -486,8 +510,7 @@ var AppointmentTable = class AppointmentTable {
|
|
|
486
510
|
},
|
|
487
511
|
outputs: {
|
|
488
512
|
editAppointment: "editAppointment",
|
|
489
|
-
|
|
490
|
-
deleteAppointment: "deleteAppointment"
|
|
513
|
+
appointmentActions: "appointmentActions"
|
|
491
514
|
},
|
|
492
515
|
host: { classAttribute: "block h-full w-full" },
|
|
493
516
|
ngImport: i0,
|
|
@@ -539,13 +562,9 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
539
562
|
type: i0.Output,
|
|
540
563
|
args: ["editAppointment"]
|
|
541
564
|
}],
|
|
542
|
-
|
|
565
|
+
appointmentActions: [{
|
|
543
566
|
type: i0.Output,
|
|
544
|
-
args: ["
|
|
545
|
-
}],
|
|
546
|
-
deleteAppointment: [{
|
|
547
|
-
type: i0.Output,
|
|
548
|
-
args: ["deleteAppointment"]
|
|
567
|
+
args: ["appointmentActions"]
|
|
549
568
|
}]
|
|
550
569
|
}
|
|
551
570
|
});
|
|
@@ -617,6 +636,16 @@ var CalendarPage = class CalendarPage {
|
|
|
617
636
|
day: new Date(appointment.start)
|
|
618
637
|
}, "weaver-apps-calendar.dialog.editTitle");
|
|
619
638
|
}
|
|
639
|
+
actions({ appointment, x, y }) {
|
|
640
|
+
calendarHost.ui.openMenu(appointmentMenu(appointment, {
|
|
641
|
+
edit: () => this.edit(appointment),
|
|
642
|
+
cancel: () => void this.cancel(appointment),
|
|
643
|
+
remove: () => void this.remove(appointment)
|
|
644
|
+
}), {
|
|
645
|
+
x,
|
|
646
|
+
y
|
|
647
|
+
});
|
|
648
|
+
}
|
|
620
649
|
async cancel(appointment) {
|
|
621
650
|
const reason = await calendarHost.ui.prompt({
|
|
622
651
|
title: "weaver-apps-calendar.cancel.title",
|
|
@@ -681,17 +710,13 @@ var CalendarPage = class CalendarPage {
|
|
|
681
710
|
isStandalone: true,
|
|
682
711
|
selector: "wa-calendar-page",
|
|
683
712
|
ngImport: i0,
|
|
684
|
-
template: "<div class=\"flex h-full flex-col gap-4 p-6\">\n <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <h2 class=\"text-lg font-semibold text-content\">{{ calendar.value()?.name ?? ('weaver-apps-calendar.title' | transloco) }}</h2>\n <div class=\"flex flex-wrap items-center gap-2\">\n <input\n type=\"search\"\n class=\"lw-field w-56\"\n [attr.aria-label]=\"'weaver-apps-calendar.search.label' | transloco\"\n [attr.placeholder]=\"'weaver-apps-calendar.search.placeholder' | transloco\"\n (input)=\"search($event)\"\n />\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"add()\">\n <lw-icon name=\"add\" size=\"1rem\"></lw-icon>\n {{ 'weaver-apps-calendar.actions.add' | transloco }}\n </button>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.previous' | transloco\" (click)=\"previousWeek()\">\n <lw-icon name=\"weaver-apps-calendar.previous\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.previous' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <button type=\"button\" class=\"lw-btn lw-btn--default lw-btn--sm\" (click)=\"thisWeek()\">{{ 'weaver-apps-calendar.week.current' | transloco }}</button>\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.next' | transloco\" (click)=\"nextWeek()\">\n <lw-icon name=\"weaver-apps-calendar.next\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.next' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <span class=\"text-sm font-medium text-content\" data-testid=\"week-label\">{{ weekLabel() }}</span>\n </div>\n\n @if (appointments.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-calendar.errors.load' | transloco }}</p>\n } @else {\n <div class=\"min-h-80 flex-1\">\n <wa-appointment-table\n [appointments]=\"appointments.value() ?? []\"\n [locale]=\"locale()\"\n (editAppointment)=\"edit($event)\"\n (
|
|
713
|
+
template: "<div class=\"flex h-full flex-col gap-4 p-6\">\n <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <h2 class=\"text-lg font-semibold text-content\">{{ calendar.value()?.name ?? ('weaver-apps-calendar.title' | transloco) }}</h2>\n <div class=\"flex flex-wrap items-center gap-2\">\n <input\n type=\"search\"\n class=\"lw-field w-56\"\n [attr.aria-label]=\"'weaver-apps-calendar.search.label' | transloco\"\n [attr.placeholder]=\"'weaver-apps-calendar.search.placeholder' | transloco\"\n (input)=\"search($event)\"\n />\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"add()\">\n <lw-icon name=\"add\" size=\"1rem\"></lw-icon>\n {{ 'weaver-apps-calendar.actions.add' | transloco }}\n </button>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.previous' | transloco\" (click)=\"previousWeek()\">\n <lw-icon name=\"weaver-apps-calendar.previous\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.previous' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <button type=\"button\" class=\"lw-btn lw-btn--default lw-btn--sm\" (click)=\"thisWeek()\">{{ 'weaver-apps-calendar.week.current' | transloco }}</button>\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.next' | transloco\" (click)=\"nextWeek()\">\n <lw-icon name=\"weaver-apps-calendar.next\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.next' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <span class=\"text-sm font-medium text-content\" data-testid=\"week-label\">{{ weekLabel() }}</span>\n </div>\n\n @if (appointments.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-calendar.errors.load' | transloco }}</p>\n } @else {\n <div class=\"min-h-80 flex-1\">\n <wa-appointment-table\n [appointments]=\"appointments.value() ?? []\"\n [locale]=\"locale()\"\n (editAppointment)=\"edit($event)\"\n (appointmentActions)=\"actions($event)\"\n />\n </div>\n }\n</div>\n",
|
|
685
714
|
dependencies: [{
|
|
686
715
|
kind: "component",
|
|
687
716
|
type: AppointmentTable,
|
|
688
717
|
selector: "wa-appointment-table",
|
|
689
718
|
inputs: ["appointments", "locale"],
|
|
690
|
-
outputs: [
|
|
691
|
-
"editAppointment",
|
|
692
|
-
"cancelAppointment",
|
|
693
|
-
"deleteAppointment"
|
|
694
|
-
]
|
|
719
|
+
outputs: ["editAppointment", "appointmentActions"]
|
|
695
720
|
}, {
|
|
696
721
|
kind: "pipe",
|
|
697
722
|
type: TranslocoPipe,
|
|
@@ -712,11 +737,11 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
712
737
|
imports: [TranslocoPipe, AppointmentTable],
|
|
713
738
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
714
739
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
715
|
-
template: "<div class=\"flex h-full flex-col gap-4 p-6\">\n <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <h2 class=\"text-lg font-semibold text-content\">{{ calendar.value()?.name ?? ('weaver-apps-calendar.title' | transloco) }}</h2>\n <div class=\"flex flex-wrap items-center gap-2\">\n <input\n type=\"search\"\n class=\"lw-field w-56\"\n [attr.aria-label]=\"'weaver-apps-calendar.search.label' | transloco\"\n [attr.placeholder]=\"'weaver-apps-calendar.search.placeholder' | transloco\"\n (input)=\"search($event)\"\n />\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"add()\">\n <lw-icon name=\"add\" size=\"1rem\"></lw-icon>\n {{ 'weaver-apps-calendar.actions.add' | transloco }}\n </button>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.previous' | transloco\" (click)=\"previousWeek()\">\n <lw-icon name=\"weaver-apps-calendar.previous\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.previous' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <button type=\"button\" class=\"lw-btn lw-btn--default lw-btn--sm\" (click)=\"thisWeek()\">{{ 'weaver-apps-calendar.week.current' | transloco }}</button>\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.next' | transloco\" (click)=\"nextWeek()\">\n <lw-icon name=\"weaver-apps-calendar.next\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.next' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <span class=\"text-sm font-medium text-content\" data-testid=\"week-label\">{{ weekLabel() }}</span>\n </div>\n\n @if (appointments.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-calendar.errors.load' | transloco }}</p>\n } @else {\n <div class=\"min-h-80 flex-1\">\n <wa-appointment-table\n [appointments]=\"appointments.value() ?? []\"\n [locale]=\"locale()\"\n (editAppointment)=\"edit($event)\"\n (
|
|
740
|
+
template: "<div class=\"flex h-full flex-col gap-4 p-6\">\n <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <h2 class=\"text-lg font-semibold text-content\">{{ calendar.value()?.name ?? ('weaver-apps-calendar.title' | transloco) }}</h2>\n <div class=\"flex flex-wrap items-center gap-2\">\n <input\n type=\"search\"\n class=\"lw-field w-56\"\n [attr.aria-label]=\"'weaver-apps-calendar.search.label' | transloco\"\n [attr.placeholder]=\"'weaver-apps-calendar.search.placeholder' | transloco\"\n (input)=\"search($event)\"\n />\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"add()\">\n <lw-icon name=\"add\" size=\"1rem\"></lw-icon>\n {{ 'weaver-apps-calendar.actions.add' | transloco }}\n </button>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.previous' | transloco\" (click)=\"previousWeek()\">\n <lw-icon name=\"weaver-apps-calendar.previous\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.previous' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <button type=\"button\" class=\"lw-btn lw-btn--default lw-btn--sm\" (click)=\"thisWeek()\">{{ 'weaver-apps-calendar.week.current' | transloco }}</button>\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.next' | transloco\" (click)=\"nextWeek()\">\n <lw-icon name=\"weaver-apps-calendar.next\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.next' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <span class=\"text-sm font-medium text-content\" data-testid=\"week-label\">{{ weekLabel() }}</span>\n </div>\n\n @if (appointments.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-calendar.errors.load' | transloco }}</p>\n } @else {\n <div class=\"min-h-80 flex-1\">\n <wa-appointment-table\n [appointments]=\"appointments.value() ?? []\"\n [locale]=\"locale()\"\n (editAppointment)=\"edit($event)\"\n (appointmentActions)=\"actions($event)\"\n />\n </div>\n }\n</div>\n"
|
|
716
741
|
}]
|
|
717
742
|
}],
|
|
718
743
|
ctorParameters: () => []
|
|
719
744
|
});
|
|
720
745
|
export { CalendarPage };
|
|
721
746
|
|
|
722
|
-
//# sourceMappingURL=weaver-apps-calendar-calendar-page-
|
|
747
|
+
//# sourceMappingURL=weaver-apps-calendar-calendar-page-nhRq6se2.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weaver-apps-calendar-calendar-page-nhRq6se2.mjs","names":[],"sources":["../../../../libs/calendar/src/lib/week.ts","../../../../libs/calendar/src/lib/views/active-locale.ts","../../../../libs/calendar/src/lib/api/models/appointmentStatus.ts","../../../../libs/calendar/src/lib/views/appointment-dialog.ts","../../../../libs/calendar/src/lib/views/appointment-dialog.html","../../../../libs/calendar/src/lib/views/appointment-menu.ts","../../../../libs/calendar/src/lib/views/appointment-cell.ts","../../../../libs/calendar/src/lib/views/appointment-cell.html","../../../../libs/calendar/src/lib/views/appointment-table.ts","../../../../libs/calendar/src/lib/views/appointment-table.html","../../../../libs/calendar/src/lib/views/calendar-page.ts","../../../../libs/calendar/src/lib/views/calendar-page.html"],"sourcesContent":["import type { CalendarRange } from './gateway/calendar-gateway';\n\nconst daysPerWeek = 7;\nconst sunday = 0;\nconst daysFromSundayToMonday = 6;\n\nexport function startOfWeek(date: Date): Date {\n const start = new Date(date.getFullYear(), date.getMonth(), date.getDate());\n const offset = start.getDay() === sunday ? daysFromSundayToMonday : start.getDay() - 1;\n start.setDate(start.getDate() - offset);\n return start;\n}\n\nexport function addWeeks(weekStart: Date, weeks: number): Date {\n const moved = new Date(weekStart);\n moved.setDate(moved.getDate() + weeks * daysPerWeek);\n return moved;\n}\n\nexport function weekRange(weekStart: Date): CalendarRange {\n return { from: weekStart, until: addWeeks(weekStart, 1) };\n}\n\nexport function localDate(value: Date): string {\n const pad = (part: number) => `${part}`.padStart(2, '0');\n return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;\n}\n\nexport function localTime(value: Date): string {\n const pad = (part: number) => `${part}`.padStart(2, '0');\n return `${pad(value.getHours())}:${pad(value.getMinutes())}`;\n}\n\nexport function combine(date: string, time: string): Date | null {\n const combined = new Date(`${date}T${time}`);\n return Number.isNaN(combined.getTime()) ? null : combined;\n}\n","import { inject, Signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { TranslocoService } from '@jsverse/transloco';\n\nexport function activeLocale(): Signal<string> {\n const transloco = inject(TranslocoService);\n return toSignal(transloco.langChanges$, { initialValue: transloco.getActiveLang() });\n}\n","/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Weaver Apps\n * OpenAPI spec version: 1.0.0\n */\n\n/**\n * Where an appointment stands.\n */\nexport type AppointmentStatus = typeof AppointmentStatus[keyof typeof AppointmentStatus];\n\n\nexport const AppointmentStatus = {\n scheduled: 'scheduled',\n confirmed: 'confirmed',\n completed: 'completed',\n noShow: 'noShow',\n cancelled: 'cancelled',\n} as const;\n","import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';\nimport { DialogRef } from '@loomweaver/plugin-sdk';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport { AppointmentStatus, type AppointmentRequest, type AppointmentResponse } from '../api/models';\nimport { AppointmentOutcome, CALENDAR_GATEWAY, ConflictingAppointment } from '../gateway/calendar-gateway';\nimport { combine, localDate, localTime } from '../week';\nimport { activeLocale } from './active-locale';\n\nexport interface AppointmentDialogData {\n readonly calendarId: string;\n readonly appointment: AppointmentResponse | null;\n readonly day: Date;\n}\n\ntype Problem =\n | { readonly kind: 'none' }\n | { readonly kind: 'message'; readonly key: string }\n | { readonly kind: 'conflict'; readonly conflicts: readonly ConflictingAppointment[] };\n\nconst defaultStart = '09:00';\nconst defaultEnd = '10:00';\nconst settableStatuses: readonly AppointmentStatus[] = [\n AppointmentStatus.scheduled,\n AppointmentStatus.confirmed,\n AppointmentStatus.completed,\n AppointmentStatus.noShow,\n];\n\n@Component({\n selector: 'wa-appointment-dialog',\n imports: [TranslocoPipe],\n templateUrl: './appointment-dialog.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AppointmentDialog {\n private readonly dialog = inject<DialogRef<boolean>>(DialogRef);\n private readonly gateway = inject(CALENDAR_GATEWAY);\n private readonly data = this.dialog.data as AppointmentDialogData;\n private existing = this.data.appointment;\n\n protected readonly locale = activeLocale();\n protected readonly editing = this.existing !== null;\n protected readonly statuses = settableStatuses;\n\n protected readonly title = signal(this.existing?.title ?? '');\n protected readonly date = signal(localDate(this.existing ? new Date(this.existing.start) : this.data.day));\n protected readonly start = signal(this.existing ? localTime(new Date(this.existing.start)) : defaultStart);\n protected readonly endDate = signal(localDate(this.existing ? new Date(this.existing.end) : this.data.day));\n protected readonly end = signal(this.existing ? localTime(new Date(this.existing.end)) : defaultEnd);\n protected readonly location = signal(this.existing?.location ?? '');\n protected readonly contactName = signal(this.existing?.contactName ?? '');\n protected readonly contactPhone = signal(this.existing?.contactPhone ?? '');\n protected readonly contactEmail = signal(this.existing?.contactEmail ?? '');\n protected readonly description = signal(this.existing?.description ?? '');\n protected readonly status = signal<AppointmentStatus>(this.existing?.status ?? AppointmentStatus.scheduled);\n protected readonly saving = signal(false);\n protected readonly problem = signal<Problem>({ kind: 'none' });\n private persisted = false;\n\n protected readonly problemKey = computed(() => {\n const problem = this.problem();\n return problem.kind === 'message' ? problem.key : '';\n });\n\n protected readonly conflictTimes = computed(() => {\n const problem = this.problem();\n if (problem.kind !== 'conflict') {\n return [];\n }\n const format = new Intl.DateTimeFormat(this.locale(), { dateStyle: 'short', timeStyle: 'short' });\n return problem.conflicts.map((conflict) => `${format.format(new Date(conflict.start))} – ${format.format(new Date(conflict.end))}`);\n });\n\n protected bind(target: { set(value: string): void }, event: Event): void {\n target.set((event.target as HTMLInputElement).value);\n }\n\n protected bindDate(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n if (this.endDate() === this.date()) {\n this.endDate.set(value);\n }\n this.date.set(value);\n }\n\n protected chooseStatus(event: Event): void {\n const chosen = settableStatuses.find((status) => status === (event.target as HTMLSelectElement).value);\n if (chosen) {\n this.status.set(chosen);\n }\n }\n\n protected close(): void {\n this.dialog.close(this.persisted);\n }\n\n protected async save(): Promise<void> {\n const request = this.request();\n if (!request) {\n return;\n }\n this.saving.set(true);\n try {\n const outcome = this.existing\n ? await this.gateway.update(this.data.calendarId, this.existing.id, request)\n : await this.gateway.book(this.data.calendarId, request);\n const final = await this.applyStatus(outcome);\n this.settle(final);\n } catch {\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.errors.save' });\n } finally {\n this.saving.set(false);\n }\n }\n\n private request(): AppointmentRequest | null {\n const startsAt = combine(this.date(), this.start());\n const endsAt = combine(this.endDate(), this.end());\n if (!this.title().trim()) {\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.titleRequired' });\n return null;\n }\n if (!startsAt || !endsAt || endsAt <= startsAt) {\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.periodInvalid' });\n return null;\n }\n return {\n title: this.title().trim(),\n start: startsAt.toISOString(),\n end: endsAt.toISOString(),\n description: this.optional(this.description()),\n location: this.optional(this.location()),\n contactName: this.optional(this.contactName()),\n contactPhone: this.optional(this.contactPhone()),\n contactEmail: this.optional(this.contactEmail()),\n };\n }\n\n private async applyStatus(outcome: AppointmentOutcome): Promise<AppointmentOutcome> {\n if (outcome.kind === 'saved') {\n this.existing = outcome.appointment;\n this.persisted = true;\n }\n if (outcome.kind !== 'saved' || outcome.appointment.status === this.status()) {\n return outcome;\n }\n return this.gateway.setStatus(this.data.calendarId, outcome.appointment.id, this.status());\n }\n\n private settle(outcome: AppointmentOutcome): void {\n switch (outcome.kind) {\n case 'saved':\n this.dialog.close(true);\n return;\n case 'conflict':\n this.problem.set({ kind: 'conflict', conflicts: outcome.conflicts });\n return;\n case 'closed':\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.closed' });\n return;\n case 'changed':\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.changed' });\n return;\n case 'invalid':\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.invalid' });\n return;\n default:\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.missing' });\n }\n }\n\n private optional(value: string): string | null {\n return value.trim() ? value.trim() : null;\n }\n}\n","<form class=\"flex flex-col gap-4\" (submit)=\"$event.preventDefault(); save()\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.title' | transloco }}\n <input class=\"lw-field\" required [value]=\"title()\" (input)=\"bind(title, $event)\" />\n </label>\n\n <div class=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.date' | transloco }}\n <input class=\"lw-field\" type=\"date\" required [value]=\"date()\" (input)=\"bindDate($event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.start' | transloco }}\n <input class=\"lw-field\" type=\"time\" required [value]=\"start()\" (input)=\"bind(start, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.endDate' | transloco }}\n <input class=\"lw-field\" type=\"date\" required [value]=\"endDate()\" (input)=\"bind(endDate, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.end' | transloco }}\n <input class=\"lw-field\" type=\"time\" required [value]=\"end()\" (input)=\"bind(end, $event)\" />\n </label>\n </div>\n\n <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.contactName' | transloco }}\n <input class=\"lw-field\" [value]=\"contactName()\" (input)=\"bind(contactName, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.contactPhone' | transloco }}\n <input class=\"lw-field\" type=\"tel\" [value]=\"contactPhone()\" (input)=\"bind(contactPhone, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.contactEmail' | transloco }}\n <input class=\"lw-field\" type=\"email\" [value]=\"contactEmail()\" (input)=\"bind(contactEmail, $event)\" />\n </label>\n </div>\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.location' | transloco }}\n <input class=\"lw-field\" [value]=\"location()\" (input)=\"bind(location, $event)\" />\n </label>\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.description' | transloco }}\n <textarea class=\"lw-field\" rows=\"3\" [value]=\"description()\" (input)=\"bind(description, $event)\"></textarea>\n </label>\n\n @if (editing) {\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.status' | transloco }}\n <select class=\"lw-field\" [value]=\"status()\" (change)=\"chooseStatus($event)\">\n @for (choice of statuses; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === status()\">{{ 'weaver-apps-calendar.status.' + choice | transloco }}</option>\n }\n </select>\n </label>\n }\n\n @switch (problem().kind) {\n @case ('message') {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ problemKey() | transloco }}</p>\n }\n @case ('conflict') {\n <div class=\"text-sm text-negative\" role=\"alert\">\n <p>{{ 'weaver-apps-calendar.dialog.conflict' | transloco }}</p>\n <ul class=\"list-disc pl-5\">\n @for (time of conflictTimes(); track time) {\n <li>{{ time }}</li>\n }\n </ul>\n </div>\n }\n }\n\n <div class=\"flex justify-end gap-2\">\n <button type=\"button\" class=\"lw-btn lw-btn--default\" (click)=\"close()\">{{ 'weaver-apps-calendar.dialog.cancel' | transloco }}</button>\n <button type=\"submit\" class=\"lw-btn lw-btn--primary\" [disabled]=\"saving()\">{{ 'weaver-apps-calendar.dialog.save' | transloco }}</button>\n </div>\n</form>\n","import type { UiMenuAction } from '@loomweaver/plugin-sdk';\nimport { AppointmentStatus, type AppointmentResponse } from '../api/models';\nimport { calendarIconNames } from '../plugin/calendar-ids';\n\nexport interface AppointmentMenuRuns {\n readonly edit: () => void;\n readonly cancel: () => void;\n readonly remove: () => void;\n}\n\nexport function appointmentMenu(appointment: AppointmentResponse, runs: AppointmentMenuRuns): UiMenuAction[] {\n const cancellable = appointment.status !== AppointmentStatus.cancelled;\n return [\n { label: 'weaver-apps-calendar.actions.edit', icon: 'edit', run: runs.edit },\n ...(cancellable ? [{ label: 'weaver-apps-calendar.actions.cancel', icon: calendarIconNames.cancel, run: runs.cancel }] : []),\n { label: 'weaver-apps-calendar.actions.delete', icon: 'trash', run: runs.remove },\n ];\n}\n","import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, computed, signal } from '@angular/core';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport type { ICellRendererAngularComp } from 'ag-grid-angular';\nimport type { ICellRendererParams } from 'ag-grid-community';\nimport type { AppointmentResponse } from '../api/models';\n\nexport interface AppointmentActions {\n readonly appointment: AppointmentResponse;\n readonly x: number;\n readonly y: number;\n}\n\nexport type AppointmentCellKind = 'when' | 'title' | 'contact' | 'location' | 'status' | 'actions';\n\nexport interface AppointmentCellParams {\n readonly kind: AppointmentCellKind;\n readonly locale: () => string;\n readonly actions: (actions: AppointmentActions) => void;\n}\n\ntype Params = ICellRendererParams<AppointmentResponse> & AppointmentCellParams;\n\n@Component({\n selector: 'wa-appointment-cell',\n imports: [TranslocoPipe],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './appointment-cell.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AppointmentCell implements ICellRendererAngularComp {\n private params: Params | null = null;\n private readonly locale = signal('en');\n\n protected readonly kind = signal<AppointmentCellKind>('title');\n protected readonly appointment = signal<AppointmentResponse | null>(null);\n protected readonly cancelled = computed(() => this.appointment()?.status === 'cancelled');\n protected readonly when = computed(() => {\n const appointment = this.appointment();\n if (!appointment) {\n return { day: '', hours: '' };\n }\n const day = new Intl.DateTimeFormat(this.locale(), { weekday: 'short', day: '2-digit', month: '2-digit', year: 'numeric' });\n const time = new Intl.DateTimeFormat(this.locale(), { hour: '2-digit', minute: '2-digit' });\n return {\n day: day.format(new Date(appointment.start)),\n hours: `${time.format(new Date(appointment.start))} – ${time.format(new Date(appointment.end))}`,\n };\n });\n\n agInit(params: Params): void {\n this.params = params;\n this.kind.set(params.kind);\n this.appointment.set(params.data ?? null);\n this.locale.set(params.locale());\n }\n\n refresh(params: Params): boolean {\n this.agInit(params);\n return true;\n }\n\n protected openActions(event: MouseEvent): void {\n const appointment = this.appointment();\n if (appointment) {\n this.params?.actions({ appointment, ...menuAnchor(event) });\n }\n }\n}\n\nfunction menuAnchor(event: MouseEvent): { readonly x: number; readonly y: number } {\n if (event.detail > 0) {\n return { x: event.clientX, y: event.clientY };\n }\n const bounds = (event.currentTarget as HTMLElement).getBoundingClientRect();\n return { x: bounds.left, y: bounds.bottom };\n}\n","@if (appointment(); as appointment) {\n @switch (kind()) {\n @case ('when') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ when().day }}</span>\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ when().hours }}</span>\n }\n @case ('title') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ appointment.title }}</span>\n }\n @case ('contact') {\n <span [class.line-through]=\"cancelled()\">{{ appointment.contactName ?? '–' }}</span>\n @if (appointment.contactPhone) {\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.contactPhone }}</span>\n }\n }\n @case ('location') {\n <span class=\"text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.location ?? '–' }}</span>\n }\n @case ('status') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"appointment-status\">{{ 'weaver-apps-calendar.status.' + appointment.status | transloco }}</span>\n }\n @case ('actions') {\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.actions.more' | transloco\" (click)=\"openActions($event)\">\n <lw-icon name=\"more\" size=\"1rem\"></lw-icon>\n </button>\n }\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, effect, input, output, signal, type Signal } from '@angular/core';\nimport { translateObjectSignal } from '@jsverse/transloco';\nimport { DataGrid, ListFilter, type ListFilterParams, openOnRow } from '@weaver-apps/data-grid';\nimport type { ColDef, GridApi, GridOptions } from 'ag-grid-community';\nimport { AppointmentStatus, type AppointmentResponse } from '../api/models';\nimport { AppointmentCell, type AppointmentActions, type AppointmentCellKind, type AppointmentCellParams } from './appointment-cell';\n\ntype Texts = Readonly<Record<string, string>>;\n\nexport const appointmentColumn = {\n when: 'start',\n title: 'title',\n contact: 'contact',\n location: 'location',\n status: 'status',\n actions: 'actions',\n} as const;\n\nconst actionsWidthPx = 72;\nconst whenMinWidthPx = 230;\nconst textMinWidthPx = 110;\nconst locationMinWidthPx = 90;\nconst statusMinWidthPx = 112;\n\nexport function byStart(left: string | undefined, right: string | undefined): number {\n return Date.parse(left ?? '') - Date.parse(right ?? '');\n}\n\nexport function statusOf(appointment: AppointmentResponse): string {\n return appointment.status;\n}\n\n@Component({\n selector: 'wa-appointment-table',\n imports: [DataGrid],\n templateUrl: './appointment-table.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: 'block h-full w-full' },\n})\nexport class AppointmentTable {\n private readonly table = translateObjectSignal('weaver-apps-calendar.table') as Signal<Texts>;\n private readonly statuses = translateObjectSignal('weaver-apps-calendar.status') as Signal<Texts>;\n private readonly api = signal<GridApi<AppointmentResponse> | null>(null);\n\n readonly appointments = input.required<readonly AppointmentResponse[]>();\n readonly locale = input.required<string>();\n\n readonly editAppointment = output<AppointmentResponse>();\n readonly appointmentActions = output<AppointmentActions>();\n\n protected readonly translated = computed(() => typeof this.table() === 'object' && typeof this.table()['title'] === 'string');\n\n protected readonly empty = computed(() => ({ noRowsToShow: this.table()['empty'] ?? '' }));\n\n protected readonly options = computed<GridOptions<AppointmentResponse>>(() => ({\n rowModelType: 'clientSide',\n columnDefs: this.columns(this.table()),\n defaultColDef: { sortable: false, resizable: true, suppressHeaderMenuButton: true },\n getRowId: (params) => params.data.id,\n initialState: { sort: { sortModel: [{ colId: appointmentColumn.when, sort: 'asc' }] } },\n rowClassRules: { 'text-content-faint': (params) => params.data?.status === AppointmentStatus.cancelled },\n ...openOnRow<AppointmentResponse>((appointment) => this.editAppointment.emit(appointment), { except: [appointmentColumn.actions] }),\n }));\n\n constructor() {\n effect(() => {\n const api = this.api();\n const appointments = this.appointments();\n if (api && !api.isDestroyed()) {\n api.setGridOption('rowData', [...appointments]);\n }\n });\n }\n\n protected ready(api: GridApi<AppointmentResponse>): void {\n this.api.set(api);\n }\n\n private columns(table: Texts): ColDef<AppointmentResponse>[] {\n return [\n { ...this.cell(appointmentColumn.when, 'when', table['when']), field: 'start', sortable: true, comparator: byStart, flex: 2, minWidth: whenMinWidthPx },\n { ...this.cell(appointmentColumn.title, 'title', table['title']), field: 'title', sortable: true, flex: 2, minWidth: textMinWidthPx },\n {\n ...this.cell(appointmentColumn.contact, 'contact', table['contact']),\n valueGetter: (params) => params.data?.contactName ?? '',\n sortable: true,\n flex: 2,\n minWidth: textMinWidthPx,\n },\n { ...this.cell(appointmentColumn.location, 'location', table['location']), field: 'location', sortable: true, flex: 1, minWidth: locationMinWidthPx },\n {\n ...this.cell(appointmentColumn.status, 'status', table['status']),\n field: 'status',\n sortable: true,\n flex: 1,\n minWidth: statusMinWidthPx,\n filter: ListFilter,\n filterParams: this.statusChoices(),\n },\n { ...this.cell(appointmentColumn.actions, 'actions', ''), width: actionsWidthPx, resizable: false, pinned: 'right' },\n ];\n }\n\n private cell(colId: string, kind: AppointmentCellKind, headerName: string | undefined): ColDef<AppointmentResponse> {\n const params: AppointmentCellParams = {\n kind,\n locale: () => this.locale(),\n actions: (actions) => this.appointmentActions.emit(actions),\n };\n return { colId, headerName: headerName ?? '', cellRenderer: AppointmentCell, cellRendererParams: params };\n }\n\n private statusChoices(): ListFilterParams {\n const values = Object.values(AppointmentStatus);\n return {\n options: () => values.map((value) => ({ value, label: this.statuses()[value] ?? value })),\n rowValue: statusOf,\n };\n }\n}\n","@if (translated()) {\n <wa-data-grid [options]=\"options()\" [localeOverrides]=\"empty()\" (ready)=\"ready($event)\" />\n}\n","import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, computed, DestroyRef, inject, resource, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute } from '@angular/router';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport { map } from 'rxjs';\nimport type { AppointmentResponse } from '../api/models';\nimport { CALENDAR_GATEWAY } from '../gateway/calendar-gateway';\nimport { calendarHost } from '../plugin/calendar-host';\nimport { addWeeks, startOfWeek, weekRange } from '../week';\nimport { activeLocale } from './active-locale';\nimport type { AppointmentActions } from './appointment-cell';\nimport { AppointmentDialog, AppointmentDialogData } from './appointment-dialog';\nimport { appointmentMenu } from './appointment-menu';\nimport { AppointmentTable } from './appointment-table';\n\nconst searchDelayMs = 300;\n\n@Component({\n selector: 'wa-calendar-page',\n imports: [TranslocoPipe, AppointmentTable],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './calendar-page.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CalendarPage {\n private readonly gateway = inject(CALENDAR_GATEWAY);\n private readonly route = inject(ActivatedRoute);\n private searchTimer?: ReturnType<typeof setTimeout>;\n\n protected readonly locale = activeLocale();\n protected readonly calendarId = toSignal(this.route.paramMap.pipe(map((params) => params.get('calendarId') ?? '')), {\n initialValue: this.route.snapshot.paramMap.get('calendarId') ?? '',\n });\n protected readonly weekStart = signal(startOfWeek(new Date()));\n protected readonly contact = signal('');\n\n protected readonly calendar = resource({\n params: () => this.calendarId(),\n loader: ({ params }) => this.gateway.calendar(params),\n });\n\n protected readonly appointments = resource({\n params: () => ({ calendarId: this.calendarId(), range: weekRange(this.weekStart()), contact: this.contact().trim() }),\n loader: ({ params, abortSignal }) =>\n this.gateway.appointments(params.calendarId, params.range, params.contact || undefined, abortSignal),\n });\n\n constructor() {\n inject(DestroyRef).onDestroy(() => clearTimeout(this.searchTimer));\n }\n\n protected readonly weekLabel = computed(() => {\n const range = weekRange(this.weekStart());\n const last = addWeeks(range.until, 0);\n last.setDate(last.getDate() - 1);\n const format = new Intl.DateTimeFormat(this.locale(), { day: '2-digit', month: '2-digit', year: 'numeric' });\n return `${format.format(range.from)} – ${format.format(last)}`;\n });\n\n protected previousWeek(): void {\n this.weekStart.update((start) => addWeeks(start, -1));\n }\n\n protected nextWeek(): void {\n this.weekStart.update((start) => addWeeks(start, 1));\n }\n\n protected thisWeek(): void {\n this.weekStart.set(startOfWeek(new Date()));\n }\n\n protected search(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n clearTimeout(this.searchTimer);\n this.searchTimer = setTimeout(() => this.contact.set(value), searchDelayMs);\n }\n\n protected add(): void {\n const today = new Date();\n const shown = weekRange(this.weekStart());\n const day = today >= shown.from && today < shown.until ? today : this.weekStart();\n this.openDialog({ calendarId: this.calendarId(), appointment: null, day }, 'weaver-apps-calendar.dialog.addTitle');\n }\n\n protected edit(appointment: AppointmentResponse): void {\n this.openDialog({ calendarId: this.calendarId(), appointment, day: new Date(appointment.start) }, 'weaver-apps-calendar.dialog.editTitle');\n }\n\n protected actions({ appointment, x, y }: AppointmentActions): void {\n const runs = { edit: () => this.edit(appointment), cancel: () => void this.cancel(appointment), remove: () => void this.remove(appointment) };\n calendarHost.ui.openMenu(appointmentMenu(appointment, runs), { x, y });\n }\n\n protected async cancel(appointment: AppointmentResponse): Promise<void> {\n const reason = await calendarHost.ui.prompt({\n title: 'weaver-apps-calendar.cancel.title',\n message: 'weaver-apps-calendar.cancel.message',\n confirmLabel: 'weaver-apps-calendar.cancel.confirm',\n tone: 'warning',\n });\n if (reason === null) {\n return;\n }\n await this.attempt(async () => {\n const outcome = await this.gateway.cancel(this.calendarId(), appointment.id, reason.trim() || null);\n this.report(outcome.kind === 'saved' ? 'weaver-apps-calendar.cancel.done' : 'weaver-apps-calendar.errors.save', outcome.kind === 'saved');\n });\n }\n\n protected async remove(appointment: AppointmentResponse): Promise<void> {\n const confirmed = await calendarHost.ui.confirm({\n title: 'weaver-apps-calendar.delete.title',\n message: 'weaver-apps-calendar.delete.message',\n confirmLabel: 'weaver-apps-calendar.delete.confirm',\n tone: 'danger',\n });\n if (!confirmed) {\n return;\n }\n await this.attempt(async () => {\n await this.gateway.remove(this.calendarId(), appointment.id);\n this.report('weaver-apps-calendar.delete.done', true);\n });\n }\n\n private async attempt(work: () => Promise<void>): Promise<void> {\n try {\n await work();\n } catch {\n this.report('weaver-apps-calendar.errors.save', false);\n }\n }\n\n private openDialog(data: AppointmentDialogData, title: string): void {\n const dialog = calendarHost.ui.open<boolean>(AppointmentDialog, { data, title, size: 'lg', dismiss: 'explicit' });\n void dialog.closed.then((saved) => {\n if (saved) {\n this.report('weaver-apps-calendar.dialog.saved', true);\n }\n });\n }\n\n private report(message: string, success: boolean): void {\n calendarHost.ui.toast({ message, kind: success ? 'success' : 'error' });\n this.appointments.reload();\n }\n}\n","<div class=\"flex h-full flex-col gap-4 p-6\">\n <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <h2 class=\"text-lg font-semibold text-content\">{{ calendar.value()?.name ?? ('weaver-apps-calendar.title' | transloco) }}</h2>\n <div class=\"flex flex-wrap items-center gap-2\">\n <input\n type=\"search\"\n class=\"lw-field w-56\"\n [attr.aria-label]=\"'weaver-apps-calendar.search.label' | transloco\"\n [attr.placeholder]=\"'weaver-apps-calendar.search.placeholder' | transloco\"\n (input)=\"search($event)\"\n />\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"add()\">\n <lw-icon name=\"add\" size=\"1rem\"></lw-icon>\n {{ 'weaver-apps-calendar.actions.add' | transloco }}\n </button>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.previous' | transloco\" (click)=\"previousWeek()\">\n <lw-icon name=\"weaver-apps-calendar.previous\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.previous' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <button type=\"button\" class=\"lw-btn lw-btn--default lw-btn--sm\" (click)=\"thisWeek()\">{{ 'weaver-apps-calendar.week.current' | transloco }}</button>\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.next' | transloco\" (click)=\"nextWeek()\">\n <lw-icon name=\"weaver-apps-calendar.next\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.next' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <span class=\"text-sm font-medium text-content\" data-testid=\"week-label\">{{ weekLabel() }}</span>\n </div>\n\n @if (appointments.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-calendar.errors.load' | transloco }}</p>\n } @else {\n <div class=\"min-h-80 flex-1\">\n <wa-appointment-table\n [appointments]=\"appointments.value() ?? []\"\n [locale]=\"locale()\"\n (editAppointment)=\"edit($event)\"\n (appointmentActions)=\"actions($event)\"\n />\n </div>\n }\n</div>\n"],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { o as calendarPluginId } from "./weaver-apps-calendar-calendar-gateway-DqiL9VbI.mjs";
|
|
2
|
+
const calendarListPath = calendarPluginId;
|
|
3
|
+
function calendarPath(calendarId) {
|
|
4
|
+
return `${calendarListPath}/${calendarId}`;
|
|
5
|
+
}
|
|
6
|
+
export { calendarPath as n, calendarListPath as t };
|
|
7
|
+
|
|
8
|
+
//# sourceMappingURL=weaver-apps-calendar-calendar-paths-AFikgbRs.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weaver-apps-calendar-calendar-paths-AFikgbRs.mjs","names":[],"sources":["../../../../libs/calendar/src/lib/plugin/calendar-paths.ts"],"sourcesContent":["import { calendarPluginId } from './calendar-ids';\n\nexport const calendarListPath = calendarPluginId;\n\nexport function calendarPath(calendarId: string): string {\n return `${calendarListPath}/${calendarId}`;\n}\n"],"mappings":";AAEA,MAAa,mBAAmB;AAEhC,SAAgB,aAAa,YAAkB;CAC7C,OAAO,GAAG,iBAAgB,GAAI;AAChC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { i as
|
|
2
|
-
import {
|
|
1
|
+
import { a as calendarIconNames, i as calendarContributionIds, n as CalendarGatewayError, o as calendarPluginId, r as provideCalendarGateway, s as calendarHost, t as CALENDAR_GATEWAY } from "./weaver-apps-calendar-calendar-gateway-DqiL9VbI.mjs";
|
|
2
|
+
import { n as calendarPath, t as calendarListPath } from "./weaver-apps-calendar-calendar-paths-AFikgbRs.mjs";
|
|
3
3
|
const stroke = "fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"";
|
|
4
4
|
const calendarIcons = {
|
|
5
5
|
[calendarIconNames.calendar]: `<svg viewBox="0 0 24 24" ${stroke}><rect x="3.5" y="5" width="17" height="15" rx="2.5"/><path d="M3.5 10h17M8 3v4M16 3v4"/></svg>`,
|
|
@@ -26,14 +26,14 @@ function createCalendarPlugin(options = {}) {
|
|
|
26
26
|
title: "weaver-apps-calendar.calendars.title",
|
|
27
27
|
icon: calendarIconNames.calendar,
|
|
28
28
|
routable: { path: calendarListPath },
|
|
29
|
-
loadComponent: () => import("./weaver-apps-calendar-calendar-list-page-
|
|
29
|
+
loadComponent: () => import("./weaver-apps-calendar-calendar-list-page-CF8odTAv.mjs").then((module) => module.CalendarListPage)
|
|
30
30
|
});
|
|
31
31
|
ctx.registerSurface({
|
|
32
32
|
id: calendarContributionIds.calendar,
|
|
33
33
|
title: "weaver-apps-calendar.title",
|
|
34
34
|
icon: calendarIconNames.calendar,
|
|
35
35
|
routable: { path: `${calendarListPath}/:calendarId` },
|
|
36
|
-
loadComponent: () => import("./weaver-apps-calendar-calendar-page-
|
|
36
|
+
loadComponent: () => import("./weaver-apps-calendar-calendar-page-nhRq6se2.mjs").then((module) => module.CalendarPage)
|
|
37
37
|
});
|
|
38
38
|
if (options.railItem) ctx.registerRailItem({
|
|
39
39
|
id: calendarContributionIds.rail,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weaver-apps/calendar",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"description": "The calendar app's weaver for a LoomWeaver distribution: a calendar's appointments by week, booking, editing, cancelling and deleting, behind a gateway the host supplies.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"sideEffects": false,
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@weaver-apps/data-grid": "0.3.
|
|
22
|
+
"@weaver-apps/data-grid": "0.3.2",
|
|
23
23
|
"tslib": "^2.3.0"
|
|
24
24
|
},
|
|
25
25
|
"module": "fesm2022/weaver-apps-calendar.mjs",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"weaver-apps-calendar-calendar-gateway-DUo797bn.mjs","names":[],"sources":["../../../../libs/calendar/src/lib/plugin/calendar-host.ts","../../../../libs/calendar/src/lib/gateway/calendar-gateway.ts"],"sourcesContent":["import type { PluginContext } from '@loomweaver/plugin-sdk';\n\ntype HostContext = Pick<PluginContext, 'ui' | 'navigateContent'>;\n\nclass CalendarHost {\n private ctx?: HostContext;\n\n bind(ctx: HostContext): void {\n this.ctx = ctx;\n }\n\n unbind(): void {\n this.ctx = undefined;\n }\n\n get ui(): HostContext['ui'] {\n if (!this.ctx) {\n throw new Error('The calendar plugin is not active.');\n }\n return this.ctx.ui;\n }\n\n navigate(path: string): void {\n this.ctx?.navigateContent(path);\n }\n}\n\nexport const calendarHost = new CalendarHost();\n","import { InjectionToken, Provider } from '@angular/core';\nimport type { AppointmentRequest, AppointmentResponse, AppointmentStatus, CalendarResponse } from '../api/models';\n\nexport interface CalendarRange {\n readonly from: Date;\n readonly until: Date;\n}\n\nexport interface ConflictingAppointment {\n readonly id: string;\n readonly start: string;\n readonly end: string;\n}\n\nexport type AppointmentOutcome =\n | { readonly kind: 'saved'; readonly appointment: AppointmentResponse }\n | { readonly kind: 'conflict'; readonly conflicts: readonly ConflictingAppointment[] }\n | { readonly kind: 'closed' }\n | { readonly kind: 'changed' }\n | { readonly kind: 'invalid' }\n | { readonly kind: 'missing' };\n\nexport interface CalendarGateway {\n calendars(): Promise<readonly CalendarResponse[]>;\n calendar(calendarId: string): Promise<CalendarResponse | null>;\n saveCalendar(calendarId: string, name: string): Promise<CalendarResponse>;\n appointments(calendarId: string, range: CalendarRange, contact?: string, signal?: AbortSignal): Promise<readonly AppointmentResponse[]>;\n book(calendarId: string, request: AppointmentRequest): Promise<AppointmentOutcome>;\n update(calendarId: string, appointmentId: string, request: AppointmentRequest): Promise<AppointmentOutcome>;\n cancel(calendarId: string, appointmentId: string, reason: string | null): Promise<AppointmentOutcome>;\n setStatus(calendarId: string, appointmentId: string, status: AppointmentStatus): Promise<AppointmentOutcome>;\n remove(calendarId: string, appointmentId: string): Promise<void>;\n}\n\nexport class CalendarGatewayError extends Error {\n constructor(readonly status: number) {\n super(`The calendar answered ${status}.`);\n }\n}\n\nexport const CALENDAR_GATEWAY = new InjectionToken<CalendarGateway>('weaver-apps.calendar.gateway');\n\nexport function provideCalendarGateway(gateway: CalendarGateway): Provider {\n return { provide: CALENDAR_GATEWAY, useValue: gateway };\n}\n"],"mappings":";AAIA,IAAM,eAAN,MAAkB;CACR;CAER,KAAK,KAAgB;EACnB,KAAK,MAAM;CACb;CAEA,SAAM;EACJ,KAAK,MAAM;CACb;CAEA,IAAI,KAAE;EACJ,IAAI,CAAC,KAAK,KACR,MAAM,IAAI,MAAM,oCAAoC;EAEtD,OAAO,KAAK,IAAI;CAClB;CAEA,SAAS,MAAY;EACnB,KAAK,KAAK,gBAAgB,IAAI;CAChC;AACD;AAED,MAAa,eAAe,IAAI,aAAY;ACO5C,IAAa,uBAAb,cAA0C,MAAK;CACxB;CAArB,YAAqB,QAAc;EACjC,MAAM,yBAAyB,OAAM,EAAG;EADrB,KAAA,SAAA;CAErB;AACD;AAED,MAAa,mBAAmB,IAAI,eAAgC,8BAA8B;AAElG,SAAgB,uBAAuB,SAAwB;CAC7D,OAAO;EAAE,SAAS;EAAkB,UAAU;CAAO;AACvD"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"weaver-apps-calendar-calendar-page-DpdU_Vfy.mjs","names":[],"sources":["../../../../libs/calendar/src/lib/week.ts","../../../../libs/calendar/src/lib/views/active-locale.ts","../../../../libs/calendar/src/lib/api/models/appointmentStatus.ts","../../../../libs/calendar/src/lib/views/appointment-dialog.ts","../../../../libs/calendar/src/lib/views/appointment-dialog.html","../../../../libs/calendar/src/lib/views/appointment-cell.ts","../../../../libs/calendar/src/lib/views/appointment-cell.html","../../../../libs/calendar/src/lib/views/appointment-table.ts","../../../../libs/calendar/src/lib/views/appointment-table.html","../../../../libs/calendar/src/lib/views/calendar-page.ts","../../../../libs/calendar/src/lib/views/calendar-page.html"],"sourcesContent":["import type { CalendarRange } from './gateway/calendar-gateway';\n\nconst daysPerWeek = 7;\nconst sunday = 0;\nconst daysFromSundayToMonday = 6;\n\nexport function startOfWeek(date: Date): Date {\n const start = new Date(date.getFullYear(), date.getMonth(), date.getDate());\n const offset = start.getDay() === sunday ? daysFromSundayToMonday : start.getDay() - 1;\n start.setDate(start.getDate() - offset);\n return start;\n}\n\nexport function addWeeks(weekStart: Date, weeks: number): Date {\n const moved = new Date(weekStart);\n moved.setDate(moved.getDate() + weeks * daysPerWeek);\n return moved;\n}\n\nexport function weekRange(weekStart: Date): CalendarRange {\n return { from: weekStart, until: addWeeks(weekStart, 1) };\n}\n\nexport function localDate(value: Date): string {\n const pad = (part: number) => `${part}`.padStart(2, '0');\n return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;\n}\n\nexport function localTime(value: Date): string {\n const pad = (part: number) => `${part}`.padStart(2, '0');\n return `${pad(value.getHours())}:${pad(value.getMinutes())}`;\n}\n\nexport function combine(date: string, time: string): Date | null {\n const combined = new Date(`${date}T${time}`);\n return Number.isNaN(combined.getTime()) ? null : combined;\n}\n","import { inject, Signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { TranslocoService } from '@jsverse/transloco';\n\nexport function activeLocale(): Signal<string> {\n const transloco = inject(TranslocoService);\n return toSignal(transloco.langChanges$, { initialValue: transloco.getActiveLang() });\n}\n","/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Weaver Apps\n * OpenAPI spec version: 1.0.0\n */\n\n/**\n * Where an appointment stands.\n */\nexport type AppointmentStatus = typeof AppointmentStatus[keyof typeof AppointmentStatus];\n\n\nexport const AppointmentStatus = {\n scheduled: 'scheduled',\n confirmed: 'confirmed',\n completed: 'completed',\n noShow: 'noShow',\n cancelled: 'cancelled',\n} as const;\n","import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';\nimport { DialogRef } from '@loomweaver/plugin-sdk';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport { AppointmentStatus, type AppointmentRequest, type AppointmentResponse } from '../api/models';\nimport { AppointmentOutcome, CALENDAR_GATEWAY, ConflictingAppointment } from '../gateway/calendar-gateway';\nimport { combine, localDate, localTime } from '../week';\nimport { activeLocale } from './active-locale';\n\nexport interface AppointmentDialogData {\n readonly calendarId: string;\n readonly appointment: AppointmentResponse | null;\n readonly day: Date;\n}\n\ntype Problem =\n | { readonly kind: 'none' }\n | { readonly kind: 'message'; readonly key: string }\n | { readonly kind: 'conflict'; readonly conflicts: readonly ConflictingAppointment[] };\n\nconst defaultStart = '09:00';\nconst defaultEnd = '10:00';\nconst settableStatuses: readonly AppointmentStatus[] = [\n AppointmentStatus.scheduled,\n AppointmentStatus.confirmed,\n AppointmentStatus.completed,\n AppointmentStatus.noShow,\n];\n\n@Component({\n selector: 'wa-appointment-dialog',\n imports: [TranslocoPipe],\n templateUrl: './appointment-dialog.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AppointmentDialog {\n private readonly dialog = inject<DialogRef<boolean>>(DialogRef);\n private readonly gateway = inject(CALENDAR_GATEWAY);\n private readonly data = this.dialog.data as AppointmentDialogData;\n private existing = this.data.appointment;\n\n protected readonly locale = activeLocale();\n protected readonly editing = this.existing !== null;\n protected readonly statuses = settableStatuses;\n\n protected readonly title = signal(this.existing?.title ?? '');\n protected readonly date = signal(localDate(this.existing ? new Date(this.existing.start) : this.data.day));\n protected readonly start = signal(this.existing ? localTime(new Date(this.existing.start)) : defaultStart);\n protected readonly endDate = signal(localDate(this.existing ? new Date(this.existing.end) : this.data.day));\n protected readonly end = signal(this.existing ? localTime(new Date(this.existing.end)) : defaultEnd);\n protected readonly location = signal(this.existing?.location ?? '');\n protected readonly contactName = signal(this.existing?.contactName ?? '');\n protected readonly contactPhone = signal(this.existing?.contactPhone ?? '');\n protected readonly contactEmail = signal(this.existing?.contactEmail ?? '');\n protected readonly description = signal(this.existing?.description ?? '');\n protected readonly status = signal<AppointmentStatus>(this.existing?.status ?? AppointmentStatus.scheduled);\n protected readonly saving = signal(false);\n protected readonly problem = signal<Problem>({ kind: 'none' });\n private persisted = false;\n\n protected readonly problemKey = computed(() => {\n const problem = this.problem();\n return problem.kind === 'message' ? problem.key : '';\n });\n\n protected readonly conflictTimes = computed(() => {\n const problem = this.problem();\n if (problem.kind !== 'conflict') {\n return [];\n }\n const format = new Intl.DateTimeFormat(this.locale(), { dateStyle: 'short', timeStyle: 'short' });\n return problem.conflicts.map((conflict) => `${format.format(new Date(conflict.start))} – ${format.format(new Date(conflict.end))}`);\n });\n\n protected bind(target: { set(value: string): void }, event: Event): void {\n target.set((event.target as HTMLInputElement).value);\n }\n\n protected bindDate(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n if (this.endDate() === this.date()) {\n this.endDate.set(value);\n }\n this.date.set(value);\n }\n\n protected chooseStatus(event: Event): void {\n const chosen = settableStatuses.find((status) => status === (event.target as HTMLSelectElement).value);\n if (chosen) {\n this.status.set(chosen);\n }\n }\n\n protected close(): void {\n this.dialog.close(this.persisted);\n }\n\n protected async save(): Promise<void> {\n const request = this.request();\n if (!request) {\n return;\n }\n this.saving.set(true);\n try {\n const outcome = this.existing\n ? await this.gateway.update(this.data.calendarId, this.existing.id, request)\n : await this.gateway.book(this.data.calendarId, request);\n const final = await this.applyStatus(outcome);\n this.settle(final);\n } catch {\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.errors.save' });\n } finally {\n this.saving.set(false);\n }\n }\n\n private request(): AppointmentRequest | null {\n const startsAt = combine(this.date(), this.start());\n const endsAt = combine(this.endDate(), this.end());\n if (!this.title().trim()) {\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.titleRequired' });\n return null;\n }\n if (!startsAt || !endsAt || endsAt <= startsAt) {\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.periodInvalid' });\n return null;\n }\n return {\n title: this.title().trim(),\n start: startsAt.toISOString(),\n end: endsAt.toISOString(),\n description: this.optional(this.description()),\n location: this.optional(this.location()),\n contactName: this.optional(this.contactName()),\n contactPhone: this.optional(this.contactPhone()),\n contactEmail: this.optional(this.contactEmail()),\n };\n }\n\n private async applyStatus(outcome: AppointmentOutcome): Promise<AppointmentOutcome> {\n if (outcome.kind === 'saved') {\n this.existing = outcome.appointment;\n this.persisted = true;\n }\n if (outcome.kind !== 'saved' || outcome.appointment.status === this.status()) {\n return outcome;\n }\n return this.gateway.setStatus(this.data.calendarId, outcome.appointment.id, this.status());\n }\n\n private settle(outcome: AppointmentOutcome): void {\n switch (outcome.kind) {\n case 'saved':\n this.dialog.close(true);\n return;\n case 'conflict':\n this.problem.set({ kind: 'conflict', conflicts: outcome.conflicts });\n return;\n case 'closed':\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.closed' });\n return;\n case 'changed':\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.changed' });\n return;\n case 'invalid':\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.invalid' });\n return;\n default:\n this.problem.set({ kind: 'message', key: 'weaver-apps-calendar.dialog.missing' });\n }\n }\n\n private optional(value: string): string | null {\n return value.trim() ? value.trim() : null;\n }\n}\n","<form class=\"flex flex-col gap-4\" (submit)=\"$event.preventDefault(); save()\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.title' | transloco }}\n <input class=\"lw-field\" required [value]=\"title()\" (input)=\"bind(title, $event)\" />\n </label>\n\n <div class=\"grid grid-cols-2 gap-4 sm:grid-cols-4\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.date' | transloco }}\n <input class=\"lw-field\" type=\"date\" required [value]=\"date()\" (input)=\"bindDate($event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.start' | transloco }}\n <input class=\"lw-field\" type=\"time\" required [value]=\"start()\" (input)=\"bind(start, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.endDate' | transloco }}\n <input class=\"lw-field\" type=\"date\" required [value]=\"endDate()\" (input)=\"bind(endDate, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.end' | transloco }}\n <input class=\"lw-field\" type=\"time\" required [value]=\"end()\" (input)=\"bind(end, $event)\" />\n </label>\n </div>\n\n <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-3\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.contactName' | transloco }}\n <input class=\"lw-field\" [value]=\"contactName()\" (input)=\"bind(contactName, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.contactPhone' | transloco }}\n <input class=\"lw-field\" type=\"tel\" [value]=\"contactPhone()\" (input)=\"bind(contactPhone, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.contactEmail' | transloco }}\n <input class=\"lw-field\" type=\"email\" [value]=\"contactEmail()\" (input)=\"bind(contactEmail, $event)\" />\n </label>\n </div>\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.location' | transloco }}\n <input class=\"lw-field\" [value]=\"location()\" (input)=\"bind(location, $event)\" />\n </label>\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.description' | transloco }}\n <textarea class=\"lw-field\" rows=\"3\" [value]=\"description()\" (input)=\"bind(description, $event)\"></textarea>\n </label>\n\n @if (editing) {\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-calendar.fields.status' | transloco }}\n <select class=\"lw-field\" [value]=\"status()\" (change)=\"chooseStatus($event)\">\n @for (choice of statuses; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === status()\">{{ 'weaver-apps-calendar.status.' + choice | transloco }}</option>\n }\n </select>\n </label>\n }\n\n @switch (problem().kind) {\n @case ('message') {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ problemKey() | transloco }}</p>\n }\n @case ('conflict') {\n <div class=\"text-sm text-negative\" role=\"alert\">\n <p>{{ 'weaver-apps-calendar.dialog.conflict' | transloco }}</p>\n <ul class=\"list-disc pl-5\">\n @for (time of conflictTimes(); track time) {\n <li>{{ time }}</li>\n }\n </ul>\n </div>\n }\n }\n\n <div class=\"flex justify-end gap-2\">\n <button type=\"button\" class=\"lw-btn lw-btn--default\" (click)=\"close()\">{{ 'weaver-apps-calendar.dialog.cancel' | transloco }}</button>\n <button type=\"submit\" class=\"lw-btn lw-btn--primary\" [disabled]=\"saving()\">{{ 'weaver-apps-calendar.dialog.save' | transloco }}</button>\n </div>\n</form>\n","import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, computed, signal } from '@angular/core';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport type { ICellRendererAngularComp } from 'ag-grid-angular';\nimport type { ICellRendererParams } from 'ag-grid-community';\nimport type { AppointmentResponse } from '../api/models';\n\nexport type AppointmentCellKind = 'when' | 'title' | 'contact' | 'location' | 'status' | 'actions';\n\nexport interface AppointmentCellParams {\n readonly kind: AppointmentCellKind;\n readonly locale: () => string;\n readonly edit: (appointment: AppointmentResponse) => void;\n readonly cancel: (appointment: AppointmentResponse) => void;\n readonly remove: (appointment: AppointmentResponse) => void;\n}\n\ntype Params = ICellRendererParams<AppointmentResponse> & AppointmentCellParams;\n\n@Component({\n selector: 'wa-appointment-cell',\n imports: [TranslocoPipe],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './appointment-cell.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class AppointmentCell implements ICellRendererAngularComp {\n private params: Params | null = null;\n private readonly locale = signal('en');\n\n protected readonly kind = signal<AppointmentCellKind>('title');\n protected readonly appointment = signal<AppointmentResponse | null>(null);\n protected readonly cancelled = computed(() => this.appointment()?.status === 'cancelled');\n protected readonly when = computed(() => {\n const appointment = this.appointment();\n if (!appointment) {\n return { day: '', hours: '' };\n }\n const day = new Intl.DateTimeFormat(this.locale(), { weekday: 'short', day: '2-digit', month: '2-digit', year: 'numeric' });\n const time = new Intl.DateTimeFormat(this.locale(), { hour: '2-digit', minute: '2-digit' });\n return {\n day: day.format(new Date(appointment.start)),\n hours: `${time.format(new Date(appointment.start))} – ${time.format(new Date(appointment.end))}`,\n };\n });\n\n agInit(params: Params): void {\n this.params = params;\n this.kind.set(params.kind);\n this.appointment.set(params.data ?? null);\n this.locale.set(params.locale());\n }\n\n refresh(params: Params): boolean {\n this.agInit(params);\n return true;\n }\n\n protected edit(): void {\n this.hand(this.params?.edit);\n }\n\n protected cancel(): void {\n this.hand(this.params?.cancel);\n }\n\n protected remove(): void {\n this.hand(this.params?.remove);\n }\n\n private hand(to: ((appointment: AppointmentResponse) => void) | undefined): void {\n const appointment = this.appointment();\n if (appointment) {\n to?.(appointment);\n }\n }\n}\n","@if (appointment(); as appointment) {\n @switch (kind()) {\n @case ('when') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ when().day }}</span>\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ when().hours }}</span>\n }\n @case ('title') {\n <span class=\"font-medium\" [class.text-content]=\"!cancelled()\" [class.line-through]=\"cancelled()\">{{ appointment.title }}</span>\n }\n @case ('contact') {\n <span [class.line-through]=\"cancelled()\">{{ appointment.contactName ?? '–' }}</span>\n @if (appointment.contactPhone) {\n <span class=\"ml-2 text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.contactPhone }}</span>\n }\n }\n @case ('location') {\n <span class=\"text-content-faint\" [class.line-through]=\"cancelled()\">{{ appointment.location ?? '–' }}</span>\n }\n @case ('status') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"appointment-status\">{{ 'weaver-apps-calendar.status.' + appointment.status | transloco }}</span>\n }\n @case ('actions') {\n <span class=\"flex justify-end\">\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.actions.edit' | transloco\" (click)=\"edit()\">\n <lw-icon name=\"edit\" size=\"1rem\"></lw-icon>\n </button>\n @if (!cancelled()) {\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.actions.cancel' | transloco\" (click)=\"cancel()\">\n <lw-icon name=\"weaver-apps-calendar.cancel\" size=\"1rem\"></lw-icon>\n </button>\n }\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.actions.delete' | transloco\" (click)=\"remove()\">\n <lw-icon name=\"trash\" size=\"1rem\"></lw-icon>\n </button>\n </span>\n }\n }\n}\n","import { ChangeDetectionStrategy, Component, computed, effect, input, output, signal, type Signal } from '@angular/core';\nimport { translateObjectSignal } from '@jsverse/transloco';\nimport { DataGrid, ListFilter, type ListFilterParams } from '@weaver-apps/data-grid';\nimport type { ColDef, GridApi, GridOptions } from 'ag-grid-community';\nimport { AppointmentStatus, type AppointmentResponse } from '../api/models';\nimport { AppointmentCell, type AppointmentCellKind, type AppointmentCellParams } from './appointment-cell';\n\ntype Texts = Readonly<Record<string, string>>;\n\nexport const appointmentColumn = {\n when: 'start',\n title: 'title',\n contact: 'contact',\n location: 'location',\n status: 'status',\n actions: 'actions',\n} as const;\n\nconst actionsWidthPx = 136;\nconst whenMinWidthPx = 230;\nconst textMinWidthPx = 110;\nconst locationMinWidthPx = 90;\nconst statusMinWidthPx = 112;\n\nexport function byStart(left: string | undefined, right: string | undefined): number {\n return Date.parse(left ?? '') - Date.parse(right ?? '');\n}\n\nexport function statusOf(appointment: AppointmentResponse): string {\n return appointment.status;\n}\n\n@Component({\n selector: 'wa-appointment-table',\n imports: [DataGrid],\n templateUrl: './appointment-table.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: 'block h-full w-full' },\n})\nexport class AppointmentTable {\n private readonly table = translateObjectSignal('weaver-apps-calendar.table') as Signal<Texts>;\n private readonly statuses = translateObjectSignal('weaver-apps-calendar.status') as Signal<Texts>;\n private readonly api = signal<GridApi<AppointmentResponse> | null>(null);\n\n readonly appointments = input.required<readonly AppointmentResponse[]>();\n readonly locale = input.required<string>();\n\n readonly editAppointment = output<AppointmentResponse>();\n readonly cancelAppointment = output<AppointmentResponse>();\n readonly deleteAppointment = output<AppointmentResponse>();\n\n protected readonly translated = computed(() => typeof this.table() === 'object' && typeof this.table()['title'] === 'string');\n\n protected readonly empty = computed(() => ({ noRowsToShow: this.table()['empty'] ?? '' }));\n\n protected readonly options = computed<GridOptions<AppointmentResponse>>(() => ({\n rowModelType: 'clientSide',\n columnDefs: this.columns(this.table()),\n defaultColDef: { sortable: false, resizable: true, suppressHeaderMenuButton: true },\n getRowId: (params) => params.data.id,\n initialState: { sort: { sortModel: [{ colId: appointmentColumn.when, sort: 'asc' }] } },\n rowClassRules: { 'text-content-faint': (params) => params.data?.status === AppointmentStatus.cancelled },\n }));\n\n constructor() {\n effect(() => {\n const api = this.api();\n const appointments = this.appointments();\n if (api && !api.isDestroyed()) {\n api.setGridOption('rowData', [...appointments]);\n }\n });\n }\n\n protected ready(api: GridApi<AppointmentResponse>): void {\n this.api.set(api);\n }\n\n private columns(table: Texts): ColDef<AppointmentResponse>[] {\n return [\n { ...this.cell(appointmentColumn.when, 'when', table['when']), field: 'start', sortable: true, comparator: byStart, flex: 2, minWidth: whenMinWidthPx },\n { ...this.cell(appointmentColumn.title, 'title', table['title']), field: 'title', sortable: true, flex: 2, minWidth: textMinWidthPx },\n {\n ...this.cell(appointmentColumn.contact, 'contact', table['contact']),\n valueGetter: (params) => params.data?.contactName ?? '',\n sortable: true,\n flex: 2,\n minWidth: textMinWidthPx,\n },\n { ...this.cell(appointmentColumn.location, 'location', table['location']), field: 'location', sortable: true, flex: 1, minWidth: locationMinWidthPx },\n {\n ...this.cell(appointmentColumn.status, 'status', table['status']),\n field: 'status',\n sortable: true,\n flex: 1,\n minWidth: statusMinWidthPx,\n filter: ListFilter,\n filterParams: this.statusChoices(),\n },\n { ...this.cell(appointmentColumn.actions, 'actions', ''), width: actionsWidthPx, resizable: false },\n ];\n }\n\n private cell(colId: string, kind: AppointmentCellKind, headerName: string | undefined): ColDef<AppointmentResponse> {\n const params: AppointmentCellParams = {\n kind,\n locale: () => this.locale(),\n edit: (appointment) => this.editAppointment.emit(appointment),\n cancel: (appointment) => this.cancelAppointment.emit(appointment),\n remove: (appointment) => this.deleteAppointment.emit(appointment),\n };\n return { colId, headerName: headerName ?? '', cellRenderer: AppointmentCell, cellRendererParams: params };\n }\n\n private statusChoices(): ListFilterParams {\n const values = Object.values(AppointmentStatus);\n return {\n options: () => values.map((value) => ({ value, label: this.statuses()[value] ?? value })),\n rowValue: statusOf,\n };\n }\n}\n","@if (translated()) {\n <wa-data-grid [options]=\"options()\" [localeOverrides]=\"empty()\" (ready)=\"ready($event)\" />\n}\n","import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, computed, DestroyRef, inject, resource, signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute } from '@angular/router';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport { map } from 'rxjs';\nimport type { AppointmentResponse } from '../api/models';\nimport { CALENDAR_GATEWAY } from '../gateway/calendar-gateway';\nimport { calendarHost } from '../plugin/calendar-host';\nimport { addWeeks, startOfWeek, weekRange } from '../week';\nimport { activeLocale } from './active-locale';\nimport { AppointmentDialog, AppointmentDialogData } from './appointment-dialog';\nimport { AppointmentTable } from './appointment-table';\n\nconst searchDelayMs = 300;\n\n@Component({\n selector: 'wa-calendar-page',\n imports: [TranslocoPipe, AppointmentTable],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './calendar-page.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class CalendarPage {\n private readonly gateway = inject(CALENDAR_GATEWAY);\n private readonly route = inject(ActivatedRoute);\n private searchTimer?: ReturnType<typeof setTimeout>;\n\n protected readonly locale = activeLocale();\n protected readonly calendarId = toSignal(this.route.paramMap.pipe(map((params) => params.get('calendarId') ?? '')), {\n initialValue: this.route.snapshot.paramMap.get('calendarId') ?? '',\n });\n protected readonly weekStart = signal(startOfWeek(new Date()));\n protected readonly contact = signal('');\n\n protected readonly calendar = resource({\n params: () => this.calendarId(),\n loader: ({ params }) => this.gateway.calendar(params),\n });\n\n protected readonly appointments = resource({\n params: () => ({ calendarId: this.calendarId(), range: weekRange(this.weekStart()), contact: this.contact().trim() }),\n loader: ({ params, abortSignal }) =>\n this.gateway.appointments(params.calendarId, params.range, params.contact || undefined, abortSignal),\n });\n\n constructor() {\n inject(DestroyRef).onDestroy(() => clearTimeout(this.searchTimer));\n }\n\n protected readonly weekLabel = computed(() => {\n const range = weekRange(this.weekStart());\n const last = addWeeks(range.until, 0);\n last.setDate(last.getDate() - 1);\n const format = new Intl.DateTimeFormat(this.locale(), { day: '2-digit', month: '2-digit', year: 'numeric' });\n return `${format.format(range.from)} – ${format.format(last)}`;\n });\n\n protected previousWeek(): void {\n this.weekStart.update((start) => addWeeks(start, -1));\n }\n\n protected nextWeek(): void {\n this.weekStart.update((start) => addWeeks(start, 1));\n }\n\n protected thisWeek(): void {\n this.weekStart.set(startOfWeek(new Date()));\n }\n\n protected search(event: Event): void {\n const value = (event.target as HTMLInputElement).value;\n clearTimeout(this.searchTimer);\n this.searchTimer = setTimeout(() => this.contact.set(value), searchDelayMs);\n }\n\n protected add(): void {\n const today = new Date();\n const shown = weekRange(this.weekStart());\n const day = today >= shown.from && today < shown.until ? today : this.weekStart();\n this.openDialog({ calendarId: this.calendarId(), appointment: null, day }, 'weaver-apps-calendar.dialog.addTitle');\n }\n\n protected edit(appointment: AppointmentResponse): void {\n this.openDialog({ calendarId: this.calendarId(), appointment, day: new Date(appointment.start) }, 'weaver-apps-calendar.dialog.editTitle');\n }\n\n protected async cancel(appointment: AppointmentResponse): Promise<void> {\n const reason = await calendarHost.ui.prompt({\n title: 'weaver-apps-calendar.cancel.title',\n message: 'weaver-apps-calendar.cancel.message',\n confirmLabel: 'weaver-apps-calendar.cancel.confirm',\n tone: 'warning',\n });\n if (reason === null) {\n return;\n }\n await this.attempt(async () => {\n const outcome = await this.gateway.cancel(this.calendarId(), appointment.id, reason.trim() || null);\n this.report(outcome.kind === 'saved' ? 'weaver-apps-calendar.cancel.done' : 'weaver-apps-calendar.errors.save', outcome.kind === 'saved');\n });\n }\n\n protected async remove(appointment: AppointmentResponse): Promise<void> {\n const confirmed = await calendarHost.ui.confirm({\n title: 'weaver-apps-calendar.delete.title',\n message: 'weaver-apps-calendar.delete.message',\n confirmLabel: 'weaver-apps-calendar.delete.confirm',\n tone: 'danger',\n });\n if (!confirmed) {\n return;\n }\n await this.attempt(async () => {\n await this.gateway.remove(this.calendarId(), appointment.id);\n this.report('weaver-apps-calendar.delete.done', true);\n });\n }\n\n private async attempt(work: () => Promise<void>): Promise<void> {\n try {\n await work();\n } catch {\n this.report('weaver-apps-calendar.errors.save', false);\n }\n }\n\n private openDialog(data: AppointmentDialogData, title: string): void {\n const dialog = calendarHost.ui.open<boolean>(AppointmentDialog, { data, title, size: 'lg', dismiss: 'explicit' });\n void dialog.closed.then((saved) => {\n if (saved) {\n this.report('weaver-apps-calendar.dialog.saved', true);\n }\n });\n }\n\n private report(message: string, success: boolean): void {\n calendarHost.ui.toast({ message, kind: success ? 'success' : 'error' });\n this.appointments.reload();\n }\n}\n","<div class=\"flex h-full flex-col gap-4 p-6\">\n <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <h2 class=\"text-lg font-semibold text-content\">{{ calendar.value()?.name ?? ('weaver-apps-calendar.title' | transloco) }}</h2>\n <div class=\"flex flex-wrap items-center gap-2\">\n <input\n type=\"search\"\n class=\"lw-field w-56\"\n [attr.aria-label]=\"'weaver-apps-calendar.search.label' | transloco\"\n [attr.placeholder]=\"'weaver-apps-calendar.search.placeholder' | transloco\"\n (input)=\"search($event)\"\n />\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"add()\">\n <lw-icon name=\"add\" size=\"1rem\"></lw-icon>\n {{ 'weaver-apps-calendar.actions.add' | transloco }}\n </button>\n </div>\n </div>\n\n <div class=\"flex items-center gap-2\">\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.previous' | transloco\" (click)=\"previousWeek()\">\n <lw-icon name=\"weaver-apps-calendar.previous\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.previous' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <button type=\"button\" class=\"lw-btn lw-btn--default lw-btn--sm\" (click)=\"thisWeek()\">{{ 'weaver-apps-calendar.week.current' | transloco }}</button>\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'weaver-apps-calendar.week.next' | transloco\" (click)=\"nextWeek()\">\n <lw-icon name=\"weaver-apps-calendar.next\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'weaver-apps-calendar.week.next' | transloco\" position=\"bottom\"></lw-tooltip>\n </button>\n <span class=\"text-sm font-medium text-content\" data-testid=\"week-label\">{{ weekLabel() }}</span>\n </div>\n\n @if (appointments.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-calendar.errors.load' | transloco }}</p>\n } @else {\n <div class=\"min-h-80 flex-1\">\n <wa-appointment-table\n [appointments]=\"appointments.value() ?? []\"\n [locale]=\"locale()\"\n (editAppointment)=\"edit($event)\"\n (cancelAppointment)=\"cancel($event)\"\n (deleteAppointment)=\"remove($event)\"\n />\n </div>\n }\n</div>\n"],"mappings":"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"}
|
|
@@ -1,19 +0,0 @@
|
|
|
1
|
-
const calendarPluginId = "weaver-apps-calendar";
|
|
2
|
-
const calendarContributionIds = {
|
|
3
|
-
calendars: `${calendarPluginId}.calendars`,
|
|
4
|
-
calendar: `${calendarPluginId}.calendar`,
|
|
5
|
-
rail: `${calendarPluginId}.rail`
|
|
6
|
-
};
|
|
7
|
-
const calendarIconNames = {
|
|
8
|
-
calendar: `${calendarPluginId}.calendar`,
|
|
9
|
-
previous: `${calendarPluginId}.previous`,
|
|
10
|
-
next: `${calendarPluginId}.next`,
|
|
11
|
-
cancel: `${calendarPluginId}.cancel`
|
|
12
|
-
};
|
|
13
|
-
const calendarListPath = calendarPluginId;
|
|
14
|
-
function calendarPath(calendarId) {
|
|
15
|
-
return `${calendarListPath}/${calendarId}`;
|
|
16
|
-
}
|
|
17
|
-
export { calendarPluginId as a, calendarIconNames as i, calendarPath as n, calendarContributionIds as r, calendarListPath as t };
|
|
18
|
-
|
|
19
|
-
//# sourceMappingURL=weaver-apps-calendar-calendar-paths-qzIR-lDU.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"weaver-apps-calendar-calendar-paths-qzIR-lDU.mjs","names":[],"sources":["../../../../libs/calendar/src/lib/plugin/calendar-ids.ts","../../../../libs/calendar/src/lib/plugin/calendar-paths.ts"],"sourcesContent":["export const calendarPluginId = 'weaver-apps-calendar';\n\nexport const calendarContributionIds = {\n calendars: `${calendarPluginId}.calendars`,\n calendar: `${calendarPluginId}.calendar`,\n rail: `${calendarPluginId}.rail`,\n} as const;\n\nexport const calendarIconNames = {\n calendar: `${calendarPluginId}.calendar`,\n previous: `${calendarPluginId}.previous`,\n next: `${calendarPluginId}.next`,\n cancel: `${calendarPluginId}.cancel`,\n} as const;\n","import { calendarPluginId } from './calendar-ids';\n\nexport const calendarListPath = calendarPluginId;\n\nexport function calendarPath(calendarId: string): string {\n return `${calendarListPath}/${calendarId}`;\n}\n"],"mappings":"AAAA,MAAa,mBAAmB;AAEhC,MAAa,0BAA0B;CACrC,WAAW,GAAG,iBAAgB;CAC9B,UAAU,GAAG,iBAAgB;CAC7B,MAAM,GAAG,iBAAgB;AACjB;AAEV,MAAa,oBAAoB;CAC/B,UAAU,GAAG,iBAAgB;CAC7B,UAAU,GAAG,iBAAgB;CAC7B,MAAM,GAAG,iBAAgB;CACzB,QAAQ,GAAG,iBAAgB;AACnB;ACXV,MAAa,mBAAmB;AAEhC,SAAgB,aAAa,YAAkB;CAC7C,OAAO,GAAG,iBAAgB,GAAI;AAChC"}
|