@weaver-apps/calendar 0.1.3 → 0.3.0
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/README.md +42 -18
- package/fesm2022/weaver-apps-calendar-calendar-gateway-DUo797bn.mjs +35 -0
- package/fesm2022/weaver-apps-calendar-calendar-gateway-DUo797bn.mjs.map +1 -0
- package/fesm2022/weaver-apps-calendar-calendar-list-page-dgF-nnQF.mjs +74 -0
- package/fesm2022/weaver-apps-calendar-calendar-list-page-dgF-nnQF.mjs.map +1 -0
- package/fesm2022/weaver-apps-calendar-calendar-page-DpdU_Vfy.mjs +722 -0
- package/fesm2022/weaver-apps-calendar-calendar-page-DpdU_Vfy.mjs.map +1 -0
- package/fesm2022/weaver-apps-calendar-calendar-paths-qzIR-lDU.mjs +19 -0
- package/fesm2022/weaver-apps-calendar-calendar-paths-qzIR-lDU.mjs.map +1 -0
- package/fesm2022/weaver-apps-calendar.mjs +19 -640
- package/fesm2022/weaver-apps-calendar.mjs.map +1 -1
- package/package.json +10 -6
- package/types/weaver-apps-calendar.d.ts +7 -1
- /package/i18n/{calendar → weaver-apps-calendar}/de.json +0 -0
- /package/i18n/{calendar → weaver-apps-calendar}/en.json +0 -0
|
@@ -1,637 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
3
|
-
import { TranslocoPipe, TranslocoService } from "@jsverse/transloco";
|
|
4
|
-
import { toSignal } from "@angular/core/rxjs-interop";
|
|
5
|
-
import { ActivatedRoute } from "@angular/router";
|
|
6
|
-
import { map } from "rxjs";
|
|
7
|
-
import { DialogRef } from "@loomweaver/plugin-sdk";
|
|
8
|
-
var CalendarGatewayError = class extends Error {
|
|
9
|
-
status;
|
|
10
|
-
constructor(status) {
|
|
11
|
-
super(`The calendar answered ${status}.`);
|
|
12
|
-
this.status = status;
|
|
13
|
-
}
|
|
14
|
-
};
|
|
15
|
-
const CALENDAR_GATEWAY = new InjectionToken("weaver-apps.calendar.gateway");
|
|
16
|
-
function provideCalendarGateway(gateway) {
|
|
17
|
-
return {
|
|
18
|
-
provide: CALENDAR_GATEWAY,
|
|
19
|
-
useValue: gateway
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
var CalendarHost = class {
|
|
23
|
-
ctx;
|
|
24
|
-
bind(ctx) {
|
|
25
|
-
this.ctx = ctx;
|
|
26
|
-
}
|
|
27
|
-
unbind() {
|
|
28
|
-
this.ctx = void 0;
|
|
29
|
-
}
|
|
30
|
-
get ui() {
|
|
31
|
-
if (!this.ctx) throw new Error("The calendar plugin is not active.");
|
|
32
|
-
return this.ctx.ui;
|
|
33
|
-
}
|
|
34
|
-
navigate(path) {
|
|
35
|
-
this.ctx?.navigateContent(path);
|
|
36
|
-
}
|
|
37
|
-
};
|
|
38
|
-
const calendarHost = new CalendarHost();
|
|
39
|
-
const calendarListPath = "calendar";
|
|
40
|
-
function calendarPath(calendarId) {
|
|
41
|
-
return `${calendarListPath}/${calendarId}`;
|
|
42
|
-
}
|
|
43
|
-
var CalendarListPage = class CalendarListPage {
|
|
44
|
-
gateway = inject(CALENDAR_GATEWAY);
|
|
45
|
-
calendars = resource({
|
|
46
|
-
...ngDevMode ? { debugName: "calendars" } : /* istanbul ignore next */ {},
|
|
47
|
-
loader: () => this.gateway.calendars()
|
|
48
|
-
});
|
|
49
|
-
open(calendarId) {
|
|
50
|
-
calendarHost.navigate(calendarPath(calendarId));
|
|
51
|
-
}
|
|
52
|
-
async create() {
|
|
53
|
-
const name = await calendarHost.ui.prompt({
|
|
54
|
-
title: "calendar.calendars.create",
|
|
55
|
-
message: "calendar.calendars.createMessage",
|
|
56
|
-
confirmLabel: "calendar.calendars.createConfirm"
|
|
57
|
-
});
|
|
58
|
-
if (!name?.trim()) return;
|
|
59
|
-
try {
|
|
60
|
-
const calendar = await this.gateway.saveCalendar(crypto.randomUUID(), name.trim());
|
|
61
|
-
this.open(calendar.id);
|
|
62
|
-
} catch {
|
|
63
|
-
calendarHost.ui.toast({
|
|
64
|
-
message: "calendar.errors.create",
|
|
65
|
-
kind: "error"
|
|
66
|
-
});
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
70
|
-
minVersion: "12.0.0",
|
|
71
|
-
version: "22.2.3",
|
|
72
|
-
ngImport: i0,
|
|
73
|
-
type: CalendarListPage,
|
|
74
|
-
deps: [],
|
|
75
|
-
target: i0.ɵɵFactoryTarget.Component
|
|
76
|
-
});
|
|
77
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
78
|
-
minVersion: "17.0.0",
|
|
79
|
-
version: "22.2.3",
|
|
80
|
-
type: CalendarListPage,
|
|
81
|
-
isStandalone: true,
|
|
82
|
-
selector: "wa-calendar-list-page",
|
|
83
|
-
ngImport: i0,
|
|
84
|
-
template: "<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\">{{ 'calendar.calendars.title' | transloco }}</h2>\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"create()\">{{ 'calendar.calendars.create' | transloco }}</button>\n </div>\n @if (calendars.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ '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=\"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\">{{ 'calendar.calendars.empty' | transloco }}</li>\n }\n </ul>\n }\n</div>\n",
|
|
85
|
-
dependencies: [{
|
|
86
|
-
kind: "pipe",
|
|
87
|
-
type: TranslocoPipe,
|
|
88
|
-
name: "transloco"
|
|
89
|
-
}],
|
|
90
|
-
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
91
|
-
});
|
|
92
|
-
};
|
|
93
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
94
|
-
minVersion: "12.0.0",
|
|
95
|
-
version: "22.2.3",
|
|
96
|
-
ngImport: i0,
|
|
97
|
-
type: CalendarListPage,
|
|
98
|
-
decorators: [{
|
|
99
|
-
type: Component,
|
|
100
|
-
args: [{
|
|
101
|
-
selector: "wa-calendar-list-page",
|
|
102
|
-
imports: [TranslocoPipe],
|
|
103
|
-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
104
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
105
|
-
template: "<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\">{{ 'calendar.calendars.title' | transloco }}</h2>\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"create()\">{{ 'calendar.calendars.create' | transloco }}</button>\n </div>\n @if (calendars.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ '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=\"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\">{{ 'calendar.calendars.empty' | transloco }}</li>\n }\n </ul>\n }\n</div>\n"
|
|
106
|
-
}]
|
|
107
|
-
}]
|
|
108
|
-
});
|
|
109
|
-
const daysPerWeek = 7;
|
|
110
|
-
const sunday = 0;
|
|
111
|
-
const daysFromSundayToMonday = 6;
|
|
112
|
-
function startOfWeek(date) {
|
|
113
|
-
const start = new Date(date.getFullYear(), date.getMonth(), date.getDate());
|
|
114
|
-
const offset = start.getDay() === sunday ? daysFromSundayToMonday : start.getDay() - 1;
|
|
115
|
-
start.setDate(start.getDate() - offset);
|
|
116
|
-
return start;
|
|
117
|
-
}
|
|
118
|
-
function addWeeks(weekStart, weeks) {
|
|
119
|
-
const moved = new Date(weekStart);
|
|
120
|
-
moved.setDate(moved.getDate() + weeks * daysPerWeek);
|
|
121
|
-
return moved;
|
|
122
|
-
}
|
|
123
|
-
function weekRange(weekStart) {
|
|
124
|
-
return {
|
|
125
|
-
from: weekStart,
|
|
126
|
-
until: addWeeks(weekStart, 1)
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
function localDate(value) {
|
|
130
|
-
const pad = (part) => `${part}`.padStart(2, "0");
|
|
131
|
-
return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;
|
|
132
|
-
}
|
|
133
|
-
function localTime(value) {
|
|
134
|
-
const pad = (part) => `${part}`.padStart(2, "0");
|
|
135
|
-
return `${pad(value.getHours())}:${pad(value.getMinutes())}`;
|
|
136
|
-
}
|
|
137
|
-
function combine(date, time) {
|
|
138
|
-
const combined = /* @__PURE__ */ new Date(`${date}T${time}`);
|
|
139
|
-
return Number.isNaN(combined.getTime()) ? null : combined;
|
|
140
|
-
}
|
|
141
|
-
function activeLocale() {
|
|
142
|
-
const transloco = inject(TranslocoService);
|
|
143
|
-
return toSignal(transloco.langChanges$, { initialValue: transloco.getActiveLang() });
|
|
144
|
-
}
|
|
145
|
-
const AppointmentStatus = {
|
|
146
|
-
scheduled: "scheduled",
|
|
147
|
-
confirmed: "confirmed",
|
|
148
|
-
completed: "completed",
|
|
149
|
-
noShow: "noShow",
|
|
150
|
-
cancelled: "cancelled"
|
|
151
|
-
};
|
|
152
|
-
const defaultStart = "09:00";
|
|
153
|
-
const defaultEnd = "10:00";
|
|
154
|
-
const settableStatuses = [
|
|
155
|
-
AppointmentStatus.scheduled,
|
|
156
|
-
AppointmentStatus.confirmed,
|
|
157
|
-
AppointmentStatus.completed,
|
|
158
|
-
AppointmentStatus.noShow
|
|
159
|
-
];
|
|
160
|
-
var AppointmentDialog = class AppointmentDialog {
|
|
161
|
-
dialog = inject(DialogRef);
|
|
162
|
-
gateway = inject(CALENDAR_GATEWAY);
|
|
163
|
-
data = this.dialog.data;
|
|
164
|
-
existing = this.data.appointment;
|
|
165
|
-
locale = activeLocale();
|
|
166
|
-
editing = this.existing !== null;
|
|
167
|
-
statuses = settableStatuses;
|
|
168
|
-
title = signal(this.existing?.title ?? "", ...ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []);
|
|
169
|
-
date = signal(localDate(this.existing ? new Date(this.existing.start) : this.data.day), ...ngDevMode ? [{ debugName: "date" }] : /* istanbul ignore next */ []);
|
|
170
|
-
start = signal(this.existing ? localTime(new Date(this.existing.start)) : defaultStart, ...ngDevMode ? [{ debugName: "start" }] : /* istanbul ignore next */ []);
|
|
171
|
-
endDate = signal(localDate(this.existing ? new Date(this.existing.end) : this.data.day), ...ngDevMode ? [{ debugName: "endDate" }] : /* istanbul ignore next */ []);
|
|
172
|
-
end = signal(this.existing ? localTime(new Date(this.existing.end)) : defaultEnd, ...ngDevMode ? [{ debugName: "end" }] : /* istanbul ignore next */ []);
|
|
173
|
-
location = signal(this.existing?.location ?? "", ...ngDevMode ? [{ debugName: "location" }] : /* istanbul ignore next */ []);
|
|
174
|
-
contactName = signal(this.existing?.contactName ?? "", ...ngDevMode ? [{ debugName: "contactName" }] : /* istanbul ignore next */ []);
|
|
175
|
-
contactPhone = signal(this.existing?.contactPhone ?? "", ...ngDevMode ? [{ debugName: "contactPhone" }] : /* istanbul ignore next */ []);
|
|
176
|
-
contactEmail = signal(this.existing?.contactEmail ?? "", ...ngDevMode ? [{ debugName: "contactEmail" }] : /* istanbul ignore next */ []);
|
|
177
|
-
description = signal(this.existing?.description ?? "", ...ngDevMode ? [{ debugName: "description" }] : /* istanbul ignore next */ []);
|
|
178
|
-
status = signal(this.existing?.status ?? AppointmentStatus.scheduled, ...ngDevMode ? [{ debugName: "status" }] : /* istanbul ignore next */ []);
|
|
179
|
-
saving = signal(false, ...ngDevMode ? [{ debugName: "saving" }] : /* istanbul ignore next */ []);
|
|
180
|
-
problem = signal({ kind: "none" }, ...ngDevMode ? [{ debugName: "problem" }] : /* istanbul ignore next */ []);
|
|
181
|
-
persisted = false;
|
|
182
|
-
problemKey = computed(() => {
|
|
183
|
-
const problem = this.problem();
|
|
184
|
-
return problem.kind === "message" ? problem.key : "";
|
|
185
|
-
}, ...ngDevMode ? [{ debugName: "problemKey" }] : /* istanbul ignore next */ []);
|
|
186
|
-
conflictTimes = computed(() => {
|
|
187
|
-
const problem = this.problem();
|
|
188
|
-
if (problem.kind !== "conflict") return [];
|
|
189
|
-
const format = new Intl.DateTimeFormat(this.locale(), {
|
|
190
|
-
dateStyle: "short",
|
|
191
|
-
timeStyle: "short"
|
|
192
|
-
});
|
|
193
|
-
return problem.conflicts.map((conflict) => `${format.format(new Date(conflict.start))} – ${format.format(new Date(conflict.end))}`);
|
|
194
|
-
}, ...ngDevMode ? [{ debugName: "conflictTimes" }] : /* istanbul ignore next */ []);
|
|
195
|
-
bind(target, event) {
|
|
196
|
-
target.set(event.target.value);
|
|
197
|
-
}
|
|
198
|
-
bindDate(event) {
|
|
199
|
-
const value = event.target.value;
|
|
200
|
-
if (this.endDate() === this.date()) this.endDate.set(value);
|
|
201
|
-
this.date.set(value);
|
|
202
|
-
}
|
|
203
|
-
chooseStatus(event) {
|
|
204
|
-
const chosen = settableStatuses.find((status) => status === event.target.value);
|
|
205
|
-
if (chosen) this.status.set(chosen);
|
|
206
|
-
}
|
|
207
|
-
close() {
|
|
208
|
-
this.dialog.close(this.persisted);
|
|
209
|
-
}
|
|
210
|
-
async save() {
|
|
211
|
-
const request = this.request();
|
|
212
|
-
if (!request) return;
|
|
213
|
-
this.saving.set(true);
|
|
214
|
-
try {
|
|
215
|
-
const outcome = this.existing ? await this.gateway.update(this.data.calendarId, this.existing.id, request) : await this.gateway.book(this.data.calendarId, request);
|
|
216
|
-
const final = await this.applyStatus(outcome);
|
|
217
|
-
this.settle(final);
|
|
218
|
-
} catch {
|
|
219
|
-
this.problem.set({
|
|
220
|
-
kind: "message",
|
|
221
|
-
key: "calendar.errors.save"
|
|
222
|
-
});
|
|
223
|
-
} finally {
|
|
224
|
-
this.saving.set(false);
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
request() {
|
|
228
|
-
const startsAt = combine(this.date(), this.start());
|
|
229
|
-
const endsAt = combine(this.endDate(), this.end());
|
|
230
|
-
if (!this.title().trim()) {
|
|
231
|
-
this.problem.set({
|
|
232
|
-
kind: "message",
|
|
233
|
-
key: "calendar.dialog.titleRequired"
|
|
234
|
-
});
|
|
235
|
-
return null;
|
|
236
|
-
}
|
|
237
|
-
if (!startsAt || !endsAt || endsAt <= startsAt) {
|
|
238
|
-
this.problem.set({
|
|
239
|
-
kind: "message",
|
|
240
|
-
key: "calendar.dialog.periodInvalid"
|
|
241
|
-
});
|
|
242
|
-
return null;
|
|
243
|
-
}
|
|
244
|
-
return {
|
|
245
|
-
title: this.title().trim(),
|
|
246
|
-
start: startsAt.toISOString(),
|
|
247
|
-
end: endsAt.toISOString(),
|
|
248
|
-
description: this.optional(this.description()),
|
|
249
|
-
location: this.optional(this.location()),
|
|
250
|
-
contactName: this.optional(this.contactName()),
|
|
251
|
-
contactPhone: this.optional(this.contactPhone()),
|
|
252
|
-
contactEmail: this.optional(this.contactEmail())
|
|
253
|
-
};
|
|
254
|
-
}
|
|
255
|
-
async applyStatus(outcome) {
|
|
256
|
-
if (outcome.kind === "saved") {
|
|
257
|
-
this.existing = outcome.appointment;
|
|
258
|
-
this.persisted = true;
|
|
259
|
-
}
|
|
260
|
-
if (outcome.kind !== "saved" || outcome.appointment.status === this.status()) return outcome;
|
|
261
|
-
return this.gateway.setStatus(this.data.calendarId, outcome.appointment.id, this.status());
|
|
262
|
-
}
|
|
263
|
-
settle(outcome) {
|
|
264
|
-
switch (outcome.kind) {
|
|
265
|
-
case "saved":
|
|
266
|
-
this.dialog.close(true);
|
|
267
|
-
return;
|
|
268
|
-
case "conflict":
|
|
269
|
-
this.problem.set({
|
|
270
|
-
kind: "conflict",
|
|
271
|
-
conflicts: outcome.conflicts
|
|
272
|
-
});
|
|
273
|
-
return;
|
|
274
|
-
case "closed":
|
|
275
|
-
this.problem.set({
|
|
276
|
-
kind: "message",
|
|
277
|
-
key: "calendar.dialog.closed"
|
|
278
|
-
});
|
|
279
|
-
return;
|
|
280
|
-
case "changed":
|
|
281
|
-
this.problem.set({
|
|
282
|
-
kind: "message",
|
|
283
|
-
key: "calendar.dialog.changed"
|
|
284
|
-
});
|
|
285
|
-
return;
|
|
286
|
-
case "invalid":
|
|
287
|
-
this.problem.set({
|
|
288
|
-
kind: "message",
|
|
289
|
-
key: "calendar.dialog.invalid"
|
|
290
|
-
});
|
|
291
|
-
return;
|
|
292
|
-
default: this.problem.set({
|
|
293
|
-
kind: "message",
|
|
294
|
-
key: "calendar.dialog.missing"
|
|
295
|
-
});
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
optional(value) {
|
|
299
|
-
return value.trim() ? value.trim() : null;
|
|
300
|
-
}
|
|
301
|
-
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
302
|
-
minVersion: "12.0.0",
|
|
303
|
-
version: "22.2.3",
|
|
304
|
-
ngImport: i0,
|
|
305
|
-
type: AppointmentDialog,
|
|
306
|
-
deps: [],
|
|
307
|
-
target: i0.ɵɵFactoryTarget.Component
|
|
308
|
-
});
|
|
309
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
310
|
-
minVersion: "17.0.0",
|
|
311
|
-
version: "22.2.3",
|
|
312
|
-
type: AppointmentDialog,
|
|
313
|
-
isStandalone: true,
|
|
314
|
-
selector: "wa-appointment-dialog",
|
|
315
|
-
ngImport: i0,
|
|
316
|
-
template: "<form class=\"flex flex-col gap-4\" (submit)=\"$event.preventDefault(); save()\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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()\">{{ '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>{{ '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()\">{{ 'calendar.dialog.cancel' | transloco }}</button>\n <button type=\"submit\" class=\"lw-btn lw-btn--primary\" [disabled]=\"saving()\">{{ 'calendar.dialog.save' | transloco }}</button>\n </div>\n</form>\n",
|
|
317
|
-
dependencies: [{
|
|
318
|
-
kind: "pipe",
|
|
319
|
-
type: TranslocoPipe,
|
|
320
|
-
name: "transloco"
|
|
321
|
-
}],
|
|
322
|
-
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
323
|
-
});
|
|
324
|
-
};
|
|
325
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
326
|
-
minVersion: "12.0.0",
|
|
327
|
-
version: "22.2.3",
|
|
328
|
-
ngImport: i0,
|
|
329
|
-
type: AppointmentDialog,
|
|
330
|
-
decorators: [{
|
|
331
|
-
type: Component,
|
|
332
|
-
args: [{
|
|
333
|
-
selector: "wa-appointment-dialog",
|
|
334
|
-
imports: [TranslocoPipe],
|
|
335
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
336
|
-
template: "<form class=\"flex flex-col gap-4\" (submit)=\"$event.preventDefault(); save()\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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 {{ '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()\">{{ '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>{{ '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()\">{{ 'calendar.dialog.cancel' | transloco }}</button>\n <button type=\"submit\" class=\"lw-btn lw-btn--primary\" [disabled]=\"saving()\">{{ 'calendar.dialog.save' | transloco }}</button>\n </div>\n</form>\n"
|
|
337
|
-
}]
|
|
338
|
-
}]
|
|
339
|
-
});
|
|
340
|
-
var AppointmentTable = class AppointmentTable {
|
|
341
|
-
appointments = input.required(...ngDevMode ? [{ debugName: "appointments" }] : /* istanbul ignore next */ []);
|
|
342
|
-
locale = input.required(...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
|
|
343
|
-
editAppointment = output();
|
|
344
|
-
cancelAppointment = output();
|
|
345
|
-
deleteAppointment = output();
|
|
346
|
-
rows = computed(() => {
|
|
347
|
-
const day = new Intl.DateTimeFormat(this.locale(), {
|
|
348
|
-
weekday: "short",
|
|
349
|
-
day: "2-digit",
|
|
350
|
-
month: "2-digit",
|
|
351
|
-
year: "numeric"
|
|
352
|
-
});
|
|
353
|
-
const time = new Intl.DateTimeFormat(this.locale(), {
|
|
354
|
-
hour: "2-digit",
|
|
355
|
-
minute: "2-digit"
|
|
356
|
-
});
|
|
357
|
-
return this.appointments().map((appointment) => ({
|
|
358
|
-
appointment,
|
|
359
|
-
day: day.format(new Date(appointment.start)),
|
|
360
|
-
hours: `${time.format(new Date(appointment.start))} – ${time.format(new Date(appointment.end))}`,
|
|
361
|
-
cancelled: appointment.status === "cancelled",
|
|
362
|
-
statusKey: `calendar.status.${appointment.status}`
|
|
363
|
-
}));
|
|
364
|
-
}, ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
|
|
365
|
-
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
366
|
-
minVersion: "12.0.0",
|
|
367
|
-
version: "22.2.3",
|
|
368
|
-
ngImport: i0,
|
|
369
|
-
type: AppointmentTable,
|
|
370
|
-
deps: [],
|
|
371
|
-
target: i0.ɵɵFactoryTarget.Component
|
|
372
|
-
});
|
|
373
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
374
|
-
minVersion: "17.0.0",
|
|
375
|
-
version: "22.2.3",
|
|
376
|
-
type: AppointmentTable,
|
|
377
|
-
isStandalone: true,
|
|
378
|
-
selector: "wa-appointment-table",
|
|
379
|
-
inputs: {
|
|
380
|
-
appointments: {
|
|
381
|
-
classPropertyName: "appointments",
|
|
382
|
-
publicName: "appointments",
|
|
383
|
-
isSignal: true,
|
|
384
|
-
isRequired: true,
|
|
385
|
-
transformFunction: null
|
|
386
|
-
},
|
|
387
|
-
locale: {
|
|
388
|
-
classPropertyName: "locale",
|
|
389
|
-
publicName: "locale",
|
|
390
|
-
isSignal: true,
|
|
391
|
-
isRequired: true,
|
|
392
|
-
transformFunction: null
|
|
393
|
-
}
|
|
394
|
-
},
|
|
395
|
-
outputs: {
|
|
396
|
-
editAppointment: "editAppointment",
|
|
397
|
-
cancelAppointment: "cancelAppointment",
|
|
398
|
-
deleteAppointment: "deleteAppointment"
|
|
399
|
-
},
|
|
400
|
-
ngImport: i0,
|
|
401
|
-
template: "<table class=\"w-full border-collapse text-sm\">\n <thead class=\"border-y border-border bg-surface-raised\">\n <tr>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'calendar.table.when' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'calendar.table.title' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'calendar.table.contact' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'calendar.table.location' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'calendar.table.status' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2\"><span class=\"sr-only\">{{ 'calendar.table.actions' | transloco }}</span></th>\n </tr>\n </thead>\n <tbody>\n @for (row of rows(); track row.appointment.id) {\n <tr class=\"border-b border-border\" [class.text-content-faint]=\"row.cancelled\" data-testid=\"appointment-row\">\n <td class=\"px-4 py-3 whitespace-nowrap\" [class.line-through]=\"row.cancelled\">\n <div class=\"font-medium text-content\">{{ row.day }}</div>\n <div class=\"text-content-faint\">{{ row.hours }}</div>\n </td>\n <td class=\"px-4 py-3 font-medium text-content\" [class.line-through]=\"row.cancelled\">{{ row.appointment.title }}</td>\n <td class=\"px-4 py-3\" [class.line-through]=\"row.cancelled\">\n <div class=\"text-content\">{{ row.appointment.contactName ?? '–' }}</div>\n @if (row.appointment.contactPhone) {\n <div class=\"text-content-faint\">{{ row.appointment.contactPhone }}</div>\n }\n </td>\n <td class=\"px-4 py-3 text-content-faint\" [class.line-through]=\"row.cancelled\">{{ row.appointment.location ?? '–' }}</td>\n <td class=\"px-4 py-3\">\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"appointment-status\">{{ row.statusKey | transloco }}</span>\n </td>\n <td class=\"px-4 py-3 text-right whitespace-nowrap\">\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'calendar.actions.edit' | transloco\" (click)=\"editAppointment.emit(row.appointment)\">\n <lw-icon name=\"edit\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'calendar.actions.edit' | transloco\" position=\"top\"></lw-tooltip>\n </button>\n @if (!row.cancelled) {\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'calendar.actions.cancel' | transloco\" (click)=\"cancelAppointment.emit(row.appointment)\">\n <lw-icon name=\"calendarCancel\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'calendar.actions.cancel' | transloco\" position=\"top\"></lw-tooltip>\n </button>\n }\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'calendar.actions.delete' | transloco\" (click)=\"deleteAppointment.emit(row.appointment)\">\n <lw-icon name=\"trash\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'calendar.actions.delete' | transloco\" position=\"top\"></lw-tooltip>\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"6\" class=\"px-4 py-8 text-center text-content-faint\">{{ 'calendar.table.empty' | transloco }}</td>\n </tr>\n }\n </tbody>\n</table>\n",
|
|
402
|
-
dependencies: [{
|
|
403
|
-
kind: "pipe",
|
|
404
|
-
type: TranslocoPipe,
|
|
405
|
-
name: "transloco"
|
|
406
|
-
}],
|
|
407
|
-
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
408
|
-
});
|
|
409
|
-
};
|
|
410
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
411
|
-
minVersion: "12.0.0",
|
|
412
|
-
version: "22.2.3",
|
|
413
|
-
ngImport: i0,
|
|
414
|
-
type: AppointmentTable,
|
|
415
|
-
decorators: [{
|
|
416
|
-
type: Component,
|
|
417
|
-
args: [{
|
|
418
|
-
selector: "wa-appointment-table",
|
|
419
|
-
imports: [TranslocoPipe],
|
|
420
|
-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
421
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
422
|
-
template: "<table class=\"w-full border-collapse text-sm\">\n <thead class=\"border-y border-border bg-surface-raised\">\n <tr>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'calendar.table.when' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'calendar.table.title' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'calendar.table.contact' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'calendar.table.location' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'calendar.table.status' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2\"><span class=\"sr-only\">{{ 'calendar.table.actions' | transloco }}</span></th>\n </tr>\n </thead>\n <tbody>\n @for (row of rows(); track row.appointment.id) {\n <tr class=\"border-b border-border\" [class.text-content-faint]=\"row.cancelled\" data-testid=\"appointment-row\">\n <td class=\"px-4 py-3 whitespace-nowrap\" [class.line-through]=\"row.cancelled\">\n <div class=\"font-medium text-content\">{{ row.day }}</div>\n <div class=\"text-content-faint\">{{ row.hours }}</div>\n </td>\n <td class=\"px-4 py-3 font-medium text-content\" [class.line-through]=\"row.cancelled\">{{ row.appointment.title }}</td>\n <td class=\"px-4 py-3\" [class.line-through]=\"row.cancelled\">\n <div class=\"text-content\">{{ row.appointment.contactName ?? '–' }}</div>\n @if (row.appointment.contactPhone) {\n <div class=\"text-content-faint\">{{ row.appointment.contactPhone }}</div>\n }\n </td>\n <td class=\"px-4 py-3 text-content-faint\" [class.line-through]=\"row.cancelled\">{{ row.appointment.location ?? '–' }}</td>\n <td class=\"px-4 py-3\">\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"appointment-status\">{{ row.statusKey | transloco }}</span>\n </td>\n <td class=\"px-4 py-3 text-right whitespace-nowrap\">\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'calendar.actions.edit' | transloco\" (click)=\"editAppointment.emit(row.appointment)\">\n <lw-icon name=\"edit\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'calendar.actions.edit' | transloco\" position=\"top\"></lw-tooltip>\n </button>\n @if (!row.cancelled) {\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'calendar.actions.cancel' | transloco\" (click)=\"cancelAppointment.emit(row.appointment)\">\n <lw-icon name=\"calendarCancel\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'calendar.actions.cancel' | transloco\" position=\"top\"></lw-tooltip>\n </button>\n }\n <button type=\"button\" class=\"relative lw-btn lw-btn--ghost lw-btn--sm lw-btn--icon\" [attr.aria-label]=\"'calendar.actions.delete' | transloco\" (click)=\"deleteAppointment.emit(row.appointment)\">\n <lw-icon name=\"trash\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'calendar.actions.delete' | transloco\" position=\"top\"></lw-tooltip>\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"6\" class=\"px-4 py-8 text-center text-content-faint\">{{ 'calendar.table.empty' | transloco }}</td>\n </tr>\n }\n </tbody>\n</table>\n"
|
|
423
|
-
}]
|
|
424
|
-
}],
|
|
425
|
-
propDecorators: {
|
|
426
|
-
appointments: [{
|
|
427
|
-
type: i0.Input,
|
|
428
|
-
args: [{
|
|
429
|
-
isSignal: true,
|
|
430
|
-
alias: "appointments",
|
|
431
|
-
required: true
|
|
432
|
-
}]
|
|
433
|
-
}],
|
|
434
|
-
locale: [{
|
|
435
|
-
type: i0.Input,
|
|
436
|
-
args: [{
|
|
437
|
-
isSignal: true,
|
|
438
|
-
alias: "locale",
|
|
439
|
-
required: true
|
|
440
|
-
}]
|
|
441
|
-
}],
|
|
442
|
-
editAppointment: [{
|
|
443
|
-
type: i0.Output,
|
|
444
|
-
args: ["editAppointment"]
|
|
445
|
-
}],
|
|
446
|
-
cancelAppointment: [{
|
|
447
|
-
type: i0.Output,
|
|
448
|
-
args: ["cancelAppointment"]
|
|
449
|
-
}],
|
|
450
|
-
deleteAppointment: [{
|
|
451
|
-
type: i0.Output,
|
|
452
|
-
args: ["deleteAppointment"]
|
|
453
|
-
}]
|
|
454
|
-
}
|
|
455
|
-
});
|
|
456
|
-
const searchDelayMs = 300;
|
|
457
|
-
var CalendarPage = class CalendarPage {
|
|
458
|
-
gateway = inject(CALENDAR_GATEWAY);
|
|
459
|
-
route = inject(ActivatedRoute);
|
|
460
|
-
searchTimer;
|
|
461
|
-
locale = activeLocale();
|
|
462
|
-
calendarId = toSignal(this.route.paramMap.pipe(map((params) => params.get("calendarId") ?? "")), { initialValue: this.route.snapshot.paramMap.get("calendarId") ?? "" });
|
|
463
|
-
weekStart = signal(startOfWeek(/* @__PURE__ */ new Date()), ...ngDevMode ? [{ debugName: "weekStart" }] : /* istanbul ignore next */ []);
|
|
464
|
-
contact = signal("", ...ngDevMode ? [{ debugName: "contact" }] : /* istanbul ignore next */ []);
|
|
465
|
-
calendar = resource({
|
|
466
|
-
...ngDevMode ? { debugName: "calendar" } : /* istanbul ignore next */ {},
|
|
467
|
-
params: () => this.calendarId(),
|
|
468
|
-
loader: ({ params }) => this.gateway.calendar(params)
|
|
469
|
-
});
|
|
470
|
-
appointments = resource({
|
|
471
|
-
...ngDevMode ? { debugName: "appointments" } : /* istanbul ignore next */ {},
|
|
472
|
-
params: () => ({
|
|
473
|
-
calendarId: this.calendarId(),
|
|
474
|
-
range: weekRange(this.weekStart()),
|
|
475
|
-
contact: this.contact().trim()
|
|
476
|
-
}),
|
|
477
|
-
loader: ({ params, abortSignal }) => this.gateway.appointments(params.calendarId, params.range, params.contact || void 0, abortSignal)
|
|
478
|
-
});
|
|
479
|
-
constructor() {
|
|
480
|
-
inject(DestroyRef).onDestroy(() => clearTimeout(this.searchTimer));
|
|
481
|
-
}
|
|
482
|
-
weekLabel = computed(() => {
|
|
483
|
-
const range = weekRange(this.weekStart());
|
|
484
|
-
const last = addWeeks(range.until, 0);
|
|
485
|
-
last.setDate(last.getDate() - 1);
|
|
486
|
-
const format = new Intl.DateTimeFormat(this.locale(), {
|
|
487
|
-
day: "2-digit",
|
|
488
|
-
month: "2-digit",
|
|
489
|
-
year: "numeric"
|
|
490
|
-
});
|
|
491
|
-
return `${format.format(range.from)} – ${format.format(last)}`;
|
|
492
|
-
}, ...ngDevMode ? [{ debugName: "weekLabel" }] : /* istanbul ignore next */ []);
|
|
493
|
-
previousWeek() {
|
|
494
|
-
this.weekStart.update((start) => addWeeks(start, -1));
|
|
495
|
-
}
|
|
496
|
-
nextWeek() {
|
|
497
|
-
this.weekStart.update((start) => addWeeks(start, 1));
|
|
498
|
-
}
|
|
499
|
-
thisWeek() {
|
|
500
|
-
this.weekStart.set(startOfWeek(/* @__PURE__ */ new Date()));
|
|
501
|
-
}
|
|
502
|
-
search(event) {
|
|
503
|
-
const value = event.target.value;
|
|
504
|
-
clearTimeout(this.searchTimer);
|
|
505
|
-
this.searchTimer = setTimeout(() => this.contact.set(value), searchDelayMs);
|
|
506
|
-
}
|
|
507
|
-
add() {
|
|
508
|
-
const today = /* @__PURE__ */ new Date();
|
|
509
|
-
const shown = weekRange(this.weekStart());
|
|
510
|
-
const day = today >= shown.from && today < shown.until ? today : this.weekStart();
|
|
511
|
-
this.openDialog({
|
|
512
|
-
calendarId: this.calendarId(),
|
|
513
|
-
appointment: null,
|
|
514
|
-
day
|
|
515
|
-
}, "calendar.dialog.addTitle");
|
|
516
|
-
}
|
|
517
|
-
edit(appointment) {
|
|
518
|
-
this.openDialog({
|
|
519
|
-
calendarId: this.calendarId(),
|
|
520
|
-
appointment,
|
|
521
|
-
day: new Date(appointment.start)
|
|
522
|
-
}, "calendar.dialog.editTitle");
|
|
523
|
-
}
|
|
524
|
-
async cancel(appointment) {
|
|
525
|
-
const reason = await calendarHost.ui.prompt({
|
|
526
|
-
title: "calendar.cancel.title",
|
|
527
|
-
message: "calendar.cancel.message",
|
|
528
|
-
confirmLabel: "calendar.cancel.confirm",
|
|
529
|
-
tone: "warning"
|
|
530
|
-
});
|
|
531
|
-
if (reason === null) return;
|
|
532
|
-
await this.attempt(async () => {
|
|
533
|
-
const outcome = await this.gateway.cancel(this.calendarId(), appointment.id, reason.trim() || null);
|
|
534
|
-
this.report(outcome.kind === "saved" ? "calendar.cancel.done" : "calendar.errors.save", outcome.kind === "saved");
|
|
535
|
-
});
|
|
536
|
-
}
|
|
537
|
-
async remove(appointment) {
|
|
538
|
-
if (!await calendarHost.ui.confirm({
|
|
539
|
-
title: "calendar.delete.title",
|
|
540
|
-
message: "calendar.delete.message",
|
|
541
|
-
confirmLabel: "calendar.delete.confirm",
|
|
542
|
-
tone: "danger"
|
|
543
|
-
})) return;
|
|
544
|
-
await this.attempt(async () => {
|
|
545
|
-
await this.gateway.remove(this.calendarId(), appointment.id);
|
|
546
|
-
this.report("calendar.delete.done", true);
|
|
547
|
-
});
|
|
548
|
-
}
|
|
549
|
-
async attempt(work) {
|
|
550
|
-
try {
|
|
551
|
-
await work();
|
|
552
|
-
} catch {
|
|
553
|
-
this.report("calendar.errors.save", false);
|
|
554
|
-
}
|
|
555
|
-
}
|
|
556
|
-
openDialog(data, title) {
|
|
557
|
-
calendarHost.ui.open(AppointmentDialog, {
|
|
558
|
-
data,
|
|
559
|
-
title,
|
|
560
|
-
size: "lg",
|
|
561
|
-
dismiss: "explicit"
|
|
562
|
-
}).closed.then((saved) => {
|
|
563
|
-
if (saved) this.report("calendar.dialog.saved", true);
|
|
564
|
-
});
|
|
565
|
-
}
|
|
566
|
-
report(message, success) {
|
|
567
|
-
calendarHost.ui.toast({
|
|
568
|
-
message,
|
|
569
|
-
kind: success ? "success" : "error"
|
|
570
|
-
});
|
|
571
|
-
this.appointments.reload();
|
|
572
|
-
}
|
|
573
|
-
static ɵfac = i0.ɵɵngDeclareFactory({
|
|
574
|
-
minVersion: "12.0.0",
|
|
575
|
-
version: "22.2.3",
|
|
576
|
-
ngImport: i0,
|
|
577
|
-
type: CalendarPage,
|
|
578
|
-
deps: [],
|
|
579
|
-
target: i0.ɵɵFactoryTarget.Component
|
|
580
|
-
});
|
|
581
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({
|
|
582
|
-
minVersion: "17.0.0",
|
|
583
|
-
version: "22.2.3",
|
|
584
|
-
type: CalendarPage,
|
|
585
|
-
isStandalone: true,
|
|
586
|
-
selector: "wa-calendar-page",
|
|
587
|
-
ngImport: i0,
|
|
588
|
-
template: "<div class=\"flex h-full flex-col gap-4 overflow-auto 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 ?? ('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]=\"'calendar.search.label' | transloco\"\n [attr.placeholder]=\"'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 {{ '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]=\"'calendar.week.previous' | transloco\" (click)=\"previousWeek()\">\n <lw-icon name=\"calendarPrevious\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'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()\">{{ '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]=\"'calendar.week.next' | transloco\" (click)=\"nextWeek()\">\n <lw-icon name=\"calendarNext\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'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\">{{ 'calendar.errors.load' | transloco }}</p>\n } @else {\n <div class=\"overflow-hidden rounded-xl border border-border bg-surface\">\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",
|
|
589
|
-
dependencies: [{
|
|
590
|
-
kind: "component",
|
|
591
|
-
type: AppointmentTable,
|
|
592
|
-
selector: "wa-appointment-table",
|
|
593
|
-
inputs: ["appointments", "locale"],
|
|
594
|
-
outputs: [
|
|
595
|
-
"editAppointment",
|
|
596
|
-
"cancelAppointment",
|
|
597
|
-
"deleteAppointment"
|
|
598
|
-
]
|
|
599
|
-
}, {
|
|
600
|
-
kind: "pipe",
|
|
601
|
-
type: TranslocoPipe,
|
|
602
|
-
name: "transloco"
|
|
603
|
-
}],
|
|
604
|
-
changeDetection: i0.ChangeDetectionStrategy.OnPush
|
|
605
|
-
});
|
|
606
|
-
};
|
|
607
|
-
i0.ɵɵngDeclareClassMetadata({
|
|
608
|
-
minVersion: "12.0.0",
|
|
609
|
-
version: "22.2.3",
|
|
610
|
-
ngImport: i0,
|
|
611
|
-
type: CalendarPage,
|
|
612
|
-
decorators: [{
|
|
613
|
-
type: Component,
|
|
614
|
-
args: [{
|
|
615
|
-
selector: "wa-calendar-page",
|
|
616
|
-
imports: [TranslocoPipe, AppointmentTable],
|
|
617
|
-
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
618
|
-
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
619
|
-
template: "<div class=\"flex h-full flex-col gap-4 overflow-auto 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 ?? ('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]=\"'calendar.search.label' | transloco\"\n [attr.placeholder]=\"'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 {{ '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]=\"'calendar.week.previous' | transloco\" (click)=\"previousWeek()\">\n <lw-icon name=\"calendarPrevious\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'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()\">{{ '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]=\"'calendar.week.next' | transloco\" (click)=\"nextWeek()\">\n <lw-icon name=\"calendarNext\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'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\">{{ 'calendar.errors.load' | transloco }}</p>\n } @else {\n <div class=\"overflow-hidden rounded-xl border border-border bg-surface\">\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"
|
|
620
|
-
}]
|
|
621
|
-
}],
|
|
622
|
-
ctorParameters: () => []
|
|
623
|
-
});
|
|
1
|
+
import { i as calendarHost, n as CalendarGatewayError, r as provideCalendarGateway, t as CALENDAR_GATEWAY } from "./weaver-apps-calendar-calendar-gateway-DUo797bn.mjs";
|
|
2
|
+
import { a as calendarPluginId, i as calendarIconNames, n as calendarPath, r as calendarContributionIds, t as calendarListPath } from "./weaver-apps-calendar-calendar-paths-qzIR-lDU.mjs";
|
|
624
3
|
const stroke = "fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"";
|
|
625
4
|
const calendarIcons = {
|
|
626
|
-
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>`,
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
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>`,
|
|
6
|
+
[calendarIconNames.previous]: `<svg viewBox="0 0 24 24" ${stroke}><path d="M15 5l-7 7 7 7"/></svg>`,
|
|
7
|
+
[calendarIconNames.next]: `<svg viewBox="0 0 24 24" ${stroke}><path d="M9 5l7 7-7 7"/></svg>`,
|
|
8
|
+
[calendarIconNames.cancel]: `<svg viewBox="0 0 24 24" ${stroke}><circle cx="12" cy="12" r="8.5"/><path d="M9 9l6 6M15 9l-6 6"/></svg>`
|
|
630
9
|
};
|
|
631
10
|
function createCalendarPlugin(options = {}) {
|
|
632
11
|
return {
|
|
633
12
|
manifest: {
|
|
634
|
-
id:
|
|
13
|
+
id: calendarPluginId,
|
|
635
14
|
name: "Calendar",
|
|
636
15
|
capabilities: [
|
|
637
16
|
"contributions",
|
|
@@ -643,24 +22,24 @@ function createCalendarPlugin(options = {}) {
|
|
|
643
22
|
calendarHost.bind(ctx);
|
|
644
23
|
ctx.contributeIcons(calendarIcons);
|
|
645
24
|
ctx.registerSurface({
|
|
646
|
-
id:
|
|
647
|
-
title: "calendar.calendars.title",
|
|
648
|
-
icon:
|
|
25
|
+
id: calendarContributionIds.calendars,
|
|
26
|
+
title: "weaver-apps-calendar.calendars.title",
|
|
27
|
+
icon: calendarIconNames.calendar,
|
|
649
28
|
routable: { path: calendarListPath },
|
|
650
|
-
|
|
29
|
+
loadComponent: () => import("./weaver-apps-calendar-calendar-list-page-dgF-nnQF.mjs").then((module) => module.CalendarListPage)
|
|
651
30
|
});
|
|
652
31
|
ctx.registerSurface({
|
|
653
|
-
id:
|
|
654
|
-
title: "calendar.title",
|
|
655
|
-
icon:
|
|
32
|
+
id: calendarContributionIds.calendar,
|
|
33
|
+
title: "weaver-apps-calendar.title",
|
|
34
|
+
icon: calendarIconNames.calendar,
|
|
656
35
|
routable: { path: `${calendarListPath}/:calendarId` },
|
|
657
|
-
|
|
36
|
+
loadComponent: () => import("./weaver-apps-calendar-calendar-page-DpdU_Vfy.mjs").then((module) => module.CalendarPage)
|
|
658
37
|
});
|
|
659
38
|
if (options.railItem) ctx.registerRailItem({
|
|
660
|
-
id:
|
|
39
|
+
id: calendarContributionIds.rail,
|
|
661
40
|
rail: "primary",
|
|
662
|
-
icon:
|
|
663
|
-
title: "calendar.calendars.title",
|
|
41
|
+
icon: calendarIconNames.calendar,
|
|
42
|
+
title: "weaver-apps-calendar.calendars.title",
|
|
664
43
|
run: () => ctx.navigateContent(calendarListPath)
|
|
665
44
|
});
|
|
666
45
|
},
|
|
@@ -765,6 +144,6 @@ function createHttpCalendarGateway(options) {
|
|
|
765
144
|
}
|
|
766
145
|
};
|
|
767
146
|
}
|
|
768
|
-
export { CALENDAR_GATEWAY, CalendarGatewayError, calendarListPath, calendarPath, calendarPlugin, createCalendarPlugin, createHttpCalendarGateway, provideCalendarGateway };
|
|
147
|
+
export { CALENDAR_GATEWAY, CalendarGatewayError, calendarContributionIds, calendarListPath, calendarPath, calendarPlugin, calendarPluginId, createCalendarPlugin, createHttpCalendarGateway, provideCalendarGateway };
|
|
769
148
|
|
|
770
149
|
//# sourceMappingURL=weaver-apps-calendar.mjs.map
|