@weaver-apps/service-desk 0.3.2 → 0.4.1
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 +23 -0
- package/fesm2022/{weaver-apps-service-desk-desk-list-page-DPxs6zRX.mjs → weaver-apps-service-desk-desk-list-page-BDEm1BI8.mjs} +11 -6
- package/fesm2022/weaver-apps-service-desk-desk-list-page-BDEm1BI8.mjs.map +1 -0
- package/fesm2022/{weaver-apps-service-desk-desk-page-BWqYIYd1.mjs → weaver-apps-service-desk-desk-page-Dy1aAdoM.mjs} +6 -4
- package/fesm2022/{weaver-apps-service-desk-desk-page-BWqYIYd1.mjs.map → weaver-apps-service-desk-desk-page-Dy1aAdoM.mjs.map} +1 -1
- package/fesm2022/{weaver-apps-service-desk-service-desk-paths-BmQrbz-V.mjs → weaver-apps-service-desk-service-desk-paths-DDSDBqD8.mjs} +2 -2
- package/fesm2022/{weaver-apps-service-desk-service-desk-paths-BmQrbz-V.mjs.map → weaver-apps-service-desk-service-desk-paths-DDSDBqD8.mjs.map} +1 -1
- package/fesm2022/{weaver-apps-service-desk-service-desk-gateway-BK42Rgow.mjs → weaver-apps-service-desk-service-desk-titles-DcyaaS7c.mjs} +12 -2
- package/fesm2022/weaver-apps-service-desk-service-desk-titles-DcyaaS7c.mjs.map +1 -0
- package/fesm2022/weaver-apps-service-desk.mjs +5 -5
- package/package.json +2 -2
- package/types/weaver-apps-service-desk.d.ts +3 -1
- package/fesm2022/weaver-apps-service-desk-desk-list-page-DPxs6zRX.mjs.map +0 -1
- package/fesm2022/weaver-apps-service-desk-service-desk-gateway-BK42Rgow.mjs.map +0 -1
package/README.md
CHANGED
|
@@ -30,6 +30,29 @@ provideServiceDeskGateway(createHttpServiceDeskGateway({ baseUrl: '/weaver-apps'
|
|
|
30
30
|
dependency; the host composes nothing for it.
|
|
31
31
|
- If the host compiles the shell's theme with Tailwind, add the package and `@weaver-apps/data-grid` as an `@source`.
|
|
32
32
|
|
|
33
|
+
## Titles from the host
|
|
34
|
+
|
|
35
|
+
By default a desk is headed and listed by the name stored with it. A host that names desks itself, say in the
|
|
36
|
+
reader's language, hands those titles to the weaver:
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import { provideServiceDeskTitles } from '@weaver-apps/service-desk';
|
|
40
|
+
|
|
41
|
+
provideServiceDeskTitles(() => {
|
|
42
|
+
const names = inject(MyDeskNames);
|
|
43
|
+
return (deskId) => names.titleOf(deskId);
|
|
44
|
+
}),
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
- The factory runs in the host's injection context, so it may `inject` what it needs.
|
|
48
|
+
- The returned function answers a desk id with a title, or `null` (or a blank string) when the host has none. The
|
|
49
|
+
desk page's heading and the list then show the host's title, otherwise the stored name, and while neither is
|
|
50
|
+
known the weaver's own translated title.
|
|
51
|
+
- The weaver calls the function inside a `computed`. Every signal it reads, such as the host's active language, is
|
|
52
|
+
tracked, so a changed title re-titles the open page. A host with an observable turns it into a signal with
|
|
53
|
+
`toSignal` first.
|
|
54
|
+
- The function runs for every listed desk whenever a signal it reads changes; keep it a lookup.
|
|
55
|
+
|
|
33
56
|
## Names in the host
|
|
34
57
|
|
|
35
58
|
The plugin id is `weaver-apps-service-desk` (`serviceDeskPluginId`). Every id the plugin registers and every
|
|
@@ -1,14 +1,19 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as deskPath } from "./weaver-apps-service-desk-service-desk-paths-
|
|
1
|
+
import { i as SERVICE_DESK_GATEWAY, n as deskTitle, t as SERVICE_DESK_TITLES, u as serviceDeskHost } from "./weaver-apps-service-desk-service-desk-titles-DcyaaS7c.mjs";
|
|
2
|
+
import { n as deskPath } from "./weaver-apps-service-desk-service-desk-paths-DDSDBqD8.mjs";
|
|
3
3
|
import * as i0 from "@angular/core";
|
|
4
|
-
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, inject, resource } from "@angular/core";
|
|
4
|
+
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, computed, inject, resource } from "@angular/core";
|
|
5
5
|
import { TranslocoPipe } from "@jsverse/transloco";
|
|
6
6
|
var DeskListPage = class DeskListPage {
|
|
7
7
|
gateway = inject(SERVICE_DESK_GATEWAY);
|
|
8
|
+
titles = inject(SERVICE_DESK_TITLES, { optional: true });
|
|
8
9
|
desks = resource({
|
|
9
10
|
...ngDevMode ? { debugName: "desks" } : /* istanbul ignore next */ {},
|
|
10
11
|
loader: () => this.gateway.desks()
|
|
11
12
|
});
|
|
13
|
+
entries = computed(() => this.desks.hasValue() ? this.desks.value().map((desk) => ({
|
|
14
|
+
id: desk.id,
|
|
15
|
+
title: deskTitle(this.titles, desk.id, desk.name)
|
|
16
|
+
})) : [], ...ngDevMode ? [{ debugName: "entries" }] : /* istanbul ignore next */ []);
|
|
12
17
|
open(deskId) {
|
|
13
18
|
serviceDeskHost.navigate(deskPath(deskId));
|
|
14
19
|
}
|
|
@@ -44,7 +49,7 @@ var DeskListPage = class DeskListPage {
|
|
|
44
49
|
isStandalone: true,
|
|
45
50
|
selector: "wa-desk-list-page",
|
|
46
51
|
ngImport: i0,
|
|
47
|
-
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 <
|
|
52
|
+
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 <h1 class=\"text-lg font-semibold text-content\">{{ 'weaver-apps-service-desk.desks.title' | transloco }}</h1>\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 entries(); 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.title }}</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",
|
|
48
53
|
dependencies: [{
|
|
49
54
|
kind: "pipe",
|
|
50
55
|
type: TranslocoPipe,
|
|
@@ -65,10 +70,10 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
65
70
|
imports: [TranslocoPipe],
|
|
66
71
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
67
72
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
68
|
-
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 <
|
|
73
|
+
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 <h1 class=\"text-lg font-semibold text-content\">{{ 'weaver-apps-service-desk.desks.title' | transloco }}</h1>\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 entries(); 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.title }}</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"
|
|
69
74
|
}]
|
|
70
75
|
}]
|
|
71
76
|
});
|
|
72
77
|
export { DeskListPage };
|
|
73
78
|
|
|
74
|
-
//# sourceMappingURL=weaver-apps-service-desk-desk-list-page-
|
|
79
|
+
//# sourceMappingURL=weaver-apps-service-desk-desk-list-page-BDEm1BI8.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weaver-apps-service-desk-desk-list-page-BDEm1BI8.mjs","names":[],"sources":["../../../../libs/service-desk/src/lib/views/desk-list-page.ts","../../../../libs/service-desk/src/lib/views/desk-list-page.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, computed, CUSTOM_ELEMENTS_SCHEMA, inject, resource } from '@angular/core';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport { SERVICE_DESK_GATEWAY } from '../gateway/service-desk-gateway';\nimport { deskTitle, SERVICE_DESK_TITLES } from '../gateway/service-desk-titles';\nimport { serviceDeskHost } from '../plugin/service-desk-host';\nimport { deskPath } from '../plugin/service-desk-paths';\n\n@Component({\n selector: 'wa-desk-list-page',\n imports: [TranslocoPipe],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './desk-list-page.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DeskListPage {\n private readonly gateway = inject(SERVICE_DESK_GATEWAY);\n private readonly titles = inject(SERVICE_DESK_TITLES, { optional: true });\n\n protected readonly desks = resource({ loader: () => this.gateway.desks() });\n\n protected readonly entries = computed(() =>\n this.desks.hasValue() ? this.desks.value().map((desk) => ({ id: desk.id, title: deskTitle(this.titles, desk.id, desk.name) })) : [],\n );\n\n protected open(deskId: string): void {\n serviceDeskHost.navigate(deskPath(deskId));\n }\n\n protected async create(): Promise<void> {\n const name = await serviceDeskHost.ui.prompt({\n title: 'weaver-apps-service-desk.desks.create',\n message: 'weaver-apps-service-desk.desks.createMessage',\n confirmLabel: 'weaver-apps-service-desk.desks.createConfirm',\n });\n if (!name?.trim()) {\n return;\n }\n try {\n const desk = await this.gateway.saveDesk(crypto.randomUUID(), name.trim());\n this.open(desk.id);\n } catch {\n serviceDeskHost.ui.toast({ message: 'weaver-apps-service-desk.errors.create', kind: 'error' });\n }\n }\n}\n","<div class=\"flex h-full flex-col gap-4 overflow-auto p-6\">\n <div class=\"flex items-center justify-between gap-4\">\n <h1 class=\"text-lg font-semibold text-content\">{{ 'weaver-apps-service-desk.desks.title' | transloco }}</h1>\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 entries(); 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.title }}</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"],"mappings":";;;;;AAcA,IAAa,eAAb,MAAa,aAAY;CACN,UAAU,OAAO,oBAAoB;CACrC,SAAS,OAAO,qBAAqB,EAAE,UAAU,KAAI,CAAE;CAErD,QAAQ,SAAQ;EAAA,GAAA,YAAA,EAAA,WAAA,QAAA,+BAAA,CAAA;EAAG,cAAc,KAAK,QAAQ,MAAK;CAAE,CAAA;CAErD,UAAU,eAC3B,KAAK,MAAM,SAAQ,IAAK,KAAK,MAAM,MAAK,CAAE,CAAC,KAAK,UAAU;EAAE,IAAI,KAAK;EAAI,OAAO,UAAU,KAAK,QAAQ,KAAK,IAAI,KAAK,IAAI;CAAC,EAAG,IAAI,CAAA,GAClI,GAAA,YAAA,CAAA,EAAA,WAAA,UAAA,CAAA,+BAAA,CAAA,CAAA;CAES,KAAK,QAAc;EAC3B,gBAAgB,SAAS,SAAS,MAAM,CAAC;CAC3C;CAEU,MAAM,SAAM;EACpB,MAAM,OAAO,MAAM,gBAAgB,GAAG,OAAO;GAC3C,OAAO;GACP,SAAS;GACT,cAAc;EACf,CAAA;EACD,IAAI,CAAC,MAAM,KAAI,GACb;EAEF,IAAI;GACF,MAAM,OAAO,MAAM,KAAK,QAAQ,SAAS,OAAO,WAAU,GAAI,KAAK,KAAI,CAAE;GACzE,KAAK,KAAK,KAAK,EAAE;EACnB,QAAQ;GACN,gBAAgB,GAAG,MAAM;IAAE,SAAS;IAA0C,MAAM;GAAO,CAAE;EAC/F;CACF;CA7BW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAY,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAZ,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAY,cAAA;EAAA,UAAA;EAAA,UAAA;EAAA,UCdzB;EAsBA,cAAA,CAAA;GAAA,MAAA;GAAA,MDbY;GAAa,MAAA;EAAA,CAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAKZ,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAY,YAAA,CAAA;EAPxB,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAmB,SACpB,CAAC,aAAa;GAAC,SACf,CAAC,sBAAsB;GAAC,iBAEhB,wBAAwB;GAAM,UAAA;EAAA,CAAA"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as serviceDeskIconNames, i as SERVICE_DESK_GATEWAY, n as deskTitle, t as SERVICE_DESK_TITLES, u as serviceDeskHost } from "./weaver-apps-service-desk-service-desk-titles-DcyaaS7c.mjs";
|
|
2
2
|
import * as i0 from "@angular/core";
|
|
3
3
|
import { CUSTOM_ELEMENTS_SCHEMA, ChangeDetectionStrategy, Component, computed, inject, input, output, resource, signal, viewChild } from "@angular/core";
|
|
4
4
|
import { TranslocoPipe, TranslocoService, translateObjectSignal } from "@jsverse/transloco";
|
|
@@ -580,6 +580,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
580
580
|
});
|
|
581
581
|
var DeskPage = class DeskPage {
|
|
582
582
|
gateway = inject(SERVICE_DESK_GATEWAY);
|
|
583
|
+
titles = inject(SERVICE_DESK_TITLES, { optional: true });
|
|
583
584
|
route = inject(ActivatedRoute);
|
|
584
585
|
grid = viewChild(RequestGrid, ...ngDevMode ? [{ debugName: "grid" }] : /* istanbul ignore next */ []);
|
|
585
586
|
locale = activeLocale();
|
|
@@ -591,6 +592,7 @@ var DeskPage = class DeskPage {
|
|
|
591
592
|
params: () => this.deskId(),
|
|
592
593
|
loader: ({ params }) => this.gateway.desk(params)
|
|
593
594
|
});
|
|
595
|
+
title = computed(() => deskTitle(this.titles, this.deskId(), this.desk.hasValue() ? this.desk.value()?.name ?? null : null), ...ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []);
|
|
594
596
|
fetch = computed(() => {
|
|
595
597
|
const deskId = this.deskId();
|
|
596
598
|
return async (request) => {
|
|
@@ -737,7 +739,7 @@ var DeskPage = class DeskPage {
|
|
|
737
739
|
isSignal: true
|
|
738
740
|
}],
|
|
739
741
|
ngImport: i0,
|
|
740
|
-
template: "<div class=\"flex h-full flex-col gap-4 p-6\">\n <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <
|
|
742
|
+
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 <h1 class=\"text-lg font-semibold text-content\">{{ title() ?? ('weaver-apps-service-desk.title' | transloco) }}</h1>\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)\" (openRequest)=\"open($event)\" (counted)=\"counted($event)\" (failed)=\"failed()\" />\n </div>\n</div>\n",
|
|
741
743
|
dependencies: [{
|
|
742
744
|
kind: "component",
|
|
743
745
|
type: RequestGrid,
|
|
@@ -769,7 +771,7 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
769
771
|
imports: [TranslocoPipe, RequestGrid],
|
|
770
772
|
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
771
773
|
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
772
|
-
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 <
|
|
774
|
+
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 <h1 class=\"text-lg font-semibold text-content\">{{ title() ?? ('weaver-apps-service-desk.title' | transloco) }}</h1>\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)\" (openRequest)=\"open($event)\" (counted)=\"counted($event)\" (failed)=\"failed()\" />\n </div>\n</div>\n"
|
|
773
775
|
}]
|
|
774
776
|
}],
|
|
775
777
|
propDecorators: { grid: [{
|
|
@@ -779,4 +781,4 @@ i0.ɵɵngDeclareClassMetadata({
|
|
|
779
781
|
});
|
|
780
782
|
export { DeskPage };
|
|
781
783
|
|
|
782
|
-
//# sourceMappingURL=weaver-apps-service-desk-desk-page-
|
|
784
|
+
//# sourceMappingURL=weaver-apps-service-desk-desk-page-Dy1aAdoM.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weaver-apps-service-desk-desk-page-BWqYIYd1.mjs","names":[],"sources":["../../../../libs/service-desk/src/lib/views/active-locale.ts","../../../../libs/service-desk/src/lib/api/models/serviceRequestOrigin.ts","../../../../libs/service-desk/src/lib/api/models/serviceRequestPriority.ts","../../../../libs/service-desk/src/lib/api/models/serviceRequestStatus.ts","../../../../libs/service-desk/src/lib/api/models/serviceRequestType.ts","../../../../libs/service-desk/src/lib/views/vocabulary.ts","../../../../libs/service-desk/src/lib/views/request-dialog.ts","../../../../libs/service-desk/src/lib/views/request-dialog.html","../../../../libs/service-desk/src/lib/views/request-cell.ts","../../../../libs/service-desk/src/lib/views/request-cell.html","../../../../libs/service-desk/src/lib/views/request-listing.ts","../../../../libs/service-desk/src/lib/views/request-grid.ts","../../../../libs/service-desk/src/lib/views/request-grid.html","../../../../libs/service-desk/src/lib/views/desk-page.ts","../../../../libs/service-desk/src/lib/views/desk-page.html"],"sourcesContent":["import { inject, Signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { TranslocoService } from '@jsverse/transloco';\n\nexport function activeLocale(): Signal<string> {\n const transloco = inject(TranslocoService);\n return toSignal(transloco.langChanges$, { initialValue: transloco.getActiveLang() });\n}\n","/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Weaver Apps\n * OpenAPI spec version: 1.0.0\n */\n\n/**\n * How a service request came about.\n */\nexport type ServiceRequestOrigin = typeof ServiceRequestOrigin[keyof typeof ServiceRequestOrigin];\n\n\nexport const ServiceRequestOrigin = {\n manual: 'manual',\n voiceChat: 'voiceChat',\n textChat: 'textChat',\n widget: 'widget',\n email: 'email',\n phone: 'phone',\n} as const;\n","/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Weaver Apps\n * OpenAPI spec version: 1.0.0\n */\n\n/**\n * How urgent a service request is.\n */\nexport type ServiceRequestPriority = typeof ServiceRequestPriority[keyof typeof ServiceRequestPriority];\n\n\nexport const ServiceRequestPriority = {\n low: 'low',\n normal: 'normal',\n high: 'high',\n urgent: 'urgent',\n} as const;\n","/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Weaver Apps\n * OpenAPI spec version: 1.0.0\n */\n\n/**\n * Where a service request stands.\n */\nexport type ServiceRequestStatus = typeof ServiceRequestStatus[keyof typeof ServiceRequestStatus];\n\n\nexport const ServiceRequestStatus = {\n open: 'open',\n inProgress: 'inProgress',\n completed: 'completed',\n cancelled: 'cancelled',\n} as const;\n","/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Weaver Apps\n * OpenAPI spec version: 1.0.0\n */\n\n/**\n * What kind of matter a service request is.\n */\nexport type ServiceRequestType = typeof ServiceRequestType[keyof typeof ServiceRequestType];\n\n\nexport const ServiceRequestType = {\n inquiry: 'inquiry',\n task: 'task',\n callbackRequest: 'callbackRequest',\n conversationSummary: 'conversationSummary',\n complaint: 'complaint',\n quote: 'quote',\n} as const;\n","import {\n ServiceRequestOrigin,\n ServiceRequestPriority,\n ServiceRequestStatus,\n ServiceRequestType,\n} from '../api/models';\n\nexport const requestTypes: readonly ServiceRequestType[] = Object.values(ServiceRequestType);\nexport const requestPriorities: readonly ServiceRequestPriority[] = Object.values(ServiceRequestPriority);\nexport const requestOrigins: readonly ServiceRequestOrigin[] = Object.values(ServiceRequestOrigin);\nexport const settableStatuses: readonly ServiceRequestStatus[] = [ServiceRequestStatus.open, ServiceRequestStatus.inProgress];\n\nexport function isClosed(status: ServiceRequestStatus): boolean {\n return status === ServiceRequestStatus.completed || status === ServiceRequestStatus.cancelled;\n}\n\nexport function localDate(value: Date): string {\n const pad = (part: number) => `${part}`.padStart(2, '0');\n return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;\n}\n\nexport function pick<T extends string>(choices: readonly T[], value: string): T | undefined {\n return choices.find((choice) => choice === value);\n}\n","import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { DialogRef } from '@loomweaver/plugin-sdk';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport {\n ServiceRequestOrigin,\n ServiceRequestPriority,\n ServiceRequestType,\n type ServiceRequestChanges,\n type ServiceRequestResponse,\n type ServiceRequestStatus,\n} from '../api/models';\nimport { SERVICE_DESK_GATEWAY, ServiceRequestOutcome } from '../gateway/service-desk-gateway';\nimport { isClosed, localDate, pick, requestOrigins, requestPriorities, requestTypes, settableStatuses } from './vocabulary';\n\nexport interface RequestDialogData {\n readonly deskId: string;\n readonly request: ServiceRequestResponse | null;\n}\n\n@Component({\n selector: 'wa-request-dialog',\n imports: [TranslocoPipe],\n templateUrl: './request-dialog.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class RequestDialog {\n private readonly dialog = inject<DialogRef<boolean>>(DialogRef);\n private readonly gateway = inject(SERVICE_DESK_GATEWAY);\n private readonly data = this.dialog.data as RequestDialogData;\n private existing = this.data.request;\n\n protected readonly editing = this.existing !== null;\n protected readonly closed = this.existing !== null && isClosed(this.existing.status);\n protected readonly types = requestTypes;\n protected readonly priorities = requestPriorities;\n protected readonly origins = requestOrigins;\n protected readonly statuses = settableStatuses;\n\n protected readonly title = signal(this.existing?.title ?? '');\n protected readonly type = signal<ServiceRequestType>(this.existing?.type ?? ServiceRequestType.inquiry);\n protected readonly priority = signal<ServiceRequestPriority>(this.existing?.priority ?? ServiceRequestPriority.normal);\n protected readonly origin = signal<ServiceRequestOrigin>(this.existing?.origin ?? ServiceRequestOrigin.manual);\n protected readonly status = signal<ServiceRequestStatus>(this.existing?.status ?? 'open');\n protected readonly contactName = signal(this.existing?.contactName ?? '');\n protected readonly contactPhone = signal(this.existing?.contactPhone ?? '');\n protected readonly contactEmail = signal(this.existing?.contactEmail ?? '');\n protected readonly assignee = signal(this.existing?.assignee ?? '');\n protected readonly dueDate = signal(this.existing?.dueAt ? localDate(new Date(this.existing.dueAt)) : '');\n protected readonly description = signal(this.existing?.description ?? '');\n protected readonly cancellationReason = this.existing?.cancellationReason ?? null;\n protected readonly saving = signal(false);\n protected readonly problem = signal('');\n private persisted = false;\n\n protected bind(target: { set(value: string): void }, event: Event): void {\n target.set((event.target as HTMLInputElement).value);\n }\n\n protected chooseType(event: Event): void {\n this.choose(this.type, requestTypes, event);\n }\n\n protected choosePriority(event: Event): void {\n this.choose(this.priority, requestPriorities, event);\n }\n\n protected chooseOrigin(event: Event): void {\n this.choose(this.origin, requestOrigins, event);\n }\n\n protected chooseStatus(event: Event): void {\n this.choose(this.status, settableStatuses, event);\n }\n\n protected close(): void {\n this.dialog.close(this.persisted);\n }\n\n protected async save(): Promise<void> {\n if (!this.title().trim()) {\n this.problem.set('weaver-apps-service-desk.dialog.titleRequired');\n return;\n }\n this.saving.set(true);\n try {\n const changes = this.changes();\n const outcome = this.existing\n ? await this.gateway.edit(this.data.deskId, this.existing.id, changes)\n : await this.gateway.raise(this.data.deskId, { ...changes, origin: this.origin() });\n this.settle(await this.applyStatus(outcome));\n } catch {\n this.problem.set('weaver-apps-service-desk.errors.save');\n } finally {\n this.saving.set(false);\n }\n }\n\n private changes(): ServiceRequestChanges {\n const keptDue = this.existing?.dueAt && localDate(new Date(this.existing.dueAt)) === this.dueDate() ? this.existing.dueAt : null;\n if (keptDue) {\n return { ...this.fields(), dueAt: keptDue };\n }\n const due = this.dueDate() ? new Date(`${this.dueDate()}T12:00`) : null;\n return { ...this.fields(), dueAt: due && !Number.isNaN(due.getTime()) ? due.toISOString() : null };\n }\n\n private fields(): Omit<ServiceRequestChanges, 'dueAt'> {\n return {\n title: this.title().trim(),\n description: this.optional(this.description()),\n type: this.type(),\n priority: this.priority(),\n contactName: this.optional(this.contactName()),\n contactPhone: this.optional(this.contactPhone()),\n contactEmail: this.optional(this.contactEmail()),\n assignee: this.optional(this.assignee()),\n };\n }\n\n private async applyStatus(outcome: ServiceRequestOutcome): Promise<ServiceRequestOutcome> {\n if (outcome.kind === 'saved') {\n this.existing = outcome.request;\n this.persisted = true;\n }\n if (outcome.kind !== 'saved' || outcome.request.status === this.status()) {\n return outcome;\n }\n return this.gateway.setStatus(this.data.deskId, outcome.request.id, this.status());\n }\n\n private settle(outcome: ServiceRequestOutcome): void {\n const messages: Record<Exclude<ServiceRequestOutcome['kind'], 'saved'>, string> = {\n closed: 'weaver-apps-service-desk.dialog.closed',\n changed: 'weaver-apps-service-desk.dialog.changed',\n invalid: 'weaver-apps-service-desk.dialog.invalid',\n missing: 'weaver-apps-service-desk.dialog.missing',\n };\n if (outcome.kind === 'saved') {\n this.dialog.close(true);\n return;\n }\n this.problem.set(messages[outcome.kind]);\n }\n\n private choose<T extends string>(target: { set(value: T): void }, choices: readonly T[], event: Event): void {\n const chosen = pick(choices, (event.target as HTMLSelectElement).value);\n if (chosen) {\n target.set(chosen);\n }\n }\n\n private optional(value: string): string | null {\n return value.trim() ? value.trim() : null;\n }\n}\n","<form class=\"flex flex-col gap-4\" (submit)=\"$event.preventDefault(); save()\">\n @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","import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, computed, signal } from '@angular/core';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport type { ICellRendererAngularComp } from 'ag-grid-angular';\nimport type { ICellRendererParams } from 'ag-grid-community';\nimport type { ServiceRequestResponse } from '../api/models';\nimport { isClosed } from './vocabulary';\n\nexport interface RequestActions {\n readonly request: ServiceRequestResponse;\n readonly x: number;\n readonly y: number;\n}\n\nexport type RequestCellKind = 'title' | 'type' | 'priority' | 'status' | 'contact' | 'raised' | 'due' | 'actions';\n\nexport interface RequestCellParams {\n readonly kind: RequestCellKind;\n readonly locale: () => string;\n readonly actions: (actions: RequestActions) => void;\n}\n\ntype Params = ICellRendererParams<ServiceRequestResponse> & RequestCellParams;\n\n@Component({\n selector: 'wa-request-cell',\n imports: [TranslocoPipe],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './request-cell.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class RequestCell implements ICellRendererAngularComp {\n private params: Params | null = null;\n\n protected readonly kind = signal<RequestCellKind>('title');\n protected readonly request = signal<ServiceRequestResponse | null>(null);\n private readonly locale = signal('en');\n protected readonly closed = computed(() => {\n const request = this.request();\n return request !== null && isClosed(request.status);\n });\n protected readonly dates = computed(() => {\n const request = this.request();\n const date = new Intl.DateTimeFormat(this.locale(), { day: '2-digit', month: '2-digit', year: 'numeric' });\n return {\n raised: request ? date.format(new Date(request.raisedAt)) : '',\n due: request?.dueAt ? date.format(new Date(request.dueAt)) : '',\n };\n });\n\n agInit(params: Params): void {\n this.params = params;\n this.kind.set(params.kind);\n this.request.set(params.data ?? null);\n this.locale.set(params.locale());\n }\n\n refresh(params: Params): boolean {\n this.agInit(params);\n return true;\n }\n\n protected openActions(event: MouseEvent): void {\n const request = this.request();\n if (request) {\n this.params?.actions({ request, ...menuAnchor(event) });\n }\n }\n}\n\nfunction menuAnchor(event: MouseEvent): { readonly x: number; readonly y: number } {\n if (event.detail > 0) {\n return { x: event.clientX, y: event.clientY };\n }\n const bounds = (event.currentTarget as HTMLElement).getBoundingClientRect();\n return { x: bounds.left, y: bounds.bottom };\n}\n","@if (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","import type { ListFilterModel, ListingRequest } from '@weaver-apps/data-grid';\nimport { ServiceRequestStatus, type ServiceDeskRequestsListParams } from '../api/models';\n\nexport const requestColumn = {\n title: 'title',\n type: 'type',\n priority: 'priority',\n status: 'status',\n contact: 'contact',\n raised: 'raisedAt',\n due: 'dueAt',\n actions: 'actions',\n} as const;\n\nexport interface TextFilterModel {\n readonly filterType: 'text';\n readonly type: 'contains';\n readonly filter?: string | null;\n}\n\nexport interface RequestFilterModel {\n readonly [requestColumn.title]?: TextFilterModel;\n readonly [requestColumn.contact]?: TextFilterModel;\n readonly [requestColumn.type]?: ListFilterModel;\n readonly [requestColumn.status]?: ListFilterModel;\n readonly [requestColumn.priority]?: ListFilterModel;\n}\n\nexport const defaultStatuses: readonly ServiceRequestStatus[] = [\n ServiceRequestStatus.open,\n ServiceRequestStatus.inProgress,\n ServiceRequestStatus.completed,\n];\n\nexport const defaultRequestFilter: RequestFilterModel = {\n [requestColumn.status]: { values: [...defaultStatuses] },\n};\n\nconst sortableColumns: ReadonlySet<string> = new Set([\n requestColumn.title,\n requestColumn.priority,\n requestColumn.status,\n requestColumn.raised,\n requestColumn.due,\n]);\n\nexport function listingOf(request: ListingRequest<RequestFilterModel>): ServiceDeskRequestsListParams {\n const { filters, sort } = request;\n const sorted = sort && sortableColumns.has(sort.column) ? { sort: sort.column, descending: sort.descending } : {};\n return {\n ...sorted,\n ...textOf('text', filters[requestColumn.title]),\n ...textOf('contact', filters[requestColumn.contact]),\n ...valuesOf('type', filters[requestColumn.type]),\n ...valuesOf('status', filters[requestColumn.status]),\n ...valuesOf('priority', filters[requestColumn.priority]),\n offset: request.offset,\n limit: request.limit,\n };\n}\n\nfunction textOf(key: 'text' | 'contact', filter: TextFilterModel | undefined): ServiceDeskRequestsListParams {\n const text = filter?.filter?.trim();\n return text ? { [key]: text } : {};\n}\n\nfunction valuesOf(key: 'type' | 'status' | 'priority', filter: ListFilterModel | undefined): ServiceDeskRequestsListParams {\n const values = filter?.values ?? [];\n return values.length > 0 ? { [key]: [...values] } : {};\n}\n","import { ChangeDetectionStrategy, Component, computed, input, output, type Signal } from '@angular/core';\nimport { translateObjectSignal } from '@jsverse/transloco';\nimport { DataGrid, ListFilter, type ListFilterParams, type ListingFetch, listingDatasource, openOnRow } from '@weaver-apps/data-grid';\nimport type { ColDef, GridApi, GridOptions } from 'ag-grid-community';\nimport type { ServiceRequestResponse } from '../api/models';\nimport { RequestCell, type RequestActions, type RequestCellKind, type RequestCellParams } from './request-cell';\nimport { defaultRequestFilter, requestColumn, type RequestFilterModel } from './request-listing';\nimport { isClosed, requestPriorities, requestTypes } from './vocabulary';\n\ntype Texts = Readonly<Record<string, string>>;\n\nconst allStatuses = ['open', 'inProgress', 'completed', 'cancelled'] as const;\nconst actionsWidthPx = 72;\nconst titleMinWidthPx = 160;\nconst contactMinWidthPx = 140;\nconst narrowMinWidthPx = 112;\n\n@Component({\n selector: 'wa-request-grid',\n imports: [DataGrid],\n templateUrl: './request-grid.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: 'block h-full w-full' },\n})\nexport class RequestGrid {\n private readonly table = translateObjectSignal('weaver-apps-service-desk.table') as Signal<Texts>;\n private readonly types = translateObjectSignal('weaver-apps-service-desk.type') as Signal<Texts>;\n private readonly statuses = translateObjectSignal('weaver-apps-service-desk.status') as Signal<Texts>;\n private readonly priorities = translateObjectSignal('weaver-apps-service-desk.priority') as Signal<Texts>;\n private api: GridApi<ServiceRequestResponse> | null = null;\n\n readonly fetch = input.required<ListingFetch<ServiceRequestResponse, RequestFilterModel>>();\n readonly locale = input.required<string>();\n\n readonly requestActions = output<RequestActions>();\n readonly openRequest = output<ServiceRequestResponse>();\n readonly counted = output<number>();\n readonly failed = output<void>();\n\n protected readonly translated = computed(() => typeof this.table() === 'object' && typeof this.table()['title'] === 'string');\n\n protected readonly empty = computed(() => ({ noRowsToShow: this.table()['empty'] ?? '' }));\n\n protected readonly options = computed<GridOptions<ServiceRequestResponse>>(() => ({\n columnDefs: this.columns(this.table()),\n defaultColDef: { sortable: false, resizable: true, suppressHeaderMenuButton: true },\n datasource: listingDatasource(this.fetch(), {\n answered: (answer) => this.counted.emit(answer.total),\n failed: () => this.failed.emit(),\n }),\n initialState: {\n sort: { sortModel: [{ colId: requestColumn.raised, sort: 'desc' }] },\n filter: { filterModel: { ...defaultRequestFilter } },\n },\n rowClassRules: { 'text-content-faint': (params) => !!params.data && isClosed(params.data.status) },\n ...openOnRow<ServiceRequestResponse>((request) => this.openRequest.emit(request), { except: [requestColumn.actions] }),\n }));\n\n reload(): void {\n this.api?.refreshInfiniteCache();\n }\n\n protected ready(api: GridApi<ServiceRequestResponse>): void {\n this.api = api;\n }\n\n private columns(table: Texts): ColDef<ServiceRequestResponse>[] {\n const narrow: ColDef<ServiceRequestResponse> = { flex: 1, minWidth: narrowMinWidthPx };\n return [\n {\n ...this.cell(requestColumn.title, 'title', table['title']),\n sortable: true,\n flex: 3,\n minWidth: titleMinWidthPx,\n filter: 'agTextColumnFilter',\n filterParams: { filterOptions: ['contains'], maxNumConditions: 1 },\n },\n {\n ...this.cell(requestColumn.type, 'type', table['type']),\n ...narrow,\n filter: ListFilter,\n filterParams: this.choices(requestTypes, this.types),\n },\n {\n ...this.cell(requestColumn.priority, 'priority', table['priority']),\n ...narrow,\n sortable: true,\n filter: ListFilter,\n filterParams: this.choices(requestPriorities, this.priorities),\n },\n {\n ...this.cell(requestColumn.status, 'status', table['status']),\n ...narrow,\n sortable: true,\n filter: ListFilter,\n filterParams: this.choices(allStatuses, this.statuses),\n },\n {\n ...this.cell(requestColumn.contact, 'contact', table['contact']),\n flex: 2,\n minWidth: contactMinWidthPx,\n filter: 'agTextColumnFilter',\n filterParams: { filterOptions: ['contains'], maxNumConditions: 1 },\n },\n { ...this.cell(requestColumn.raised, 'raised', table['raised']), ...narrow, sortable: true, sortingOrder: ['desc', 'asc'] },\n { ...this.cell(requestColumn.due, 'due', table['due']), ...narrow, sortable: true },\n { ...this.cell(requestColumn.actions, 'actions', ''), width: actionsWidthPx, resizable: false, sortable: false, pinned: 'right' },\n ];\n }\n\n private cell(colId: string, kind: RequestCellKind, headerName: string | undefined): ColDef<ServiceRequestResponse> {\n const params: RequestCellParams = {\n kind,\n locale: () => this.locale(),\n actions: (actions) => this.requestActions.emit(actions),\n };\n return { colId, headerName: headerName ?? '', cellRenderer: RequestCell, cellRendererParams: params };\n }\n\n private choices(values: readonly string[], labels: Signal<Texts>): ListFilterParams {\n return { options: () => values.map((value) => ({ value, label: labels()[value] ?? value })) };\n }\n}\n","@if (translated()) {\n <wa-data-grid [options]=\"options()\" [localeOverrides]=\"empty()\" (ready)=\"ready($event)\" />\n}\n","import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, computed, inject, resource, signal, viewChild } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute } from '@angular/router';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport type { ListingAnswer, ListingFetch, ListingRequest } from '@weaver-apps/data-grid';\nimport { map } from 'rxjs';\nimport type { ServiceRequestResponse } from '../api/models';\nimport { SERVICE_DESK_GATEWAY, ServiceRequestOutcome } from '../gateway/service-desk-gateway';\nimport { serviceDeskHost } from '../plugin/service-desk-host';\nimport { serviceDeskIconNames } from '../plugin/service-desk-ids';\nimport { activeLocale } from './active-locale';\nimport type { RequestActions } from './request-cell';\nimport { RequestDialog, RequestDialogData } from './request-dialog';\nimport { RequestGrid } from './request-grid';\nimport { listingOf, type RequestFilterModel } from './request-listing';\nimport { isClosed } from './vocabulary';\n\n@Component({\n selector: 'wa-desk-page',\n imports: [TranslocoPipe, RequestGrid],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './desk-page.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DeskPage {\n private readonly gateway = inject(SERVICE_DESK_GATEWAY);\n private readonly route = inject(ActivatedRoute);\n private readonly grid = viewChild(RequestGrid);\n\n protected readonly locale = activeLocale();\n protected readonly deskId = toSignal(this.route.paramMap.pipe(map((params) => params.get('deskId') ?? '')), {\n initialValue: this.route.snapshot.paramMap.get('deskId') ?? '',\n });\n protected readonly count = signal<number | null>(null);\n protected readonly loadFailed = signal(false);\n\n protected readonly desk = resource({\n params: () => this.deskId(),\n loader: ({ params }) => this.gateway.desk(params),\n });\n\n protected readonly fetch = computed<ListingFetch<ServiceRequestResponse, RequestFilterModel>>(() => {\n const deskId = this.deskId();\n return async (request: ListingRequest<RequestFilterModel>): Promise<ListingAnswer<ServiceRequestResponse>> => {\n const page = await this.gateway.requests(deskId, listingOf(request));\n return { items: page.items, total: Number(page.total) };\n };\n });\n\n protected counted(total: number): void {\n this.loadFailed.set(false);\n this.count.set(total);\n }\n\n protected failed(): void {\n this.loadFailed.set(true);\n }\n\n protected add(): void {\n this.openDialog({ deskId: this.deskId(), request: null }, 'weaver-apps-service-desk.dialog.addTitle');\n }\n\n protected actions({ request, x, y }: RequestActions): void {\n const closed = isClosed(request.status);\n serviceDeskHost.ui.openMenu(\n [\n { label: closed ? 'weaver-apps-service-desk.actions.view' : 'weaver-apps-service-desk.actions.edit', icon: closed ? 'preview' : 'edit', run: () => this.open(request) },\n ...(closed\n ? []\n : [\n { label: 'weaver-apps-service-desk.actions.complete', icon: serviceDeskIconNames.complete, run: () => void this.complete(request) },\n { label: 'weaver-apps-service-desk.actions.cancel', icon: serviceDeskIconNames.cancel, run: () => void this.cancel(request) },\n ]),\n { label: 'weaver-apps-service-desk.actions.delete', icon: 'trash', run: () => void this.remove(request) },\n ],\n { x, y },\n );\n }\n\n protected open(request: ServiceRequestResponse): void {\n const closed = isClosed(request.status);\n this.openDialog({ deskId: this.deskId(), request }, closed ? 'weaver-apps-service-desk.dialog.viewTitle' : 'weaver-apps-service-desk.dialog.editTitle');\n }\n\n protected async complete(request: ServiceRequestResponse): Promise<void> {\n await this.attempt(async () => this.report(await this.gateway.complete(this.deskId(), request.id), 'weaver-apps-service-desk.complete.done'));\n }\n\n protected async cancel(request: ServiceRequestResponse): Promise<void> {\n const reason = await serviceDeskHost.ui.prompt({\n title: 'weaver-apps-service-desk.cancel.title',\n message: 'weaver-apps-service-desk.cancel.message',\n confirmLabel: 'weaver-apps-service-desk.cancel.confirm',\n tone: 'warning',\n });\n if (reason === null) {\n return;\n }\n await this.attempt(async () =>\n this.report(await this.gateway.cancel(this.deskId(), request.id, reason.trim() || null), 'weaver-apps-service-desk.cancel.done'),\n );\n }\n\n protected async remove(request: ServiceRequestResponse): Promise<void> {\n const confirmed = await serviceDeskHost.ui.confirm({\n title: 'weaver-apps-service-desk.delete.title',\n message: 'weaver-apps-service-desk.delete.message',\n confirmLabel: 'weaver-apps-service-desk.delete.confirm',\n tone: 'danger',\n });\n if (!confirmed) {\n return;\n }\n await this.attempt(async () => {\n await this.gateway.remove(this.deskId(), request.id);\n serviceDeskHost.ui.toast({ message: 'weaver-apps-service-desk.delete.done', kind: 'success' });\n this.reload();\n });\n }\n\n private reload(): void {\n this.grid()?.reload();\n }\n\n private async attempt(work: () => Promise<void>): Promise<void> {\n try {\n await work();\n } catch {\n serviceDeskHost.ui.toast({ message: 'weaver-apps-service-desk.errors.save', kind: 'error' });\n this.reload();\n }\n }\n\n private openDialog(data: RequestDialogData, title: string): void {\n const dialog = serviceDeskHost.ui.open<boolean>(RequestDialog, { data, title, size: 'lg', dismiss: 'explicit' });\n void dialog.closed.then((saved) => {\n if (saved) {\n serviceDeskHost.ui.toast({ message: 'weaver-apps-service-desk.dialog.saved', kind: 'success' });\n this.reload();\n }\n });\n }\n\n private report(outcome: ServiceRequestOutcome, done: string): void {\n const saved = outcome.kind === 'saved';\n serviceDeskHost.ui.toast({ message: saved ? done : 'weaver-apps-service-desk.errors.save', kind: saved ? 'success' : 'error' });\n this.reload();\n }\n}\n","<div class=\"flex h-full flex-col gap-4 p-6\">\n <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <h2 class=\"text-lg font-semibold text-content\">{{ 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)\" (openRequest)=\"open($event)\" (counted)=\"counted($event)\" (failed)=\"failed()\" />\n </div>\n</div>\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"weaver-apps-service-desk-desk-page-Dy1aAdoM.mjs","names":[],"sources":["../../../../libs/service-desk/src/lib/views/active-locale.ts","../../../../libs/service-desk/src/lib/api/models/serviceRequestOrigin.ts","../../../../libs/service-desk/src/lib/api/models/serviceRequestPriority.ts","../../../../libs/service-desk/src/lib/api/models/serviceRequestStatus.ts","../../../../libs/service-desk/src/lib/api/models/serviceRequestType.ts","../../../../libs/service-desk/src/lib/views/vocabulary.ts","../../../../libs/service-desk/src/lib/views/request-dialog.ts","../../../../libs/service-desk/src/lib/views/request-dialog.html","../../../../libs/service-desk/src/lib/views/request-cell.ts","../../../../libs/service-desk/src/lib/views/request-cell.html","../../../../libs/service-desk/src/lib/views/request-listing.ts","../../../../libs/service-desk/src/lib/views/request-grid.ts","../../../../libs/service-desk/src/lib/views/request-grid.html","../../../../libs/service-desk/src/lib/views/desk-page.ts","../../../../libs/service-desk/src/lib/views/desk-page.html"],"sourcesContent":["import { inject, Signal } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { TranslocoService } from '@jsverse/transloco';\n\nexport function activeLocale(): Signal<string> {\n const transloco = inject(TranslocoService);\n return toSignal(transloco.langChanges$, { initialValue: transloco.getActiveLang() });\n}\n","/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Weaver Apps\n * OpenAPI spec version: 1.0.0\n */\n\n/**\n * How a service request came about.\n */\nexport type ServiceRequestOrigin = typeof ServiceRequestOrigin[keyof typeof ServiceRequestOrigin];\n\n\nexport const ServiceRequestOrigin = {\n manual: 'manual',\n voiceChat: 'voiceChat',\n textChat: 'textChat',\n widget: 'widget',\n email: 'email',\n phone: 'phone',\n} as const;\n","/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Weaver Apps\n * OpenAPI spec version: 1.0.0\n */\n\n/**\n * How urgent a service request is.\n */\nexport type ServiceRequestPriority = typeof ServiceRequestPriority[keyof typeof ServiceRequestPriority];\n\n\nexport const ServiceRequestPriority = {\n low: 'low',\n normal: 'normal',\n high: 'high',\n urgent: 'urgent',\n} as const;\n","/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Weaver Apps\n * OpenAPI spec version: 1.0.0\n */\n\n/**\n * Where a service request stands.\n */\nexport type ServiceRequestStatus = typeof ServiceRequestStatus[keyof typeof ServiceRequestStatus];\n\n\nexport const ServiceRequestStatus = {\n open: 'open',\n inProgress: 'inProgress',\n completed: 'completed',\n cancelled: 'cancelled',\n} as const;\n","/**\n * Generated by orval 🍺\n * Do not edit manually.\n * Weaver Apps\n * OpenAPI spec version: 1.0.0\n */\n\n/**\n * What kind of matter a service request is.\n */\nexport type ServiceRequestType = typeof ServiceRequestType[keyof typeof ServiceRequestType];\n\n\nexport const ServiceRequestType = {\n inquiry: 'inquiry',\n task: 'task',\n callbackRequest: 'callbackRequest',\n conversationSummary: 'conversationSummary',\n complaint: 'complaint',\n quote: 'quote',\n} as const;\n","import {\n ServiceRequestOrigin,\n ServiceRequestPriority,\n ServiceRequestStatus,\n ServiceRequestType,\n} from '../api/models';\n\nexport const requestTypes: readonly ServiceRequestType[] = Object.values(ServiceRequestType);\nexport const requestPriorities: readonly ServiceRequestPriority[] = Object.values(ServiceRequestPriority);\nexport const requestOrigins: readonly ServiceRequestOrigin[] = Object.values(ServiceRequestOrigin);\nexport const settableStatuses: readonly ServiceRequestStatus[] = [ServiceRequestStatus.open, ServiceRequestStatus.inProgress];\n\nexport function isClosed(status: ServiceRequestStatus): boolean {\n return status === ServiceRequestStatus.completed || status === ServiceRequestStatus.cancelled;\n}\n\nexport function localDate(value: Date): string {\n const pad = (part: number) => `${part}`.padStart(2, '0');\n return `${value.getFullYear()}-${pad(value.getMonth() + 1)}-${pad(value.getDate())}`;\n}\n\nexport function pick<T extends string>(choices: readonly T[], value: string): T | undefined {\n return choices.find((choice) => choice === value);\n}\n","import { ChangeDetectionStrategy, Component, inject, signal } from '@angular/core';\nimport { DialogRef } from '@loomweaver/plugin-sdk';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport {\n ServiceRequestOrigin,\n ServiceRequestPriority,\n ServiceRequestType,\n type ServiceRequestChanges,\n type ServiceRequestResponse,\n type ServiceRequestStatus,\n} from '../api/models';\nimport { SERVICE_DESK_GATEWAY, ServiceRequestOutcome } from '../gateway/service-desk-gateway';\nimport { isClosed, localDate, pick, requestOrigins, requestPriorities, requestTypes, settableStatuses } from './vocabulary';\n\nexport interface RequestDialogData {\n readonly deskId: string;\n readonly request: ServiceRequestResponse | null;\n}\n\n@Component({\n selector: 'wa-request-dialog',\n imports: [TranslocoPipe],\n templateUrl: './request-dialog.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class RequestDialog {\n private readonly dialog = inject<DialogRef<boolean>>(DialogRef);\n private readonly gateway = inject(SERVICE_DESK_GATEWAY);\n private readonly data = this.dialog.data as RequestDialogData;\n private existing = this.data.request;\n\n protected readonly editing = this.existing !== null;\n protected readonly closed = this.existing !== null && isClosed(this.existing.status);\n protected readonly types = requestTypes;\n protected readonly priorities = requestPriorities;\n protected readonly origins = requestOrigins;\n protected readonly statuses = settableStatuses;\n\n protected readonly title = signal(this.existing?.title ?? '');\n protected readonly type = signal<ServiceRequestType>(this.existing?.type ?? ServiceRequestType.inquiry);\n protected readonly priority = signal<ServiceRequestPriority>(this.existing?.priority ?? ServiceRequestPriority.normal);\n protected readonly origin = signal<ServiceRequestOrigin>(this.existing?.origin ?? ServiceRequestOrigin.manual);\n protected readonly status = signal<ServiceRequestStatus>(this.existing?.status ?? 'open');\n protected readonly contactName = signal(this.existing?.contactName ?? '');\n protected readonly contactPhone = signal(this.existing?.contactPhone ?? '');\n protected readonly contactEmail = signal(this.existing?.contactEmail ?? '');\n protected readonly assignee = signal(this.existing?.assignee ?? '');\n protected readonly dueDate = signal(this.existing?.dueAt ? localDate(new Date(this.existing.dueAt)) : '');\n protected readonly description = signal(this.existing?.description ?? '');\n protected readonly cancellationReason = this.existing?.cancellationReason ?? null;\n protected readonly saving = signal(false);\n protected readonly problem = signal('');\n private persisted = false;\n\n protected bind(target: { set(value: string): void }, event: Event): void {\n target.set((event.target as HTMLInputElement).value);\n }\n\n protected chooseType(event: Event): void {\n this.choose(this.type, requestTypes, event);\n }\n\n protected choosePriority(event: Event): void {\n this.choose(this.priority, requestPriorities, event);\n }\n\n protected chooseOrigin(event: Event): void {\n this.choose(this.origin, requestOrigins, event);\n }\n\n protected chooseStatus(event: Event): void {\n this.choose(this.status, settableStatuses, event);\n }\n\n protected close(): void {\n this.dialog.close(this.persisted);\n }\n\n protected async save(): Promise<void> {\n if (!this.title().trim()) {\n this.problem.set('weaver-apps-service-desk.dialog.titleRequired');\n return;\n }\n this.saving.set(true);\n try {\n const changes = this.changes();\n const outcome = this.existing\n ? await this.gateway.edit(this.data.deskId, this.existing.id, changes)\n : await this.gateway.raise(this.data.deskId, { ...changes, origin: this.origin() });\n this.settle(await this.applyStatus(outcome));\n } catch {\n this.problem.set('weaver-apps-service-desk.errors.save');\n } finally {\n this.saving.set(false);\n }\n }\n\n private changes(): ServiceRequestChanges {\n const keptDue = this.existing?.dueAt && localDate(new Date(this.existing.dueAt)) === this.dueDate() ? this.existing.dueAt : null;\n if (keptDue) {\n return { ...this.fields(), dueAt: keptDue };\n }\n const due = this.dueDate() ? new Date(`${this.dueDate()}T12:00`) : null;\n return { ...this.fields(), dueAt: due && !Number.isNaN(due.getTime()) ? due.toISOString() : null };\n }\n\n private fields(): Omit<ServiceRequestChanges, 'dueAt'> {\n return {\n title: this.title().trim(),\n description: this.optional(this.description()),\n type: this.type(),\n priority: this.priority(),\n contactName: this.optional(this.contactName()),\n contactPhone: this.optional(this.contactPhone()),\n contactEmail: this.optional(this.contactEmail()),\n assignee: this.optional(this.assignee()),\n };\n }\n\n private async applyStatus(outcome: ServiceRequestOutcome): Promise<ServiceRequestOutcome> {\n if (outcome.kind === 'saved') {\n this.existing = outcome.request;\n this.persisted = true;\n }\n if (outcome.kind !== 'saved' || outcome.request.status === this.status()) {\n return outcome;\n }\n return this.gateway.setStatus(this.data.deskId, outcome.request.id, this.status());\n }\n\n private settle(outcome: ServiceRequestOutcome): void {\n const messages: Record<Exclude<ServiceRequestOutcome['kind'], 'saved'>, string> = {\n closed: 'weaver-apps-service-desk.dialog.closed',\n changed: 'weaver-apps-service-desk.dialog.changed',\n invalid: 'weaver-apps-service-desk.dialog.invalid',\n missing: 'weaver-apps-service-desk.dialog.missing',\n };\n if (outcome.kind === 'saved') {\n this.dialog.close(true);\n return;\n }\n this.problem.set(messages[outcome.kind]);\n }\n\n private choose<T extends string>(target: { set(value: T): void }, choices: readonly T[], event: Event): void {\n const chosen = pick(choices, (event.target as HTMLSelectElement).value);\n if (chosen) {\n target.set(chosen);\n }\n }\n\n private optional(value: string): string | null {\n return value.trim() ? value.trim() : null;\n }\n}\n","<form class=\"flex flex-col gap-4\" (submit)=\"$event.preventDefault(); save()\">\n @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","import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, computed, signal } from '@angular/core';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport type { ICellRendererAngularComp } from 'ag-grid-angular';\nimport type { ICellRendererParams } from 'ag-grid-community';\nimport type { ServiceRequestResponse } from '../api/models';\nimport { isClosed } from './vocabulary';\n\nexport interface RequestActions {\n readonly request: ServiceRequestResponse;\n readonly x: number;\n readonly y: number;\n}\n\nexport type RequestCellKind = 'title' | 'type' | 'priority' | 'status' | 'contact' | 'raised' | 'due' | 'actions';\n\nexport interface RequestCellParams {\n readonly kind: RequestCellKind;\n readonly locale: () => string;\n readonly actions: (actions: RequestActions) => void;\n}\n\ntype Params = ICellRendererParams<ServiceRequestResponse> & RequestCellParams;\n\n@Component({\n selector: 'wa-request-cell',\n imports: [TranslocoPipe],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './request-cell.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class RequestCell implements ICellRendererAngularComp {\n private params: Params | null = null;\n\n protected readonly kind = signal<RequestCellKind>('title');\n protected readonly request = signal<ServiceRequestResponse | null>(null);\n private readonly locale = signal('en');\n protected readonly closed = computed(() => {\n const request = this.request();\n return request !== null && isClosed(request.status);\n });\n protected readonly dates = computed(() => {\n const request = this.request();\n const date = new Intl.DateTimeFormat(this.locale(), { day: '2-digit', month: '2-digit', year: 'numeric' });\n return {\n raised: request ? date.format(new Date(request.raisedAt)) : '',\n due: request?.dueAt ? date.format(new Date(request.dueAt)) : '',\n };\n });\n\n agInit(params: Params): void {\n this.params = params;\n this.kind.set(params.kind);\n this.request.set(params.data ?? null);\n this.locale.set(params.locale());\n }\n\n refresh(params: Params): boolean {\n this.agInit(params);\n return true;\n }\n\n protected openActions(event: MouseEvent): void {\n const request = this.request();\n if (request) {\n this.params?.actions({ request, ...menuAnchor(event) });\n }\n }\n}\n\nfunction menuAnchor(event: MouseEvent): { readonly x: number; readonly y: number } {\n if (event.detail > 0) {\n return { x: event.clientX, y: event.clientY };\n }\n const bounds = (event.currentTarget as HTMLElement).getBoundingClientRect();\n return { x: bounds.left, y: bounds.bottom };\n}\n","@if (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","import type { ListFilterModel, ListingRequest } from '@weaver-apps/data-grid';\nimport { ServiceRequestStatus, type ServiceDeskRequestsListParams } from '../api/models';\n\nexport const requestColumn = {\n title: 'title',\n type: 'type',\n priority: 'priority',\n status: 'status',\n contact: 'contact',\n raised: 'raisedAt',\n due: 'dueAt',\n actions: 'actions',\n} as const;\n\nexport interface TextFilterModel {\n readonly filterType: 'text';\n readonly type: 'contains';\n readonly filter?: string | null;\n}\n\nexport interface RequestFilterModel {\n readonly [requestColumn.title]?: TextFilterModel;\n readonly [requestColumn.contact]?: TextFilterModel;\n readonly [requestColumn.type]?: ListFilterModel;\n readonly [requestColumn.status]?: ListFilterModel;\n readonly [requestColumn.priority]?: ListFilterModel;\n}\n\nexport const defaultStatuses: readonly ServiceRequestStatus[] = [\n ServiceRequestStatus.open,\n ServiceRequestStatus.inProgress,\n ServiceRequestStatus.completed,\n];\n\nexport const defaultRequestFilter: RequestFilterModel = {\n [requestColumn.status]: { values: [...defaultStatuses] },\n};\n\nconst sortableColumns: ReadonlySet<string> = new Set([\n requestColumn.title,\n requestColumn.priority,\n requestColumn.status,\n requestColumn.raised,\n requestColumn.due,\n]);\n\nexport function listingOf(request: ListingRequest<RequestFilterModel>): ServiceDeskRequestsListParams {\n const { filters, sort } = request;\n const sorted = sort && sortableColumns.has(sort.column) ? { sort: sort.column, descending: sort.descending } : {};\n return {\n ...sorted,\n ...textOf('text', filters[requestColumn.title]),\n ...textOf('contact', filters[requestColumn.contact]),\n ...valuesOf('type', filters[requestColumn.type]),\n ...valuesOf('status', filters[requestColumn.status]),\n ...valuesOf('priority', filters[requestColumn.priority]),\n offset: request.offset,\n limit: request.limit,\n };\n}\n\nfunction textOf(key: 'text' | 'contact', filter: TextFilterModel | undefined): ServiceDeskRequestsListParams {\n const text = filter?.filter?.trim();\n return text ? { [key]: text } : {};\n}\n\nfunction valuesOf(key: 'type' | 'status' | 'priority', filter: ListFilterModel | undefined): ServiceDeskRequestsListParams {\n const values = filter?.values ?? [];\n return values.length > 0 ? { [key]: [...values] } : {};\n}\n","import { ChangeDetectionStrategy, Component, computed, input, output, type Signal } from '@angular/core';\nimport { translateObjectSignal } from '@jsverse/transloco';\nimport { DataGrid, ListFilter, type ListFilterParams, type ListingFetch, listingDatasource, openOnRow } from '@weaver-apps/data-grid';\nimport type { ColDef, GridApi, GridOptions } from 'ag-grid-community';\nimport type { ServiceRequestResponse } from '../api/models';\nimport { RequestCell, type RequestActions, type RequestCellKind, type RequestCellParams } from './request-cell';\nimport { defaultRequestFilter, requestColumn, type RequestFilterModel } from './request-listing';\nimport { isClosed, requestPriorities, requestTypes } from './vocabulary';\n\ntype Texts = Readonly<Record<string, string>>;\n\nconst allStatuses = ['open', 'inProgress', 'completed', 'cancelled'] as const;\nconst actionsWidthPx = 72;\nconst titleMinWidthPx = 160;\nconst contactMinWidthPx = 140;\nconst narrowMinWidthPx = 112;\n\n@Component({\n selector: 'wa-request-grid',\n imports: [DataGrid],\n templateUrl: './request-grid.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n host: { class: 'block h-full w-full' },\n})\nexport class RequestGrid {\n private readonly table = translateObjectSignal('weaver-apps-service-desk.table') as Signal<Texts>;\n private readonly types = translateObjectSignal('weaver-apps-service-desk.type') as Signal<Texts>;\n private readonly statuses = translateObjectSignal('weaver-apps-service-desk.status') as Signal<Texts>;\n private readonly priorities = translateObjectSignal('weaver-apps-service-desk.priority') as Signal<Texts>;\n private api: GridApi<ServiceRequestResponse> | null = null;\n\n readonly fetch = input.required<ListingFetch<ServiceRequestResponse, RequestFilterModel>>();\n readonly locale = input.required<string>();\n\n readonly requestActions = output<RequestActions>();\n readonly openRequest = output<ServiceRequestResponse>();\n readonly counted = output<number>();\n readonly failed = output<void>();\n\n protected readonly translated = computed(() => typeof this.table() === 'object' && typeof this.table()['title'] === 'string');\n\n protected readonly empty = computed(() => ({ noRowsToShow: this.table()['empty'] ?? '' }));\n\n protected readonly options = computed<GridOptions<ServiceRequestResponse>>(() => ({\n columnDefs: this.columns(this.table()),\n defaultColDef: { sortable: false, resizable: true, suppressHeaderMenuButton: true },\n datasource: listingDatasource(this.fetch(), {\n answered: (answer) => this.counted.emit(answer.total),\n failed: () => this.failed.emit(),\n }),\n initialState: {\n sort: { sortModel: [{ colId: requestColumn.raised, sort: 'desc' }] },\n filter: { filterModel: { ...defaultRequestFilter } },\n },\n rowClassRules: { 'text-content-faint': (params) => !!params.data && isClosed(params.data.status) },\n ...openOnRow<ServiceRequestResponse>((request) => this.openRequest.emit(request), { except: [requestColumn.actions] }),\n }));\n\n reload(): void {\n this.api?.refreshInfiniteCache();\n }\n\n protected ready(api: GridApi<ServiceRequestResponse>): void {\n this.api = api;\n }\n\n private columns(table: Texts): ColDef<ServiceRequestResponse>[] {\n const narrow: ColDef<ServiceRequestResponse> = { flex: 1, minWidth: narrowMinWidthPx };\n return [\n {\n ...this.cell(requestColumn.title, 'title', table['title']),\n sortable: true,\n flex: 3,\n minWidth: titleMinWidthPx,\n filter: 'agTextColumnFilter',\n filterParams: { filterOptions: ['contains'], maxNumConditions: 1 },\n },\n {\n ...this.cell(requestColumn.type, 'type', table['type']),\n ...narrow,\n filter: ListFilter,\n filterParams: this.choices(requestTypes, this.types),\n },\n {\n ...this.cell(requestColumn.priority, 'priority', table['priority']),\n ...narrow,\n sortable: true,\n filter: ListFilter,\n filterParams: this.choices(requestPriorities, this.priorities),\n },\n {\n ...this.cell(requestColumn.status, 'status', table['status']),\n ...narrow,\n sortable: true,\n filter: ListFilter,\n filterParams: this.choices(allStatuses, this.statuses),\n },\n {\n ...this.cell(requestColumn.contact, 'contact', table['contact']),\n flex: 2,\n minWidth: contactMinWidthPx,\n filter: 'agTextColumnFilter',\n filterParams: { filterOptions: ['contains'], maxNumConditions: 1 },\n },\n { ...this.cell(requestColumn.raised, 'raised', table['raised']), ...narrow, sortable: true, sortingOrder: ['desc', 'asc'] },\n { ...this.cell(requestColumn.due, 'due', table['due']), ...narrow, sortable: true },\n { ...this.cell(requestColumn.actions, 'actions', ''), width: actionsWidthPx, resizable: false, sortable: false, pinned: 'right' },\n ];\n }\n\n private cell(colId: string, kind: RequestCellKind, headerName: string | undefined): ColDef<ServiceRequestResponse> {\n const params: RequestCellParams = {\n kind,\n locale: () => this.locale(),\n actions: (actions) => this.requestActions.emit(actions),\n };\n return { colId, headerName: headerName ?? '', cellRenderer: RequestCell, cellRendererParams: params };\n }\n\n private choices(values: readonly string[], labels: Signal<Texts>): ListFilterParams {\n return { options: () => values.map((value) => ({ value, label: labels()[value] ?? value })) };\n }\n}\n","@if (translated()) {\n <wa-data-grid [options]=\"options()\" [localeOverrides]=\"empty()\" (ready)=\"ready($event)\" />\n}\n","import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, computed, inject, resource, signal, viewChild } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport { ActivatedRoute } from '@angular/router';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport type { ListingAnswer, ListingFetch, ListingRequest } from '@weaver-apps/data-grid';\nimport { map } from 'rxjs';\nimport type { ServiceRequestResponse } from '../api/models';\nimport { SERVICE_DESK_GATEWAY, ServiceRequestOutcome } from '../gateway/service-desk-gateway';\nimport { deskTitle, SERVICE_DESK_TITLES } from '../gateway/service-desk-titles';\nimport { serviceDeskHost } from '../plugin/service-desk-host';\nimport { serviceDeskIconNames } from '../plugin/service-desk-ids';\nimport { activeLocale } from './active-locale';\nimport type { RequestActions } from './request-cell';\nimport { RequestDialog, RequestDialogData } from './request-dialog';\nimport { RequestGrid } from './request-grid';\nimport { listingOf, type RequestFilterModel } from './request-listing';\nimport { isClosed } from './vocabulary';\n\n@Component({\n selector: 'wa-desk-page',\n imports: [TranslocoPipe, RequestGrid],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './desk-page.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DeskPage {\n private readonly gateway = inject(SERVICE_DESK_GATEWAY);\n private readonly titles = inject(SERVICE_DESK_TITLES, { optional: true });\n private readonly route = inject(ActivatedRoute);\n private readonly grid = viewChild(RequestGrid);\n\n protected readonly locale = activeLocale();\n protected readonly deskId = toSignal(this.route.paramMap.pipe(map((params) => params.get('deskId') ?? '')), {\n initialValue: this.route.snapshot.paramMap.get('deskId') ?? '',\n });\n protected readonly count = signal<number | null>(null);\n protected readonly loadFailed = signal(false);\n\n protected readonly desk = resource({\n params: () => this.deskId(),\n loader: ({ params }) => this.gateway.desk(params),\n });\n\n protected readonly title = computed(() => deskTitle(this.titles, this.deskId(), this.desk.hasValue() ? (this.desk.value()?.name ?? null) : null));\n\n protected readonly fetch = computed<ListingFetch<ServiceRequestResponse, RequestFilterModel>>(() => {\n const deskId = this.deskId();\n return async (request: ListingRequest<RequestFilterModel>): Promise<ListingAnswer<ServiceRequestResponse>> => {\n const page = await this.gateway.requests(deskId, listingOf(request));\n return { items: page.items, total: Number(page.total) };\n };\n });\n\n protected counted(total: number): void {\n this.loadFailed.set(false);\n this.count.set(total);\n }\n\n protected failed(): void {\n this.loadFailed.set(true);\n }\n\n protected add(): void {\n this.openDialog({ deskId: this.deskId(), request: null }, 'weaver-apps-service-desk.dialog.addTitle');\n }\n\n protected actions({ request, x, y }: RequestActions): void {\n const closed = isClosed(request.status);\n serviceDeskHost.ui.openMenu(\n [\n { label: closed ? 'weaver-apps-service-desk.actions.view' : 'weaver-apps-service-desk.actions.edit', icon: closed ? 'preview' : 'edit', run: () => this.open(request) },\n ...(closed\n ? []\n : [\n { label: 'weaver-apps-service-desk.actions.complete', icon: serviceDeskIconNames.complete, run: () => void this.complete(request) },\n { label: 'weaver-apps-service-desk.actions.cancel', icon: serviceDeskIconNames.cancel, run: () => void this.cancel(request) },\n ]),\n { label: 'weaver-apps-service-desk.actions.delete', icon: 'trash', run: () => void this.remove(request) },\n ],\n { x, y },\n );\n }\n\n protected open(request: ServiceRequestResponse): void {\n const closed = isClosed(request.status);\n this.openDialog({ deskId: this.deskId(), request }, closed ? 'weaver-apps-service-desk.dialog.viewTitle' : 'weaver-apps-service-desk.dialog.editTitle');\n }\n\n protected async complete(request: ServiceRequestResponse): Promise<void> {\n await this.attempt(async () => this.report(await this.gateway.complete(this.deskId(), request.id), 'weaver-apps-service-desk.complete.done'));\n }\n\n protected async cancel(request: ServiceRequestResponse): Promise<void> {\n const reason = await serviceDeskHost.ui.prompt({\n title: 'weaver-apps-service-desk.cancel.title',\n message: 'weaver-apps-service-desk.cancel.message',\n confirmLabel: 'weaver-apps-service-desk.cancel.confirm',\n tone: 'warning',\n });\n if (reason === null) {\n return;\n }\n await this.attempt(async () =>\n this.report(await this.gateway.cancel(this.deskId(), request.id, reason.trim() || null), 'weaver-apps-service-desk.cancel.done'),\n );\n }\n\n protected async remove(request: ServiceRequestResponse): Promise<void> {\n const confirmed = await serviceDeskHost.ui.confirm({\n title: 'weaver-apps-service-desk.delete.title',\n message: 'weaver-apps-service-desk.delete.message',\n confirmLabel: 'weaver-apps-service-desk.delete.confirm',\n tone: 'danger',\n });\n if (!confirmed) {\n return;\n }\n await this.attempt(async () => {\n await this.gateway.remove(this.deskId(), request.id);\n serviceDeskHost.ui.toast({ message: 'weaver-apps-service-desk.delete.done', kind: 'success' });\n this.reload();\n });\n }\n\n private reload(): void {\n this.grid()?.reload();\n }\n\n private async attempt(work: () => Promise<void>): Promise<void> {\n try {\n await work();\n } catch {\n serviceDeskHost.ui.toast({ message: 'weaver-apps-service-desk.errors.save', kind: 'error' });\n this.reload();\n }\n }\n\n private openDialog(data: RequestDialogData, title: string): void {\n const dialog = serviceDeskHost.ui.open<boolean>(RequestDialog, { data, title, size: 'lg', dismiss: 'explicit' });\n void dialog.closed.then((saved) => {\n if (saved) {\n serviceDeskHost.ui.toast({ message: 'weaver-apps-service-desk.dialog.saved', kind: 'success' });\n this.reload();\n }\n });\n }\n\n private report(outcome: ServiceRequestOutcome, done: string): void {\n const saved = outcome.kind === 'saved';\n serviceDeskHost.ui.toast({ message: saved ? done : 'weaver-apps-service-desk.errors.save', kind: saved ? 'success' : 'error' });\n this.reload();\n }\n}\n","<div class=\"flex h-full flex-col gap-4 p-6\">\n <div class=\"flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between\">\n <h1 class=\"text-lg font-semibold text-content\">{{ title() ?? ('weaver-apps-service-desk.title' | transloco) }}</h1>\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)\" (openRequest)=\"open($event)\" (counted)=\"counted($event)\" (failed)=\"failed()\" />\n </div>\n</div>\n"],"mappings":"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"}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { l as serviceDeskPluginId } from "./weaver-apps-service-desk-service-desk-titles-DcyaaS7c.mjs";
|
|
2
2
|
const deskListPath = serviceDeskPluginId;
|
|
3
3
|
function deskPath(deskId) {
|
|
4
4
|
return `${deskListPath}/${deskId}`;
|
|
5
5
|
}
|
|
6
6
|
export { deskPath as n, deskListPath as t };
|
|
7
7
|
|
|
8
|
-
//# sourceMappingURL=weaver-apps-service-desk-service-desk-paths-
|
|
8
|
+
//# sourceMappingURL=weaver-apps-service-desk-service-desk-paths-DDSDBqD8.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"weaver-apps-service-desk-service-desk-paths-
|
|
1
|
+
{"version":3,"file":"weaver-apps-service-desk-service-desk-paths-DDSDBqD8.mjs","names":[],"sources":["../../../../libs/service-desk/src/lib/plugin/service-desk-paths.ts"],"sourcesContent":["import { serviceDeskPluginId } from './service-desk-ids';\n\nexport const deskListPath = serviceDeskPluginId;\n\nexport function deskPath(deskId: string): string {\n return `${deskListPath}/${deskId}`;\n}\n"],"mappings":";AAEA,MAAa,eAAe;AAE5B,SAAgB,SAAS,QAAc;CACrC,OAAO,GAAG,aAAY,GAAI;AAC5B"}
|
|
@@ -43,6 +43,16 @@ function provideServiceDeskGateway(gateway) {
|
|
|
43
43
|
useValue: gateway
|
|
44
44
|
};
|
|
45
45
|
}
|
|
46
|
-
|
|
46
|
+
const SERVICE_DESK_TITLES = new InjectionToken("weaver-apps.service-desk.titles");
|
|
47
|
+
function provideServiceDeskTitles(factory) {
|
|
48
|
+
return {
|
|
49
|
+
provide: SERVICE_DESK_TITLES,
|
|
50
|
+
useFactory: factory
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
function deskTitle(titles, deskId, storedName) {
|
|
54
|
+
return titles?.(deskId)?.trim() || storedName;
|
|
55
|
+
}
|
|
56
|
+
export { ServiceDeskGatewayError as a, serviceDeskIconNames as c, SERVICE_DESK_GATEWAY as i, serviceDeskPluginId as l, deskTitle as n, provideServiceDeskGateway as o, provideServiceDeskTitles as r, serviceDeskContributionIds as s, SERVICE_DESK_TITLES as t, serviceDeskHost as u };
|
|
47
57
|
|
|
48
|
-
//# sourceMappingURL=weaver-apps-service-desk-service-desk-
|
|
58
|
+
//# sourceMappingURL=weaver-apps-service-desk-service-desk-titles-DcyaaS7c.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"weaver-apps-service-desk-service-desk-titles-DcyaaS7c.mjs","names":[],"sources":["../../../../libs/service-desk/src/lib/plugin/service-desk-host.ts","../../../../libs/service-desk/src/lib/plugin/service-desk-ids.ts","../../../../libs/service-desk/src/lib/gateway/service-desk-gateway.ts","../../../../libs/service-desk/src/lib/gateway/service-desk-titles.ts"],"sourcesContent":["import type { PluginContext } from '@loomweaver/plugin-sdk';\n\ntype HostContext = Pick<PluginContext, 'ui' | 'navigateContent'>;\n\nclass ServiceDeskHost {\n private ctx?: HostContext;\n\n bind(ctx: HostContext): void {\n this.ctx = ctx;\n }\n\n unbind(): void {\n this.ctx = undefined;\n }\n\n get ui(): HostContext['ui'] {\n if (!this.ctx) {\n throw new Error('The service-desk plugin is not active.');\n }\n return this.ctx.ui;\n }\n\n navigate(path: string): void {\n this.ctx?.navigateContent(path);\n }\n}\n\nexport const serviceDeskHost = new ServiceDeskHost();\n","export const serviceDeskPluginId = 'weaver-apps-service-desk';\n\nexport const serviceDeskContributionIds = {\n desks: `${serviceDeskPluginId}.desks`,\n desk: `${serviceDeskPluginId}.desk`,\n rail: `${serviceDeskPluginId}.rail`,\n} as const;\n\nexport const serviceDeskIconNames = {\n desk: `${serviceDeskPluginId}.desk`,\n complete: `${serviceDeskPluginId}.complete`,\n cancel: `${serviceDeskPluginId}.cancel`,\n previous: `${serviceDeskPluginId}.previous`,\n next: `${serviceDeskPluginId}.next`,\n} as const;\n","import { InjectionToken, Provider } from '@angular/core';\nimport type {\n DeskResponse,\n NewServiceRequest,\n ServiceDeskRequestsListParams,\n ServiceRequestChanges,\n ServiceRequestPage,\n ServiceRequestResponse,\n ServiceRequestStatus,\n} from '../api/models';\n\nexport type ServiceRequestOutcome =\n | { readonly kind: 'saved'; readonly request: ServiceRequestResponse }\n | { readonly kind: 'closed' }\n | { readonly kind: 'changed' }\n | { readonly kind: 'invalid' }\n | { readonly kind: 'missing' };\n\nexport type ServiceRequestListing = ServiceDeskRequestsListParams;\n\nexport interface ServiceDeskGateway {\n desks(): Promise<readonly DeskResponse[]>;\n desk(deskId: string): Promise<DeskResponse | null>;\n saveDesk(deskId: string, name: string): Promise<DeskResponse>;\n requests(deskId: string, listing: ServiceRequestListing, signal?: AbortSignal): Promise<ServiceRequestPage>;\n raise(deskId: string, request: NewServiceRequest): Promise<ServiceRequestOutcome>;\n edit(deskId: string, requestId: string, changes: ServiceRequestChanges): Promise<ServiceRequestOutcome>;\n setStatus(deskId: string, requestId: string, status: ServiceRequestStatus): Promise<ServiceRequestOutcome>;\n complete(deskId: string, requestId: string): Promise<ServiceRequestOutcome>;\n cancel(deskId: string, requestId: string, reason: string | null): Promise<ServiceRequestOutcome>;\n remove(deskId: string, requestId: string): Promise<void>;\n}\n\nexport class ServiceDeskGatewayError extends Error {\n constructor(readonly status: number) {\n super(`The service desk answered ${status}.`);\n }\n}\n\nexport const SERVICE_DESK_GATEWAY = new InjectionToken<ServiceDeskGateway>('weaver-apps.service-desk.gateway');\n\nexport function provideServiceDeskGateway(gateway: ServiceDeskGateway): Provider {\n return { provide: SERVICE_DESK_GATEWAY, useValue: gateway };\n}\n","import { InjectionToken, Provider } from '@angular/core';\n\nexport type ServiceDeskTitles = (deskId: string) => string | null;\n\nexport const SERVICE_DESK_TITLES = new InjectionToken<ServiceDeskTitles>('weaver-apps.service-desk.titles');\n\nexport function provideServiceDeskTitles(factory: () => ServiceDeskTitles): Provider {\n return { provide: SERVICE_DESK_TITLES, useFactory: factory };\n}\n\nexport function deskTitle(titles: ServiceDeskTitles | null, deskId: string, storedName: string | null): string | null {\n return titles?.(deskId)?.trim() || storedName;\n}\n"],"mappings":";AAIA,IAAM,kBAAN,MAAqB;CACX;CAER,KAAK,KAAgB;EACnB,KAAK,MAAM;CACb;CAEA,SAAM;EACJ,KAAK,MAAM;CACb;CAEA,IAAI,KAAE;EACJ,IAAI,CAAC,KAAK,KACR,MAAM,IAAI,MAAM,wCAAwC;EAE1D,OAAO,KAAK,IAAI;CAClB;CAEA,SAAS,MAAY;EACnB,KAAK,KAAK,gBAAgB,IAAI;CAChC;AACD;AAED,MAAa,kBAAkB,IAAI,gBAAe;AC3BlD,MAAa,sBAAsB;AAEnC,MAAa,6BAA6B;CACxC,OAAO,GAAG,oBAAmB;CAC7B,MAAM,GAAG,oBAAmB;CAC5B,MAAM,GAAG,oBAAmB;AACpB;AAEV,MAAa,uBAAuB;CAClC,MAAM,GAAG,oBAAmB;CAC5B,UAAU,GAAG,oBAAmB;CAChC,QAAQ,GAAG,oBAAmB;CAC9B,UAAU,GAAG,oBAAmB;CAChC,MAAM,GAAG,oBAAmB;AACpB;ACmBV,IAAa,0BAAb,cAA6C,MAAK;CAC3B;CAArB,YAAqB,QAAc;EACjC,MAAM,6BAA6B,OAAM,EAAG;EADzB,KAAA,SAAA;CAErB;AACD;AAED,MAAa,uBAAuB,IAAI,eAAmC,kCAAkC;AAE7G,SAAgB,0BAA0B,SAA2B;CACnE,OAAO;EAAE,SAAS;EAAsB,UAAU;CAAO;AAC3D;ACvCA,MAAa,sBAAsB,IAAI,eAAkC,iCAAiC;AAE1G,SAAgB,yBAAyB,SAAgC;CACvE,OAAO;EAAE,SAAS;EAAqB,YAAY;CAAO;AAC5D;AAEA,SAAgB,UAAU,QAAkC,QAAgB,YAAyB;CACnG,OAAO,SAAS,MAAM,CAAC,EAAE,KAAI,KAAM;AACrC"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { a as serviceDeskIconNames, i as
|
|
2
|
-
import { n as deskPath, t as deskListPath } from "./weaver-apps-service-desk-service-desk-paths-
|
|
1
|
+
import { a as ServiceDeskGatewayError, c as serviceDeskIconNames, i as SERVICE_DESK_GATEWAY, l as serviceDeskPluginId, o as provideServiceDeskGateway, r as provideServiceDeskTitles, s as serviceDeskContributionIds, u as serviceDeskHost } from "./weaver-apps-service-desk-service-desk-titles-DcyaaS7c.mjs";
|
|
2
|
+
import { n as deskPath, t as deskListPath } from "./weaver-apps-service-desk-service-desk-paths-DDSDBqD8.mjs";
|
|
3
3
|
const stroke = "fill=\"none\" stroke=\"currentColor\" stroke-width=\"1.5\" stroke-linecap=\"round\" stroke-linejoin=\"round\" aria-hidden=\"true\"";
|
|
4
4
|
const serviceDeskIcons = {
|
|
5
5
|
[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>`,
|
|
@@ -27,14 +27,14 @@ function createServiceDeskPlugin(options = {}) {
|
|
|
27
27
|
title: "weaver-apps-service-desk.desks.title",
|
|
28
28
|
icon: serviceDeskIconNames.desk,
|
|
29
29
|
routable: { path: deskListPath },
|
|
30
|
-
loadComponent: () => import("./weaver-apps-service-desk-desk-list-page-
|
|
30
|
+
loadComponent: () => import("./weaver-apps-service-desk-desk-list-page-BDEm1BI8.mjs").then((module) => module.DeskListPage)
|
|
31
31
|
});
|
|
32
32
|
ctx.registerSurface({
|
|
33
33
|
id: serviceDeskContributionIds.desk,
|
|
34
34
|
title: "weaver-apps-service-desk.title",
|
|
35
35
|
icon: serviceDeskIconNames.desk,
|
|
36
36
|
routable: { path: `${deskListPath}/:deskId` },
|
|
37
|
-
loadComponent: () => import("./weaver-apps-service-desk-desk-page-
|
|
37
|
+
loadComponent: () => import("./weaver-apps-service-desk-desk-page-Dy1aAdoM.mjs").then((module) => module.DeskPage)
|
|
38
38
|
});
|
|
39
39
|
if (options.railItem) ctx.registerRailItem({
|
|
40
40
|
id: serviceDeskContributionIds.rail,
|
|
@@ -146,6 +146,6 @@ function createHttpServiceDeskGateway(options) {
|
|
|
146
146
|
}
|
|
147
147
|
};
|
|
148
148
|
}
|
|
149
|
-
export { SERVICE_DESK_GATEWAY, ServiceDeskGatewayError, createHttpServiceDeskGateway, createServiceDeskPlugin, deskListPath, deskPath, provideServiceDeskGateway, serviceDeskContributionIds, serviceDeskPlugin, serviceDeskPluginId };
|
|
149
|
+
export { SERVICE_DESK_GATEWAY, ServiceDeskGatewayError, createHttpServiceDeskGateway, createServiceDeskPlugin, deskListPath, deskPath, provideServiceDeskGateway, provideServiceDeskTitles, serviceDeskContributionIds, serviceDeskPlugin, serviceDeskPluginId };
|
|
150
150
|
|
|
151
151
|
//# sourceMappingURL=weaver-apps-service-desk.mjs.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@weaver-apps/service-desk",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"description": "The service-desk app's weaver for a LoomWeaver distribution: a desk's service requests, filtered and paged, raising, editing, completing, cancelling and deleting, behind a gateway the host supplies.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"publishConfig": {
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
},
|
|
20
20
|
"sideEffects": false,
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@weaver-apps/data-grid": "0.
|
|
22
|
+
"@weaver-apps/data-grid": "0.4.1",
|
|
23
23
|
"tslib": "^2.3.0"
|
|
24
24
|
},
|
|
25
25
|
"module": "fesm2022/weaver-apps-service-desk.mjs",
|
|
@@ -347,10 +347,12 @@ export declare class ServiceDeskGatewayError extends Error {
|
|
|
347
347
|
}
|
|
348
348
|
export declare const SERVICE_DESK_GATEWAY: InjectionToken<ServiceDeskGateway>;
|
|
349
349
|
export declare function provideServiceDeskGateway(gateway: ServiceDeskGateway): Provider;
|
|
350
|
+
type ServiceDeskTitles = (deskId: string) => string | null;
|
|
351
|
+
export declare function provideServiceDeskTitles(factory: () => ServiceDeskTitles): Provider;
|
|
350
352
|
interface HttpServiceDeskGatewayOptions {
|
|
351
353
|
readonly baseUrl: string;
|
|
352
354
|
readonly headers?: Readonly<Record<string, string>>;
|
|
353
355
|
readonly fetch?: typeof fetch;
|
|
354
356
|
}
|
|
355
357
|
export declare function createHttpServiceDeskGateway(options: HttpServiceDeskGatewayOptions): ServiceDeskGateway;
|
|
356
|
-
export type { DeskResponse, HttpServiceDeskGatewayOptions, NewServiceRequest, ServiceDeskGateway, ServiceDeskPluginOptions, ServiceRequestChanges, ServiceRequestListing, ServiceRequestOrigin, ServiceRequestOutcome, ServiceRequestPage, ServiceRequestPriority, ServiceRequestResponse, ServiceRequestStatus, ServiceRequestType };
|
|
358
|
+
export type { DeskResponse, HttpServiceDeskGatewayOptions, NewServiceRequest, ServiceDeskGateway, ServiceDeskPluginOptions, ServiceDeskTitles, ServiceRequestChanges, ServiceRequestListing, ServiceRequestOrigin, ServiceRequestOutcome, ServiceRequestPage, ServiceRequestPriority, ServiceRequestResponse, ServiceRequestStatus, ServiceRequestType };
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"weaver-apps-service-desk-desk-list-page-DPxs6zRX.mjs","names":[],"sources":["../../../../libs/service-desk/src/lib/views/desk-list-page.ts","../../../../libs/service-desk/src/lib/views/desk-list-page.html"],"sourcesContent":["import { ChangeDetectionStrategy, Component, CUSTOM_ELEMENTS_SCHEMA, inject, resource } from '@angular/core';\nimport { TranslocoPipe } from '@jsverse/transloco';\nimport { SERVICE_DESK_GATEWAY } from '../gateway/service-desk-gateway';\nimport { serviceDeskHost } from '../plugin/service-desk-host';\nimport { deskPath } from '../plugin/service-desk-paths';\n\n@Component({\n selector: 'wa-desk-list-page',\n imports: [TranslocoPipe],\n schemas: [CUSTOM_ELEMENTS_SCHEMA],\n templateUrl: './desk-list-page.html',\n changeDetection: ChangeDetectionStrategy.OnPush,\n})\nexport class DeskListPage {\n private readonly gateway = inject(SERVICE_DESK_GATEWAY);\n\n protected readonly desks = resource({ loader: () => this.gateway.desks() });\n\n protected open(deskId: string): void {\n serviceDeskHost.navigate(deskPath(deskId));\n }\n\n protected async create(): Promise<void> {\n const name = await serviceDeskHost.ui.prompt({\n title: 'weaver-apps-service-desk.desks.create',\n message: 'weaver-apps-service-desk.desks.createMessage',\n confirmLabel: 'weaver-apps-service-desk.desks.createConfirm',\n });\n if (!name?.trim()) {\n return;\n }\n try {\n const desk = await this.gateway.saveDesk(crypto.randomUUID(), name.trim());\n this.open(desk.id);\n } catch {\n serviceDeskHost.ui.toast({ message: 'weaver-apps-service-desk.errors.create', kind: 'error' });\n }\n }\n}\n","<div class=\"flex h-full flex-col gap-4 overflow-auto p-6\">\n <div class=\"flex items-center justify-between gap-4\">\n <h2 class=\"text-lg font-semibold text-content\">{{ 'weaver-apps-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"],"mappings":";;;;;AAaA,IAAa,eAAb,MAAa,aAAY;CACN,UAAU,OAAO,oBAAoB;CAEnC,QAAQ,SAAQ;EAAA,GAAA,YAAA,EAAA,WAAA,QAAA,+BAAA,CAAA;EAAG,cAAc,KAAK,QAAQ,MAAK;CAAE,CAAA;CAE9D,KAAK,QAAc;EAC3B,gBAAgB,SAAS,SAAS,MAAM,CAAC;CAC3C;CAEU,MAAM,SAAM;EACpB,MAAM,OAAO,MAAM,gBAAgB,GAAG,OAAO;GAC3C,OAAO;GACP,SAAS;GACT,cAAc;EACf,CAAA;EACD,IAAI,CAAC,MAAM,KAAI,GACb;EAEF,IAAI;GACF,MAAM,OAAO,MAAM,KAAK,QAAQ,SAAS,OAAO,WAAU,GAAI,KAAK,KAAI,CAAE;GACzE,KAAK,KAAK,KAAK,EAAE;EACnB,QAAQ;GACN,gBAAgB,GAAG,MAAM;IAAE,SAAS;IAA0C,MAAM;GAAO,CAAE;EAC/F;CACF;CAxBW,OAAA,OAAA,GAAA,mBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,UAAA;EAAA,MAAA;EAAY,MAAA,CAAA;EAAA,QAAA,GAAA,gBAAA;CAAA,CAAA;CAAZ,OAAA,OAAA,GAAA,qBAAA;EAAA,YAAA;EAAA,SAAA;EAAA,MAAA;EAAY,cAAA;EAAA,UAAA;EAAA,UAAA;EAAA,UCbzB;EAsBA,cAAA,CAAA;GAAA,MAAA;GAAA,MDdY;GAAa,MAAA;EAAA,CAAA;EAAA,iBAAA,GAAA,wBAAA;CAAA,CAAA;;AAKZ,GAAA,yBAAA;CAAA,YAAA;CAAA,SAAA;CAAA,UAAA;CAAA,MAAA;CAAY,YAAA,CAAA;EAPxB,MAAA;EACW,MAAA,CAAA;GAAA,UAAA;GAAmB,SACpB,CAAC,aAAa;GAAC,SACf,CAAC,sBAAsB;GAAC,iBAEhB,wBAAwB;GAAM,UAAA;EAAA,CAAA"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"weaver-apps-service-desk-service-desk-gateway-BK42Rgow.mjs","names":[],"sources":["../../../../libs/service-desk/src/lib/plugin/service-desk-host.ts","../../../../libs/service-desk/src/lib/plugin/service-desk-ids.ts","../../../../libs/service-desk/src/lib/gateway/service-desk-gateway.ts"],"sourcesContent":["import type { PluginContext } from '@loomweaver/plugin-sdk';\n\ntype HostContext = Pick<PluginContext, 'ui' | 'navigateContent'>;\n\nclass ServiceDeskHost {\n private ctx?: HostContext;\n\n bind(ctx: HostContext): void {\n this.ctx = ctx;\n }\n\n unbind(): void {\n this.ctx = undefined;\n }\n\n get ui(): HostContext['ui'] {\n if (!this.ctx) {\n throw new Error('The service-desk plugin is not active.');\n }\n return this.ctx.ui;\n }\n\n navigate(path: string): void {\n this.ctx?.navigateContent(path);\n }\n}\n\nexport const serviceDeskHost = new ServiceDeskHost();\n","export const serviceDeskPluginId = 'weaver-apps-service-desk';\n\nexport const serviceDeskContributionIds = {\n desks: `${serviceDeskPluginId}.desks`,\n desk: `${serviceDeskPluginId}.desk`,\n rail: `${serviceDeskPluginId}.rail`,\n} as const;\n\nexport const serviceDeskIconNames = {\n desk: `${serviceDeskPluginId}.desk`,\n complete: `${serviceDeskPluginId}.complete`,\n cancel: `${serviceDeskPluginId}.cancel`,\n previous: `${serviceDeskPluginId}.previous`,\n next: `${serviceDeskPluginId}.next`,\n} as const;\n","import { InjectionToken, Provider } from '@angular/core';\nimport type {\n DeskResponse,\n NewServiceRequest,\n ServiceDeskRequestsListParams,\n ServiceRequestChanges,\n ServiceRequestPage,\n ServiceRequestResponse,\n ServiceRequestStatus,\n} from '../api/models';\n\nexport type ServiceRequestOutcome =\n | { readonly kind: 'saved'; readonly request: ServiceRequestResponse }\n | { readonly kind: 'closed' }\n | { readonly kind: 'changed' }\n | { readonly kind: 'invalid' }\n | { readonly kind: 'missing' };\n\nexport type ServiceRequestListing = ServiceDeskRequestsListParams;\n\nexport interface ServiceDeskGateway {\n desks(): Promise<readonly DeskResponse[]>;\n desk(deskId: string): Promise<DeskResponse | null>;\n saveDesk(deskId: string, name: string): Promise<DeskResponse>;\n requests(deskId: string, listing: ServiceRequestListing, signal?: AbortSignal): Promise<ServiceRequestPage>;\n raise(deskId: string, request: NewServiceRequest): Promise<ServiceRequestOutcome>;\n edit(deskId: string, requestId: string, changes: ServiceRequestChanges): Promise<ServiceRequestOutcome>;\n setStatus(deskId: string, requestId: string, status: ServiceRequestStatus): Promise<ServiceRequestOutcome>;\n complete(deskId: string, requestId: string): Promise<ServiceRequestOutcome>;\n cancel(deskId: string, requestId: string, reason: string | null): Promise<ServiceRequestOutcome>;\n remove(deskId: string, requestId: string): Promise<void>;\n}\n\nexport class ServiceDeskGatewayError extends Error {\n constructor(readonly status: number) {\n super(`The service desk answered ${status}.`);\n }\n}\n\nexport const SERVICE_DESK_GATEWAY = new InjectionToken<ServiceDeskGateway>('weaver-apps.service-desk.gateway');\n\nexport function provideServiceDeskGateway(gateway: ServiceDeskGateway): Provider {\n return { provide: SERVICE_DESK_GATEWAY, useValue: gateway };\n}\n"],"mappings":";AAIA,IAAM,kBAAN,MAAqB;CACX;CAER,KAAK,KAAgB;EACnB,KAAK,MAAM;CACb;CAEA,SAAM;EACJ,KAAK,MAAM;CACb;CAEA,IAAI,KAAE;EACJ,IAAI,CAAC,KAAK,KACR,MAAM,IAAI,MAAM,wCAAwC;EAE1D,OAAO,KAAK,IAAI;CAClB;CAEA,SAAS,MAAY;EACnB,KAAK,KAAK,gBAAgB,IAAI;CAChC;AACD;AAED,MAAa,kBAAkB,IAAI,gBAAe;AC3BlD,MAAa,sBAAsB;AAEnC,MAAa,6BAA6B;CACxC,OAAO,GAAG,oBAAmB;CAC7B,MAAM,GAAG,oBAAmB;CAC5B,MAAM,GAAG,oBAAmB;AACpB;AAEV,MAAa,uBAAuB;CAClC,MAAM,GAAG,oBAAmB;CAC5B,UAAU,GAAG,oBAAmB;CAChC,QAAQ,GAAG,oBAAmB;CAC9B,UAAU,GAAG,oBAAmB;CAChC,MAAM,GAAG,oBAAmB;AACpB;ACmBV,IAAa,0BAAb,cAA6C,MAAK;CAC3B;CAArB,YAAqB,QAAc;EACjC,MAAM,6BAA6B,OAAM,EAAG;EADzB,KAAA,SAAA;CAErB;AACD;AAED,MAAa,uBAAuB,IAAI,eAAmC,kCAAkC;AAE7G,SAAgB,0BAA0B,SAA2B;CACnE,OAAO;EAAE,SAAS;EAAsB,UAAU;CAAO;AAC3D"}
|