@webority/support-react 0.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1225 @@
1
+ import { createContext as e, useCallback as t, useContext as n, useEffect as r, useId as i, useMemo as a, useRef as o, useState as s } from "react";
2
+ import { AppAlert as c, AppAvatar as l, AppBadge as u, AppButton as d, AppCard as f, AppCopyButton as p, AppDataTable as m, AppFileAttachment as h, AppIcon as g, AppInfoList as _, AppInput as v, AppLink as y, AppModal as b, AppSearch as x, AppSelect as S, AppSkeleton as C, AppSpinner as w, AppStatusBadge as T, AppTableEmptyState as E, AppTabs as D, AppTextarea as O, usePagination as k } from "@webority/ui-react";
3
+ import { toBlob as A } from "html-to-image";
4
+ import { Fragment as ee, jsx as j, jsxs as M } from "react/jsx-runtime";
5
+ //#region src/client.js
6
+ var N = "Something went wrong. Please try again.", P = class extends Error {
7
+ constructor(e, t, n) {
8
+ super(t), this.name = "SupportApiError", this.code = e, this.status = n;
9
+ }
10
+ };
11
+ async function te(e) {
12
+ if (e && typeof Blob < "u" && e instanceof Blob) try {
13
+ return JSON.parse(await e.text());
14
+ } catch {
15
+ return null;
16
+ }
17
+ if (typeof e == "string") try {
18
+ return JSON.parse(e);
19
+ } catch {
20
+ return null;
21
+ }
22
+ return e ?? null;
23
+ }
24
+ async function ne(e) {
25
+ if (e instanceof P) return e;
26
+ let t = e?.response;
27
+ if (!t) return new P("network", N);
28
+ let n = await te(t.data);
29
+ return new P(typeof n?.code == "string" && n.code ? n.code : "unknown", typeof n?.message == "string" && n.message ? n.message : N, t.status);
30
+ }
31
+ async function F(e) {
32
+ try {
33
+ return (await e()).data;
34
+ } catch (e) {
35
+ throw await ne(e);
36
+ }
37
+ }
38
+ function I(e, t, n) {
39
+ n != null && String(n).trim() !== "" && e.append(t, String(n));
40
+ }
41
+ function L(e, t, n, r) {
42
+ (n ?? []).forEach((n, i) => {
43
+ let a = n?.name || `${r}-${i + 1}.png`;
44
+ e.append(t, n, a);
45
+ });
46
+ }
47
+ function re(e, t = "/api/support") {
48
+ let n = t.replace(/\/+$/, ""), r = (e) => `${n}/tickets/${encodeURIComponent(e)}`;
49
+ return {
50
+ getConfig: () => F(() => e.get(`${n}/config`)),
51
+ listTickets: () => F(() => e.get(`${n}/tickets`)),
52
+ getTicket: (t) => F(() => e.get(r(t))),
53
+ createTicket: ({ category: t, subject: r, body: i, pageUrl: a, userAgent: o, appVersion: s, files: c, screenshots: l }) => {
54
+ let u = new FormData();
55
+ return u.append("category", t ?? ""), u.append("subject", r ?? ""), u.append("body", i ?? ""), I(u, "pageUrl", a), I(u, "userAgent", o), I(u, "appVersion", s), L(u, "files", c, "file"), L(u, "screenshots", l, "screenshot"), F(() => e.post(`${n}/tickets`, u));
56
+ },
57
+ reply: (t, { body: n, files: i, screenshots: a }) => {
58
+ let o = new FormData();
59
+ return o.append("body", n ?? ""), L(o, "files", i, "file"), L(o, "screenshots", a, "screenshot"), F(() => e.post(`${r(t)}/replies`, o));
60
+ },
61
+ reopen: (t) => F(() => e.post(`${r(t)}/reopen`)),
62
+ rate: (t, { satisfaction: n, comment: i }) => {
63
+ let a = { satisfaction: n };
64
+ return i != null && i.trim() !== "" && (a.comment = i.trim()), F(() => e.post(`${r(t)}/rating`, a));
65
+ },
66
+ fetchAttachment: (t) => F(() => e.get(`${n}/attachments/${encodeURIComponent(t)}`, { responseType: "blob" }))
67
+ };
68
+ }
69
+ //#endregion
70
+ //#region src/context.js
71
+ var ie = e(null);
72
+ function ae() {
73
+ let e = n(ie);
74
+ if (!e) throw Error("Support components and hooks must be used inside <SupportProvider>.");
75
+ return e;
76
+ }
77
+ function R() {
78
+ return ae().client;
79
+ }
80
+ function z(e, n, i = !0) {
81
+ let [a, c] = s({
82
+ data: null,
83
+ loading: i,
84
+ error: null
85
+ }), [l, u] = s(0), d = o(e);
86
+ d.current = e;
87
+ let f = o(n);
88
+ r(() => {
89
+ let e = f.current !== n;
90
+ if (f.current = n, !i) {
91
+ c({
92
+ data: null,
93
+ loading: !1,
94
+ error: null
95
+ });
96
+ return;
97
+ }
98
+ let t = !0;
99
+ return c((t) => ({
100
+ data: e ? null : t.data,
101
+ loading: !0,
102
+ error: null
103
+ })), d.current(l > 0).then((e) => t && c({
104
+ data: e,
105
+ loading: !1,
106
+ error: null
107
+ }), (e) => t && c((t) => ({
108
+ ...t,
109
+ loading: !1,
110
+ error: e
111
+ }))), () => {
112
+ t = !1;
113
+ };
114
+ }, [
115
+ n,
116
+ i,
117
+ l
118
+ ]);
119
+ let p = t(() => u((e) => e + 1), []), m = t((e) => c((t) => ({
120
+ ...t,
121
+ data: e
122
+ })), []);
123
+ return {
124
+ ...a,
125
+ reload: p,
126
+ setData: m
127
+ };
128
+ }
129
+ function B() {
130
+ let { loadConfig: e } = ae(), { data: t, loading: n, error: r, reload: i } = z(e, "config");
131
+ return {
132
+ config: t,
133
+ loading: n,
134
+ error: r,
135
+ reload: i
136
+ };
137
+ }
138
+ function V() {
139
+ let e = R(), { data: t, loading: n, error: r, reload: i } = z(() => e.listTickets(), "tickets");
140
+ return {
141
+ tickets: t ?? [],
142
+ loading: n,
143
+ error: r,
144
+ reload: i
145
+ };
146
+ }
147
+ function H(e) {
148
+ let n = R(), { data: r, loading: i, error: a, reload: o, setData: s } = z(() => n.getTicket(e), e ?? "", !!e), c = t(async (e) => {
149
+ let t = await e();
150
+ return s(t), t;
151
+ }, [s]);
152
+ return {
153
+ ticket: r,
154
+ loading: i,
155
+ error: a,
156
+ reload: o,
157
+ reply: t((t) => c(() => n.reply(e, t)), [
158
+ c,
159
+ n,
160
+ e
161
+ ]),
162
+ reopen: t(() => c(() => n.reopen(e)), [
163
+ c,
164
+ n,
165
+ e
166
+ ]),
167
+ rate: t((t) => c(() => n.rate(e, t)), [
168
+ c,
169
+ n,
170
+ e
171
+ ])
172
+ };
173
+ }
174
+ //#endregion
175
+ //#region src/lib/format.js
176
+ var oe = new Intl.DateTimeFormat(void 0, {
177
+ day: "numeric",
178
+ month: "short",
179
+ year: "numeric",
180
+ hour: "numeric",
181
+ minute: "2-digit",
182
+ timeZoneName: "short"
183
+ });
184
+ function U(e) {
185
+ if (!e) return "-";
186
+ let t = new Date(e);
187
+ return Number.isNaN(t.getTime()) ? "-" : oe.format(t);
188
+ }
189
+ function W(e) {
190
+ if (!Number.isFinite(e) || e < 0) return "-";
191
+ if (e < 1024) return `${e} B`;
192
+ if (e < 1024 * 1024) return `${Math.round(e / 1024)} KB`;
193
+ let t = e / (1024 * 1024);
194
+ return `${Number.isInteger(t) ? t : t.toFixed(1)} MB`;
195
+ }
196
+ //#endregion
197
+ //#region src/lib/files.js
198
+ function se(e) {
199
+ let t = e?.allowedContentTypes ?? [];
200
+ return t.length > 0 ? t.join(",") : void 0;
201
+ }
202
+ function ce(e, t) {
203
+ if (t.length === 0) return !0;
204
+ let n = (e || "").toLowerCase();
205
+ return t.some((e) => {
206
+ let t = e.toLowerCase();
207
+ return t.endsWith("/*") ? n.startsWith(t.slice(0, -1)) : n === t;
208
+ });
209
+ }
210
+ function G(e, t) {
211
+ let n = e ?? [], r = t?.maxAttachmentsPerMessage ?? 0, i = t?.maxAttachmentBytes ?? 0, a = t?.allowedContentTypes ?? [];
212
+ if (r > 0 && n.length > r) return r === 1 ? "You can attach one file." : `You can attach up to ${r} files.`;
213
+ for (let e of n) {
214
+ let t = e.name || "This file";
215
+ if (!ce(e.type, a)) return `${t} is a file type we cannot accept.`;
216
+ if (i > 0 && e.size > i) return `${t} is larger than ${W(i)}.`;
217
+ }
218
+ return null;
219
+ }
220
+ function K(e) {
221
+ let t = Array.from(e.clipboardData?.items ?? []), n = Date.now();
222
+ return t.filter((e) => e.kind === "file" && e.type.startsWith("image/")).map((e) => e.getAsFile()).filter(Boolean).map((e, t) => {
223
+ let r = (e.type.split("/")[1] || "png").replace("jpeg", "jpg");
224
+ return new File([e], `pasted-image-${n}-${t + 1}.${r}`, { type: e.type });
225
+ });
226
+ }
227
+ //#endregion
228
+ //#region src/components/AttachmentField.jsx
229
+ function q({ id: e, label: t = "Attachments", files: n, onChange: r, config: i, error: a }) {
230
+ let o = i?.maxAttachmentBytes ?? 0, s = o > 0 ? Math.round(o / (1024 * 1024) * 10) / 10 : void 0;
231
+ return /* @__PURE__ */ j(h, {
232
+ id: e,
233
+ label: t,
234
+ optional: !0,
235
+ multiple: !0,
236
+ value: n,
237
+ onChange: r,
238
+ accept: se(i),
239
+ maxSizeMb: s,
240
+ error: a || void 0
241
+ });
242
+ }
243
+ //#endregion
244
+ //#region src/components/TicketForm.jsx
245
+ function le(e, t) {
246
+ return t && e.includes(t) ? t : e[0] ?? "";
247
+ }
248
+ function J({ id: e, config: t, defaultCategory: n, extraFiles: r = [], onSubmit: i, onBusyChange: a, children: o }) {
249
+ let l = t?.categories ?? [], [u, d] = s(() => le(l, n)), [f, p] = s(""), [m, h] = s(""), [g, _] = s([]), [y, b] = s({}), [x, C] = s(""), w = (e) => {
250
+ _(e), b((n) => ({
251
+ ...n,
252
+ files: G([...r, ...e], t) ?? void 0
253
+ }));
254
+ }, T = (e) => {
255
+ let t = K(e);
256
+ t.length > 0 && w([...g, ...t]);
257
+ };
258
+ function E() {
259
+ let e = {};
260
+ u || (e.category = "Choose a category."), f.trim() || (e.subject = "Give your ticket a subject."), m.trim() || (e.body = "Tell us what happened.");
261
+ let n = G([...r, ...g], t);
262
+ return n && (e.files = n), b(e), Object.keys(e).length === 0;
263
+ }
264
+ async function D(e) {
265
+ if (e.preventDefault(), C(""), E()) {
266
+ a?.(!0);
267
+ try {
268
+ await i({
269
+ category: u,
270
+ subject: f.trim(),
271
+ body: m.trim(),
272
+ files: g
273
+ });
274
+ } catch (e) {
275
+ C(e?.message || "Your ticket did not send. Please try again.");
276
+ } finally {
277
+ a?.(!1);
278
+ }
279
+ }
280
+ }
281
+ return /* @__PURE__ */ M("form", {
282
+ id: e,
283
+ onSubmit: D,
284
+ noValidate: !0,
285
+ className: "d-flex flex-column gap-3",
286
+ children: [
287
+ x && /* @__PURE__ */ j(c, {
288
+ variant: "danger",
289
+ children: x
290
+ }),
291
+ /* @__PURE__ */ j(S, {
292
+ id: `${e}-category`,
293
+ label: "Category",
294
+ required: !0,
295
+ value: u,
296
+ onChange: d,
297
+ options: l.map((e) => ({
298
+ value: e,
299
+ label: e
300
+ })),
301
+ placeholder: "Choose a category",
302
+ error: y.category
303
+ }),
304
+ /* @__PURE__ */ j(v, {
305
+ id: `${e}-subject`,
306
+ label: "Subject",
307
+ required: !0,
308
+ maxLength: t?.subjectMaxLength,
309
+ placeholder: "A short summary",
310
+ value: f,
311
+ onChange: (e) => p(e.target.value),
312
+ error: y.subject
313
+ }),
314
+ /* @__PURE__ */ j(O, {
315
+ id: `${e}-body`,
316
+ label: "Message",
317
+ required: !0,
318
+ rows: 6,
319
+ maxLength: t?.bodyMaxLength,
320
+ placeholder: "What happened, and what did you expect to happen?",
321
+ value: m,
322
+ onChange: (e) => h(e.target.value),
323
+ onPaste: T,
324
+ hint: "You can paste an image into the message.",
325
+ error: y.body
326
+ }),
327
+ o,
328
+ /* @__PURE__ */ j(q, {
329
+ id: `${e}-files`,
330
+ files: g,
331
+ onChange: w,
332
+ config: t,
333
+ error: y.files
334
+ })
335
+ ]
336
+ });
337
+ }
338
+ //#endregion
339
+ //#region src/ReportIssue.jsx
340
+ var ue = "supportCapture", de = { zIndex: "var(--wui-z-fixed)" }, fe = (e) => e?.dataset?.[ue] !== "exclude";
341
+ async function pe() {
342
+ let e = await A(document.body, {
343
+ filter: fe,
344
+ pixelRatio: Math.min(window.devicePixelRatio || 1, 2)
345
+ });
346
+ if (!e) throw Error("The page capture came back empty.");
347
+ return new File([e], "screenshot.png", { type: "image/png" });
348
+ }
349
+ function me({ file: e, onRemove: t }) {
350
+ let [n, i] = s(null);
351
+ return r(() => {
352
+ let t = URL.createObjectURL(e);
353
+ return i(t), () => URL.revokeObjectURL(t);
354
+ }, [e]), /* @__PURE__ */ M("figure", {
355
+ className: "mb-0 d-flex flex-column gap-2",
356
+ children: [
357
+ /* @__PURE__ */ j("figcaption", {
358
+ className: "wui-text-sm fw-semibold",
359
+ children: "Screenshot of this page"
360
+ }),
361
+ n && /* @__PURE__ */ j("img", {
362
+ src: n,
363
+ alt: "Screenshot of the page you were on",
364
+ className: "d-block w-100 border rounded"
365
+ }),
366
+ /* @__PURE__ */ j(d, {
367
+ variant: "ghost",
368
+ size: "sm",
369
+ leftIcon: "x",
370
+ onClick: t,
371
+ className: "align-self-start",
372
+ children: "Remove screenshot"
373
+ })
374
+ ]
375
+ });
376
+ }
377
+ function he({ label: e = "Report an issue", floating: t = !1, defaultCategory: n = "Bug report", appVersion: r, getTicketHref: a, onOpenTicket: o, onCreated: l }) {
378
+ let u = R(), { config: f, error: p } = B(), m = `support-report-${i().replace(/[^a-zA-Z0-9-]/g, "")}`, [h, g] = s(!1), [_, v] = s(!1), [y, x] = s(null), [S, C] = s(!1), [w, T] = s(!1), [E, D] = s(null);
379
+ async function O() {
380
+ v(!0), D(null);
381
+ try {
382
+ x(await pe()), C(!1);
383
+ } catch {
384
+ x(null), C(!0);
385
+ } finally {
386
+ v(!1), g(!0);
387
+ }
388
+ }
389
+ let k = () => {
390
+ w || (g(!1), x(null), D(null));
391
+ };
392
+ async function A(e) {
393
+ let t = await u.createTicket({
394
+ ...e,
395
+ pageUrl: window.location.href,
396
+ userAgent: navigator.userAgent,
397
+ appVersion: r,
398
+ screenshots: y ? [y] : []
399
+ });
400
+ D(t), x(null), l?.(t);
401
+ }
402
+ let N = E && a ? a(E.id) : void 0;
403
+ return /* @__PURE__ */ M(ee, { children: [/* @__PURE__ */ j("span", {
404
+ "data-support-capture": "exclude",
405
+ className: t ? "position-fixed bottom-0 end-0 m-3" : "d-inline-block",
406
+ style: t ? de : void 0,
407
+ children: /* @__PURE__ */ j(d, {
408
+ leftIcon: "bug",
409
+ variant: t ? "primary" : "outline",
410
+ loading: _,
411
+ onClick: O,
412
+ "aria-haspopup": "dialog",
413
+ children: e
414
+ })
415
+ }), /* @__PURE__ */ j(b, {
416
+ open: h,
417
+ size: "lg",
418
+ title: "Report an issue",
419
+ subtitle: "Tell us what went wrong. We attach a screenshot of this page to help us see it.",
420
+ headerIcon: "bug",
421
+ staticBackdrop: !0,
422
+ onClose: k,
423
+ footer: E ? /* @__PURE__ */ M("div", {
424
+ className: "d-flex justify-content-end gap-2 w-100",
425
+ children: [/* @__PURE__ */ j(d, {
426
+ variant: "secondary",
427
+ onClick: k,
428
+ children: "Close"
429
+ }), E && (N || o) ? /* @__PURE__ */ j(d, {
430
+ href: N,
431
+ onClick: N ? void 0 : () => {
432
+ g(!1), D(null), o(E);
433
+ },
434
+ children: "View ticket"
435
+ }) : null]
436
+ }) : /* @__PURE__ */ M("div", {
437
+ className: "d-flex justify-content-end gap-2 w-100",
438
+ children: [/* @__PURE__ */ j(d, {
439
+ variant: "secondary",
440
+ onClick: k,
441
+ disabled: w,
442
+ children: "Cancel"
443
+ }), /* @__PURE__ */ j(d, {
444
+ type: "submit",
445
+ form: m,
446
+ loading: w,
447
+ disabled: !f,
448
+ children: "Send report"
449
+ })]
450
+ }),
451
+ children: E ? /* @__PURE__ */ j(c, {
452
+ variant: "success",
453
+ role: "status",
454
+ title: `Ticket ${E.number} is with the support team`,
455
+ children: "We reply by email. You can follow the ticket on your support page."
456
+ }) : f ? /* @__PURE__ */ j(J, {
457
+ id: m,
458
+ config: f,
459
+ defaultCategory: n,
460
+ extraFiles: y ? [y] : [],
461
+ onSubmit: A,
462
+ onBusyChange: T,
463
+ children: y ? /* @__PURE__ */ j(me, {
464
+ file: y,
465
+ onRemove: () => x(null)
466
+ }) : S ? /* @__PURE__ */ j(c, {
467
+ variant: "info",
468
+ children: "We could not take a screenshot of this page. You can attach an image instead."
469
+ }) : null
470
+ }) : /* @__PURE__ */ j(c, {
471
+ variant: p ? "danger" : "info",
472
+ children: p ? p.message || "We could not load the support settings." : "Loading the form."
473
+ })
474
+ })] });
475
+ }
476
+ //#endregion
477
+ //#region src/components/NewTicketModal.jsx
478
+ function ge({ open: e, onClose: t, onCreated: n, config: r, defaultCategory: a, appVersion: o }) {
479
+ let c = R(), l = `support-new-${i().replace(/[^a-zA-Z0-9-]/g, "")}`, [u, f] = s(!1);
480
+ async function p(e) {
481
+ n(await c.createTicket({
482
+ ...e,
483
+ userAgent: typeof navigator > "u" ? void 0 : navigator.userAgent,
484
+ appVersion: o
485
+ }));
486
+ }
487
+ return /* @__PURE__ */ j(b, {
488
+ open: e,
489
+ size: "lg",
490
+ title: "New ticket",
491
+ subtitle: "Tell us what you need. We reply by email and on this page.",
492
+ headerIcon: "message-square",
493
+ staticBackdrop: !0,
494
+ onClose: () => {
495
+ u || t();
496
+ },
497
+ footer: /* @__PURE__ */ M("div", {
498
+ className: "d-flex justify-content-end gap-2 w-100",
499
+ children: [/* @__PURE__ */ j(d, {
500
+ variant: "secondary",
501
+ onClick: t,
502
+ disabled: u,
503
+ children: "Cancel"
504
+ }), /* @__PURE__ */ j(d, {
505
+ type: "submit",
506
+ form: l,
507
+ loading: u,
508
+ children: "Send ticket"
509
+ })]
510
+ }),
511
+ children: e && /* @__PURE__ */ j(J, {
512
+ id: l,
513
+ config: r,
514
+ defaultCategory: a,
515
+ onSubmit: p,
516
+ onBusyChange: f
517
+ })
518
+ });
519
+ }
520
+ //#endregion
521
+ //#region src/lib/status.js
522
+ var _e = {
523
+ Open: "warning",
524
+ InProgress: "info",
525
+ Resolved: "success",
526
+ Closed: "neutral"
527
+ };
528
+ function Y(e) {
529
+ return _e[e] ?? "neutral";
530
+ }
531
+ //#endregion
532
+ //#region src/components/AttachmentThumb.jsx
533
+ var ve = {
534
+ width: 140,
535
+ maxWidth: "100%"
536
+ }, ye = { height: 84 };
537
+ function be({ attachment: e }) {
538
+ let t = R(), n = (e.contentType || "").startsWith("image/"), [i, a] = s(null), [o, c] = s(!1);
539
+ r(() => {
540
+ let n = !0, r = null;
541
+ return a(null), c(!1), t.fetchAttachment(e.id).then((e) => {
542
+ r = URL.createObjectURL(e), n ? a(r) : URL.revokeObjectURL(r);
543
+ }, () => n && c(!0)), () => {
544
+ n = !1, r && URL.revokeObjectURL(r);
545
+ };
546
+ }, [t, e.id]);
547
+ let l = `Open ${e.fileName}`, u = /* @__PURE__ */ M("span", {
548
+ className: "d-block text-truncate px-2 py-1 wui-text-xs text-body-secondary",
549
+ children: [
550
+ e.fileName,
551
+ ", ",
552
+ W(e.sizeBytes)
553
+ ]
554
+ });
555
+ return o ? /* @__PURE__ */ M("span", {
556
+ className: "d-inline-flex flex-column border rounded overflow-hidden",
557
+ style: ve,
558
+ children: [/* @__PURE__ */ M("span", {
559
+ className: "d-flex align-items-center justify-content-center gap-1 p-2 wui-text-xs text-body-secondary",
560
+ children: [/* @__PURE__ */ j(g, {
561
+ name: "x-circle",
562
+ size: 14
563
+ }), "Could not load this file."]
564
+ }), u]
565
+ }) : /* @__PURE__ */ M("a", {
566
+ className: "d-inline-flex flex-column border rounded overflow-hidden text-decoration-none",
567
+ style: ve,
568
+ href: i ?? void 0,
569
+ target: "_blank",
570
+ rel: "noopener noreferrer",
571
+ "aria-label": l,
572
+ "aria-busy": !i || void 0,
573
+ children: [/* @__PURE__ */ j("span", {
574
+ className: "d-flex align-items-center justify-content-center bg-body-tertiary",
575
+ style: ye,
576
+ children: i ? n ? /* @__PURE__ */ j("img", {
577
+ src: i,
578
+ alt: e.fileName,
579
+ className: "d-block w-100 h-100 object-fit-cover"
580
+ }) : /* @__PURE__ */ j(g, {
581
+ name: "file-text",
582
+ size: 28
583
+ }) : /* @__PURE__ */ j(w, {
584
+ size: "sm",
585
+ label: `Loading ${e.fileName}`
586
+ })
587
+ }), u]
588
+ });
589
+ }
590
+ //#endregion
591
+ //#region src/components/MessageThread.jsx
592
+ var xe = {
593
+ whiteSpace: "pre-wrap",
594
+ overflowWrap: "anywhere"
595
+ };
596
+ function Se({ messages: e }) {
597
+ return /* @__PURE__ */ j("ol", {
598
+ className: "list-unstyled d-flex flex-column gap-3 mb-0",
599
+ "aria-label": "Conversation",
600
+ children: e.map((e) => {
601
+ let t = /* @__PURE__ */ j("time", {
602
+ dateTime: e.createdDateTimeUtc,
603
+ className: "wui-text-xs text-body-secondary text-nowrap",
604
+ children: U(e.createdDateTimeUtc)
605
+ });
606
+ if (e.authorKind === "System") return /* @__PURE__ */ M("li", {
607
+ className: "text-center wui-text-xs text-body-secondary",
608
+ "data-author": "system",
609
+ children: [/* @__PURE__ */ j("span", {
610
+ className: "d-block",
611
+ children: e.body
612
+ }), t]
613
+ }, e.id);
614
+ let n = e.authorKind === "Customer", r = e.authorName || (n ? "You" : "Support team");
615
+ return /* @__PURE__ */ M("li", {
616
+ className: `d-flex align-items-start gap-2${n ? " flex-row-reverse" : ""}`,
617
+ "data-author": n ? "customer" : "agent",
618
+ children: [/* @__PURE__ */ j(l, {
619
+ name: r,
620
+ size: 32,
621
+ tone: n ? "neutral" : "primary"
622
+ }), /* @__PURE__ */ M(f, {
623
+ className: "flex-grow-1 border",
624
+ padding: "sm",
625
+ variant: n ? "default" : "accent",
626
+ style: { minWidth: 0 },
627
+ children: [
628
+ /* @__PURE__ */ M("div", {
629
+ className: "d-flex flex-wrap align-items-center justify-content-between gap-2 mb-2",
630
+ children: [/* @__PURE__ */ M("span", {
631
+ className: "d-inline-flex flex-wrap align-items-center gap-2 fw-semibold wui-text-sm text-break",
632
+ children: [r, !n && /* @__PURE__ */ j(u, {
633
+ tone: "primary",
634
+ children: "Support"
635
+ })]
636
+ }), t]
637
+ }),
638
+ /* @__PURE__ */ j("div", {
639
+ className: "wui-text-sm",
640
+ style: xe,
641
+ children: e.body
642
+ }),
643
+ e.attachments?.length > 0 && /* @__PURE__ */ j("div", {
644
+ className: "d-flex flex-wrap gap-2 mt-2",
645
+ children: e.attachments.map((e) => /* @__PURE__ */ j(be, { attachment: e }, e.id))
646
+ })
647
+ ]
648
+ })]
649
+ }, e.id);
650
+ })
651
+ });
652
+ }
653
+ //#endregion
654
+ //#region src/components/RatingPanel.jsx
655
+ function Ce({ satisfaction: e, comment: t, canRate: n = !1, commentMaxLength: r, onRate: a }) {
656
+ let o = i(), [l, u] = s(""), [p, m] = s(null), [h, g] = s("");
657
+ if (e) return /* @__PURE__ */ M(c, {
658
+ variant: e === "Satisfied" ? "success" : "info",
659
+ role: "status",
660
+ children: [e === "Satisfied" ? "You told us this solved your problem." : "You told us this did not solve your problem.", t ? /* @__PURE__ */ M("span", {
661
+ className: "d-block mt-1",
662
+ children: ["Your comment: ", t]
663
+ }) : null]
664
+ });
665
+ if (!n) return null;
666
+ async function _(e) {
667
+ g(""), m(e);
668
+ try {
669
+ await a({
670
+ satisfaction: e,
671
+ comment: l
672
+ });
673
+ } catch (e) {
674
+ g(e?.message || "Your rating did not send. Please try again.");
675
+ } finally {
676
+ m(null);
677
+ }
678
+ }
679
+ return /* @__PURE__ */ j(f, {
680
+ padding: "sm",
681
+ className: "border",
682
+ title: "Did this solve your problem?",
683
+ children: /* @__PURE__ */ M("div", {
684
+ className: "d-flex flex-column gap-2",
685
+ children: [
686
+ h && /* @__PURE__ */ j(c, {
687
+ variant: "danger",
688
+ children: h
689
+ }),
690
+ /* @__PURE__ */ j(O, {
691
+ id: `${o}-comment`,
692
+ label: "Anything to add?",
693
+ rows: 2,
694
+ maxLength: r,
695
+ value: l,
696
+ onChange: (e) => u(e.target.value),
697
+ hint: "Optional. Your comment goes to the support team."
698
+ }),
699
+ /* @__PURE__ */ M("div", {
700
+ className: "d-flex flex-wrap gap-2",
701
+ children: [/* @__PURE__ */ j(d, {
702
+ variant: "outline",
703
+ leftIcon: "check-circle",
704
+ loading: p === "Satisfied",
705
+ disabled: p !== null,
706
+ onClick: () => _("Satisfied"),
707
+ children: "Yes, it is solved"
708
+ }), /* @__PURE__ */ j(d, {
709
+ variant: "outline",
710
+ leftIcon: "x-circle",
711
+ loading: p === "Unsatisfied",
712
+ disabled: p !== null,
713
+ onClick: () => _("Unsatisfied"),
714
+ children: "No, it is not"
715
+ })]
716
+ })
717
+ ]
718
+ })
719
+ });
720
+ }
721
+ //#endregion
722
+ //#region src/components/ReplyBox.jsx
723
+ function we({ onSend: e, config: t, reopens: n = !1 }) {
724
+ let r = i(), [a, o] = s(""), [l, u] = s([]), [f, p] = s(""), [m, h] = s(""), [g, _] = s(!1), v = (e) => {
725
+ u(e), p(G(e, t) ?? "");
726
+ }, y = (e) => {
727
+ let t = K(e);
728
+ t.length > 0 && v([...l, ...t]);
729
+ };
730
+ async function b(n) {
731
+ n.preventDefault();
732
+ let r = a.trim();
733
+ if (!r) {
734
+ h("Write a message before you send it.");
735
+ return;
736
+ }
737
+ let i = G(l, t);
738
+ if (i) {
739
+ p(i);
740
+ return;
741
+ }
742
+ h(""), _(!0);
743
+ try {
744
+ await e({
745
+ body: r,
746
+ files: l
747
+ }), o(""), u([]), p("");
748
+ } catch (e) {
749
+ h(e?.message || "Your reply did not send. Please try again.");
750
+ } finally {
751
+ _(!1);
752
+ }
753
+ }
754
+ return /* @__PURE__ */ M("form", {
755
+ onSubmit: b,
756
+ noValidate: !0,
757
+ className: "d-flex flex-column gap-2",
758
+ "aria-label": "Reply to this ticket",
759
+ children: [
760
+ m && /* @__PURE__ */ j(c, {
761
+ variant: "danger",
762
+ children: m
763
+ }),
764
+ /* @__PURE__ */ j(O, {
765
+ id: `${r}-body`,
766
+ label: "Your reply",
767
+ rows: 4,
768
+ autoGrow: !0,
769
+ maxLength: t?.bodyMaxLength,
770
+ value: a,
771
+ onChange: (e) => o(e.target.value),
772
+ onPaste: y,
773
+ hint: n ? "Sending a reply reopens this ticket. You can paste an image into the message." : "You can paste an image into the message.",
774
+ required: !0
775
+ }),
776
+ /* @__PURE__ */ j(q, {
777
+ id: `${r}-files`,
778
+ files: l,
779
+ onChange: v,
780
+ config: t,
781
+ error: f
782
+ }),
783
+ /* @__PURE__ */ j("div", {
784
+ className: "d-flex justify-content-end",
785
+ children: /* @__PURE__ */ j(d, {
786
+ type: "submit",
787
+ leftIcon: "send",
788
+ loading: g,
789
+ children: "Send reply"
790
+ })
791
+ })
792
+ ]
793
+ });
794
+ }
795
+ //#endregion
796
+ //#region src/components/TicketDetail.jsx
797
+ function X({ label: e, children: t }) {
798
+ return /* @__PURE__ */ M("div", {
799
+ className: "d-flex flex-column",
800
+ children: [/* @__PURE__ */ j("span", {
801
+ className: "wui-text-xs fw-semibold text-body-secondary",
802
+ children: e
803
+ }), /* @__PURE__ */ j("span", {
804
+ className: "wui-text-sm",
805
+ children: t
806
+ })]
807
+ });
808
+ }
809
+ function Te({ ticketId: e, onBack: t, onChanged: n }) {
810
+ let { config: r } = B(), { ticket: i, loading: a, error: o, reload: l, reply: u, reopen: f, rate: p } = H(e), [m, h] = s(!1), [g, _] = s(""), v = async (e) => {
811
+ let t = await e();
812
+ return n?.(), t;
813
+ };
814
+ async function y() {
815
+ _(""), h(!0);
816
+ try {
817
+ await v(f);
818
+ } catch (e) {
819
+ _(e?.message || "The ticket did not reopen. Please try again.");
820
+ } finally {
821
+ h(!1);
822
+ }
823
+ }
824
+ let b = /* @__PURE__ */ j(d, {
825
+ variant: "ghost",
826
+ size: "sm",
827
+ leftIcon: "arrow-left",
828
+ onClick: t,
829
+ className: "align-self-start",
830
+ children: "Back to tickets"
831
+ });
832
+ if (!i) return /* @__PURE__ */ M("section", {
833
+ className: "d-flex flex-column gap-3",
834
+ "aria-busy": a || void 0,
835
+ children: [b, o ? /* @__PURE__ */ M(c, {
836
+ variant: "danger",
837
+ children: [
838
+ o.message || "We could not load this ticket.",
839
+ " ",
840
+ /* @__PURE__ */ j(d, {
841
+ variant: "ghost",
842
+ size: "sm",
843
+ onClick: l,
844
+ children: "Try again"
845
+ })
846
+ ]
847
+ }) : /* @__PURE__ */ j(C, { height: 160 })]
848
+ });
849
+ let x = i.status === "Resolved" || i.status === "Closed";
850
+ return /* @__PURE__ */ M("section", {
851
+ className: "d-flex flex-column gap-3",
852
+ "aria-labelledby": `support-ticket-${i.id}`,
853
+ children: [
854
+ b,
855
+ /* @__PURE__ */ M("header", {
856
+ className: "d-flex flex-column gap-2",
857
+ children: [
858
+ /* @__PURE__ */ M("div", {
859
+ className: "d-flex flex-wrap align-items-center gap-2",
860
+ children: [/* @__PURE__ */ j("h2", {
861
+ id: `support-ticket-${i.id}`,
862
+ className: "h5 mb-0 text-break",
863
+ children: i.subject
864
+ }), i.statusLabel && /* @__PURE__ */ j(T, {
865
+ label: i.statusLabel,
866
+ tone: Y(i.status)
867
+ })]
868
+ }),
869
+ /* @__PURE__ */ M("div", {
870
+ className: "row row-cols-2 row-cols-md-4 g-3",
871
+ children: [
872
+ /* @__PURE__ */ j(X, {
873
+ label: "Ticket",
874
+ children: i.number
875
+ }),
876
+ /* @__PURE__ */ j(X, {
877
+ label: "Category",
878
+ children: i.category || "-"
879
+ }),
880
+ /* @__PURE__ */ j(X, {
881
+ label: "Opened",
882
+ children: /* @__PURE__ */ j("time", {
883
+ dateTime: i.createdDateTimeUtc,
884
+ children: U(i.createdDateTimeUtc)
885
+ })
886
+ }),
887
+ /* @__PURE__ */ j(X, {
888
+ label: "Last update",
889
+ children: /* @__PURE__ */ j("time", {
890
+ dateTime: i.updatedDateTimeUtc,
891
+ children: U(i.updatedDateTimeUtc)
892
+ })
893
+ })
894
+ ]
895
+ }),
896
+ /* @__PURE__ */ j("p", {
897
+ className: "wui-text-xs text-body-secondary mb-0",
898
+ children: "Times are shown in your time zone."
899
+ })
900
+ ]
901
+ }),
902
+ g && /* @__PURE__ */ j(c, {
903
+ variant: "danger",
904
+ children: g
905
+ }),
906
+ x && /* @__PURE__ */ j(c, {
907
+ variant: "success",
908
+ role: "status",
909
+ children: "If you still need help, reopen the ticket or send a reply."
910
+ }),
911
+ i.canReopen && /* @__PURE__ */ j(d, {
912
+ variant: "secondary",
913
+ leftIcon: "history",
914
+ loading: m,
915
+ onClick: y,
916
+ className: "align-self-start",
917
+ children: "Reopen ticket"
918
+ }),
919
+ /* @__PURE__ */ j(Ce, {
920
+ satisfaction: i.satisfaction,
921
+ comment: i.satisfactionComment,
922
+ canRate: i.canRate,
923
+ commentMaxLength: r?.satisfactionCommentMaxLength,
924
+ onRate: (e) => v(() => p(e))
925
+ }),
926
+ /* @__PURE__ */ j(Se, { messages: i.messages ?? [] }),
927
+ /* @__PURE__ */ j(we, {
928
+ config: r,
929
+ reopens: x,
930
+ onSend: (e) => v(() => u(e))
931
+ })
932
+ ]
933
+ });
934
+ }
935
+ //#endregion
936
+ //#region src/lib/url.js
937
+ var Z = "ticket";
938
+ function Ee() {
939
+ return typeof window > "u" ? null : new URLSearchParams(window.location.search).get(Z);
940
+ }
941
+ function De(e) {
942
+ if (typeof window > "u") return e ? `?${Z}=${encodeURIComponent(e)}` : "?";
943
+ let t = new URL(window.location.href);
944
+ return e ? t.searchParams.set(Z, e) : t.searchParams.delete(Z), `${t.pathname}${t.search}${t.hash}`;
945
+ }
946
+ function Oe(e) {
947
+ if (typeof window > "u") return;
948
+ let t = De(e);
949
+ t !== `${window.location.pathname}${window.location.search}${window.location.hash}` && window.history.replaceState(window.history.state, "", t);
950
+ }
951
+ //#endregion
952
+ //#region src/components/TicketList.jsx
953
+ var Q = [
954
+ {
955
+ key: "active",
956
+ label: "Active"
957
+ },
958
+ {
959
+ key: "Open",
960
+ label: "Open"
961
+ },
962
+ {
963
+ key: "InProgress",
964
+ label: "In progress"
965
+ },
966
+ {
967
+ key: "Resolved",
968
+ label: "Resolved"
969
+ },
970
+ {
971
+ key: "Closed",
972
+ label: "Closed"
973
+ },
974
+ {
975
+ key: "all",
976
+ label: "All"
977
+ }
978
+ ];
979
+ function ke(e, t) {
980
+ return t === "all" ? !0 : t === "active" ? e.status === "Open" || e.status === "InProgress" : e.status === t;
981
+ }
982
+ function Ae(e, t) {
983
+ return !t || [
984
+ e.number,
985
+ e.subject,
986
+ e.category
987
+ ].some((e) => (e || "").toLowerCase().includes(t));
988
+ }
989
+ function je({ tickets: e, loading: t, error: n, onRetry: r, onOpen: i }) {
990
+ let [o, l] = s("active"), [u, f] = s(""), p = u.trim().toLowerCase(), h = a(() => e.filter((e) => Ae(e, p)), [e, p]), g = a(() => Object.fromEntries(Q.map((e) => [e.key, h.filter((t) => ke(t, e.key)).length])), [h]), _ = k(a(() => h.filter((e) => ke(e, o)), [h, o]));
991
+ return /* @__PURE__ */ M("div", {
992
+ className: "d-flex flex-column gap-3",
993
+ children: [
994
+ n && /* @__PURE__ */ M(c, {
995
+ variant: "danger",
996
+ children: [
997
+ n.message || "We could not load your tickets.",
998
+ " ",
999
+ /* @__PURE__ */ j(d, {
1000
+ variant: "ghost",
1001
+ size: "sm",
1002
+ onClick: r,
1003
+ children: "Try again"
1004
+ })
1005
+ ]
1006
+ }),
1007
+ /* @__PURE__ */ M("div", {
1008
+ className: "d-flex flex-column flex-md-row gap-2 align-items-md-center justify-content-between",
1009
+ children: [/* @__PURE__ */ j(D, {
1010
+ tabs: Q.map((e) => ({
1011
+ ...e,
1012
+ count: g[e.key]
1013
+ })),
1014
+ value: o,
1015
+ onChange: l,
1016
+ ariaLabel: "Filter tickets by status",
1017
+ className: "flex-grow-1"
1018
+ }), /* @__PURE__ */ j(x, {
1019
+ value: u,
1020
+ onChange: f,
1021
+ placeholder: "Search tickets",
1022
+ ariaLabel: "Search tickets"
1023
+ })]
1024
+ }),
1025
+ /* @__PURE__ */ j(m, {
1026
+ columns: [
1027
+ {
1028
+ key: "subject",
1029
+ label: "Subject",
1030
+ render: (e) => /* @__PURE__ */ M("div", {
1031
+ className: "d-flex flex-column",
1032
+ style: { minWidth: 0 },
1033
+ children: [/* @__PURE__ */ j(y, {
1034
+ href: De(e.id),
1035
+ animation: "none",
1036
+ className: "fw-semibold text-break",
1037
+ onClick: (t) => {
1038
+ t.metaKey || t.ctrlKey || t.shiftKey || t.altKey || (t.preventDefault(), i(e.id));
1039
+ },
1040
+ children: e.subject
1041
+ }), /* @__PURE__ */ M("span", {
1042
+ className: "wui-text-xs text-body-secondary",
1043
+ children: [
1044
+ e.number,
1045
+ ", ",
1046
+ e.category,
1047
+ e.awaitingCustomer ? ". Waiting for your reply" : ""
1048
+ ]
1049
+ })]
1050
+ })
1051
+ },
1052
+ {
1053
+ key: "status",
1054
+ label: "Status",
1055
+ render: (e) => e.statusLabel ? /* @__PURE__ */ j(T, {
1056
+ label: e.statusLabel,
1057
+ tone: Y(e.status)
1058
+ }) : "-"
1059
+ },
1060
+ {
1061
+ key: "messageCount",
1062
+ label: "Messages",
1063
+ cellClassName: "wui-text-sm text-body-secondary",
1064
+ render: (e) => e.messageCount
1065
+ },
1066
+ {
1067
+ key: "updatedDateTimeUtc",
1068
+ label: "Last update",
1069
+ cellClassName: "wui-text-sm text-body-secondary text-nowrap",
1070
+ render: (e) => /* @__PURE__ */ j("time", {
1071
+ dateTime: e.updatedDateTimeUtc,
1072
+ children: U(e.updatedDateTimeUtc)
1073
+ })
1074
+ }
1075
+ ],
1076
+ rows: _.pageRows,
1077
+ loading: t,
1078
+ skeletonRows: 4,
1079
+ ariaLabel: "Support tickets",
1080
+ sourceCount: e.length,
1081
+ pagination: _,
1082
+ empty: /* @__PURE__ */ j(E, {
1083
+ icon: "message-square",
1084
+ title: "No tickets yet",
1085
+ text: "When you raise a ticket, it shows here with its status."
1086
+ }),
1087
+ emptyFiltered: /* @__PURE__ */ j(E, {
1088
+ icon: "search",
1089
+ title: "No tickets match",
1090
+ text: "Try another search or status."
1091
+ })
1092
+ })
1093
+ ]
1094
+ });
1095
+ }
1096
+ //#endregion
1097
+ //#region src/SupportCenter.jsx
1098
+ function Me({ initialTicketId: e, defaultCategory: t, appVersion: n }) {
1099
+ let { config: r, error: i, reload: a } = B(), { tickets: o, loading: l, error: u, reload: f } = V(), [p, m] = s(() => e || Ee()), [h, g] = s(!1), _ = (e) => {
1100
+ m(e), Oe(e);
1101
+ };
1102
+ return p ? /* @__PURE__ */ j(Te, {
1103
+ ticketId: p,
1104
+ onBack: () => _(null),
1105
+ onChanged: f
1106
+ }, p) : /* @__PURE__ */ M("section", {
1107
+ className: "d-flex flex-column gap-3",
1108
+ "aria-labelledby": "support-center-title",
1109
+ children: [
1110
+ /* @__PURE__ */ M("div", {
1111
+ className: "d-flex flex-wrap align-items-center justify-content-between gap-2",
1112
+ children: [/* @__PURE__ */ j("h2", {
1113
+ id: "support-center-title",
1114
+ className: "h5 mb-0",
1115
+ children: "Your tickets"
1116
+ }), /* @__PURE__ */ j(d, {
1117
+ leftIcon: "plus",
1118
+ onClick: () => g(!0),
1119
+ disabled: !r,
1120
+ children: "New ticket"
1121
+ })]
1122
+ }),
1123
+ i && /* @__PURE__ */ M(c, {
1124
+ variant: "warning",
1125
+ children: [
1126
+ i.message || "We could not load the support settings.",
1127
+ " ",
1128
+ /* @__PURE__ */ j(d, {
1129
+ variant: "ghost",
1130
+ size: "sm",
1131
+ onClick: a,
1132
+ children: "Try again"
1133
+ })
1134
+ ]
1135
+ }),
1136
+ /* @__PURE__ */ j(je, {
1137
+ tickets: o,
1138
+ loading: l,
1139
+ error: u,
1140
+ onRetry: f,
1141
+ onOpen: _
1142
+ }),
1143
+ r && /* @__PURE__ */ j(ge, {
1144
+ open: h,
1145
+ onClose: () => g(!1),
1146
+ config: r,
1147
+ defaultCategory: t,
1148
+ appVersion: n,
1149
+ onCreated: (e) => {
1150
+ g(!1), f(), _(e.id);
1151
+ }
1152
+ })
1153
+ ]
1154
+ });
1155
+ }
1156
+ //#endregion
1157
+ //#region src/SupportContact.jsx
1158
+ var $ = (e) => typeof e == "string" && e.trim() !== "";
1159
+ function Ne({ className: e }) {
1160
+ let { config: t } = B();
1161
+ if (!t) return null;
1162
+ let n = [];
1163
+ return $(t.supportEmail) && n.push({
1164
+ key: "email",
1165
+ icon: "mail",
1166
+ label: "Email",
1167
+ value: /* @__PURE__ */ M("span", {
1168
+ className: "d-inline-flex flex-wrap align-items-center gap-2",
1169
+ children: [/* @__PURE__ */ j("a", {
1170
+ href: `mailto:${t.supportEmail}`,
1171
+ className: "text-break",
1172
+ children: t.supportEmail
1173
+ }), /* @__PURE__ */ j(p, {
1174
+ text: t.supportEmail,
1175
+ size: "sm",
1176
+ label: "Copy email",
1177
+ copiedLabel: "Email copied"
1178
+ })]
1179
+ })
1180
+ }), $(t.supportPhone) && n.push({
1181
+ key: "phone",
1182
+ icon: "phone",
1183
+ label: "Phone",
1184
+ value: t.supportPhone,
1185
+ href: `tel:${t.supportPhone.replace(/[^\d+]/g, "")}`
1186
+ }), $(t.supportHours) && n.push({
1187
+ key: "hours",
1188
+ icon: "clock",
1189
+ label: "Hours",
1190
+ value: t.supportHours
1191
+ }), n.length === 0 ? null : /* @__PURE__ */ M("section", {
1192
+ className: e,
1193
+ "aria-label": $(t.supportName) ? `Contact ${t.supportName}` : "Contact support",
1194
+ children: [$(t.supportName) && /* @__PURE__ */ j("p", {
1195
+ className: "fw-semibold mb-2",
1196
+ children: t.supportName
1197
+ }), /* @__PURE__ */ j(_, { items: n })]
1198
+ });
1199
+ }
1200
+ //#endregion
1201
+ //#region src/SupportProvider.jsx
1202
+ function Pe({ client: e, children: n }) {
1203
+ let r = o(null), i = t((t = !1) => {
1204
+ let n = r.current;
1205
+ if (t || !n || n.client !== e) {
1206
+ let t = e.getConfig(), n = {
1207
+ client: e,
1208
+ pending: t
1209
+ };
1210
+ return r.current = n, t.catch(() => {
1211
+ r.current === n && (r.current = null);
1212
+ }), t;
1213
+ }
1214
+ return n.pending;
1215
+ }, [e]), s = a(() => ({
1216
+ client: e,
1217
+ loadConfig: i
1218
+ }), [e, i]);
1219
+ return /* @__PURE__ */ j(ie.Provider, {
1220
+ value: s,
1221
+ children: n
1222
+ });
1223
+ }
1224
+ //#endregion
1225
+ export { he as ReportIssue, P as SupportApiError, Me as SupportCenter, Ne as SupportContact, Pe as SupportProvider, re as createSupportClient, R as useSupportClient, B as useSupportConfig, H as useSupportTicket, V as useSupportTickets };