@weaver-apps/service-desk 0.1.3 → 0.2.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.
@@ -1,10 +1,11 @@
1
1
  import * as i0 from "@angular/core";
2
- import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, DestroyRef, InjectionToken, computed, effect, inject, input, output, resource, signal } from "@angular/core";
3
- import { TranslocoPipe, TranslocoService } from "@jsverse/transloco";
2
+ import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, InjectionToken, computed, inject, input, output, resource, signal, viewChild } from "@angular/core";
3
+ import { TranslocoPipe, TranslocoService, translateObjectSignal } from "@jsverse/transloco";
4
4
  import { toSignal } from "@angular/core/rxjs-interop";
5
5
  import { ActivatedRoute } from "@angular/router";
6
6
  import { map } from "rxjs";
7
7
  import { DialogRef } from "@loomweaver/plugin-sdk";
8
+ import { DataGrid, ListFilter, listingDatasource } from "@weaver-apps/data-grid";
8
9
  var ServiceDeskGatewayError = class extends Error {
9
10
  status;
10
11
  constructor(status) {
@@ -36,7 +37,20 @@ var ServiceDeskHost = class {
36
37
  }
37
38
  };
38
39
  const serviceDeskHost = new ServiceDeskHost();
39
- const deskListPath = "service-desk";
40
+ const serviceDeskPluginId = "weaver-apps-service-desk";
41
+ const serviceDeskContributionIds = {
42
+ desks: `${serviceDeskPluginId}.desks`,
43
+ desk: `${serviceDeskPluginId}.desk`,
44
+ rail: `${serviceDeskPluginId}.rail`
45
+ };
46
+ const serviceDeskIconNames = {
47
+ desk: `${serviceDeskPluginId}.desk`,
48
+ complete: `${serviceDeskPluginId}.complete`,
49
+ cancel: `${serviceDeskPluginId}.cancel`,
50
+ previous: `${serviceDeskPluginId}.previous`,
51
+ next: `${serviceDeskPluginId}.next`
52
+ };
53
+ const deskListPath = serviceDeskPluginId;
40
54
  function deskPath(deskId) {
41
55
  return `${deskListPath}/${deskId}`;
42
56
  }
@@ -51,9 +65,9 @@ var DeskListPage = class DeskListPage {
51
65
  }
52
66
  async create() {
53
67
  const name = await serviceDeskHost.ui.prompt({
54
- title: "service-desk.desks.create",
55
- message: "service-desk.desks.createMessage",
56
- confirmLabel: "service-desk.desks.createConfirm"
68
+ title: "weaver-apps-service-desk.desks.create",
69
+ message: "weaver-apps-service-desk.desks.createMessage",
70
+ confirmLabel: "weaver-apps-service-desk.desks.createConfirm"
57
71
  });
58
72
  if (!name?.trim()) return;
59
73
  try {
@@ -61,7 +75,7 @@ var DeskListPage = class DeskListPage {
61
75
  this.open(desk.id);
62
76
  } catch {
63
77
  serviceDeskHost.ui.toast({
64
- message: "service-desk.errors.create",
78
+ message: "weaver-apps-service-desk.errors.create",
65
79
  kind: "error"
66
80
  });
67
81
  }
@@ -81,7 +95,7 @@ var DeskListPage = class DeskListPage {
81
95
  isStandalone: true,
82
96
  selector: "wa-desk-list-page",
83
97
  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\">{{ 'service-desk.desks.title' | transloco }}</h2>\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"create()\">{{ 'service-desk.desks.create' | transloco }}</button>\n </div>\n @if (desks.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'service-desk.errors.load' | transloco }}</p>\n } @else if (desks.hasValue()) {\n <ul role=\"list\" class=\"divide-y divide-border rounded-xl border border-border bg-surface\">\n @for (desk of desks.value(); track desk.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(desk.id)\">\n <lw-icon name=\"serviceDesk\" size=\"1.25rem\"></lw-icon>\n <span>{{ desk.name }}</span>\n </button>\n </li>\n } @empty {\n <li class=\"px-4 py-8 text-center text-sm text-content-faint\">{{ 'service-desk.desks.empty' | transloco }}</li>\n }\n </ul>\n }\n</div>\n",
98
+ 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\">{{ 'weaver-apps-service-desk.desks.title' | transloco }}</h2>\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"create()\">{{ 'weaver-apps-service-desk.desks.create' | transloco }}</button>\n </div>\n @if (desks.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-service-desk.errors.load' | transloco }}</p>\n } @else if (desks.hasValue()) {\n <ul role=\"list\" class=\"divide-y divide-border rounded-xl border border-border bg-surface\">\n @for (desk of desks.value(); track desk.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(desk.id)\">\n <lw-icon name=\"weaver-apps-service-desk.desk\" size=\"1.25rem\"></lw-icon>\n <span>{{ desk.name }}</span>\n </button>\n </li>\n } @empty {\n <li class=\"px-4 py-8 text-center text-sm text-content-faint\">{{ 'weaver-apps-service-desk.desks.empty' | transloco }}</li>\n }\n </ul>\n }\n</div>\n",
85
99
  dependencies: [{
86
100
  kind: "pipe",
87
101
  type: TranslocoPipe,
@@ -102,7 +116,7 @@ i0.ɵɵngDeclareClassMetadata({
102
116
  imports: [TranslocoPipe],
103
117
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
104
118
  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\">{{ 'service-desk.desks.title' | transloco }}</h2>\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"create()\">{{ 'service-desk.desks.create' | transloco }}</button>\n </div>\n @if (desks.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'service-desk.errors.load' | transloco }}</p>\n } @else if (desks.hasValue()) {\n <ul role=\"list\" class=\"divide-y divide-border rounded-xl border border-border bg-surface\">\n @for (desk of desks.value(); track desk.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(desk.id)\">\n <lw-icon name=\"serviceDesk\" size=\"1.25rem\"></lw-icon>\n <span>{{ desk.name }}</span>\n </button>\n </li>\n } @empty {\n <li class=\"px-4 py-8 text-center text-sm text-content-faint\">{{ 'service-desk.desks.empty' | transloco }}</li>\n }\n </ul>\n }\n</div>\n"
119
+ 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\">{{ 'weaver-apps-service-desk.desks.title' | transloco }}</h2>\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"create()\">{{ 'weaver-apps-service-desk.desks.create' | transloco }}</button>\n </div>\n @if (desks.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-service-desk.errors.load' | transloco }}</p>\n } @else if (desks.hasValue()) {\n <ul role=\"list\" class=\"divide-y divide-border rounded-xl border border-border bg-surface\">\n @for (desk of desks.value(); track desk.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(desk.id)\">\n <lw-icon name=\"weaver-apps-service-desk.desk\" size=\"1.25rem\"></lw-icon>\n <span>{{ desk.name }}</span>\n </button>\n </li>\n } @empty {\n <li class=\"px-4 py-8 text-center text-sm text-content-faint\">{{ 'weaver-apps-service-desk.desks.empty' | transloco }}</li>\n }\n </ul>\n }\n</div>\n"
106
120
  }]
107
121
  }]
108
122
  });
@@ -142,14 +156,6 @@ const requestTypes = Object.values(ServiceRequestType);
142
156
  const requestPriorities = Object.values(ServiceRequestPriority);
143
157
  const requestOrigins = Object.values(ServiceRequestOrigin);
144
158
  const settableStatuses = [ServiceRequestStatus.open, ServiceRequestStatus.inProgress];
145
- const statusFilters = [
146
- "active",
147
- "open",
148
- "inProgress",
149
- "completed",
150
- "cancelled",
151
- "all"
152
- ];
153
159
  function isClosed(status) {
154
160
  return status === ServiceRequestStatus.completed || status === ServiceRequestStatus.cancelled;
155
161
  }
@@ -206,7 +212,7 @@ var RequestDialog = class RequestDialog {
206
212
  }
207
213
  async save() {
208
214
  if (!this.title().trim()) {
209
- this.problem.set("service-desk.dialog.titleRequired");
215
+ this.problem.set("weaver-apps-service-desk.dialog.titleRequired");
210
216
  return;
211
217
  }
212
218
  this.saving.set(true);
@@ -218,7 +224,7 @@ var RequestDialog = class RequestDialog {
218
224
  });
219
225
  this.settle(await this.applyStatus(outcome));
220
226
  } catch {
221
- this.problem.set("service-desk.errors.save");
227
+ this.problem.set("weaver-apps-service-desk.errors.save");
222
228
  } finally {
223
229
  this.saving.set(false);
224
230
  }
@@ -257,10 +263,10 @@ var RequestDialog = class RequestDialog {
257
263
  }
258
264
  settle(outcome) {
259
265
  const messages = {
260
- closed: "service-desk.dialog.closed",
261
- changed: "service-desk.dialog.changed",
262
- invalid: "service-desk.dialog.invalid",
263
- missing: "service-desk.dialog.missing"
266
+ closed: "weaver-apps-service-desk.dialog.closed",
267
+ changed: "weaver-apps-service-desk.dialog.changed",
268
+ invalid: "weaver-apps-service-desk.dialog.invalid",
269
+ missing: "weaver-apps-service-desk.dialog.missing"
264
270
  };
265
271
  if (outcome.kind === "saved") {
266
272
  this.dialog.close(true);
@@ -290,7 +296,7 @@ var RequestDialog = class RequestDialog {
290
296
  isStandalone: true,
291
297
  selector: "wa-request-dialog",
292
298
  ngImport: i0,
293
- template: "<form class=\"flex flex-col gap-4\" (submit)=\"$event.preventDefault(); save()\">\n @if (closed) {\n <p class=\"text-sm text-content-faint\" role=\"status\">\n {{ 'service-desk.dialog.closedNote' | transloco }}\n @if (cancellationReason) {\n {{ 'service-desk.dialog.reason' | transloco: { reason: cancellationReason } }}\n }\n </p>\n }\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.title' | transloco }}\n <input class=\"lw-field\" required [disabled]=\"closed\" [value]=\"title()\" (input)=\"bind(title, $event)\" />\n </label>\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 {{ 'service-desk.fields.type' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"chooseType($event)\">\n @for (choice of types; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === type()\">{{ 'service-desk.type.' + choice | transloco }}</option>\n }\n </select>\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.priority' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"choosePriority($event)\">\n @for (choice of priorities; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === priority()\">{{ 'service-desk.priority.' + choice | transloco }}</option>\n }\n </select>\n </label>\n @if (editing) {\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.status' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"chooseStatus($event)\">\n @if (closed) {\n <option selected>{{ 'service-desk.status.' + status() | transloco }}</option>\n } @else {\n @for (choice of statuses; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === status()\">{{ 'service-desk.status.' + choice | transloco }}</option>\n }\n }\n </select>\n </label>\n } @else {\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.origin' | transloco }}\n <select class=\"lw-field\" (change)=\"chooseOrigin($event)\">\n @for (choice of origins; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === origin()\">{{ 'service-desk.origin.' + choice | transloco }}</option>\n }\n </select>\n </label>\n }\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 {{ 'service-desk.fields.contactName' | transloco }}\n <input class=\"lw-field\" [disabled]=\"closed\" [value]=\"contactName()\" (input)=\"bind(contactName, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.contactPhone' | transloco }}\n <input class=\"lw-field\" type=\"tel\" [disabled]=\"closed\" [value]=\"contactPhone()\" (input)=\"bind(contactPhone, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.contactEmail' | transloco }}\n <input class=\"lw-field\" type=\"email\" [disabled]=\"closed\" [value]=\"contactEmail()\" (input)=\"bind(contactEmail, $event)\" />\n </label>\n </div>\n\n <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.assignee' | transloco }}\n <input class=\"lw-field\" [disabled]=\"closed\" [value]=\"assignee()\" (input)=\"bind(assignee, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.due' | transloco }}\n <input class=\"lw-field\" type=\"date\" [disabled]=\"closed\" [value]=\"dueDate()\" (input)=\"bind(dueDate, $event)\" />\n </label>\n </div>\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.description' | transloco }}\n <textarea class=\"lw-field\" rows=\"3\" [disabled]=\"closed\" [value]=\"description()\" (input)=\"bind(description, $event)\"></textarea>\n </label>\n\n @if (problem()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ problem() | transloco }}</p>\n }\n\n <div class=\"flex justify-end gap-2\">\n <button type=\"button\" class=\"lw-btn lw-btn--default\" (click)=\"close()\">{{ (closed ? 'service-desk.dialog.close' : 'service-desk.dialog.cancel') | transloco }}</button>\n @if (!closed) {\n <button type=\"submit\" class=\"lw-btn lw-btn--primary\" [disabled]=\"saving()\">{{ 'service-desk.dialog.save' | transloco }}</button>\n }\n </div>\n</form>\n",
299
+ template: "<form class=\"flex flex-col gap-4\" (submit)=\"$event.preventDefault(); save()\">\n @if (closed) {\n <p class=\"text-sm text-content-faint\" role=\"status\">\n {{ 'weaver-apps-service-desk.dialog.closedNote' | transloco }}\n @if (cancellationReason) {\n {{ 'weaver-apps-service-desk.dialog.reason' | transloco: { reason: cancellationReason } }}\n }\n </p>\n }\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.title' | transloco }}\n <input class=\"lw-field\" required [disabled]=\"closed\" [value]=\"title()\" (input)=\"bind(title, $event)\" />\n </label>\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-service-desk.fields.type' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"chooseType($event)\">\n @for (choice of types; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === type()\">{{ 'weaver-apps-service-desk.type.' + choice | transloco }}</option>\n }\n </select>\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.priority' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"choosePriority($event)\">\n @for (choice of priorities; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === priority()\">{{ 'weaver-apps-service-desk.priority.' + choice | transloco }}</option>\n }\n </select>\n </label>\n @if (editing) {\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.status' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"chooseStatus($event)\">\n @if (closed) {\n <option selected>{{ 'weaver-apps-service-desk.status.' + status() | transloco }}</option>\n } @else {\n @for (choice of statuses; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === status()\">{{ 'weaver-apps-service-desk.status.' + choice | transloco }}</option>\n }\n }\n </select>\n </label>\n } @else {\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.origin' | transloco }}\n <select class=\"lw-field\" (change)=\"chooseOrigin($event)\">\n @for (choice of origins; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === origin()\">{{ 'weaver-apps-service-desk.origin.' + choice | transloco }}</option>\n }\n </select>\n </label>\n }\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-service-desk.fields.contactName' | transloco }}\n <input class=\"lw-field\" [disabled]=\"closed\" [value]=\"contactName()\" (input)=\"bind(contactName, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.contactPhone' | transloco }}\n <input class=\"lw-field\" type=\"tel\" [disabled]=\"closed\" [value]=\"contactPhone()\" (input)=\"bind(contactPhone, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.contactEmail' | transloco }}\n <input class=\"lw-field\" type=\"email\" [disabled]=\"closed\" [value]=\"contactEmail()\" (input)=\"bind(contactEmail, $event)\" />\n </label>\n </div>\n\n <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.assignee' | transloco }}\n <input class=\"lw-field\" [disabled]=\"closed\" [value]=\"assignee()\" (input)=\"bind(assignee, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.due' | transloco }}\n <input class=\"lw-field\" type=\"date\" [disabled]=\"closed\" [value]=\"dueDate()\" (input)=\"bind(dueDate, $event)\" />\n </label>\n </div>\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.description' | transloco }}\n <textarea class=\"lw-field\" rows=\"3\" [disabled]=\"closed\" [value]=\"description()\" (input)=\"bind(description, $event)\"></textarea>\n </label>\n\n @if (problem()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ problem() | transloco }}</p>\n }\n\n <div class=\"flex justify-end gap-2\">\n <button type=\"button\" class=\"lw-btn lw-btn--default\" (click)=\"close()\">{{ (closed ? 'weaver-apps-service-desk.dialog.close' : 'weaver-apps-service-desk.dialog.cancel') | transloco }}</button>\n @if (!closed) {\n <button type=\"submit\" class=\"lw-btn lw-btn--primary\" [disabled]=\"saving()\">{{ 'weaver-apps-service-desk.dialog.save' | transloco }}</button>\n }\n </div>\n</form>\n",
294
300
  dependencies: [{
295
301
  kind: "pipe",
296
302
  type: TranslocoPipe,
@@ -310,45 +316,287 @@ i0.ɵɵngDeclareClassMetadata({
310
316
  selector: "wa-request-dialog",
311
317
  imports: [TranslocoPipe],
312
318
  changeDetection: ChangeDetectionStrategy.OnPush,
313
- template: "<form class=\"flex flex-col gap-4\" (submit)=\"$event.preventDefault(); save()\">\n @if (closed) {\n <p class=\"text-sm text-content-faint\" role=\"status\">\n {{ 'service-desk.dialog.closedNote' | transloco }}\n @if (cancellationReason) {\n {{ 'service-desk.dialog.reason' | transloco: { reason: cancellationReason } }}\n }\n </p>\n }\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.title' | transloco }}\n <input class=\"lw-field\" required [disabled]=\"closed\" [value]=\"title()\" (input)=\"bind(title, $event)\" />\n </label>\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 {{ 'service-desk.fields.type' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"chooseType($event)\">\n @for (choice of types; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === type()\">{{ 'service-desk.type.' + choice | transloco }}</option>\n }\n </select>\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.priority' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"choosePriority($event)\">\n @for (choice of priorities; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === priority()\">{{ 'service-desk.priority.' + choice | transloco }}</option>\n }\n </select>\n </label>\n @if (editing) {\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.status' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"chooseStatus($event)\">\n @if (closed) {\n <option selected>{{ 'service-desk.status.' + status() | transloco }}</option>\n } @else {\n @for (choice of statuses; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === status()\">{{ 'service-desk.status.' + choice | transloco }}</option>\n }\n }\n </select>\n </label>\n } @else {\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.origin' | transloco }}\n <select class=\"lw-field\" (change)=\"chooseOrigin($event)\">\n @for (choice of origins; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === origin()\">{{ 'service-desk.origin.' + choice | transloco }}</option>\n }\n </select>\n </label>\n }\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 {{ 'service-desk.fields.contactName' | transloco }}\n <input class=\"lw-field\" [disabled]=\"closed\" [value]=\"contactName()\" (input)=\"bind(contactName, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.contactPhone' | transloco }}\n <input class=\"lw-field\" type=\"tel\" [disabled]=\"closed\" [value]=\"contactPhone()\" (input)=\"bind(contactPhone, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.contactEmail' | transloco }}\n <input class=\"lw-field\" type=\"email\" [disabled]=\"closed\" [value]=\"contactEmail()\" (input)=\"bind(contactEmail, $event)\" />\n </label>\n </div>\n\n <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.assignee' | transloco }}\n <input class=\"lw-field\" [disabled]=\"closed\" [value]=\"assignee()\" (input)=\"bind(assignee, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.due' | transloco }}\n <input class=\"lw-field\" type=\"date\" [disabled]=\"closed\" [value]=\"dueDate()\" (input)=\"bind(dueDate, $event)\" />\n </label>\n </div>\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'service-desk.fields.description' | transloco }}\n <textarea class=\"lw-field\" rows=\"3\" [disabled]=\"closed\" [value]=\"description()\" (input)=\"bind(description, $event)\"></textarea>\n </label>\n\n @if (problem()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ problem() | transloco }}</p>\n }\n\n <div class=\"flex justify-end gap-2\">\n <button type=\"button\" class=\"lw-btn lw-btn--default\" (click)=\"close()\">{{ (closed ? 'service-desk.dialog.close' : 'service-desk.dialog.cancel') | transloco }}</button>\n @if (!closed) {\n <button type=\"submit\" class=\"lw-btn lw-btn--primary\" [disabled]=\"saving()\">{{ 'service-desk.dialog.save' | transloco }}</button>\n }\n </div>\n</form>\n"
319
+ template: "<form class=\"flex flex-col gap-4\" (submit)=\"$event.preventDefault(); save()\">\n @if (closed) {\n <p class=\"text-sm text-content-faint\" role=\"status\">\n {{ 'weaver-apps-service-desk.dialog.closedNote' | transloco }}\n @if (cancellationReason) {\n {{ 'weaver-apps-service-desk.dialog.reason' | transloco: { reason: cancellationReason } }}\n }\n </p>\n }\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.title' | transloco }}\n <input class=\"lw-field\" required [disabled]=\"closed\" [value]=\"title()\" (input)=\"bind(title, $event)\" />\n </label>\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-service-desk.fields.type' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"chooseType($event)\">\n @for (choice of types; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === type()\">{{ 'weaver-apps-service-desk.type.' + choice | transloco }}</option>\n }\n </select>\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.priority' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"choosePriority($event)\">\n @for (choice of priorities; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === priority()\">{{ 'weaver-apps-service-desk.priority.' + choice | transloco }}</option>\n }\n </select>\n </label>\n @if (editing) {\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.status' | transloco }}\n <select class=\"lw-field\" [disabled]=\"closed\" (change)=\"chooseStatus($event)\">\n @if (closed) {\n <option selected>{{ 'weaver-apps-service-desk.status.' + status() | transloco }}</option>\n } @else {\n @for (choice of statuses; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === status()\">{{ 'weaver-apps-service-desk.status.' + choice | transloco }}</option>\n }\n }\n </select>\n </label>\n } @else {\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.origin' | transloco }}\n <select class=\"lw-field\" (change)=\"chooseOrigin($event)\">\n @for (choice of origins; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === origin()\">{{ 'weaver-apps-service-desk.origin.' + choice | transloco }}</option>\n }\n </select>\n </label>\n }\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-service-desk.fields.contactName' | transloco }}\n <input class=\"lw-field\" [disabled]=\"closed\" [value]=\"contactName()\" (input)=\"bind(contactName, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.contactPhone' | transloco }}\n <input class=\"lw-field\" type=\"tel\" [disabled]=\"closed\" [value]=\"contactPhone()\" (input)=\"bind(contactPhone, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.contactEmail' | transloco }}\n <input class=\"lw-field\" type=\"email\" [disabled]=\"closed\" [value]=\"contactEmail()\" (input)=\"bind(contactEmail, $event)\" />\n </label>\n </div>\n\n <div class=\"grid grid-cols-1 gap-4 sm:grid-cols-2\">\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.assignee' | transloco }}\n <input class=\"lw-field\" [disabled]=\"closed\" [value]=\"assignee()\" (input)=\"bind(assignee, $event)\" />\n </label>\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.due' | transloco }}\n <input class=\"lw-field\" type=\"date\" [disabled]=\"closed\" [value]=\"dueDate()\" (input)=\"bind(dueDate, $event)\" />\n </label>\n </div>\n\n <label class=\"flex flex-col gap-1 text-sm text-content\">\n {{ 'weaver-apps-service-desk.fields.description' | transloco }}\n <textarea class=\"lw-field\" rows=\"3\" [disabled]=\"closed\" [value]=\"description()\" (input)=\"bind(description, $event)\"></textarea>\n </label>\n\n @if (problem()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ problem() | transloco }}</p>\n }\n\n <div class=\"flex justify-end gap-2\">\n <button type=\"button\" class=\"lw-btn lw-btn--default\" (click)=\"close()\">{{ (closed ? 'weaver-apps-service-desk.dialog.close' : 'weaver-apps-service-desk.dialog.cancel') | transloco }}</button>\n @if (!closed) {\n <button type=\"submit\" class=\"lw-btn lw-btn--primary\" [disabled]=\"saving()\">{{ 'weaver-apps-service-desk.dialog.save' | transloco }}</button>\n }\n </div>\n</form>\n"
314
320
  }]
315
321
  }]
316
322
  });
317
- var RequestTable = class RequestTable {
318
- requests = input.required(...ngDevMode ? [{ debugName: "requests" }] : /* istanbul ignore next */ []);
319
- locale = input.required(...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
320
- requestActions = output();
321
- rows = computed(() => {
323
+ var RequestCell = class RequestCell {
324
+ params = null;
325
+ kind = signal("title", ...ngDevMode ? [{ debugName: "kind" }] : /* istanbul ignore next */ []);
326
+ request = signal(null, ...ngDevMode ? [{ debugName: "request" }] : /* istanbul ignore next */ []);
327
+ locale = signal("en", ...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
328
+ closed = computed(() => {
329
+ const request = this.request();
330
+ return request !== null && isClosed(request.status);
331
+ }, ...ngDevMode ? [{ debugName: "closed" }] : /* istanbul ignore next */ []);
332
+ dates = computed(() => {
333
+ const request = this.request();
322
334
  const date = new Intl.DateTimeFormat(this.locale(), {
323
335
  day: "2-digit",
324
336
  month: "2-digit",
325
337
  year: "numeric"
326
338
  });
327
- return this.requests().map((request) => ({
339
+ return {
340
+ raised: request ? date.format(new Date(request.raisedAt)) : "",
341
+ due: request?.dueAt ? date.format(new Date(request.dueAt)) : ""
342
+ };
343
+ }, ...ngDevMode ? [{ debugName: "dates" }] : /* istanbul ignore next */ []);
344
+ agInit(params) {
345
+ this.params = params;
346
+ this.kind.set(params.kind);
347
+ this.request.set(params.data ?? null);
348
+ this.locale.set(params.locale());
349
+ }
350
+ refresh(params) {
351
+ this.agInit(params);
352
+ return true;
353
+ }
354
+ openActions(event) {
355
+ const request = this.request();
356
+ if (request) this.params?.actions({
328
357
  request,
329
- raised: date.format(new Date(request.raisedAt)),
330
- due: request.dueAt ? date.format(new Date(request.dueAt)) : null,
331
- closed: isClosed(request.status)
332
- }));
333
- }, ...ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []);
358
+ x: event.clientX,
359
+ y: event.clientY
360
+ });
361
+ }
334
362
  static ɵfac = i0.ɵɵngDeclareFactory({
335
363
  minVersion: "12.0.0",
336
364
  version: "22.2.3",
337
365
  ngImport: i0,
338
- type: RequestTable,
366
+ type: RequestCell,
339
367
  deps: [],
340
368
  target: i0.ɵɵFactoryTarget.Component
341
369
  });
342
370
  static ɵcmp = i0.ɵɵngDeclareComponent({
343
371
  minVersion: "17.0.0",
344
372
  version: "22.2.3",
345
- type: RequestTable,
373
+ type: RequestCell,
346
374
  isStandalone: true,
347
- selector: "wa-request-table",
375
+ selector: "wa-request-cell",
376
+ ngImport: i0,
377
+ template: "@if (request(); as request) {\n @switch (kind()) {\n @case ('title') {\n <span class=\"font-medium\" [class.text-content]=\"!closed()\" [class.line-through]=\"request.status === 'cancelled'\" data-testid=\"request-title\">{{ request.title }}</span>\n }\n @case ('type') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\">{{ 'weaver-apps-service-desk.type.' + request.type | transloco }}</span>\n }\n @case ('priority') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs\" [class.text-negative]=\"request.priority === 'urgent'\" [class.font-semibold]=\"request.priority === 'urgent' || request.priority === 'high'\" data-testid=\"request-priority\">{{ 'weaver-apps-service-desk.priority.' + request.priority | transloco }}</span>\n }\n @case ('status') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"request-status\">{{ 'weaver-apps-service-desk.status.' + request.status | transloco }}</span>\n }\n @case ('contact') {\n <span>{{ request.contactName ?? '–' }}</span>\n @if (request.contactPhone ?? request.contactEmail; as reach) {\n <span class=\"ml-2 text-content-faint\">{{ reach }}</span>\n }\n }\n @case ('raised') {\n <span>{{ dates().raised }}</span>\n }\n @case ('due') {\n <span>{{ dates().due }}</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-service-desk.actions.more' | transloco\" (click)=\"openActions($event)\">\n <lw-icon name=\"more\" size=\"1rem\"></lw-icon>\n </button>\n }\n }\n}\n",
378
+ dependencies: [{
379
+ kind: "pipe",
380
+ type: TranslocoPipe,
381
+ name: "transloco"
382
+ }],
383
+ changeDetection: i0.ChangeDetectionStrategy.OnPush
384
+ });
385
+ };
386
+ i0.ɵɵngDeclareClassMetadata({
387
+ minVersion: "12.0.0",
388
+ version: "22.2.3",
389
+ ngImport: i0,
390
+ type: RequestCell,
391
+ decorators: [{
392
+ type: Component,
393
+ args: [{
394
+ selector: "wa-request-cell",
395
+ imports: [TranslocoPipe],
396
+ schemas: [CUSTOM_ELEMENTS_SCHEMA],
397
+ changeDetection: ChangeDetectionStrategy.OnPush,
398
+ template: "@if (request(); as request) {\n @switch (kind()) {\n @case ('title') {\n <span class=\"font-medium\" [class.text-content]=\"!closed()\" [class.line-through]=\"request.status === 'cancelled'\" data-testid=\"request-title\">{{ request.title }}</span>\n }\n @case ('type') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\">{{ 'weaver-apps-service-desk.type.' + request.type | transloco }}</span>\n }\n @case ('priority') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs\" [class.text-negative]=\"request.priority === 'urgent'\" [class.font-semibold]=\"request.priority === 'urgent' || request.priority === 'high'\" data-testid=\"request-priority\">{{ 'weaver-apps-service-desk.priority.' + request.priority | transloco }}</span>\n }\n @case ('status') {\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"request-status\">{{ 'weaver-apps-service-desk.status.' + request.status | transloco }}</span>\n }\n @case ('contact') {\n <span>{{ request.contactName ?? '–' }}</span>\n @if (request.contactPhone ?? request.contactEmail; as reach) {\n <span class=\"ml-2 text-content-faint\">{{ reach }}</span>\n }\n }\n @case ('raised') {\n <span>{{ dates().raised }}</span>\n }\n @case ('due') {\n <span>{{ dates().due }}</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-service-desk.actions.more' | transloco\" (click)=\"openActions($event)\">\n <lw-icon name=\"more\" size=\"1rem\"></lw-icon>\n </button>\n }\n }\n}\n"
399
+ }]
400
+ }]
401
+ });
402
+ const requestColumn = {
403
+ title: "title",
404
+ type: "type",
405
+ priority: "priority",
406
+ status: "status",
407
+ contact: "contact",
408
+ raised: "raisedAt",
409
+ due: "dueAt",
410
+ actions: "actions"
411
+ };
412
+ const defaultStatuses = [
413
+ ServiceRequestStatus.open,
414
+ ServiceRequestStatus.inProgress,
415
+ ServiceRequestStatus.completed
416
+ ];
417
+ const defaultRequestFilter = { [requestColumn.status]: { values: [...defaultStatuses] } };
418
+ const sortableColumns = /* @__PURE__ */ new Set([
419
+ requestColumn.title,
420
+ requestColumn.priority,
421
+ requestColumn.status,
422
+ requestColumn.raised,
423
+ requestColumn.due
424
+ ]);
425
+ function listingOf(request) {
426
+ const { filters, sort } = request;
427
+ return {
428
+ ...sort && sortableColumns.has(sort.column) ? {
429
+ sort: sort.column,
430
+ descending: sort.descending
431
+ } : {},
432
+ ...textOf("text", filters[requestColumn.title]),
433
+ ...textOf("contact", filters[requestColumn.contact]),
434
+ ...valuesOf("type", filters[requestColumn.type]),
435
+ ...valuesOf("status", filters[requestColumn.status]),
436
+ ...valuesOf("priority", filters[requestColumn.priority]),
437
+ offset: request.offset,
438
+ limit: request.limit
439
+ };
440
+ }
441
+ function textOf(key, filter) {
442
+ const text = filter?.filter?.trim();
443
+ return text ? { [key]: text } : {};
444
+ }
445
+ function valuesOf(key, filter) {
446
+ const values = filter?.values ?? [];
447
+ return values.length > 0 ? { [key]: [...values] } : {};
448
+ }
449
+ const allStatuses = [
450
+ "open",
451
+ "inProgress",
452
+ "completed",
453
+ "cancelled"
454
+ ];
455
+ const actionsWidthPx = 72;
456
+ const titleMinWidthPx = 160;
457
+ const contactMinWidthPx = 140;
458
+ const narrowMinWidthPx = 112;
459
+ var RequestGrid = class RequestGrid {
460
+ table = translateObjectSignal("weaver-apps-service-desk.table");
461
+ types = translateObjectSignal("weaver-apps-service-desk.type");
462
+ statuses = translateObjectSignal("weaver-apps-service-desk.status");
463
+ priorities = translateObjectSignal("weaver-apps-service-desk.priority");
464
+ api = null;
465
+ fetch = input.required(...ngDevMode ? [{ debugName: "fetch" }] : /* istanbul ignore next */ []);
466
+ locale = input.required(...ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []);
467
+ requestActions = output();
468
+ counted = output();
469
+ failed = output();
470
+ translated = computed(() => typeof this.table() === "object" && typeof this.table()["title"] === "string", ...ngDevMode ? [{ debugName: "translated" }] : /* istanbul ignore next */ []);
471
+ empty = computed(() => ({ noRowsToShow: this.table()["empty"] ?? "" }), ...ngDevMode ? [{ debugName: "empty" }] : /* istanbul ignore next */ []);
472
+ options = computed(() => ({
473
+ columnDefs: this.columns(this.table()),
474
+ defaultColDef: {
475
+ sortable: false,
476
+ resizable: true,
477
+ suppressHeaderMenuButton: true
478
+ },
479
+ datasource: listingDatasource(this.fetch(), {
480
+ answered: (answer) => this.counted.emit(answer.total),
481
+ failed: () => this.failed.emit()
482
+ }),
483
+ initialState: {
484
+ sort: { sortModel: [{
485
+ colId: requestColumn.raised,
486
+ sort: "desc"
487
+ }] },
488
+ filter: { filterModel: { ...defaultRequestFilter } }
489
+ },
490
+ rowClassRules: { "text-content-faint": (params) => !!params.data && isClosed(params.data.status) }
491
+ }), ...ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []);
492
+ reload() {
493
+ this.api?.refreshInfiniteCache();
494
+ }
495
+ ready(api) {
496
+ this.api = api;
497
+ }
498
+ columns(table) {
499
+ const narrow = {
500
+ flex: 1,
501
+ minWidth: narrowMinWidthPx
502
+ };
503
+ return [
504
+ {
505
+ ...this.cell(requestColumn.title, "title", table["title"]),
506
+ sortable: true,
507
+ flex: 3,
508
+ minWidth: titleMinWidthPx,
509
+ filter: "agTextColumnFilter",
510
+ filterParams: {
511
+ filterOptions: ["contains"],
512
+ maxNumConditions: 1
513
+ }
514
+ },
515
+ {
516
+ ...this.cell(requestColumn.type, "type", table["type"]),
517
+ ...narrow,
518
+ filter: ListFilter,
519
+ filterParams: this.choices(requestTypes, this.types)
520
+ },
521
+ {
522
+ ...this.cell(requestColumn.priority, "priority", table["priority"]),
523
+ ...narrow,
524
+ sortable: true,
525
+ filter: ListFilter,
526
+ filterParams: this.choices(requestPriorities, this.priorities)
527
+ },
528
+ {
529
+ ...this.cell(requestColumn.status, "status", table["status"]),
530
+ ...narrow,
531
+ sortable: true,
532
+ filter: ListFilter,
533
+ filterParams: this.choices(allStatuses, this.statuses)
534
+ },
535
+ {
536
+ ...this.cell(requestColumn.contact, "contact", table["contact"]),
537
+ flex: 2,
538
+ minWidth: contactMinWidthPx,
539
+ filter: "agTextColumnFilter",
540
+ filterParams: {
541
+ filterOptions: ["contains"],
542
+ maxNumConditions: 1
543
+ }
544
+ },
545
+ {
546
+ ...this.cell(requestColumn.raised, "raised", table["raised"]),
547
+ ...narrow,
548
+ sortable: true,
549
+ sortingOrder: ["desc", "asc"]
550
+ },
551
+ {
552
+ ...this.cell(requestColumn.due, "due", table["due"]),
553
+ ...narrow,
554
+ sortable: true
555
+ },
556
+ {
557
+ ...this.cell(requestColumn.actions, "actions", ""),
558
+ width: actionsWidthPx,
559
+ resizable: false,
560
+ sortable: false
561
+ }
562
+ ];
563
+ }
564
+ cell(colId, kind, headerName) {
565
+ return {
566
+ colId,
567
+ headerName: headerName ?? "",
568
+ cellRenderer: RequestCell,
569
+ cellRendererParams: {
570
+ kind,
571
+ locale: () => this.locale(),
572
+ actions: (actions) => this.requestActions.emit(actions)
573
+ }
574
+ };
575
+ }
576
+ choices(values, labels) {
577
+ return { options: () => values.map((value) => ({
578
+ value,
579
+ label: labels()[value] ?? value
580
+ })) };
581
+ }
582
+ static ɵfac = i0.ɵɵngDeclareFactory({
583
+ minVersion: "12.0.0",
584
+ version: "22.2.3",
585
+ ngImport: i0,
586
+ type: RequestGrid,
587
+ deps: [],
588
+ target: i0.ɵɵFactoryTarget.Component
589
+ });
590
+ static ɵcmp = i0.ɵɵngDeclareComponent({
591
+ minVersion: "17.0.0",
592
+ version: "22.2.3",
593
+ type: RequestGrid,
594
+ isStandalone: true,
595
+ selector: "wa-request-grid",
348
596
  inputs: {
349
- requests: {
350
- classPropertyName: "requests",
351
- publicName: "requests",
597
+ fetch: {
598
+ classPropertyName: "fetch",
599
+ publicName: "fetch",
352
600
  isSignal: true,
353
601
  isRequired: true,
354
602
  transformFunction: null
@@ -361,13 +609,20 @@ var RequestTable = class RequestTable {
361
609
  transformFunction: null
362
610
  }
363
611
  },
364
- outputs: { requestActions: "requestActions" },
612
+ outputs: {
613
+ requestActions: "requestActions",
614
+ counted: "counted",
615
+ failed: "failed"
616
+ },
617
+ host: { classAttribute: "block h-full w-full" },
365
618
  ngImport: i0,
366
- 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\">{{ 'service-desk.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\">{{ 'service-desk.table.type' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'service-desk.table.priority' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'service-desk.table.status' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'service-desk.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\">{{ 'service-desk.table.dates' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2\"><span class=\"sr-only\">{{ 'service-desk.table.actions' | transloco }}</span></th>\n </tr>\n </thead>\n <tbody>\n @for (row of rows(); track row.request.id) {\n <tr class=\"border-b border-border\" [class.text-content-faint]=\"row.closed\" data-testid=\"request-row\">\n <td class=\"min-w-48 px-4 py-3 font-medium text-content\" [class.line-through]=\"row.request.status === 'cancelled'\">{{ row.request.title }}</td>\n <td class=\"px-4 py-3 whitespace-nowrap\">\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\">{{ 'service-desk.type.' + row.request.type | transloco }}</span>\n </td>\n <td class=\"px-4 py-3 whitespace-nowrap\">\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs\" [class.text-negative]=\"row.request.priority === 'urgent'\" [class.font-semibold]=\"row.request.priority === 'urgent' || row.request.priority === 'high'\" data-testid=\"request-priority\">{{ 'service-desk.priority.' + row.request.priority | transloco }}</span>\n </td>\n <td class=\"px-4 py-3 whitespace-nowrap\">\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"request-status\">{{ 'service-desk.status.' + row.request.status | transloco }}</span>\n </td>\n <td class=\"px-4 py-3\">\n <div class=\"text-content\">{{ row.request.contactName ?? '–' }}</div>\n @if (row.request.contactPhone ?? row.request.contactEmail; as reach) {\n <div class=\"text-content-faint\">{{ reach }}</div>\n }\n </td>\n <td class=\"px-4 py-3 whitespace-nowrap\">\n <div class=\"text-content\">{{ row.raised }}</div>\n @if (row.due) {\n <div class=\"text-content-faint\">{{ 'service-desk.table.dueOn' | transloco: { date: row.due } }}</div>\n }\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]=\"'service-desk.actions.more' | transloco\" (click)=\"requestActions.emit({ request: row.request, x: $event.clientX, y: $event.clientY })\">\n <lw-icon name=\"more\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'service-desk.actions.more' | transloco\" position=\"top\"></lw-tooltip>\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"7\" class=\"px-4 py-8 text-center text-content-faint\">{{ 'service-desk.table.empty' | transloco }}</td>\n </tr>\n }\n </tbody>\n</table>\n",
619
+ template: "@if (translated()) {\n <wa-data-grid [options]=\"options()\" [localeOverrides]=\"empty()\" (ready)=\"ready($event)\" />\n}\n",
367
620
  dependencies: [{
368
- kind: "pipe",
369
- type: TranslocoPipe,
370
- name: "transloco"
621
+ kind: "component",
622
+ type: DataGrid,
623
+ selector: "wa-data-grid",
624
+ inputs: ["options", "localeOverrides"],
625
+ outputs: ["ready"]
371
626
  }],
372
627
  changeDetection: i0.ChangeDetectionStrategy.OnPush
373
628
  });
@@ -376,23 +631,23 @@ i0.ɵɵngDeclareClassMetadata({
376
631
  minVersion: "12.0.0",
377
632
  version: "22.2.3",
378
633
  ngImport: i0,
379
- type: RequestTable,
634
+ type: RequestGrid,
380
635
  decorators: [{
381
636
  type: Component,
382
637
  args: [{
383
- selector: "wa-request-table",
384
- imports: [TranslocoPipe],
385
- schemas: [CUSTOM_ELEMENTS_SCHEMA],
638
+ selector: "wa-request-grid",
639
+ imports: [DataGrid],
386
640
  changeDetection: ChangeDetectionStrategy.OnPush,
387
- 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\">{{ 'service-desk.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\">{{ 'service-desk.table.type' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'service-desk.table.priority' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'service-desk.table.status' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2 text-left text-xs font-medium uppercase tracking-wide text-content-faint\">{{ 'service-desk.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\">{{ 'service-desk.table.dates' | transloco }}</th>\n <th scope=\"col\" class=\"px-4 py-2\"><span class=\"sr-only\">{{ 'service-desk.table.actions' | transloco }}</span></th>\n </tr>\n </thead>\n <tbody>\n @for (row of rows(); track row.request.id) {\n <tr class=\"border-b border-border\" [class.text-content-faint]=\"row.closed\" data-testid=\"request-row\">\n <td class=\"min-w-48 px-4 py-3 font-medium text-content\" [class.line-through]=\"row.request.status === 'cancelled'\">{{ row.request.title }}</td>\n <td class=\"px-4 py-3 whitespace-nowrap\">\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\">{{ 'service-desk.type.' + row.request.type | transloco }}</span>\n </td>\n <td class=\"px-4 py-3 whitespace-nowrap\">\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs\" [class.text-negative]=\"row.request.priority === 'urgent'\" [class.font-semibold]=\"row.request.priority === 'urgent' || row.request.priority === 'high'\" data-testid=\"request-priority\">{{ 'service-desk.priority.' + row.request.priority | transloco }}</span>\n </td>\n <td class=\"px-4 py-3 whitespace-nowrap\">\n <span class=\"rounded-full border border-border px-2 py-0.5 text-xs text-content\" data-testid=\"request-status\">{{ 'service-desk.status.' + row.request.status | transloco }}</span>\n </td>\n <td class=\"px-4 py-3\">\n <div class=\"text-content\">{{ row.request.contactName ?? '–' }}</div>\n @if (row.request.contactPhone ?? row.request.contactEmail; as reach) {\n <div class=\"text-content-faint\">{{ reach }}</div>\n }\n </td>\n <td class=\"px-4 py-3 whitespace-nowrap\">\n <div class=\"text-content\">{{ row.raised }}</div>\n @if (row.due) {\n <div class=\"text-content-faint\">{{ 'service-desk.table.dueOn' | transloco: { date: row.due } }}</div>\n }\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]=\"'service-desk.actions.more' | transloco\" (click)=\"requestActions.emit({ request: row.request, x: $event.clientX, y: $event.clientY })\">\n <lw-icon name=\"more\" size=\"1rem\"></lw-icon>\n <lw-tooltip [attr.text]=\"'service-desk.actions.more' | transloco\" position=\"top\"></lw-tooltip>\n </button>\n </td>\n </tr>\n } @empty {\n <tr>\n <td colspan=\"7\" class=\"px-4 py-8 text-center text-content-faint\">{{ 'service-desk.table.empty' | transloco }}</td>\n </tr>\n }\n </tbody>\n</table>\n"
641
+ host: { class: "block h-full w-full" },
642
+ template: "@if (translated()) {\n <wa-data-grid [options]=\"options()\" [localeOverrides]=\"empty()\" (ready)=\"ready($event)\" />\n}\n"
388
643
  }]
389
644
  }],
390
645
  propDecorators: {
391
- requests: [{
646
+ fetch: [{
392
647
  type: i0.Input,
393
648
  args: [{
394
649
  isSignal: true,
395
- alias: "requests",
650
+ alias: "fetch",
396
651
  required: true
397
652
  }]
398
653
  }],
@@ -407,105 +662,72 @@ i0.ɵɵngDeclareClassMetadata({
407
662
  requestActions: [{
408
663
  type: i0.Output,
409
664
  args: ["requestActions"]
665
+ }],
666
+ counted: [{
667
+ type: i0.Output,
668
+ args: ["counted"]
669
+ }],
670
+ failed: [{
671
+ type: i0.Output,
672
+ args: ["failed"]
410
673
  }]
411
674
  }
412
675
  });
413
- const pageSize = 25;
414
- const searchDelayMs = 300;
415
676
  var DeskPage = class DeskPage {
416
677
  gateway = inject(SERVICE_DESK_GATEWAY);
417
678
  route = inject(ActivatedRoute);
418
- searchTimer;
679
+ grid = viewChild(RequestGrid, ...ngDevMode ? [{ debugName: "grid" }] : /* istanbul ignore next */ []);
419
680
  locale = activeLocale();
420
- filters = statusFilters;
421
681
  deskId = toSignal(this.route.paramMap.pipe(map((params) => params.get("deskId") ?? "")), { initialValue: this.route.snapshot.paramMap.get("deskId") ?? "" });
422
- text = signal("", ...ngDevMode ? [{ debugName: "text" }] : /* istanbul ignore next */ []);
423
- filter = signal("active", ...ngDevMode ? [{ debugName: "filter" }] : /* istanbul ignore next */ []);
424
- offset = signal(0, ...ngDevMode ? [{ debugName: "offset" }] : /* istanbul ignore next */ []);
682
+ count = signal(null, ...ngDevMode ? [{ debugName: "count" }] : /* istanbul ignore next */ []);
683
+ loadFailed = signal(false, ...ngDevMode ? [{ debugName: "loadFailed" }] : /* istanbul ignore next */ []);
425
684
  desk = resource({
426
685
  ...ngDevMode ? { debugName: "desk" } : /* istanbul ignore next */ {},
427
686
  params: () => this.deskId(),
428
687
  loader: ({ params }) => this.gateway.desk(params)
429
688
  });
430
- page = resource({
431
- ...ngDevMode ? { debugName: "page" } : /* istanbul ignore next */ {},
432
- params: () => ({
433
- deskId: this.deskId(),
434
- text: this.text().trim(),
435
- filter: this.filter(),
436
- offset: this.offset()
437
- }),
438
- loader: ({ params, abortSignal }) => this.gateway.requests(params.deskId, {
439
- text: params.text || void 0,
440
- ...params.filter === "active" ? {} : params.filter === "all" ? { includeCancelled: true } : { status: params.filter },
441
- offset: params.offset,
442
- limit: pageSize
443
- }, abortSignal)
444
- });
445
- constructor() {
446
- inject(DestroyRef).onDestroy(() => clearTimeout(this.searchTimer));
447
- effect(() => {
448
- const total = this.total();
449
- if (this.page.hasValue() && this.offset() > 0 && this.offset() >= total) this.offset.set(Math.max(0, Math.ceil(total / pageSize) - 1) * pageSize);
450
- });
451
- }
452
- total = computed(() => Number(this.page.value()?.total ?? 0), ...ngDevMode ? [{ debugName: "total" }] : /* istanbul ignore next */ []);
453
- range = computed(() => {
454
- const total = this.total();
455
- return {
456
- first: total === 0 ? 0 : this.offset() + 1,
457
- last: Math.min(this.offset() + pageSize, total),
458
- total
689
+ fetch = computed(() => {
690
+ const deskId = this.deskId();
691
+ return async (request) => {
692
+ const page = await this.gateway.requests(deskId, listingOf(request));
693
+ return {
694
+ items: page.items,
695
+ total: Number(page.total)
696
+ };
459
697
  };
460
- }, ...ngDevMode ? [{ debugName: "range" }] : /* istanbul ignore next */ []);
461
- hasPrevious = computed(() => this.offset() > 0, ...ngDevMode ? [{ debugName: "hasPrevious" }] : /* istanbul ignore next */ []);
462
- hasNext = computed(() => this.offset() + pageSize < this.total(), ...ngDevMode ? [{ debugName: "hasNext" }] : /* istanbul ignore next */ []);
463
- search(event) {
464
- const value = event.target.value;
465
- clearTimeout(this.searchTimer);
466
- this.searchTimer = setTimeout(() => {
467
- this.text.set(value);
468
- this.offset.set(0);
469
- }, searchDelayMs);
470
- }
471
- chooseFilter(event) {
472
- const chosen = statusFilters.find((filter) => filter === event.target.value);
473
- if (chosen) {
474
- this.filter.set(chosen);
475
- this.offset.set(0);
476
- }
477
- }
478
- previous() {
479
- this.offset.update((offset) => Math.max(0, offset - pageSize));
698
+ }, ...ngDevMode ? [{ debugName: "fetch" }] : /* istanbul ignore next */ []);
699
+ counted(total) {
700
+ this.loadFailed.set(false);
701
+ this.count.set(total);
480
702
  }
481
- next() {
482
- this.offset.update((offset) => offset + pageSize);
703
+ failed() {
704
+ this.loadFailed.set(true);
483
705
  }
484
706
  add() {
485
707
  this.openDialog({
486
708
  deskId: this.deskId(),
487
709
  request: null
488
- }, "service-desk.dialog.addTitle");
710
+ }, "weaver-apps-service-desk.dialog.addTitle");
489
711
  }
490
712
  actions({ request, x, y }) {
491
713
  const closed = isClosed(request.status);
492
714
  serviceDeskHost.ui.openMenu([
493
715
  {
494
- label: closed ? "service-desk.actions.view" : "service-desk.actions.edit",
716
+ label: closed ? "weaver-apps-service-desk.actions.view" : "weaver-apps-service-desk.actions.edit",
495
717
  icon: closed ? "preview" : "edit",
496
718
  run: () => this.open(request)
497
719
  },
498
720
  ...closed ? [] : [{
499
- label: "service-desk.actions.complete",
500
- icon: "serviceDeskComplete",
721
+ label: "weaver-apps-service-desk.actions.complete",
722
+ icon: serviceDeskIconNames.complete,
501
723
  run: () => void this.complete(request)
502
724
  }, {
503
- label: "service-desk.actions.cancel",
504
- icon: "serviceDeskCancel",
725
+ label: "weaver-apps-service-desk.actions.cancel",
726
+ icon: serviceDeskIconNames.cancel,
505
727
  run: () => void this.cancel(request)
506
728
  }],
507
729
  {
508
- label: "service-desk.actions.delete",
730
+ label: "weaver-apps-service-desk.actions.delete",
509
731
  icon: "trash",
510
732
  run: () => void this.remove(request)
511
733
  }
@@ -519,46 +741,49 @@ var DeskPage = class DeskPage {
519
741
  this.openDialog({
520
742
  deskId: this.deskId(),
521
743
  request
522
- }, closed ? "service-desk.dialog.viewTitle" : "service-desk.dialog.editTitle");
744
+ }, closed ? "weaver-apps-service-desk.dialog.viewTitle" : "weaver-apps-service-desk.dialog.editTitle");
523
745
  }
524
746
  async complete(request) {
525
- await this.attempt(async () => this.report(await this.gateway.complete(this.deskId(), request.id), "service-desk.complete.done"));
747
+ await this.attempt(async () => this.report(await this.gateway.complete(this.deskId(), request.id), "weaver-apps-service-desk.complete.done"));
526
748
  }
527
749
  async cancel(request) {
528
750
  const reason = await serviceDeskHost.ui.prompt({
529
- title: "service-desk.cancel.title",
530
- message: "service-desk.cancel.message",
531
- confirmLabel: "service-desk.cancel.confirm",
751
+ title: "weaver-apps-service-desk.cancel.title",
752
+ message: "weaver-apps-service-desk.cancel.message",
753
+ confirmLabel: "weaver-apps-service-desk.cancel.confirm",
532
754
  tone: "warning"
533
755
  });
534
756
  if (reason === null) return;
535
- await this.attempt(async () => this.report(await this.gateway.cancel(this.deskId(), request.id, reason.trim() || null), "service-desk.cancel.done"));
757
+ await this.attempt(async () => this.report(await this.gateway.cancel(this.deskId(), request.id, reason.trim() || null), "weaver-apps-service-desk.cancel.done"));
536
758
  }
537
759
  async remove(request) {
538
760
  if (!await serviceDeskHost.ui.confirm({
539
- title: "service-desk.delete.title",
540
- message: "service-desk.delete.message",
541
- confirmLabel: "service-desk.delete.confirm",
761
+ title: "weaver-apps-service-desk.delete.title",
762
+ message: "weaver-apps-service-desk.delete.message",
763
+ confirmLabel: "weaver-apps-service-desk.delete.confirm",
542
764
  tone: "danger"
543
765
  })) return;
544
766
  await this.attempt(async () => {
545
767
  await this.gateway.remove(this.deskId(), request.id);
546
768
  serviceDeskHost.ui.toast({
547
- message: "service-desk.delete.done",
769
+ message: "weaver-apps-service-desk.delete.done",
548
770
  kind: "success"
549
771
  });
550
- this.page.reload();
772
+ this.reload();
551
773
  });
552
774
  }
775
+ reload() {
776
+ this.grid()?.reload();
777
+ }
553
778
  async attempt(work) {
554
779
  try {
555
780
  await work();
556
781
  } catch {
557
782
  serviceDeskHost.ui.toast({
558
- message: "service-desk.errors.save",
783
+ message: "weaver-apps-service-desk.errors.save",
559
784
  kind: "error"
560
785
  });
561
- this.page.reload();
786
+ this.reload();
562
787
  }
563
788
  }
564
789
  openDialog(data, title) {
@@ -570,20 +795,20 @@ var DeskPage = class DeskPage {
570
795
  }).closed.then((saved) => {
571
796
  if (saved) {
572
797
  serviceDeskHost.ui.toast({
573
- message: "service-desk.dialog.saved",
798
+ message: "weaver-apps-service-desk.dialog.saved",
574
799
  kind: "success"
575
800
  });
576
- this.page.reload();
801
+ this.reload();
577
802
  }
578
803
  });
579
804
  }
580
805
  report(outcome, done) {
581
806
  const saved = outcome.kind === "saved";
582
807
  serviceDeskHost.ui.toast({
583
- message: saved ? done : "service-desk.errors.save",
808
+ message: saved ? done : "weaver-apps-service-desk.errors.save",
584
809
  kind: saved ? "success" : "error"
585
810
  });
586
- this.page.reload();
811
+ this.reload();
587
812
  }
588
813
  static ɵfac = i0.ɵɵngDeclareFactory({
589
814
  minVersion: "12.0.0",
@@ -599,14 +824,25 @@ var DeskPage = class DeskPage {
599
824
  type: DeskPage,
600
825
  isStandalone: true,
601
826
  selector: "wa-desk-page",
827
+ viewQueries: [{
828
+ propertyName: "grid",
829
+ first: true,
830
+ predicate: RequestGrid,
831
+ descendants: true,
832
+ isSignal: true
833
+ }],
602
834
  ngImport: i0,
603
- 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\">{{ desk.value()?.name ?? ('service-desk.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]=\"'service-desk.search.label' | transloco\"\n [attr.placeholder]=\"'service-desk.search.placeholder' | transloco\"\n (input)=\"search($event)\"\n />\n <select class=\"lw-field w-64\" [attr.aria-label]=\"'service-desk.filter.label' | transloco\" (change)=\"chooseFilter($event)\">\n @for (choice of filters; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === filter()\">{{ 'service-desk.filter.' + choice | transloco }}</option>\n }\n </select>\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"add()\">\n <lw-icon name=\"add\" size=\"1rem\"></lw-icon>\n {{ 'service-desk.actions.add' | transloco }}\n </button>\n </div>\n </div>\n\n @if (page.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'service-desk.errors.load' | transloco }}</p>\n } @else {\n <div class=\"overflow-x-auto rounded-xl border border-border bg-surface\">\n <wa-request-table\n [requests]=\"page.value()?.items ?? []\"\n [locale]=\"locale()\"\n (requestActions)=\"actions($event)\"\n />\n </div>\n <div class=\"flex items-center justify-end gap-2 text-sm text-content-faint\">\n <span data-testid=\"page-range\">{{ 'service-desk.pager.range' | transloco: range() }}</span>\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [disabled]=\"!hasPrevious()\" [attr.aria-label]=\"'service-desk.pager.previous' | transloco\" (click)=\"previous()\">\n <lw-icon name=\"serviceDeskPrevious\" size=\"1rem\"></lw-icon>\n </button>\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [disabled]=\"!hasNext()\" [attr.aria-label]=\"'service-desk.pager.next' | transloco\" (click)=\"next()\">\n <lw-icon name=\"serviceDeskNext\" size=\"1rem\"></lw-icon>\n </button>\n </div>\n }\n</div>\n",
835
+ 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\">{{ desk.value()?.name ?? ('weaver-apps-service-desk.title' | transloco) }}</h2>\n <div class=\"flex flex-wrap items-center gap-3\">\n @if (count() !== null) {\n <span class=\"text-sm text-content-faint\" data-testid=\"request-count\">{{ 'weaver-apps-service-desk.table.count' | transloco: { count: count() } }}</span>\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-service-desk.actions.add' | transloco }}\n </button>\n </div>\n </div>\n\n @if (loadFailed()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-service-desk.errors.load' | transloco }}</p>\n }\n <div class=\"min-h-80 flex-1\">\n <wa-request-grid [fetch]=\"fetch()\" [locale]=\"locale()\" (requestActions)=\"actions($event)\" (counted)=\"counted($event)\" (failed)=\"failed()\" />\n </div>\n</div>\n",
604
836
  dependencies: [{
605
837
  kind: "component",
606
- type: RequestTable,
607
- selector: "wa-request-table",
608
- inputs: ["requests", "locale"],
609
- outputs: ["requestActions"]
838
+ type: RequestGrid,
839
+ selector: "wa-request-grid",
840
+ inputs: ["fetch", "locale"],
841
+ outputs: [
842
+ "requestActions",
843
+ "counted",
844
+ "failed"
845
+ ]
610
846
  }, {
611
847
  kind: "pipe",
612
848
  type: TranslocoPipe,
@@ -624,26 +860,29 @@ i0.ɵɵngDeclareClassMetadata({
624
860
  type: Component,
625
861
  args: [{
626
862
  selector: "wa-desk-page",
627
- imports: [TranslocoPipe, RequestTable],
863
+ imports: [TranslocoPipe, RequestGrid],
628
864
  schemas: [CUSTOM_ELEMENTS_SCHEMA],
629
865
  changeDetection: ChangeDetectionStrategy.OnPush,
630
- 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\">{{ desk.value()?.name ?? ('service-desk.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]=\"'service-desk.search.label' | transloco\"\n [attr.placeholder]=\"'service-desk.search.placeholder' | transloco\"\n (input)=\"search($event)\"\n />\n <select class=\"lw-field w-64\" [attr.aria-label]=\"'service-desk.filter.label' | transloco\" (change)=\"chooseFilter($event)\">\n @for (choice of filters; track choice) {\n <option [value]=\"choice\" [selected]=\"choice === filter()\">{{ 'service-desk.filter.' + choice | transloco }}</option>\n }\n </select>\n <button type=\"button\" class=\"lw-btn lw-btn--primary\" (click)=\"add()\">\n <lw-icon name=\"add\" size=\"1rem\"></lw-icon>\n {{ 'service-desk.actions.add' | transloco }}\n </button>\n </div>\n </div>\n\n @if (page.error()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'service-desk.errors.load' | transloco }}</p>\n } @else {\n <div class=\"overflow-x-auto rounded-xl border border-border bg-surface\">\n <wa-request-table\n [requests]=\"page.value()?.items ?? []\"\n [locale]=\"locale()\"\n (requestActions)=\"actions($event)\"\n />\n </div>\n <div class=\"flex items-center justify-end gap-2 text-sm text-content-faint\">\n <span data-testid=\"page-range\">{{ 'service-desk.pager.range' | transloco: range() }}</span>\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [disabled]=\"!hasPrevious()\" [attr.aria-label]=\"'service-desk.pager.previous' | transloco\" (click)=\"previous()\">\n <lw-icon name=\"serviceDeskPrevious\" size=\"1rem\"></lw-icon>\n </button>\n <button type=\"button\" class=\"relative lw-btn lw-btn--default lw-btn--sm lw-btn--icon\" [disabled]=\"!hasNext()\" [attr.aria-label]=\"'service-desk.pager.next' | transloco\" (click)=\"next()\">\n <lw-icon name=\"serviceDeskNext\" size=\"1rem\"></lw-icon>\n </button>\n </div>\n }\n</div>\n"
866
+ 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\">{{ desk.value()?.name ?? ('weaver-apps-service-desk.title' | transloco) }}</h2>\n <div class=\"flex flex-wrap items-center gap-3\">\n @if (count() !== null) {\n <span class=\"text-sm text-content-faint\" data-testid=\"request-count\">{{ 'weaver-apps-service-desk.table.count' | transloco: { count: count() } }}</span>\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-service-desk.actions.add' | transloco }}\n </button>\n </div>\n </div>\n\n @if (loadFailed()) {\n <p class=\"text-sm text-negative\" role=\"alert\">{{ 'weaver-apps-service-desk.errors.load' | transloco }}</p>\n }\n <div class=\"min-h-80 flex-1\">\n <wa-request-grid [fetch]=\"fetch()\" [locale]=\"locale()\" (requestActions)=\"actions($event)\" (counted)=\"counted($event)\" (failed)=\"failed()\" />\n </div>\n</div>\n"
631
867
  }]
632
868
  }],
633
- ctorParameters: () => []
869
+ propDecorators: { grid: [{
870
+ type: i0.ViewChild,
871
+ args: [i0.forwardRef(() => RequestGrid), { isSignal: true }]
872
+ }] }
634
873
  });
635
874
  const stroke = "fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"";
636
875
  const serviceDeskIcons = {
637
- serviceDesk: `<svg viewBox="0 0 24 24" ${stroke}><path d="M4 13.5V7a2.5 2.5 0 0 1 2.5-2.5h11A2.5 2.5 0 0 1 20 7v6.5"/><path d="M3 13.5h5l1.5 2.5h5l1.5-2.5h5V18a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>`,
638
- serviceDeskComplete: `<svg viewBox="0 0 24 24" ${stroke}><circle cx="12" cy="12" r="8.5"/><path d="M8.5 12.5l2.5 2.5 4.5-5"/></svg>`,
639
- serviceDeskCancel: `<svg viewBox="0 0 24 24" ${stroke}><circle cx="12" cy="12" r="8.5"/><path d="M9 9l6 6M15 9l-6 6"/></svg>`,
640
- serviceDeskPrevious: `<svg viewBox="0 0 24 24" ${stroke}><path d="M15 5l-7 7 7 7"/></svg>`,
641
- serviceDeskNext: `<svg viewBox="0 0 24 24" ${stroke}><path d="M9 5l7 7-7 7"/></svg>`
876
+ [serviceDeskIconNames.desk]: `<svg viewBox="0 0 24 24" ${stroke}><path d="M4 13.5V7a2.5 2.5 0 0 1 2.5-2.5h11A2.5 2.5 0 0 1 20 7v6.5"/><path d="M3 13.5h5l1.5 2.5h5l1.5-2.5h5V18a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z"/></svg>`,
877
+ [serviceDeskIconNames.complete]: `<svg viewBox="0 0 24 24" ${stroke}><circle cx="12" cy="12" r="8.5"/><path d="M8.5 12.5l2.5 2.5 4.5-5"/></svg>`,
878
+ [serviceDeskIconNames.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>`,
879
+ [serviceDeskIconNames.previous]: `<svg viewBox="0 0 24 24" ${stroke}><path d="M15 5l-7 7 7 7"/></svg>`,
880
+ [serviceDeskIconNames.next]: `<svg viewBox="0 0 24 24" ${stroke}><path d="M9 5l7 7-7 7"/></svg>`
642
881
  };
643
882
  function createServiceDeskPlugin(options = {}) {
644
883
  return {
645
884
  manifest: {
646
- id: "service-desk",
885
+ id: serviceDeskPluginId,
647
886
  name: "Service desk",
648
887
  capabilities: [
649
888
  "contributions",
@@ -655,24 +894,24 @@ function createServiceDeskPlugin(options = {}) {
655
894
  serviceDeskHost.bind(ctx);
656
895
  ctx.contributeIcons(serviceDeskIcons);
657
896
  ctx.registerSurface({
658
- id: "service-desk.desks",
659
- title: "service-desk.desks.title",
660
- icon: "serviceDesk",
897
+ id: serviceDeskContributionIds.desks,
898
+ title: "weaver-apps-service-desk.desks.title",
899
+ icon: serviceDeskIconNames.desk,
661
900
  routable: { path: deskListPath },
662
901
  component: DeskListPage
663
902
  });
664
903
  ctx.registerSurface({
665
- id: "service-desk.desk",
666
- title: "service-desk.title",
667
- icon: "serviceDesk",
904
+ id: serviceDeskContributionIds.desk,
905
+ title: "weaver-apps-service-desk.title",
906
+ icon: serviceDeskIconNames.desk,
668
907
  routable: { path: `${deskListPath}/:deskId` },
669
908
  component: DeskPage
670
909
  });
671
910
  if (options.railItem) ctx.registerRailItem({
672
- id: "service-desk.rail",
911
+ id: serviceDeskContributionIds.rail,
673
912
  rail: "primary",
674
- icon: "serviceDesk",
675
- title: "service-desk.desks.title",
913
+ icon: serviceDeskIconNames.desk,
914
+ title: "weaver-apps-service-desk.desks.title",
676
915
  run: () => ctx.navigateContent(deskListPath)
677
916
  });
678
917
  },
@@ -697,6 +936,16 @@ const getServiceDeskRequestsRaiseUrl = (deskId) => {
697
936
  const getServiceDeskRequestsListUrl = (deskId, params) => {
698
937
  const normalizedParams = new URLSearchParams();
699
938
  Object.entries(params || {}).forEach(([key, value]) => {
939
+ if (Array.isArray(value) && [
940
+ "type",
941
+ "status",
942
+ "priority"
943
+ ].includes(key)) {
944
+ value.forEach((v) => {
945
+ normalizedParams.append(key, v === null ? "null" : String(v));
946
+ });
947
+ return;
948
+ }
700
949
  if (value !== void 0) normalizedParams.append(key, value === null ? "null" : String(value));
701
950
  });
702
951
  const stringifiedParams = normalizedParams.toString();
@@ -768,6 +1017,6 @@ function createHttpServiceDeskGateway(options) {
768
1017
  }
769
1018
  };
770
1019
  }
771
- export { SERVICE_DESK_GATEWAY, ServiceDeskGatewayError, createHttpServiceDeskGateway, createServiceDeskPlugin, deskListPath, deskPath, provideServiceDeskGateway, serviceDeskPlugin };
1020
+ export { SERVICE_DESK_GATEWAY, ServiceDeskGatewayError, createHttpServiceDeskGateway, createServiceDeskPlugin, deskListPath, deskPath, provideServiceDeskGateway, serviceDeskContributionIds, serviceDeskPlugin, serviceDeskPluginId };
772
1021
 
773
1022
  //# sourceMappingURL=weaver-apps-service-desk.mjs.map