@reqdesk/widget 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/react.cjs ADDED
@@ -0,0 +1,1642 @@
1
+ Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
2
+ const require_storage = require("./storage-Db9XjupR.cjs");
3
+ let react = require("react");
4
+ let _tanstack_react_query = require("@tanstack/react-query");
5
+ let react_jsx_runtime = require("react/jsx-runtime");
6
+ let react_dom = require("react-dom");
7
+ //#region src/auth/widget-auth.ts
8
+ let authState = {
9
+ isAuthenticated: false,
10
+ isLoading: false
11
+ };
12
+ let listeners = [];
13
+ let oidcInstance = null;
14
+ function notify() {
15
+ for (const listener of listeners) try {
16
+ listener(authState);
17
+ } catch {}
18
+ }
19
+ function setState(update) {
20
+ authState = {
21
+ ...authState,
22
+ ...update
23
+ };
24
+ notify();
25
+ }
26
+ function getAuthState() {
27
+ return authState;
28
+ }
29
+ function onAuthStateChange(listener) {
30
+ listeners.push(listener);
31
+ return () => {
32
+ listeners = listeners.filter((l) => l !== listener);
33
+ };
34
+ }
35
+ async function initWidgetAuth(config) {
36
+ setState({ isLoading: true });
37
+ try {
38
+ const { createOidc } = await import("oidc-spa/core");
39
+ const oidc = await createOidc({
40
+ issuerUri: config.issuerUri,
41
+ clientId: config.clientId
42
+ });
43
+ oidcInstance = oidc;
44
+ if (oidc.isUserLoggedIn) {
45
+ require_storage.setOidcTokenProvider(() => oidc.getTokens());
46
+ const payload = decodeJwtPayload((await oidc.getTokens()).accessToken);
47
+ setState({
48
+ isAuthenticated: true,
49
+ isLoading: false,
50
+ userEmail: payload?.email,
51
+ userName: payload?.name ?? payload?.preferred_username
52
+ });
53
+ oidc.subscribeToTokensChange(async (newTokens) => {
54
+ const p = decodeJwtPayload(newTokens.accessToken);
55
+ setState({
56
+ userEmail: p?.email ?? authState.userEmail,
57
+ userName: p?.name ?? p?.preferred_username ?? authState.userName
58
+ });
59
+ });
60
+ } else setState({
61
+ isAuthenticated: false,
62
+ isLoading: false
63
+ });
64
+ } catch (err) {
65
+ console.warn("[reqdesk-widget] oidc-spa initialization failed:", err);
66
+ setState({
67
+ isAuthenticated: false,
68
+ isLoading: false
69
+ });
70
+ }
71
+ }
72
+ async function login() {
73
+ if (!oidcInstance) return;
74
+ await oidcInstance.login({ doesCurrentHrefRequiresAuth: false });
75
+ }
76
+ async function logout() {
77
+ if (!oidcInstance) return;
78
+ require_storage.setOidcTokenProvider(null);
79
+ setState({
80
+ isAuthenticated: false,
81
+ userEmail: void 0,
82
+ userName: void 0
83
+ });
84
+ await oidcInstance.logout({ redirectTo: "current page" });
85
+ }
86
+ function isAuthConfigured() {
87
+ return oidcInstance !== null;
88
+ }
89
+ function decodeJwtPayload(token) {
90
+ try {
91
+ const parts = token.split(".");
92
+ if (parts.length !== 3) return null;
93
+ const payload = atob(parts[1].replace(/-/g, "+").replace(/_/g, "/"));
94
+ return JSON.parse(payload);
95
+ } catch {
96
+ return null;
97
+ }
98
+ }
99
+ //#endregion
100
+ //#region src/react/ReqdeskProvider.tsx
101
+ const ReqdeskContext = (0, react.createContext)(null);
102
+ function useReqdeskContext() {
103
+ const ctx = (0, react.useContext)(ReqdeskContext);
104
+ if (!ctx) throw new Error("useReqdesk must be used within a <ReqdeskProvider>");
105
+ return ctx;
106
+ }
107
+ const widgetQueryClient = new _tanstack_react_query.QueryClient({ defaultOptions: {
108
+ queries: {
109
+ staleTime: 300 * 1e3,
110
+ gcTime: 600 * 1e3,
111
+ retry: (failureCount, error) => {
112
+ const status = error?.status ?? error?.statusCode;
113
+ if (status && status >= 400 && status < 500 && status !== 408) return false;
114
+ if (status && status >= 500) return false;
115
+ return failureCount < 2;
116
+ },
117
+ retryDelay: (attemptIndex) => Math.min(1e3 * 2 ** attemptIndex, 3e4),
118
+ refetchOnWindowFocus: false
119
+ },
120
+ mutations: { retry: false }
121
+ } });
122
+ function ReqdeskProvider({ apiKey, auth, theme, language, customer, translations, children }) {
123
+ const initialized = (0, react.useRef)(false);
124
+ const [authState, setAuthState] = (0, react.useState)(getAuthState);
125
+ const saved = initialized.current ? null : require_storage.loadWidgetConfig(apiKey);
126
+ const resolvedLanguage = language ?? saved?.language ?? "en";
127
+ const resolvedTheme = theme ?? saved?.theme;
128
+ (0, react.useEffect)(() => {
129
+ if (!initialized.current) {
130
+ require_storage.configureWidgetClient(window.location.origin, apiKey);
131
+ initialized.current = true;
132
+ if (auth) initWidgetAuth(auth);
133
+ }
134
+ }, [apiKey, auth]);
135
+ (0, react.useEffect)(() => {
136
+ return onAuthStateChange(setAuthState);
137
+ }, []);
138
+ (0, react.useEffect)(() => {
139
+ require_storage.saveWidgetConfig(apiKey, {
140
+ language: resolvedLanguage,
141
+ theme: resolvedTheme
142
+ });
143
+ }, [
144
+ apiKey,
145
+ resolvedLanguage,
146
+ resolvedTheme
147
+ ]);
148
+ const value = (0, react.useMemo)(() => ({
149
+ apiKey,
150
+ auth,
151
+ theme: resolvedTheme,
152
+ language: resolvedLanguage,
153
+ customer,
154
+ translations,
155
+ isAuthenticated: authState.isAuthenticated,
156
+ userEmail: authState.userEmail,
157
+ userName: authState.userName
158
+ }), [
159
+ apiKey,
160
+ auth,
161
+ resolvedTheme,
162
+ resolvedLanguage,
163
+ customer,
164
+ translations,
165
+ authState
166
+ ]);
167
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ReqdeskContext.Provider, {
168
+ value,
169
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_tanstack_react_query.QueryClientProvider, {
170
+ client: widgetQueryClient,
171
+ children
172
+ })
173
+ });
174
+ }
175
+ //#endregion
176
+ //#region src/react/useReqdesk.ts
177
+ function useReqdesk() {
178
+ useReqdeskContext();
179
+ const [isLoading, setIsLoading] = (0, react.useState)(false);
180
+ const [error, setError] = (0, react.useState)(null);
181
+ return {
182
+ submitTicket: (0, react.useCallback)(async (data) => {
183
+ setIsLoading(true);
184
+ setError(null);
185
+ try {
186
+ return await require_storage.submitTicket("_current", data);
187
+ } catch (err) {
188
+ const widgetError = err;
189
+ setError(widgetError);
190
+ throw widgetError;
191
+ } finally {
192
+ setIsLoading(false);
193
+ }
194
+ }, []),
195
+ trackTicket: (0, react.useCallback)(async (token) => {
196
+ setIsLoading(true);
197
+ setError(null);
198
+ try {
199
+ return await require_storage.trackTicket(token);
200
+ } catch (err) {
201
+ const widgetError = err;
202
+ setError(widgetError);
203
+ throw widgetError;
204
+ } finally {
205
+ setIsLoading(false);
206
+ }
207
+ }, []),
208
+ submitTrackingReply: (0, react.useCallback)(async (token, body) => {
209
+ setIsLoading(true);
210
+ setError(null);
211
+ try {
212
+ await require_storage.submitTrackingReply(token, body);
213
+ } catch (err) {
214
+ const widgetError = err;
215
+ setError(widgetError);
216
+ throw widgetError;
217
+ } finally {
218
+ setIsLoading(false);
219
+ }
220
+ }, []),
221
+ isLoading,
222
+ error
223
+ };
224
+ }
225
+ //#endregion
226
+ //#region src/react/shadow-root.tsx
227
+ function ShadowRoot({ children }) {
228
+ const hostRef = (0, react.useRef)(null);
229
+ const [mountPoint, setMountPoint] = (0, react.useState)(null);
230
+ (0, react.useEffect)(() => {
231
+ const host = hostRef.current;
232
+ if (!host || host.shadowRoot) return;
233
+ const shadow = host.attachShadow({ mode: "open" });
234
+ const style = document.createElement("style");
235
+ style.textContent = require_storage.getWidgetStyles();
236
+ shadow.appendChild(style);
237
+ const mount = document.createElement("div");
238
+ mount.className = "rqd-root";
239
+ shadow.appendChild(mount);
240
+ setMountPoint(mount);
241
+ }, []);
242
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
243
+ ref: hostRef,
244
+ children: mountPoint && (0, react_dom.createPortal)(children, mountPoint)
245
+ });
246
+ }
247
+ //#endregion
248
+ //#region src/react/TicketForm.tsx
249
+ const translations$5 = {
250
+ en: require_storage.en,
251
+ ar: require_storage.ar
252
+ };
253
+ function TicketForm({ mode = "inline", onTicketCreated, onError, className, style }) {
254
+ const ctx = useReqdeskContext();
255
+ const { submitTicket, isLoading } = useReqdesk();
256
+ const [success, setSuccess] = (0, react.useState)(null);
257
+ const [errors, setErrors] = (0, react.useState)({});
258
+ const t = (0, react.useCallback)((key) => {
259
+ if (ctx.translations?.[key]) return ctx.translations[key];
260
+ return (translations$5[ctx.language] ?? translations$5.en)[key] ?? key;
261
+ }, [ctx.language, ctx.translations]);
262
+ const handleSubmit = (0, react.useCallback)(async (e) => {
263
+ e.preventDefault();
264
+ const form = e.currentTarget;
265
+ const formData = new FormData(form);
266
+ const title = formData.get("title")?.trim() ?? "";
267
+ const email = formData.get("email")?.trim() ?? "";
268
+ const newErrors = {};
269
+ if (!title) newErrors.title = t("error.required");
270
+ else if (title.length < 5) newErrors.title = t("error.titleMin");
271
+ if (!email) newErrors.email = t("error.required");
272
+ else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) newErrors.email = t("error.emailInvalid");
273
+ if (Object.keys(newErrors).length > 0) {
274
+ setErrors(newErrors);
275
+ return;
276
+ }
277
+ setErrors({});
278
+ const data = {
279
+ title,
280
+ description: formData.get("description")?.trim() || void 0,
281
+ email,
282
+ priority: formData.get("priority") ?? "medium"
283
+ };
284
+ try {
285
+ const result = await submitTicket(data);
286
+ if (result.trackingToken) require_storage.saveTrackingToken(ctx.apiKey, result.trackingToken);
287
+ setSuccess(result);
288
+ onTicketCreated?.(result);
289
+ } catch (err) {
290
+ onError?.(err);
291
+ }
292
+ }, [
293
+ submitTicket,
294
+ ctx.apiKey,
295
+ ctx.translations,
296
+ ctx.language,
297
+ onTicketCreated,
298
+ onError,
299
+ t
300
+ ]);
301
+ const cssVars = require_storage.themeToVars(ctx.theme);
302
+ const content = success ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
303
+ className: "rqd-success",
304
+ style: {
305
+ textAlign: "center",
306
+ padding: "24px 0"
307
+ },
308
+ children: [
309
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
310
+ style: {
311
+ fontSize: 48,
312
+ marginBottom: 12
313
+ },
314
+ children: "✅"
315
+ }),
316
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
317
+ style: {
318
+ margin: "0 0 8px",
319
+ fontSize: 18
320
+ },
321
+ children: t("success.title")
322
+ }),
323
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [t("success.ticketNumber"), success.ticketNumber] }),
324
+ success.trackingToken && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
325
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
326
+ style: {
327
+ fontSize: 13,
328
+ color: "var(--rqd-text-secondary)"
329
+ },
330
+ children: t("success.trackingHint")
331
+ }),
332
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
333
+ className: "rqd-token-box",
334
+ children: success.trackingToken
335
+ }),
336
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
337
+ className: "rqd-btn rqd-btn-secondary",
338
+ onClick: () => navigator.clipboard.writeText(success.trackingToken),
339
+ children: t("success.copyToken")
340
+ })
341
+ ] })
342
+ ]
343
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
344
+ className: "rqd-form",
345
+ onSubmit: handleSubmit,
346
+ noValidate: true,
347
+ children: [
348
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
349
+ className: "rqd-form-group",
350
+ children: [
351
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
352
+ className: "rqd-label",
353
+ children: t("form.title")
354
+ }),
355
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
356
+ className: "rqd-input",
357
+ name: "title",
358
+ placeholder: t("form.titlePlaceholder"),
359
+ required: true
360
+ }),
361
+ errors.title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
362
+ className: "rqd-error-text",
363
+ children: errors.title
364
+ })
365
+ ]
366
+ }),
367
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
368
+ className: "rqd-form-group",
369
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
370
+ className: "rqd-label",
371
+ children: t("form.description")
372
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
373
+ className: "rqd-textarea",
374
+ name: "description",
375
+ placeholder: t("form.descriptionPlaceholder")
376
+ })]
377
+ }),
378
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
379
+ className: "rqd-form-group",
380
+ children: [
381
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
382
+ className: "rqd-label",
383
+ children: t("form.email")
384
+ }),
385
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
386
+ className: "rqd-input",
387
+ name: "email",
388
+ type: "email",
389
+ placeholder: t("form.emailPlaceholder"),
390
+ required: true
391
+ }),
392
+ errors.email && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
393
+ className: "rqd-error-text",
394
+ children: errors.email
395
+ })
396
+ ]
397
+ }),
398
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
399
+ className: "rqd-form-group",
400
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
401
+ className: "rqd-label",
402
+ children: t("form.priority")
403
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
404
+ className: "rqd-select",
405
+ name: "priority",
406
+ defaultValue: "medium",
407
+ children: [
408
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
409
+ value: "low",
410
+ children: t("form.priorityLow")
411
+ }),
412
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
413
+ value: "medium",
414
+ children: t("form.priorityMedium")
415
+ }),
416
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
417
+ value: "high",
418
+ children: t("form.priorityHigh")
419
+ }),
420
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
421
+ value: "urgent",
422
+ children: t("form.priorityUrgent")
423
+ })
424
+ ]
425
+ })]
426
+ }),
427
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
428
+ className: "rqd-btn rqd-btn-primary",
429
+ type: "submit",
430
+ disabled: isLoading,
431
+ children: isLoading ? t("form.submitting") : t("form.submit")
432
+ })
433
+ ]
434
+ });
435
+ if (mode === "floating") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShadowRoot, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
436
+ className,
437
+ style: {
438
+ ...style,
439
+ cssText: cssVars
440
+ },
441
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
442
+ className: "rqd-body",
443
+ children: content
444
+ })
445
+ }) });
446
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShadowRoot, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
447
+ className: `rqd-inline ${className ?? ""}`,
448
+ style: { cssText: cssVars },
449
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
450
+ className: "rqd-body",
451
+ children: content
452
+ })
453
+ }) });
454
+ }
455
+ //#endregion
456
+ //#region src/react/SupportPortal.tsx
457
+ function SupportPortal({ className }) {
458
+ const ctx = useReqdeskContext();
459
+ const { isLoading } = useReqdesk();
460
+ const cssVars = require_storage.themeToVars(ctx.theme);
461
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShadowRoot, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
462
+ className: `rqd-inline ${className ?? ""}`,
463
+ style: { cssText: cssVars },
464
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
465
+ className: "rqd-body",
466
+ children: isLoading ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
467
+ style: {
468
+ textAlign: "center",
469
+ color: "var(--rqd-text-secondary)"
470
+ },
471
+ children: "Loading..."
472
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
473
+ style: {
474
+ textAlign: "center",
475
+ color: "var(--rqd-text-secondary)",
476
+ padding: "24px 0"
477
+ },
478
+ children: "Support Portal — coming in a future update."
479
+ })
480
+ })
481
+ }) });
482
+ }
483
+ //#endregion
484
+ //#region src/react/views/SubmitTicketView.tsx
485
+ const translations$4 = {
486
+ en: require_storage.en,
487
+ ar: require_storage.ar
488
+ };
489
+ const ALLOWED_MIME_TYPES = new Set([
490
+ "image/jpeg",
491
+ "image/png",
492
+ "image/gif",
493
+ "image/webp",
494
+ "application/pdf",
495
+ "text/plain",
496
+ "text/csv",
497
+ "application/zip",
498
+ "application/x-zip-compressed",
499
+ "application/msword",
500
+ "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
501
+ "application/vnd.ms-excel",
502
+ "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
503
+ "application/vnd.ms-powerpoint",
504
+ "application/vnd.openxmlformats-officedocument.presentationml.presentation"
505
+ ]);
506
+ const BLOCKED_EXTENSIONS = new Set([
507
+ ".exe",
508
+ ".bat",
509
+ ".cmd",
510
+ ".sh",
511
+ ".ps1",
512
+ ".msi",
513
+ ".dll",
514
+ ".scr"
515
+ ]);
516
+ const MAX_FILES = 5;
517
+ const MAX_SIZE_MB = 10;
518
+ function formatFileSize$1(bytes) {
519
+ if (bytes < 1024) return `${bytes} B`;
520
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
521
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
522
+ }
523
+ function SubmitTicketView({ projectId, onSuccess, onError, isAuthenticated, userEmail }) {
524
+ const ctx = useReqdeskContext();
525
+ const fileInputRef = (0, react.useRef)(null);
526
+ const savedEmail = require_storage.loadWidgetEmail(ctx.apiKey);
527
+ const [errors, setErrors] = (0, react.useState)({});
528
+ const [files, setFiles] = (0, react.useState)([]);
529
+ const [isDragOver, setIsDragOver] = (0, react.useState)(false);
530
+ const [uploadProgress, setUploadProgress] = (0, react.useState)(null);
531
+ const [success, setSuccess] = (0, react.useState)(null);
532
+ const [rememberEmail, setRememberEmail] = (0, react.useState)(!!savedEmail);
533
+ const t = (0, react.useCallback)((key) => {
534
+ if (ctx.translations?.[key]) return ctx.translations[key];
535
+ return (translations$4[ctx.language] ?? translations$4.en)[key] ?? key;
536
+ }, [ctx.language, ctx.translations]);
537
+ const submitMutation = (0, _tanstack_react_query.useMutation)({
538
+ mutationFn: async ({ data, validFiles }) => {
539
+ const result = await require_storage.submitTicket(projectId, data);
540
+ if (result.trackingToken) require_storage.saveTrackingToken(ctx.apiKey, result.trackingToken);
541
+ for (const queued of validFiles) {
542
+ setUploadProgress({
543
+ fileId: queued.id,
544
+ fileName: queued.file.name,
545
+ percent: 0
546
+ });
547
+ await require_storage.uploadAttachment(result.id, queued.file, (percent) => {
548
+ setUploadProgress({
549
+ fileId: queued.id,
550
+ fileName: queued.file.name,
551
+ percent
552
+ });
553
+ });
554
+ }
555
+ setUploadProgress(null);
556
+ return result;
557
+ },
558
+ onSuccess: (result) => {
559
+ setSuccess(result);
560
+ onSuccess?.(result);
561
+ },
562
+ onError: (err) => {
563
+ setUploadProgress(null);
564
+ onError?.(err);
565
+ }
566
+ });
567
+ function validateFile(file) {
568
+ const ext = "." + file.name.split(".").pop()?.toLowerCase();
569
+ if (BLOCKED_EXTENSIONS.has(ext)) return t("attach.invalidType");
570
+ if (!ALLOWED_MIME_TYPES.has(file.type) && !file.type.startsWith("application/vnd.openxmlformats-officedocument.")) return t("attach.invalidType");
571
+ if (file.size > MAX_SIZE_MB * 1024 * 1024) return t("attach.tooLarge");
572
+ return null;
573
+ }
574
+ function addFiles(newFiles) {
575
+ const toAdd = [];
576
+ for (const file of Array.from(newFiles)) {
577
+ if (files.length + toAdd.length >= MAX_FILES) break;
578
+ const error = validateFile(file);
579
+ toAdd.push({
580
+ file,
581
+ id: crypto.randomUUID(),
582
+ error: error ?? void 0
583
+ });
584
+ }
585
+ setFiles((prev) => [...prev, ...toAdd]);
586
+ }
587
+ function removeFile(id) {
588
+ setFiles((prev) => prev.filter((f) => f.id !== id));
589
+ }
590
+ function handleDragOver(e) {
591
+ e.preventDefault();
592
+ setIsDragOver(true);
593
+ }
594
+ function handleDragLeave(e) {
595
+ e.preventDefault();
596
+ setIsDragOver(false);
597
+ }
598
+ function handleDrop(e) {
599
+ e.preventDefault();
600
+ setIsDragOver(false);
601
+ if (e.dataTransfer.files.length > 0) addFiles(e.dataTransfer.files);
602
+ }
603
+ function handleSubmit(e) {
604
+ e.preventDefault();
605
+ const form = e.currentTarget;
606
+ const formData = new FormData(form);
607
+ const title = formData.get("title")?.trim() ?? "";
608
+ const email = formData.get("email")?.trim() ?? userEmail ?? "";
609
+ const newErrors = {};
610
+ if (!title) newErrors.title = t("error.required");
611
+ else if (title.length < 5) newErrors.title = t("error.titleMin");
612
+ if (!isAuthenticated && email && !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) newErrors.email = t("error.emailInvalid");
613
+ if (Object.keys(newErrors).length > 0) {
614
+ setErrors(newErrors);
615
+ return;
616
+ }
617
+ setErrors({});
618
+ if (rememberEmail && email) require_storage.saveWidgetEmail(ctx.apiKey, email);
619
+ const validFiles = files.filter((f) => !f.error);
620
+ const data = {
621
+ title,
622
+ description: formData.get("description")?.trim() || void 0,
623
+ email,
624
+ priority: formData.get("priority") ?? "medium"
625
+ };
626
+ submitMutation.mutate({
627
+ data,
628
+ validFiles
629
+ });
630
+ }
631
+ if (success) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
632
+ className: "rqd-success",
633
+ style: {
634
+ textAlign: "center",
635
+ padding: "24px 0"
636
+ },
637
+ children: [
638
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
639
+ style: {
640
+ fontSize: 48,
641
+ marginBottom: 12
642
+ },
643
+ children: "✅"
644
+ }),
645
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", {
646
+ style: {
647
+ margin: "0 0 8px",
648
+ fontSize: 18
649
+ },
650
+ children: t("success.title")
651
+ }),
652
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("p", { children: [t("success.ticketNumber"), success.ticketNumber] }),
653
+ success.trackingToken && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [
654
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
655
+ style: {
656
+ fontSize: 13,
657
+ color: "var(--rqd-text-secondary)"
658
+ },
659
+ children: t("success.trackingHint")
660
+ }),
661
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
662
+ className: "rqd-token-box",
663
+ children: success.trackingToken
664
+ }),
665
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
666
+ className: "rqd-btn rqd-btn-secondary",
667
+ onClick: () => navigator.clipboard.writeText(success.trackingToken),
668
+ children: t("success.copyToken")
669
+ })
670
+ ] })
671
+ ]
672
+ });
673
+ if (submitMutation.isPending && uploadProgress) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
674
+ style: {
675
+ padding: "24px 0",
676
+ textAlign: "center"
677
+ },
678
+ children: [
679
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
680
+ style: {
681
+ marginBottom: 12,
682
+ fontWeight: 500
683
+ },
684
+ children: t("attach.uploading")
685
+ }),
686
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
687
+ className: "rqd-file-item",
688
+ style: { marginBottom: 8 },
689
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
690
+ className: "rqd-file-item-info",
691
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
692
+ className: "rqd-file-item-name",
693
+ children: uploadProgress.fileName
694
+ })
695
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
696
+ style: {
697
+ fontSize: 12,
698
+ color: "var(--rqd-text-secondary)"
699
+ },
700
+ children: [uploadProgress.percent, "%"]
701
+ })]
702
+ }),
703
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
704
+ className: "rqd-progress",
705
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
706
+ className: "rqd-progress-bar",
707
+ style: { width: `${uploadProgress.percent}%` }
708
+ })
709
+ })
710
+ ]
711
+ });
712
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
713
+ className: "rqd-form",
714
+ onSubmit: handleSubmit,
715
+ noValidate: true,
716
+ children: [
717
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
718
+ className: "rqd-form-group",
719
+ children: [
720
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
721
+ className: "rqd-label",
722
+ children: t("form.title")
723
+ }),
724
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
725
+ className: "rqd-input",
726
+ name: "title",
727
+ placeholder: t("form.titlePlaceholder"),
728
+ required: true
729
+ }),
730
+ errors.title && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
731
+ className: "rqd-error-text",
732
+ children: errors.title
733
+ })
734
+ ]
735
+ }),
736
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
737
+ className: "rqd-form-group",
738
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
739
+ className: "rqd-label",
740
+ children: t("form.description")
741
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
742
+ className: "rqd-textarea",
743
+ name: "description",
744
+ placeholder: t("form.descriptionPlaceholder")
745
+ })]
746
+ }),
747
+ !isAuthenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
748
+ className: "rqd-form-group",
749
+ children: [
750
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
751
+ className: "rqd-label",
752
+ children: t("form.email")
753
+ }),
754
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
755
+ className: "rqd-input",
756
+ name: "email",
757
+ type: "email",
758
+ placeholder: t("form.emailPlaceholder"),
759
+ defaultValue: userEmail ?? savedEmail ?? ""
760
+ }),
761
+ errors.email && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
762
+ className: "rqd-error-text",
763
+ children: errors.email
764
+ }),
765
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
766
+ className: "rqd-checkbox-label",
767
+ style: { marginTop: 6 },
768
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
769
+ type: "checkbox",
770
+ checked: rememberEmail,
771
+ onChange: (e) => setRememberEmail(e.target.checked)
772
+ }), t("mytickets.rememberMe")]
773
+ })
774
+ ]
775
+ }),
776
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
777
+ className: "rqd-form-group",
778
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
779
+ className: "rqd-label",
780
+ children: t("form.priority")
781
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
782
+ className: "rqd-select",
783
+ name: "priority",
784
+ defaultValue: "medium",
785
+ children: [
786
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
787
+ value: "low",
788
+ children: t("form.priorityLow")
789
+ }),
790
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
791
+ value: "medium",
792
+ children: t("form.priorityMedium")
793
+ }),
794
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
795
+ value: "high",
796
+ children: t("form.priorityHigh")
797
+ }),
798
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
799
+ value: "urgent",
800
+ children: t("form.priorityUrgent")
801
+ })
802
+ ]
803
+ })]
804
+ }),
805
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
806
+ className: "rqd-form-group",
807
+ children: [
808
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
809
+ className: "rqd-label",
810
+ children: t("form.attachment")
811
+ }),
812
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
813
+ className: `rqd-dropzone${isDragOver ? " rqd-dropzone-active" : ""}`,
814
+ onDragOver: handleDragOver,
815
+ onDragLeave: handleDragLeave,
816
+ onDrop: handleDrop,
817
+ onClick: () => fileInputRef.current?.click(),
818
+ children: [isDragOver ? t("attach.dropzoneActive") : t("attach.dropzone"), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
819
+ ref: fileInputRef,
820
+ type: "file",
821
+ multiple: true,
822
+ style: { display: "none" },
823
+ onChange: (e) => {
824
+ if (e.target.files) addFiles(e.target.files);
825
+ e.target.value = "";
826
+ }
827
+ })]
828
+ }),
829
+ files.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
830
+ className: "rqd-file-list",
831
+ children: files.map((f) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
832
+ className: "rqd-file-item",
833
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
834
+ className: "rqd-file-item-info",
835
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
836
+ className: "rqd-file-item-name",
837
+ children: f.file.name
838
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
839
+ className: "rqd-file-item-size",
840
+ children: [formatFileSize$1(f.file.size), f.error && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
841
+ style: {
842
+ color: "#e74c3c",
843
+ marginLeft: 6
844
+ },
845
+ children: f.error
846
+ })]
847
+ })]
848
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
849
+ className: "rqd-file-remove",
850
+ onClick: () => removeFile(f.id),
851
+ type: "button",
852
+ children: "×"
853
+ })]
854
+ }, f.id))
855
+ })
856
+ ]
857
+ }),
858
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
859
+ className: "rqd-btn rqd-btn-primary",
860
+ type: "submit",
861
+ disabled: submitMutation.isPending,
862
+ children: submitMutation.isPending ? t("form.submitting") : t("form.submit")
863
+ })
864
+ ]
865
+ });
866
+ }
867
+ //#endregion
868
+ //#region src/react/queries.ts
869
+ const widgetTicketDetailOptions = (ticketId) => (0, _tanstack_react_query.queryOptions)({
870
+ queryKey: ["widget-ticket", ticketId],
871
+ queryFn: () => require_storage.getTicketDetail(ticketId),
872
+ staleTime: 6e4,
873
+ enabled: !!ticketId
874
+ });
875
+ const widgetMyTicketsOptions = (projectId, userId) => (0, _tanstack_react_query.queryOptions)({
876
+ queryKey: [
877
+ "widget-tickets",
878
+ projectId,
879
+ userId
880
+ ],
881
+ queryFn: () => require_storage.listMyTickets(projectId, userId),
882
+ staleTime: 3e4,
883
+ placeholderData: _tanstack_react_query.keepPreviousData,
884
+ enabled: !!userId
885
+ });
886
+ const widgetUserOptions = (projectId, email) => (0, _tanstack_react_query.queryOptions)({
887
+ queryKey: [
888
+ "widget-user",
889
+ projectId,
890
+ email
891
+ ],
892
+ queryFn: () => require_storage.resolveWidgetUser(projectId, email),
893
+ staleTime: 5 * 6e4,
894
+ enabled: !!email
895
+ });
896
+ //#endregion
897
+ //#region src/react/views/MyTicketsView.tsx
898
+ const translations$3 = {
899
+ en: require_storage.en,
900
+ ar: require_storage.ar
901
+ };
902
+ function MyTicketsView({ projectId, onSelectTicket, isAuthenticated, userEmail }) {
903
+ const ctx = useReqdeskContext();
904
+ const savedEmail = require_storage.loadWidgetEmail(ctx.apiKey);
905
+ const [email, setEmail] = (0, react.useState)(userEmail ?? savedEmail ?? "");
906
+ const [rememberMe, setRememberMe] = (0, react.useState)(!!savedEmail);
907
+ const [submittedEmail, setSubmittedEmail] = (0, react.useState)(isAuthenticated && userEmail ? userEmail : savedEmail);
908
+ const [tokenTickets, setTokenTickets] = (0, react.useState)([]);
909
+ const t = (0, react.useCallback)((key) => {
910
+ if (ctx.translations?.[key]) return ctx.translations[key];
911
+ return (translations$3[ctx.language] ?? translations$3.en)[key] ?? key;
912
+ }, [ctx.language, ctx.translations]);
913
+ const { data: widgetUser } = (0, _tanstack_react_query.useQuery)(widgetUserOptions(projectId, submittedEmail ?? ""));
914
+ const { data: emailTickets = [], isLoading } = (0, _tanstack_react_query.useQuery)(widgetMyTicketsOptions(projectId, widgetUser?.userId ?? ""));
915
+ (0, react.useEffect)(() => {
916
+ const tokens = require_storage.getTrackingTokens(ctx.apiKey);
917
+ if (tokens.length === 0) return;
918
+ Promise.allSettled(tokens.slice(0, 5).map((tk) => require_storage.trackTicket(tk))).then((results) => {
919
+ const tracked = [];
920
+ for (const r of results) if (r.status === "fulfilled") {
921
+ const t = r.value;
922
+ if (!emailTickets.some((et) => et.id === t.id)) tracked.push({
923
+ id: t.id,
924
+ ticketNumber: t.ticketNumber,
925
+ title: t.title,
926
+ status: t.status,
927
+ priority: t.priority,
928
+ createdAt: t.createdAt
929
+ });
930
+ }
931
+ setTokenTickets(tracked);
932
+ });
933
+ }, [ctx.apiKey, emailTickets]);
934
+ const allTickets = [...emailTickets, ...tokenTickets];
935
+ function handleSubmit(e) {
936
+ e.preventDefault();
937
+ const trimmed = email.trim();
938
+ if (!trimmed || !/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(trimmed)) return;
939
+ if (rememberMe) require_storage.saveWidgetEmail(ctx.apiKey, trimmed);
940
+ setSubmittedEmail(trimmed);
941
+ }
942
+ function formatDate(iso) {
943
+ try {
944
+ return new Date(iso).toLocaleDateString();
945
+ } catch {
946
+ return iso;
947
+ }
948
+ }
949
+ if (!submittedEmail && !isAuthenticated) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
950
+ className: "rqd-email-form",
951
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("mytickets.emailPrompt") }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
952
+ onSubmit: handleSubmit,
953
+ children: [
954
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
955
+ className: "rqd-form-group",
956
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
957
+ className: "rqd-input",
958
+ type: "email",
959
+ value: email,
960
+ onChange: (e) => setEmail(e.target.value),
961
+ placeholder: t("mytickets.emailPlaceholder"),
962
+ required: true
963
+ })
964
+ }),
965
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
966
+ className: "rqd-checkbox-label",
967
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
968
+ type: "checkbox",
969
+ checked: rememberMe,
970
+ onChange: (e) => setRememberMe(e.target.checked)
971
+ }), t("mytickets.rememberMe")]
972
+ }),
973
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
974
+ className: "rqd-btn rqd-btn-primary",
975
+ type: "submit",
976
+ style: { marginTop: 12 },
977
+ children: t("mytickets.lookup")
978
+ })
979
+ ]
980
+ })]
981
+ });
982
+ if (isLoading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
983
+ className: "rqd-loading",
984
+ children: t("mytickets.lookingUp")
985
+ });
986
+ if (allTickets.length === 0) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
987
+ className: "rqd-placeholder",
988
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: submittedEmail && !widgetUser ? t("mytickets.noAccount") : t("mytickets.noTickets") }), submittedEmail && !isAuthenticated && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
989
+ className: "rqd-btn rqd-btn-secondary",
990
+ style: {
991
+ width: "auto",
992
+ padding: "8px 20px"
993
+ },
994
+ onClick: () => setSubmittedEmail(null),
995
+ children: t("tracker.back")
996
+ })]
997
+ });
998
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
999
+ className: "rqd-ticket-list",
1000
+ children: allTickets.map((ticket) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1001
+ className: "rqd-ticket-card",
1002
+ onClick: () => onSelectTicket(ticket.id),
1003
+ children: [
1004
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1005
+ className: "rqd-ticket-card-top",
1006
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1007
+ className: "rqd-ticket-card-number",
1008
+ children: ticket.ticketNumber
1009
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1010
+ className: "rqd-badge",
1011
+ children: ticket.status
1012
+ })]
1013
+ }),
1014
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1015
+ className: "rqd-ticket-card-title",
1016
+ children: ticket.title
1017
+ }),
1018
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1019
+ className: "rqd-ticket-card-date",
1020
+ children: formatDate(ticket.createdAt)
1021
+ })
1022
+ ]
1023
+ }, ticket.id))
1024
+ });
1025
+ }
1026
+ //#endregion
1027
+ //#region src/react/views/TicketDetailView.tsx
1028
+ const translations$2 = {
1029
+ en: require_storage.en,
1030
+ ar: require_storage.ar
1031
+ };
1032
+ function formatDate(iso) {
1033
+ try {
1034
+ return new Date(iso).toLocaleString();
1035
+ } catch {
1036
+ return iso;
1037
+ }
1038
+ }
1039
+ function formatFileSize(bytes) {
1040
+ if (bytes < 1024) return `${bytes} B`;
1041
+ if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`;
1042
+ return `${(bytes / (1024 * 1024)).toFixed(1)} MB`;
1043
+ }
1044
+ function TicketDetailView({ ticketId, onBack }) {
1045
+ const ctx = useReqdeskContext();
1046
+ const queryClient = (0, _tanstack_react_query.useQueryClient)();
1047
+ const [replyBody, setReplyBody] = (0, react.useState)("");
1048
+ const t = (0, react.useCallback)((key) => {
1049
+ if (ctx.translations?.[key]) return ctx.translations[key];
1050
+ return (translations$2[ctx.language] ?? translations$2.en)[key] ?? key;
1051
+ }, [ctx.language, ctx.translations]);
1052
+ const { data: ticket, isLoading, error } = (0, _tanstack_react_query.useQuery)(widgetTicketDetailOptions(ticketId));
1053
+ const replyMutation = (0, _tanstack_react_query.useMutation)({
1054
+ mutationFn: (body) => require_storage.submitReply(ticketId, body),
1055
+ onMutate: async (body) => {
1056
+ await queryClient.cancelQueries({ queryKey: ["widget-ticket", ticketId] });
1057
+ const previous = queryClient.getQueryData(["widget-ticket", ticketId]);
1058
+ if (previous) queryClient.setQueryData(["widget-ticket", ticketId], {
1059
+ ...previous,
1060
+ replies: [...previous.replies, {
1061
+ id: `optimistic-${Date.now()}`,
1062
+ body,
1063
+ authorName: ctx.userName ?? ctx.userEmail ?? "You",
1064
+ isStaff: false,
1065
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1066
+ }]
1067
+ });
1068
+ return { previous };
1069
+ },
1070
+ onSuccess: () => {
1071
+ setReplyBody("");
1072
+ queryClient.invalidateQueries({ queryKey: ["widget-ticket", ticketId] });
1073
+ },
1074
+ onError: (_err, _vars, context) => {
1075
+ if (context?.previous) queryClient.setQueryData(["widget-ticket", ticketId], context.previous);
1076
+ }
1077
+ });
1078
+ if (isLoading) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1079
+ className: "rqd-loading",
1080
+ children: t("detail.loading")
1081
+ });
1082
+ if (error || !ticket) return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1083
+ className: "rqd-placeholder",
1084
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("error.generic") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1085
+ className: "rqd-btn rqd-btn-secondary",
1086
+ style: {
1087
+ width: "auto",
1088
+ padding: "8px 20px"
1089
+ },
1090
+ onClick: onBack,
1091
+ children: t("tracker.back")
1092
+ })]
1093
+ });
1094
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [
1095
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1096
+ className: "rqd-ticket-header",
1097
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h3", { children: ticket.title }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1098
+ className: "rqd-ticket-header-meta",
1099
+ children: [
1100
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1101
+ className: "rqd-ticket-card-number",
1102
+ children: ticket.ticketNumber
1103
+ }),
1104
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1105
+ className: "rqd-badge",
1106
+ children: ticket.status
1107
+ }),
1108
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1109
+ className: "rqd-badge",
1110
+ style: {
1111
+ background: "var(--rqd-bg-secondary)",
1112
+ color: "var(--rqd-text-secondary)"
1113
+ },
1114
+ children: ticket.priority
1115
+ }),
1116
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1117
+ className: "rqd-ticket-card-date",
1118
+ children: formatDate(ticket.createdAt)
1119
+ })
1120
+ ]
1121
+ })]
1122
+ }),
1123
+ ticket.description && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1124
+ className: "rqd-section-title",
1125
+ children: t("detail.description")
1126
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1127
+ className: "rqd-ticket-desc",
1128
+ children: ticket.description
1129
+ })] }),
1130
+ ticket.attachments.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1131
+ className: "rqd-section-title",
1132
+ children: t("detail.attachments")
1133
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1134
+ className: "rqd-attachment-list",
1135
+ children: ticket.attachments.map((a) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1136
+ className: "rqd-attachment-item",
1137
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1138
+ className: "rqd-file-item-info",
1139
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1140
+ className: "rqd-file-item-name",
1141
+ children: a.fileName
1142
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1143
+ className: "rqd-file-item-size",
1144
+ children: formatFileSize(a.fileSize)
1145
+ })]
1146
+ }), a.downloadUrl && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1147
+ href: a.downloadUrl,
1148
+ target: "_blank",
1149
+ rel: "noopener noreferrer",
1150
+ children: t("attach.download")
1151
+ })]
1152
+ }, a.id))
1153
+ })] }),
1154
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1155
+ className: "rqd-section-title",
1156
+ children: t("detail.replies")
1157
+ }),
1158
+ ticket.replies.length === 0 ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
1159
+ style: {
1160
+ color: "var(--rqd-text-secondary)",
1161
+ fontSize: 13,
1162
+ margin: "0 0 12px"
1163
+ },
1164
+ children: t("detail.noReplies")
1165
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: ticket.replies.map((reply) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1166
+ className: "rqd-reply",
1167
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1168
+ className: "rqd-reply-header",
1169
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1170
+ className: reply.isStaff ? "rqd-reply-staff" : "",
1171
+ children: reply.isStaff ? `${reply.authorName} \u00b7 ${t("detail.staff")}` : reply.authorName
1172
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: formatDate(reply.createdAt) })]
1173
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1174
+ className: "rqd-reply-body",
1175
+ children: reply.body
1176
+ })]
1177
+ }, reply.id)) }),
1178
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1179
+ className: "rqd-reply-compose",
1180
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
1181
+ className: "rqd-textarea",
1182
+ value: replyBody,
1183
+ onChange: (e) => setReplyBody(e.target.value),
1184
+ placeholder: t("detail.replyPlaceholder"),
1185
+ rows: 3
1186
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1187
+ className: "rqd-btn rqd-btn-primary",
1188
+ onClick: () => replyMutation.mutate(replyBody.trim()),
1189
+ disabled: !replyBody.trim() || replyMutation.isPending,
1190
+ children: replyMutation.isPending ? t("detail.sending") : t("detail.sendReply")
1191
+ })]
1192
+ })
1193
+ ] });
1194
+ }
1195
+ //#endregion
1196
+ //#region src/react/views/TrackTicketView.tsx
1197
+ const translations$1 = {
1198
+ en: require_storage.en,
1199
+ ar: require_storage.ar
1200
+ };
1201
+ function TrackTicketView({ onTrackSuccess }) {
1202
+ const ctx = useReqdeskContext();
1203
+ const [token, setToken] = (0, react.useState)("");
1204
+ const savedTokens = require_storage.getTrackingTokens(ctx.apiKey);
1205
+ const t = (0, react.useCallback)((key) => {
1206
+ if (ctx.translations?.[key]) return ctx.translations[key];
1207
+ return (translations$1[ctx.language] ?? translations$1.en)[key] ?? key;
1208
+ }, [ctx.language, ctx.translations]);
1209
+ const trackMutation = (0, _tanstack_react_query.useMutation)({
1210
+ mutationFn: (trackToken) => require_storage.trackTicket(trackToken.trim()),
1211
+ onSuccess: (result) => onTrackSuccess(result.id)
1212
+ });
1213
+ function handleSubmit(e) {
1214
+ e.preventDefault();
1215
+ if (token.trim()) trackMutation.mutate(token);
1216
+ }
1217
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("form", {
1218
+ onSubmit: handleSubmit,
1219
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1220
+ className: "rqd-form-group",
1221
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
1222
+ className: "rqd-input",
1223
+ value: token,
1224
+ onChange: (e) => setToken(e.target.value),
1225
+ placeholder: t("track.tokenPlaceholder"),
1226
+ required: true
1227
+ }), trackMutation.isError && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1228
+ className: "rqd-error-text",
1229
+ children: t("track.invalidToken")
1230
+ })]
1231
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1232
+ className: "rqd-btn rqd-btn-primary",
1233
+ type: "submit",
1234
+ disabled: trackMutation.isPending || !token.trim(),
1235
+ children: trackMutation.isPending ? t("track.tracking") : t("track.submit")
1236
+ })]
1237
+ }), savedTokens.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1238
+ style: { marginTop: 20 },
1239
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1240
+ className: "rqd-section-title",
1241
+ children: t("track.recentTickets")
1242
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1243
+ className: "rqd-ticket-list",
1244
+ children: savedTokens.slice(0, 5).map((tk) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1245
+ className: "rqd-ticket-card",
1246
+ style: {
1247
+ border: "none",
1248
+ textAlign: "start",
1249
+ width: "100%"
1250
+ },
1251
+ onClick: () => trackMutation.mutate(tk),
1252
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
1253
+ className: "rqd-ticket-card-number",
1254
+ style: {
1255
+ fontFamily: "monospace",
1256
+ fontSize: 12
1257
+ },
1258
+ children: [tk.slice(0, 20), "..."]
1259
+ })
1260
+ }, tk))
1261
+ })]
1262
+ })] });
1263
+ }
1264
+ //#endregion
1265
+ //#region src/react/FloatingWidget.tsx
1266
+ const translations = {
1267
+ en: require_storage.en,
1268
+ ar: require_storage.ar
1269
+ };
1270
+ const ICONS = {
1271
+ chat: "M20 2H4c-1.1 0-2 .9-2 2v18l4-4h14c1.1 0 2-.9 2-2V4c0-1.1-.9-2-2-2zm0 14H6l-2 2V4h16v12z",
1272
+ close: "M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z",
1273
+ newTicket: "M19 13h-6v6h-2v-6H5v-2h6V5h2v6h6v2z",
1274
+ tickets: "M14 2H6c-1.1 0-2 .9-2 2v16c0 1.1.9 2 2 2h12c1.1 0 2-.9 2-2V8l-6-6zm4 18H6V4h7v5h5v11zM8 15.01l1.41 1.41L11 14.84V19h2v-4.16l1.59 1.59L16 15.01 12.01 11z",
1275
+ track: "M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z",
1276
+ kb: "M21 5c-1.11-.35-2.33-.5-3.5-.5-1.95 0-4.05.4-5.5 1.5-1.45-1.1-3.55-1.5-5.5-1.5S2.45 4.9 1 6v14.65c0 .25.25.5.5.5.1 0 .15-.05.25-.05C3.1 20.45 5.05 20 6.5 20c1.95 0 4.05.4 5.5 1.5 1.35-.85 3.8-1.5 5.5-1.5 1.65 0 3.35.3 4.75 1.05.1.05.15.05.25.05.25 0 .5-.25.5-.5V6c-.6-.45-1.25-.75-2-1zm0 13.5c-1.1-.35-2.3-.5-3.5-.5-1.7 0-4.15.65-5.5 1.5V8c1.35-.85 3.8-1.5 5.5-1.5 1.2 0 2.4.15 3.5.5v11.5z",
1277
+ back: "M20 11H7.83l5.59-5.59L12 4l-8 8 8 8 1.41-1.41L7.83 13H20v-2z",
1278
+ backRtl: "M4 11h12.17l-5.59-5.59L12 4l8 8-8 8-1.41-1.41L16.17 13H4v-2z",
1279
+ settings: "M19.14 12.94c.04-.3.06-.61.06-.94 0-.32-.02-.64-.07-.94l2.03-1.58a.49.49 0 00.12-.61l-1.92-3.32a.49.49 0 00-.59-.22l-2.39.96c-.5-.38-1.03-.7-1.62-.94l-.36-2.54a.484.484 0 00-.48-.41h-3.84c-.24 0-.43.17-.47.41l-.36 2.54c-.59.24-1.13.57-1.62.94l-2.39-.96a.49.49 0 00-.59.22L2.74 8.87c-.12.21-.08.47.12.61l2.03 1.58c-.05.3-.07.62-.07.94s.02.64.07.94l-2.03 1.58a.49.49 0 00-.12.61l1.92 3.32c.12.22.37.29.59.22l2.39-.96c.5.38 1.03.7 1.62.94l.36 2.54c.05.24.24.41.48.41h3.84c.24 0 .44-.17.47-.41l.36-2.54c.59-.24 1.13-.56 1.62-.94l2.39.96c.22.08.47 0 .59-.22l1.92-3.32c.12-.22.07-.47-.12-.61l-2.01-1.58zM12 15.6A3.6 3.6 0 1115.6 12 3.6 3.6 0 0112 15.6z"
1280
+ };
1281
+ function SvgIcon({ path, size = 20 }) {
1282
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("svg", {
1283
+ width: size,
1284
+ height: size,
1285
+ viewBox: "0 0 24 24",
1286
+ fill: "currentColor",
1287
+ style: {
1288
+ display: "block",
1289
+ flexShrink: 0
1290
+ },
1291
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("path", { d: path })
1292
+ });
1293
+ }
1294
+ function FloatingWidget({ position = "bottom-right", contained = false, onTicketCreated, onError }) {
1295
+ const ctx = useReqdeskContext();
1296
+ const [isOpen, setIsOpen] = (0, react.useState)(false);
1297
+ const [view, setView] = (0, react.useState)("home");
1298
+ const [selectedTicketId, setSelectedTicketId] = (0, react.useState)(null);
1299
+ const [emailCleared, setEmailCleared] = (0, react.useState)(false);
1300
+ const savedPrefs = require_storage.loadWidgetConfig(ctx.apiKey);
1301
+ const [userLang, setUserLang] = (0, react.useState)(savedPrefs?.language ?? null);
1302
+ const [userThemeMode, setUserThemeMode] = (0, react.useState)(savedPrefs?.theme?.mode ?? null);
1303
+ const [userColor, setUserColor] = (0, react.useState)(savedPrefs?.theme?.primaryColor ?? null);
1304
+ const activeLang = userLang ?? ctx.language ?? "en";
1305
+ const activeThemeMode = userThemeMode ?? ctx.theme?.mode ?? "light";
1306
+ const activeColor = userColor ?? ctx.theme?.primaryColor ?? "#42b983";
1307
+ const activeTheme = {
1308
+ ...ctx.theme,
1309
+ mode: activeThemeMode,
1310
+ primaryColor: activeColor
1311
+ };
1312
+ const t = (0, react.useCallback)((key) => {
1313
+ if (ctx.translations?.[key]) return ctx.translations[key];
1314
+ return (translations[activeLang] ?? translations.en)[key] ?? key;
1315
+ }, [activeLang, ctx.translations]);
1316
+ const isRtl = activeLang === "ar";
1317
+ const cssVars = require_storage.themeToVars(activeTheme);
1318
+ const posClass = `rqd-${position}`;
1319
+ const containedClass = contained ? " rqd-contained" : "";
1320
+ const brandName = ctx.theme?.brandName;
1321
+ const brandLogo = ctx.theme?.logo;
1322
+ const hideBranding = ctx.theme?.hideBranding === true;
1323
+ const projectId = "_current";
1324
+ function toggleOpen() {
1325
+ setIsOpen((prev) => !prev);
1326
+ if (!isOpen) {
1327
+ setView("home");
1328
+ setSelectedTicketId(null);
1329
+ }
1330
+ }
1331
+ function goHome() {
1332
+ setView("home");
1333
+ setSelectedTicketId(null);
1334
+ setEmailCleared(false);
1335
+ }
1336
+ function handleLangChange(lang) {
1337
+ setUserLang(lang);
1338
+ require_storage.saveWidgetConfig(ctx.apiKey, { language: lang });
1339
+ }
1340
+ function handleThemeChange(mode) {
1341
+ setUserThemeMode(mode);
1342
+ require_storage.saveWidgetConfig(ctx.apiKey, { theme: {
1343
+ ...ctx.theme,
1344
+ mode,
1345
+ primaryColor: activeColor
1346
+ } });
1347
+ }
1348
+ function handleColorChange(color) {
1349
+ setUserColor(color);
1350
+ require_storage.saveWidgetConfig(ctx.apiKey, { theme: {
1351
+ ...ctx.theme,
1352
+ mode: activeThemeMode,
1353
+ primaryColor: color
1354
+ } });
1355
+ }
1356
+ const COLOR_PRESETS = [
1357
+ {
1358
+ color: "#42b983",
1359
+ label: "Green"
1360
+ },
1361
+ {
1362
+ color: "#3b82f6",
1363
+ label: "Blue"
1364
+ },
1365
+ {
1366
+ color: "#8b5cf6",
1367
+ label: "Purple"
1368
+ },
1369
+ {
1370
+ color: "#f59e0b",
1371
+ label: "Orange"
1372
+ },
1373
+ {
1374
+ color: "#ef4444",
1375
+ label: "Red"
1376
+ }
1377
+ ];
1378
+ function handleClearEmail() {
1379
+ require_storage.clearWidgetEmail(ctx.apiKey);
1380
+ setEmailCleared(true);
1381
+ setTimeout(() => setEmailCleared(false), 2e3);
1382
+ }
1383
+ function openTicketDetail(ticketId) {
1384
+ setSelectedTicketId(ticketId);
1385
+ setView("ticket-detail");
1386
+ }
1387
+ const menuItems = [
1388
+ {
1389
+ key: "new-ticket",
1390
+ icon: ICONS.newTicket,
1391
+ label: t("menu.newTicket"),
1392
+ desc: t("menu.newTicketDesc")
1393
+ },
1394
+ {
1395
+ key: "my-tickets",
1396
+ icon: ICONS.tickets,
1397
+ label: t("menu.myTickets"),
1398
+ desc: t("menu.myTicketsDesc")
1399
+ },
1400
+ {
1401
+ key: "track",
1402
+ icon: ICONS.track,
1403
+ label: t("menu.trackTicket"),
1404
+ desc: t("menu.trackTicketDesc")
1405
+ },
1406
+ {
1407
+ key: "kb",
1408
+ icon: ICONS.kb,
1409
+ label: t("menu.knowledgeBase"),
1410
+ desc: t("menu.knowledgeBaseDesc")
1411
+ },
1412
+ {
1413
+ key: "preferences",
1414
+ icon: ICONS.settings,
1415
+ label: t("menu.preferences"),
1416
+ desc: t("menu.preferencesDesc")
1417
+ }
1418
+ ];
1419
+ function renderViewContent() {
1420
+ switch (view) {
1421
+ case "home": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1422
+ className: "rqd-menu",
1423
+ children: menuItems.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
1424
+ className: "rqd-menu-item",
1425
+ onClick: () => setView(item.key),
1426
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1427
+ className: "rqd-menu-icon",
1428
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SvgIcon, {
1429
+ path: item.icon,
1430
+ size: 22
1431
+ })
1432
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1433
+ className: "rqd-menu-text",
1434
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1435
+ className: "rqd-menu-label",
1436
+ children: item.label
1437
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1438
+ className: "rqd-menu-desc",
1439
+ children: item.desc
1440
+ })]
1441
+ })]
1442
+ }, item.key))
1443
+ });
1444
+ case "new-ticket": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SubmitTicketView, {
1445
+ projectId,
1446
+ onSuccess: (result) => {
1447
+ onTicketCreated?.(result);
1448
+ },
1449
+ onError,
1450
+ isAuthenticated: ctx.isAuthenticated,
1451
+ userEmail: ctx.userEmail
1452
+ });
1453
+ case "my-tickets": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(MyTicketsView, {
1454
+ projectId,
1455
+ onSelectTicket: openTicketDetail,
1456
+ isAuthenticated: ctx.isAuthenticated,
1457
+ userEmail: ctx.userEmail
1458
+ });
1459
+ case "ticket-detail":
1460
+ if (!selectedTicketId) return null;
1461
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TicketDetailView, {
1462
+ ticketId: selectedTicketId,
1463
+ onBack: () => setView("my-tickets")
1464
+ });
1465
+ case "track": return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(TrackTicketView, { onTrackSuccess: openTicketDetail });
1466
+ case "kb": return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1467
+ className: "rqd-placeholder",
1468
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(SvgIcon, {
1469
+ path: ICONS.kb,
1470
+ size: 40
1471
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", { children: t("menu.kbPlaceholder") })]
1472
+ });
1473
+ case "preferences": return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1474
+ className: "rqd-prefs",
1475
+ children: [
1476
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1477
+ className: "rqd-prefs-group",
1478
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1479
+ className: "rqd-prefs-label",
1480
+ children: t("prefs.language")
1481
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1482
+ className: "rqd-prefs-options",
1483
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1484
+ className: `rqd-prefs-option${activeLang === "en" ? " rqd-active" : ""}`,
1485
+ onClick: () => handleLangChange("en"),
1486
+ children: "English"
1487
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1488
+ className: `rqd-prefs-option${activeLang === "ar" ? " rqd-active" : ""}`,
1489
+ onClick: () => handleLangChange("ar"),
1490
+ children: "العربية"
1491
+ })]
1492
+ })]
1493
+ }),
1494
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1495
+ className: "rqd-prefs-group",
1496
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1497
+ className: "rqd-prefs-label",
1498
+ children: t("prefs.theme")
1499
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1500
+ className: "rqd-prefs-options",
1501
+ children: [
1502
+ "light",
1503
+ "dark",
1504
+ "auto"
1505
+ ].map((mode) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1506
+ className: `rqd-prefs-option${activeThemeMode === mode ? " rqd-active" : ""}`,
1507
+ onClick: () => handleThemeChange(mode),
1508
+ children: t(`prefs.${mode}`)
1509
+ }, mode))
1510
+ })]
1511
+ }),
1512
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1513
+ className: "rqd-prefs-group",
1514
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1515
+ className: "rqd-prefs-label",
1516
+ children: t("prefs.accentColor")
1517
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1518
+ className: "rqd-color-presets",
1519
+ children: COLOR_PRESETS.map((preset) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1520
+ className: `rqd-color-preset${activeColor === preset.color ? " rqd-active" : ""}`,
1521
+ style: {
1522
+ background: preset.color,
1523
+ color: preset.color
1524
+ },
1525
+ onClick: () => handleColorChange(preset.color),
1526
+ "aria-label": preset.label,
1527
+ title: preset.label
1528
+ }, preset.color))
1529
+ })]
1530
+ }),
1531
+ require_storage.loadWidgetEmail(ctx.apiKey) && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1532
+ className: "rqd-prefs-group",
1533
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1534
+ className: "rqd-btn rqd-btn-secondary",
1535
+ onClick: handleClearEmail,
1536
+ style: {
1537
+ width: "auto",
1538
+ padding: "8px 16px"
1539
+ },
1540
+ children: emailCleared ? t("prefs.emailCleared") : t("prefs.clearEmail")
1541
+ })
1542
+ })
1543
+ ]
1544
+ });
1545
+ }
1546
+ }
1547
+ const viewTitle = {
1548
+ home: brandName ?? t("widget.title"),
1549
+ "new-ticket": t("widget.newTicket"),
1550
+ "my-tickets": t("menu.myTickets"),
1551
+ "ticket-detail": t("menu.myTickets"),
1552
+ track: t("widget.trackTicket"),
1553
+ kb: t("menu.knowledgeBase"),
1554
+ preferences: t("prefs.title")
1555
+ };
1556
+ const canGoBack = view !== "home";
1557
+ const goBackTarget = view === "ticket-detail" ? "my-tickets" : "home";
1558
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ShadowRoot, { children: /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1559
+ style: { cssText: cssVars },
1560
+ ...isRtl ? { dir: "rtl" } : {},
1561
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1562
+ className: `rqd-fab ${posClass}${containedClass}`,
1563
+ onClick: toggleOpen,
1564
+ "aria-label": isOpen ? t("widget.close") : t("widget.title"),
1565
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SvgIcon, {
1566
+ path: isOpen ? ICONS.close : ICONS.chat,
1567
+ size: 24
1568
+ })
1569
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1570
+ className: `rqd-panel ${posClass}${containedClass}${isOpen ? "" : " rqd-hidden"}`,
1571
+ children: [
1572
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1573
+ className: "rqd-header",
1574
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1575
+ className: "rqd-header-brand",
1576
+ children: [
1577
+ canGoBack && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1578
+ className: "rqd-header-close",
1579
+ onClick: () => goBackTarget === "home" ? goHome() : setView(goBackTarget),
1580
+ "aria-label": t("tracker.back"),
1581
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SvgIcon, {
1582
+ path: isRtl ? ICONS.backRtl : ICONS.back,
1583
+ size: 18
1584
+ })
1585
+ }),
1586
+ view === "home" && brandLogo && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
1587
+ src: brandLogo,
1588
+ alt: "",
1589
+ className: "rqd-header-logo"
1590
+ }),
1591
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
1592
+ className: "rqd-header-title",
1593
+ children: viewTitle[view]
1594
+ })
1595
+ ]
1596
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1597
+ style: {
1598
+ display: "flex",
1599
+ alignItems: "center",
1600
+ gap: 4
1601
+ },
1602
+ children: [(ctx.auth || isAuthConfigured()) && view === "home" && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1603
+ className: "rqd-auth-btn",
1604
+ onClick: () => ctx.isAuthenticated ? logout() : login(),
1605
+ "aria-label": ctx.isAuthenticated ? t("auth.logout") : t("auth.login"),
1606
+ children: ctx.isAuthenticated ? ctx.userName ?? t("auth.logout") : t("auth.login")
1607
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1608
+ className: "rqd-header-close",
1609
+ onClick: toggleOpen,
1610
+ "aria-label": t("widget.close"),
1611
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(SvgIcon, {
1612
+ path: ICONS.close,
1613
+ size: 18
1614
+ })
1615
+ })]
1616
+ })]
1617
+ }),
1618
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1619
+ className: "rqd-body",
1620
+ children: renderViewContent()
1621
+ }),
1622
+ !hideBranding && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1623
+ className: "rqd-footer",
1624
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: t("branding.poweredBy") }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
1625
+ href: "https://reqdesk.com",
1626
+ target: "_blank",
1627
+ rel: "noopener noreferrer",
1628
+ children: "Reqdesk"
1629
+ })]
1630
+ })
1631
+ ]
1632
+ })]
1633
+ }) });
1634
+ }
1635
+ //#endregion
1636
+ exports.FloatingWidget = FloatingWidget;
1637
+ exports.ReqdeskProvider = ReqdeskProvider;
1638
+ exports.ShadowRoot = ShadowRoot;
1639
+ exports.SupportPortal = SupportPortal;
1640
+ exports.TicketForm = TicketForm;
1641
+ exports.getWidgetDefaults = require_storage.loadWidgetConfig;
1642
+ exports.useReqdesk = useReqdesk;