@flamingo-stack/openframe-frontend-core 0.0.216 → 0.0.217-snapshot.20260601003634
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/{chunk-SMCG2CCC.cjs → chunk-6DCKL73F.cjs} +24 -24
- package/dist/{chunk-SMCG2CCC.cjs.map → chunk-6DCKL73F.cjs.map} +1 -1
- package/dist/{chunk-QTKU6ULP.js → chunk-BVFRD34B.js} +2 -2
- package/dist/{chunk-CDLYRFDE.js → chunk-ENBGG2K2.js} +3767 -3610
- package/dist/chunk-ENBGG2K2.js.map +1 -0
- package/dist/{chunk-K4DFAVSO.cjs → chunk-G2HHSZ3S.cjs} +9 -9
- package/dist/{chunk-K4DFAVSO.cjs.map → chunk-G2HHSZ3S.cjs.map} +1 -1
- package/dist/{chunk-2V4SACHE.js → chunk-L6IBKPVM.js} +2 -2
- package/dist/{chunk-572WQWIX.cjs → chunk-MVQ3OODK.cjs} +9 -9
- package/dist/{chunk-572WQWIX.cjs.map → chunk-MVQ3OODK.cjs.map} +1 -1
- package/dist/{chunk-GVNQAGXB.js → chunk-N5IKPYRL.js} +3 -81
- package/dist/chunk-N5IKPYRL.js.map +1 -0
- package/dist/{chunk-VC3ND5RB.js → chunk-SWZUZYWR.js} +2 -2
- package/dist/{chunk-IH76P5R6.cjs → chunk-TYIBMDUZ.cjs} +8 -86
- package/dist/chunk-TYIBMDUZ.cjs.map +1 -0
- package/dist/{chunk-ZGTDUPTW.cjs → chunk-YWDC5BXM.cjs} +382 -225
- package/dist/chunk-YWDC5BXM.cjs.map +1 -0
- package/dist/components/chat/chat-attachment-bar.d.ts +13 -2
- package/dist/components/chat/chat-attachment-bar.d.ts.map +1 -1
- package/dist/components/chat/chat-input.d.ts.map +1 -1
- package/dist/components/chat/chat-message-row.d.ts +25 -0
- package/dist/components/chat/chat-message-row.d.ts.map +1 -0
- package/dist/components/chat/index.cjs +6 -2
- package/dist/components/chat/index.cjs.map +1 -1
- package/dist/components/chat/index.d.ts +1 -0
- package/dist/components/chat/index.d.ts.map +1 -1
- package/dist/components/chat/index.js +5 -1
- package/dist/components/chat/types/component.types.d.ts +8 -1
- package/dist/components/chat/types/component.types.d.ts.map +1 -1
- package/dist/components/contact/index.cjs +3 -3
- package/dist/components/contact/index.js +2 -2
- package/dist/components/features/index.cjs +2 -2
- package/dist/components/features/index.js +1 -1
- package/dist/components/form.d.ts +1 -1
- package/dist/components/form.d.ts.map +1 -1
- package/dist/components/index.cjs +56 -52
- package/dist/components/index.cjs.map +1 -1
- package/dist/components/index.js +9 -5
- package/dist/components/index.js.map +1 -1
- package/dist/components/navigation/index.cjs +2 -2
- package/dist/components/navigation/index.js +1 -1
- package/dist/components/onboarding-guides/index.cjs +18 -18
- package/dist/components/onboarding-guides/index.js +3 -3
- package/dist/components/shared/dev-section/dev-card-row.d.ts +5 -45
- package/dist/components/shared/dev-section/dev-card-row.d.ts.map +1 -1
- package/dist/components/shared/legal-document/use-legal-docs.d.ts.map +1 -1
- package/dist/components/tickets/help-center-card.d.ts.map +1 -1
- package/dist/components/tickets/help-center-list.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-ticket-engagements.d.ts +9 -1
- package/dist/components/tickets/hooks/use-ticket-engagements.d.ts.map +1 -1
- package/dist/components/tickets/hooks/use-tickets-list.d.ts +7 -0
- package/dist/components/tickets/hooks/use-tickets-list.d.ts.map +1 -1
- package/dist/components/tickets/index.cjs +309 -256
- package/dist/components/tickets/index.cjs.map +1 -1
- package/dist/components/tickets/index.d.ts +1 -0
- package/dist/components/tickets/index.d.ts.map +1 -1
- package/dist/components/tickets/index.js +376 -323
- package/dist/components/tickets/index.js.map +1 -1
- package/dist/components/tickets/ticket-detail-drawer.d.ts.map +1 -1
- package/dist/components/tickets/ticket-reply-composer.d.ts +33 -0
- package/dist/components/tickets/ticket-reply-composer.d.ts.map +1 -0
- package/dist/components/tickets/types.d.ts +13 -0
- package/dist/components/tickets/types.d.ts.map +1 -1
- package/dist/components/ui/index.cjs +6 -2
- package/dist/components/ui/index.cjs.map +1 -1
- package/dist/components/ui/index.js +5 -1
- package/dist/components/ui/ticket-attachments-list.d.ts +5 -1
- package/dist/components/ui/ticket-attachments-list.d.ts.map +1 -1
- package/dist/index.cjs +6 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +5 -1
- package/dist/utils/index.cjs +59 -4
- package/dist/utils/index.cjs.map +1 -1
- package/dist/utils/index.js +59 -4
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/scroll-into-view.d.ts +43 -48
- package/dist/utils/scroll-into-view.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/chat/chat-attachment-bar.tsx +58 -22
- package/src/components/chat/chat-input.tsx +68 -29
- package/src/components/chat/chat-message-row.tsx +124 -0
- package/src/components/chat/index.ts +1 -0
- package/src/components/chat/types/component.types.ts +8 -1
- package/src/components/shared/dev-section/dev-card-row.tsx +5 -183
- package/src/components/shared/legal-document/use-legal-docs.ts +5 -1
- package/src/components/tickets/help-center-card.tsx +26 -29
- package/src/components/tickets/help-center-list.tsx +57 -10
- package/src/components/tickets/hooks/use-ticket-engagements.ts +41 -5
- package/src/components/tickets/hooks/use-tickets-list.ts +13 -0
- package/src/components/tickets/index.ts +4 -0
- package/src/components/tickets/ticket-detail-drawer.tsx +144 -200
- package/src/components/tickets/ticket-reply-composer.tsx +195 -0
- package/src/components/tickets/types.ts +14 -0
- package/src/components/ui/ticket-attachments-list.tsx +26 -8
- package/src/styles/app-globals.css +13 -0
- package/src/utils/scroll-into-view.ts +127 -53
- package/dist/chunk-CDLYRFDE.js.map +0 -1
- package/dist/chunk-GVNQAGXB.js.map +0 -1
- package/dist/chunk-IH76P5R6.cjs.map +0 -1
- package/dist/chunk-ZGTDUPTW.cjs.map +0 -1
- /package/dist/{chunk-QTKU6ULP.js.map → chunk-BVFRD34B.js.map} +0 -0
- /package/dist/{chunk-2V4SACHE.js.map → chunk-L6IBKPVM.js.map} +0 -0
- /package/dist/{chunk-VC3ND5RB.js.map → chunk-SWZUZYWR.js.map} +0 -0
|
@@ -4,18 +4,19 @@
|
|
|
4
4
|
|
|
5
5
|
|
|
6
6
|
|
|
7
|
-
var
|
|
7
|
+
var _chunkG2HHSZ3Scjs = require('../../chunk-G2HHSZ3S.cjs');
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
|
|
12
|
+
var _chunkTYIBMDUZcjs = require('../../chunk-TYIBMDUZ.cjs');
|
|
13
|
+
require('../../chunk-OB45JHDY.cjs');
|
|
12
14
|
|
|
13
15
|
|
|
14
|
-
var
|
|
15
|
-
|
|
16
|
+
var _chunk6DCKL73Fcjs = require('../../chunk-6DCKL73F.cjs');
|
|
17
|
+
|
|
16
18
|
|
|
17
19
|
|
|
18
|
-
var _chunkSMCG2CCCcjs = require('../../chunk-SMCG2CCC.cjs');
|
|
19
20
|
|
|
20
21
|
|
|
21
22
|
|
|
@@ -39,7 +40,8 @@ var _chunkSMCG2CCCcjs = require('../../chunk-SMCG2CCC.cjs');
|
|
|
39
40
|
|
|
40
41
|
|
|
41
42
|
|
|
42
|
-
|
|
43
|
+
|
|
44
|
+
var _chunkYWDC5BXMcjs = require('../../chunk-YWDC5BXM.cjs');
|
|
43
45
|
require('../../chunk-XL4V2PYG.cjs');
|
|
44
46
|
require('../../chunk-BZFW3FOF.cjs');
|
|
45
47
|
|
|
@@ -82,6 +84,7 @@ function isOptimistic(t) {
|
|
|
82
84
|
return t._optimistic === true;
|
|
83
85
|
}
|
|
84
86
|
var TICKET_TEXT_MAX_CHARS = 5e3;
|
|
87
|
+
var TICKET_LIVE_POLL_MS = 8e3;
|
|
85
88
|
var TOAST_COPY = {
|
|
86
89
|
open_success: { title: "Ticket opened", description: "We received your message and will follow up shortly." },
|
|
87
90
|
open_mirror_pending: { title: "Ticket opened", description: "Syncing \u2014 your ticket will appear momentarily." },
|
|
@@ -102,7 +105,7 @@ function TicketOpenForm({
|
|
|
102
105
|
}) {
|
|
103
106
|
const [subject, setSubject] = _react.useState.call(void 0, "");
|
|
104
107
|
const [content, setContent] = _react.useState.call(void 0, "");
|
|
105
|
-
const { attachments, readyAttachments, hasInflightUploads, addFiles, removeAttachment, clear } =
|
|
108
|
+
const { attachments, readyAttachments, hasInflightUploads, addFiles, removeAttachment, clear } = _chunkYWDC5BXMcjs.useChatAttachments.call(void 0, );
|
|
106
109
|
const trimmedSubject = subject.trim();
|
|
107
110
|
const trimmedContent = content.trim();
|
|
108
111
|
const overCap = content.length > TICKET_TEXT_MAX_CHARS;
|
|
@@ -122,7 +125,7 @@ function TicketOpenForm({
|
|
|
122
125
|
clear();
|
|
123
126
|
}
|
|
124
127
|
};
|
|
125
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
128
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.Card, { className: "p-6", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { onSubmit: handleSubmit, className: "flex flex-col md:flex-row gap-6", children: [
|
|
126
129
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex-1 min-w-0 md:max-w-md", children: [
|
|
127
130
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "h2", { className: "text-2xl font-semibold text-ods-text-primary mb-2", children: "Need Support?" }),
|
|
128
131
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "p", { className: "text-ods-text-secondary text-sm", children: "Can't find what you're looking for? Submit a support ticket below \u2014 we'll follow up shortly." }),
|
|
@@ -161,7 +164,7 @@ function TicketOpenForm({
|
|
|
161
164
|
}
|
|
162
165
|
),
|
|
163
166
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
164
|
-
|
|
167
|
+
_chunkYWDC5BXMcjs.Textarea,
|
|
165
168
|
{
|
|
166
169
|
id: "ticket-content",
|
|
167
170
|
placeholder: "Describe your issue or question in detail...",
|
|
@@ -185,7 +188,7 @@ function TicketOpenForm({
|
|
|
185
188
|
)
|
|
186
189
|
] }),
|
|
187
190
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
188
|
-
|
|
191
|
+
_chunkYWDC5BXMcjs.ChatAttachmentChipStrip,
|
|
189
192
|
{
|
|
190
193
|
attachments,
|
|
191
194
|
onRemove: removeAttachment,
|
|
@@ -194,7 +197,7 @@ function TicketOpenForm({
|
|
|
194
197
|
),
|
|
195
198
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center justify-between gap-3", children: [
|
|
196
199
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
197
|
-
|
|
200
|
+
_chunkYWDC5BXMcjs.ChatAttachmentAddButton,
|
|
198
201
|
{
|
|
199
202
|
attachmentsEnabled: !supportSystemDown,
|
|
200
203
|
attachmentsCount: attachments.length,
|
|
@@ -226,15 +229,16 @@ var CollapsibleContent2 = CollapsiblePrimitive.CollapsibleContent;
|
|
|
226
229
|
|
|
227
230
|
// src/components/tickets/ticket-detail-drawer.tsx
|
|
228
231
|
_chunkXDPSSE4Ocjs.init_button2.call(void 0, );
|
|
229
|
-
|
|
232
|
+
var _usesticktobottom = require('use-stick-to-bottom');
|
|
230
233
|
|
|
231
234
|
// src/components/tickets/hooks/use-ticket-engagements.ts
|
|
232
235
|
|
|
233
236
|
var LIST_ENGAGEMENTS_ENDPOINT = "/api/chat/agent/list-engagements";
|
|
234
|
-
function useTicketEngagements(externalTicketId, enabled = true) {
|
|
235
|
-
const identity =
|
|
236
|
-
const identityKey = _nullishCoalesce(_optionalChain([identity, 'access',
|
|
237
|
-
const
|
|
237
|
+
function useTicketEngagements(externalTicketId, enabled = true, refetchInterval = false) {
|
|
238
|
+
const identity = _chunkYWDC5BXMcjs.useChatIdentity.call(void 0, );
|
|
239
|
+
const identityKey = _nullishCoalesce(_optionalChain([identity, 'access', _2 => _2.user, 'optionalAccess', _3 => _3.email]), () => ( "anon"));
|
|
240
|
+
const fetchable = enabled && !!externalTicketId && !externalTicketId.startsWith("temp-");
|
|
241
|
+
const queryEnabled = fetchable && identity.authTier !== "anon" && !!_optionalChain([identity, 'access', _4 => _4.user, 'optionalAccess', _5 => _5.email]);
|
|
238
242
|
const query = _reactquery.useQuery.call(void 0, {
|
|
239
243
|
queryKey: ["ticket-engagements", externalTicketId, identityKey],
|
|
240
244
|
enabled: queryEnabled,
|
|
@@ -245,8 +249,13 @@ function useTicketEngagements(externalTicketId, enabled = true) {
|
|
|
245
249
|
gcTime: 0,
|
|
246
250
|
refetchOnMount: "always",
|
|
247
251
|
refetchOnWindowFocus: true,
|
|
252
|
+
// Live conversation: poll while the caller opts in (drawer open). New
|
|
253
|
+
// agent replies + attachments appear within one interval without a
|
|
254
|
+
// manual refresh. `refetchIntervalInBackground` stays false (default)
|
|
255
|
+
// so polling pauses on a hidden tab.
|
|
256
|
+
refetchInterval,
|
|
248
257
|
queryFn: async () => {
|
|
249
|
-
const response = await
|
|
258
|
+
const response = await _chunkYWDC5BXMcjs.embedAuthedFetch.call(void 0, LIST_ENGAGEMENTS_ENDPOINT, {
|
|
250
259
|
method: "POST",
|
|
251
260
|
body: JSON.stringify({ ticket_id: externalTicketId })
|
|
252
261
|
});
|
|
@@ -260,7 +269,20 @@ function useTicketEngagements(externalTicketId, enabled = true) {
|
|
|
260
269
|
});
|
|
261
270
|
return {
|
|
262
271
|
engagements: _nullishCoalesce(query.data, () => ( [])),
|
|
263
|
-
|
|
272
|
+
// Loading-state truth that prevents the "body → blink → skeleton → data"
|
|
273
|
+
// double-flash. The bug: `useChatIdentity` starts at anon defaults and
|
|
274
|
+
// resolves async, so on the first render `queryEnabled` is false and the
|
|
275
|
+
// OLD `queryEnabled && query.isLoading` returned FALSE — the panel rendered
|
|
276
|
+
// the ticket body, THEN identity resolved, the query enabled, isLoading
|
|
277
|
+
// flipped true → skeleton appeared (the blink), then data landed.
|
|
278
|
+
//
|
|
279
|
+
// Fix: for a fetchable ticket we are "loading" whenever we don't yet have
|
|
280
|
+
// the timeline to show — that includes the window while identity is still
|
|
281
|
+
// resolving (so we skeleton from the FIRST render, never the body) AND the
|
|
282
|
+
// cold query fetch (`data === undefined`). A background poll keeps
|
|
283
|
+
// `query.data` defined, so it never re-flashes the skeleton. Non-fetchable
|
|
284
|
+
// (optimistic/disabled) or a resolved-anon viewer → not loading.
|
|
285
|
+
isLoading: fetchable && (identity.isLoading || queryEnabled && query.data === void 0),
|
|
264
286
|
isFetching: query.isFetching,
|
|
265
287
|
error: _nullishCoalesce(query.error, () => ( null)),
|
|
266
288
|
refetch: () => {
|
|
@@ -294,7 +316,7 @@ function TicketLinkedDeliveryCard({
|
|
|
294
316
|
{
|
|
295
317
|
className: `rounded-md border border-ods-border bg-ods-bg overflow-hidden ${_nullishCoalesce(className, () => ( ""))}`,
|
|
296
318
|
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
297
|
-
|
|
319
|
+
_chunkG2HHSZ3Scjs.DeliveryRow,
|
|
298
320
|
{
|
|
299
321
|
item,
|
|
300
322
|
href: clickup.delivery_href,
|
|
@@ -305,6 +327,132 @@ function TicketLinkedDeliveryCard({
|
|
|
305
327
|
);
|
|
306
328
|
}
|
|
307
329
|
|
|
330
|
+
// src/components/tickets/ticket-reply-composer.tsx
|
|
331
|
+
_chunkXDPSSE4Ocjs.init_button2.call(void 0, );
|
|
332
|
+
|
|
333
|
+
|
|
334
|
+
function TicketReplyComposer({
|
|
335
|
+
ticket,
|
|
336
|
+
busy,
|
|
337
|
+
supportSystemDown,
|
|
338
|
+
onSendMessage,
|
|
339
|
+
onClose
|
|
340
|
+
}) {
|
|
341
|
+
const [resolution, setResolution] = _react.useState.call(void 0, "");
|
|
342
|
+
const [closeDialogOpen, setCloseDialogOpen] = _react.useState.call(void 0, false);
|
|
343
|
+
const attachments = _chunkYWDC5BXMcjs.useChatAttachments.call(void 0, );
|
|
344
|
+
const ticketRef = { id: ticket.id, external_id: ticket.external_id };
|
|
345
|
+
const hasReadyFiles = attachments.readyAttachments.length > 0;
|
|
346
|
+
const handleSend = _react.useCallback.call(void 0,
|
|
347
|
+
async (text) => {
|
|
348
|
+
const ref = { id: ticket.id, external_id: ticket.external_id };
|
|
349
|
+
const ok = await onSendMessage(ref, text.trim(), attachments.readyAttachments);
|
|
350
|
+
if (ok) attachments.clear();
|
|
351
|
+
return ok;
|
|
352
|
+
},
|
|
353
|
+
// Depend on the reactive projections, not the whole bag (a fresh object each
|
|
354
|
+
// render). `readyAttachments` is memo-stable; `clear` is callback-stable.
|
|
355
|
+
[
|
|
356
|
+
onSendMessage,
|
|
357
|
+
ticket.id,
|
|
358
|
+
ticket.external_id,
|
|
359
|
+
attachments.readyAttachments,
|
|
360
|
+
attachments.clear
|
|
361
|
+
]
|
|
362
|
+
);
|
|
363
|
+
const confirmClose = async () => {
|
|
364
|
+
setCloseDialogOpen(false);
|
|
365
|
+
await onClose(ticketRef, resolution.trim() || void 0);
|
|
366
|
+
setResolution("");
|
|
367
|
+
};
|
|
368
|
+
const disabled = busy || supportSystemDown;
|
|
369
|
+
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-2", children: [
|
|
370
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
371
|
+
_chunkYWDC5BXMcjs.ChatAttachmentChipStrip,
|
|
372
|
+
{
|
|
373
|
+
attachments: attachments.attachments,
|
|
374
|
+
onRemove: attachments.removeAttachment,
|
|
375
|
+
disabled,
|
|
376
|
+
size: "compact"
|
|
377
|
+
}
|
|
378
|
+
),
|
|
379
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
380
|
+
_chunkYWDC5BXMcjs.ChatInput,
|
|
381
|
+
{
|
|
382
|
+
fullWidth: true,
|
|
383
|
+
autoFocus: true,
|
|
384
|
+
placeholder: "Type a reply\u2026",
|
|
385
|
+
sending: busy || attachments.hasInflightUploads,
|
|
386
|
+
disabled: supportSystemDown,
|
|
387
|
+
allowEmptySend: hasReadyFiles,
|
|
388
|
+
maxLength: TICKET_TEXT_MAX_CHARS,
|
|
389
|
+
onSend: handleSend
|
|
390
|
+
}
|
|
391
|
+
),
|
|
392
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2 w-full", children: [
|
|
393
|
+
!supportSystemDown && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
394
|
+
_chunkYWDC5BXMcjs.ChatAttachmentAddButton,
|
|
395
|
+
{
|
|
396
|
+
attachmentsEnabled: true,
|
|
397
|
+
attachmentsCount: attachments.attachments.length,
|
|
398
|
+
onAddFiles: attachments.addFiles,
|
|
399
|
+
disabled,
|
|
400
|
+
size: "compact"
|
|
401
|
+
}
|
|
402
|
+
),
|
|
403
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex-1 min-w-0" }),
|
|
404
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
405
|
+
_chunkXDPSSE4Ocjs.Button,
|
|
406
|
+
{
|
|
407
|
+
type: "button",
|
|
408
|
+
variant: "transparent",
|
|
409
|
+
size: "small",
|
|
410
|
+
onClick: () => setCloseDialogOpen(true),
|
|
411
|
+
disabled,
|
|
412
|
+
className: "text-ods-text-secondary hover:text-ods-text-primary",
|
|
413
|
+
children: "Close ticket"
|
|
414
|
+
}
|
|
415
|
+
)
|
|
416
|
+
] }),
|
|
417
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.AlertDialog, { open: closeDialogOpen, onOpenChange: setCloseDialogOpen, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkYWDC5BXMcjs.AlertDialogContent, { className: "bg-ods-card border-ods-border", children: [
|
|
418
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkYWDC5BXMcjs.AlertDialogHeader, { children: [
|
|
419
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.AlertDialogTitle, { className: "text-ods-text-primary", children: "Close this ticket?" }),
|
|
420
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.AlertDialogDescription, { className: "text-ods-text-secondary", children: "Add an optional resolution note below. You can reopen the ticket later if needed." })
|
|
421
|
+
] }),
|
|
422
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
423
|
+
_chunkYWDC5BXMcjs.Textarea,
|
|
424
|
+
{
|
|
425
|
+
value: resolution,
|
|
426
|
+
onChange: (e) => setResolution(e.target.value),
|
|
427
|
+
placeholder: "Resolution (optional)",
|
|
428
|
+
rows: 3,
|
|
429
|
+
maxLength: TICKET_TEXT_MAX_CHARS,
|
|
430
|
+
className: "mt-2"
|
|
431
|
+
}
|
|
432
|
+
),
|
|
433
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkYWDC5BXMcjs.AlertDialogFooter, { children: [
|
|
434
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
435
|
+
_chunkYWDC5BXMcjs.AlertDialogCancel,
|
|
436
|
+
{
|
|
437
|
+
disabled: busy,
|
|
438
|
+
className: "bg-transparent border-ods-border text-ods-text-primary hover:bg-ods-border",
|
|
439
|
+
children: "Cancel"
|
|
440
|
+
}
|
|
441
|
+
),
|
|
442
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
443
|
+
_chunkYWDC5BXMcjs.AlertDialogAction,
|
|
444
|
+
{
|
|
445
|
+
onClick: () => void confirmClose(),
|
|
446
|
+
disabled: busy,
|
|
447
|
+
className: "bg-ods-accent text-ods-text-on-accent hover:bg-ods-accent-hover",
|
|
448
|
+
children: "Close ticket"
|
|
449
|
+
}
|
|
450
|
+
)
|
|
451
|
+
] })
|
|
452
|
+
] }) })
|
|
453
|
+
] });
|
|
454
|
+
}
|
|
455
|
+
|
|
308
456
|
// src/components/tickets/ticket-detail-drawer.tsx
|
|
309
457
|
|
|
310
458
|
function TicketDetailDrawer({
|
|
@@ -344,37 +492,49 @@ function TicketDetailDrawer({
|
|
|
344
492
|
onActionCollapsed
|
|
345
493
|
}
|
|
346
494
|
) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
347
|
-
|
|
495
|
+
TicketReplyComposer,
|
|
348
496
|
{
|
|
349
497
|
ticket,
|
|
350
498
|
busy,
|
|
351
499
|
supportSystemDown,
|
|
352
500
|
onSendMessage,
|
|
353
|
-
onClose
|
|
354
|
-
onActionCollapsed
|
|
501
|
+
onClose
|
|
355
502
|
}
|
|
356
503
|
)
|
|
357
504
|
] })
|
|
358
505
|
] });
|
|
359
506
|
}
|
|
360
507
|
var TURN_SEPARATOR_RE = /[\s]{1,16}---[\s]{1,16}/g;
|
|
508
|
+
var TICKET_FEED_FRAME = "bg-ods-card border border-ods-border rounded-[6px] overflow-y-auto w-full";
|
|
509
|
+
var TICKET_FEED_HEIGHT = "h-[60vh] md:h-[420px]";
|
|
510
|
+
var TICKET_FEED_INNER = "flex flex-col gap-4 md:gap-6 px-4 md:px-6 py-4 md:py-6";
|
|
511
|
+
var TICKET_FEED_SKELETON_ROWS = 6;
|
|
361
512
|
function TicketTimelinePanel({ ticket }) {
|
|
362
|
-
const identity =
|
|
513
|
+
const identity = _chunkYWDC5BXMcjs.useChatIdentity.call(void 0, );
|
|
363
514
|
const externalId = isOptimistic(ticket) ? null : ticket.external_id;
|
|
364
|
-
const { engagements, isLoading } = useTicketEngagements(
|
|
515
|
+
const { engagements, isLoading } = useTicketEngagements(
|
|
516
|
+
externalId,
|
|
517
|
+
!!externalId,
|
|
518
|
+
TICKET_LIVE_POLL_MS
|
|
519
|
+
);
|
|
520
|
+
const { scrollRef, contentRef } = _usesticktobottom.useStickToBottom.call(void 0, { initial: "instant", resize: "smooth" });
|
|
365
521
|
const bodyTurns = ticket.body ? ticket.body.split(TURN_SEPARATOR_RE).map((t) => t.trim()).filter(Boolean) : [];
|
|
366
|
-
const
|
|
522
|
+
const customerEngagementBodies = new Set(
|
|
523
|
+
engagements.filter((e) => e.authorRole === "customer").map((e) => (_nullishCoalesce(e.body, () => ( ""))).trim()).filter(Boolean)
|
|
524
|
+
);
|
|
525
|
+
const suppressBodyTurnZero = bodyTurns.length > 0 && customerEngagementBodies.has(bodyTurns[0]);
|
|
526
|
+
const sessionEmailLower = _nullishCoalesce(_optionalChain([identity, 'access', _6 => _6.user, 'optionalAccess', _7 => _7.email, 'optionalAccess', _8 => _8.trim, 'call', _9 => _9(), 'access', _10 => _10.toLowerCase, 'call', _11 => _11()]), () => ( null));
|
|
367
527
|
const isViewerTheCustomer = !!sessionEmailLower && ticket.customer_emails.some((e) => e.trim().toLowerCase() === sessionEmailLower);
|
|
368
|
-
const viewerName = _optionalChain([identity, 'access',
|
|
369
|
-
const ticketCustomerName = _optionalChain([ticket, 'access',
|
|
370
|
-
const customerName = (isViewerTheCustomer ? viewerName : null) || ticketCustomerName || viewerName || _optionalChain([identity, 'access',
|
|
371
|
-
const customerAvatar = isViewerTheCustomer ? _nullishCoalesce(_optionalChain([identity, 'access',
|
|
528
|
+
const viewerName = _optionalChain([identity, 'access', _12 => _12.user, 'optionalAccess', _13 => _13.name, 'optionalAccess', _14 => _14.trim, 'call', _15 => _15()]) || null;
|
|
529
|
+
const ticketCustomerName = _optionalChain([ticket, 'access', _16 => _16.customer_name, 'optionalAccess', _17 => _17.trim, 'call', _18 => _18()]) || null;
|
|
530
|
+
const customerName = (isViewerTheCustomer ? viewerName : null) || ticketCustomerName || viewerName || _optionalChain([identity, 'access', _19 => _19.user, 'optionalAccess', _20 => _20.email]) || "You";
|
|
531
|
+
const customerAvatar = isViewerTheCustomer ? _nullishCoalesce(_optionalChain([identity, 'access', _21 => _21.user, 'optionalAccess', _22 => _22.avatarUrl]), () => ( void 0)) : void 0;
|
|
532
|
+
if (isLoading) {
|
|
533
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: `${TICKET_FEED_FRAME} ${TICKET_FEED_HEIGHT}`, children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: TICKET_FEED_INNER, children: Array.from({ length: TICKET_FEED_SKELETON_ROWS }, (_, i) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.ChatMessageRowSkeleton, {}, i)) }) });
|
|
534
|
+
}
|
|
372
535
|
if (bodyTurns.length === 0 && engagements.length === 0) {
|
|
373
|
-
if (isLoading) {
|
|
374
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIH76P5R6cjs.ConversationCardRowSkeletonList, { rows: 2 });
|
|
375
|
-
}
|
|
376
536
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
377
|
-
|
|
537
|
+
_chunkG2HHSZ3Scjs.EmptyState,
|
|
378
538
|
{
|
|
379
539
|
type: "generic",
|
|
380
540
|
title: "No conversation yet",
|
|
@@ -383,31 +543,29 @@ function TicketTimelinePanel({ ticket }) {
|
|
|
383
543
|
}
|
|
384
544
|
);
|
|
385
545
|
}
|
|
386
|
-
return /* @__PURE__ */ _jsxruntime.
|
|
546
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { ref: scrollRef, className: `${TICKET_FEED_FRAME} ${TICKET_FEED_HEIGHT}`, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { ref: contentRef, className: TICKET_FEED_INNER, children: [
|
|
387
547
|
bodyTurns.map((turn, i) => {
|
|
548
|
+
if (i === 0 && suppressBodyTurnZero) return null;
|
|
388
549
|
const isResolution = turn.startsWith("[Resolution]");
|
|
389
|
-
const role = i === 0 ? "Original message" : isResolution ? "Resolution" : `Update ${i}`;
|
|
390
550
|
const text = isResolution ? turn.replace(/^\[Resolution\]\s*/, "") : turn;
|
|
391
551
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
392
|
-
|
|
552
|
+
_chunkYWDC5BXMcjs.ChatMessageRow,
|
|
393
553
|
{
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
body: text,
|
|
398
|
-
variant: "current-user"
|
|
554
|
+
displayName: customerName,
|
|
555
|
+
avatarUrl: customerAvatar,
|
|
556
|
+
body: text
|
|
399
557
|
},
|
|
400
558
|
`body-${i}-${turn.slice(0, 24)}`
|
|
401
559
|
);
|
|
402
560
|
}),
|
|
403
561
|
engagements.map((eng) => {
|
|
404
562
|
const isCustomer = eng.authorRole === "customer";
|
|
405
|
-
const isOwnReply = isCustomer && !!eng.authorId && !!_optionalChain([identity, 'access',
|
|
563
|
+
const isOwnReply = isCustomer && !!eng.authorId && !!_optionalChain([identity, 'access', _23 => _23.user, 'optionalAccess', _24 => _24.email]) && eng.authorId.toLowerCase() === identity.user.email.toLowerCase();
|
|
406
564
|
let author;
|
|
407
565
|
let avatarSrc;
|
|
408
566
|
if (isCustomer && isOwnReply) {
|
|
409
|
-
author = _optionalChain([identity, 'access',
|
|
410
|
-
avatarSrc = _nullishCoalesce(_optionalChain([identity, 'access',
|
|
567
|
+
author = _optionalChain([identity, 'access', _25 => _25.user, 'optionalAccess', _26 => _26.name, 'optionalAccess', _27 => _27.trim, 'call', _28 => _28()]) || customerName;
|
|
568
|
+
avatarSrc = _nullishCoalesce(_optionalChain([identity, 'access', _29 => _29.user, 'optionalAccess', _30 => _30.avatarUrl]), () => ( void 0));
|
|
411
569
|
} else if (isCustomer) {
|
|
412
570
|
author = ticketCustomerName || "Customer";
|
|
413
571
|
avatarSrc = void 0;
|
|
@@ -418,32 +576,30 @@ function TicketTimelinePanel({ ticket }) {
|
|
|
418
576
|
author = "Support team";
|
|
419
577
|
avatarSrc = void 0;
|
|
420
578
|
}
|
|
579
|
+
const engAttachments = mapEngagementAttachments(eng.attachments);
|
|
421
580
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
422
|
-
|
|
581
|
+
_chunkYWDC5BXMcjs.ChatMessageRow,
|
|
423
582
|
{
|
|
424
|
-
author,
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
timestamp: eng.createdAt,
|
|
583
|
+
displayName: author,
|
|
584
|
+
avatarUrl: avatarSrc,
|
|
585
|
+
timeLabel: eng.createdAt ? _chunkYWDC5BXMcjs.formatRelativeTime.call(void 0, eng.createdAt) : null,
|
|
428
586
|
body: stripAttachmentsPreamble(_nullishCoalesce(eng.body, () => ( ""))),
|
|
429
|
-
|
|
430
|
-
variant: isCustomer ? "current-user" : "support"
|
|
587
|
+
footer: engAttachments.length > 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "mt-2", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.TicketAttachmentsList, { attachments: engAttachments, size: "compact" }) }) : null
|
|
431
588
|
},
|
|
432
589
|
eng.id
|
|
433
590
|
);
|
|
434
|
-
})
|
|
435
|
-
|
|
436
|
-
// content rendered — drawer is showing the customer's body
|
|
437
|
-
// turns + cached engagements while a background refetch is
|
|
438
|
-
// in flight. Single row keeps the placeholder modest.
|
|
439
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIH76P5R6cjs.ConversationCardRowSkeletonList, { rows: 1 })
|
|
440
|
-
] });
|
|
591
|
+
})
|
|
592
|
+
] }) });
|
|
441
593
|
}
|
|
442
594
|
function mapEngagementAttachments(files) {
|
|
443
595
|
return files.map((f) => ({
|
|
444
596
|
id: f.id,
|
|
445
597
|
fileName: _nullishCoalesce(f.name, () => ( `file-${f.id}`)),
|
|
446
598
|
fileSize: f.size ? formatBytes(f.size) : "",
|
|
599
|
+
// Show an inline thumbnail for image attachments (the signed `url` is a
|
|
600
|
+
// viewable URL). Non-images fall back to the file-type icon. SquareAvatar
|
|
601
|
+
// degrades to initials on a broken/expired image URL.
|
|
602
|
+
thumbnailSrc: f.url && (_nullishCoalesce(_optionalChain([f, 'access', _31 => _31.mime, 'optionalAccess', _32 => _32.startsWith, 'call', _33 => _33("image/")]), () => ( false))) ? f.url : void 0,
|
|
447
603
|
onDownload: f.url ? () => window.open(f.url, "_blank", "noopener,noreferrer") : void 0
|
|
448
604
|
}));
|
|
449
605
|
}
|
|
@@ -470,6 +626,8 @@ function ReopenAction({
|
|
|
470
626
|
_chunkXDPSSE4Ocjs.Button,
|
|
471
627
|
{
|
|
472
628
|
type: "button",
|
|
629
|
+
variant: "outline",
|
|
630
|
+
size: "small",
|
|
473
631
|
onClick: () => void handleReopen(),
|
|
474
632
|
disabled: busy || supportSystemDown,
|
|
475
633
|
loading: busy,
|
|
@@ -477,133 +635,6 @@ function ReopenAction({
|
|
|
477
635
|
}
|
|
478
636
|
) });
|
|
479
637
|
}
|
|
480
|
-
function OpenActions({
|
|
481
|
-
ticket,
|
|
482
|
-
busy,
|
|
483
|
-
supportSystemDown,
|
|
484
|
-
onSendMessage,
|
|
485
|
-
onClose,
|
|
486
|
-
onActionCollapsed
|
|
487
|
-
}) {
|
|
488
|
-
const [messageText, setMessageText] = _react.useState.call(void 0, "");
|
|
489
|
-
const [resolution, setResolution] = _react.useState.call(void 0, "");
|
|
490
|
-
const [closeDialogOpen, setCloseDialogOpen] = _react.useState.call(void 0, false);
|
|
491
|
-
const attachments = _chunkZGTDUPTWcjs.useChatAttachments.call(void 0, );
|
|
492
|
-
const disabled = busy || supportSystemDown;
|
|
493
|
-
const ticketRef = { id: ticket.id, external_id: ticket.external_id };
|
|
494
|
-
const hasText = messageText.trim().length > 0;
|
|
495
|
-
const hasReadyFiles = attachments.readyAttachments.length > 0;
|
|
496
|
-
const canSend = !disabled && (hasText || hasReadyFiles) && !attachments.hasInflightUploads;
|
|
497
|
-
const sendMessage = async () => {
|
|
498
|
-
if (!canSend) return;
|
|
499
|
-
const ok = await onSendMessage(ticketRef, messageText.trim(), attachments.readyAttachments);
|
|
500
|
-
if (ok) {
|
|
501
|
-
setMessageText("");
|
|
502
|
-
attachments.clear();
|
|
503
|
-
}
|
|
504
|
-
};
|
|
505
|
-
const confirmClose = async () => {
|
|
506
|
-
setCloseDialogOpen(false);
|
|
507
|
-
await onClose(ticketRef, resolution.trim() || void 0);
|
|
508
|
-
setResolution("");
|
|
509
|
-
};
|
|
510
|
-
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-3", children: [
|
|
511
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-2", children: [
|
|
512
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
513
|
-
_chunkZGTDUPTWcjs.Textarea,
|
|
514
|
-
{
|
|
515
|
-
value: messageText,
|
|
516
|
-
onChange: (e) => setMessageText(e.target.value),
|
|
517
|
-
placeholder: "Type a reply\u2026 (attach files if needed)",
|
|
518
|
-
disabled,
|
|
519
|
-
rows: 3,
|
|
520
|
-
maxLength: TICKET_TEXT_MAX_CHARS
|
|
521
|
-
}
|
|
522
|
-
),
|
|
523
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
524
|
-
_chunkZGTDUPTWcjs.ChatAttachmentChipStrip,
|
|
525
|
-
{
|
|
526
|
-
attachments: attachments.attachments,
|
|
527
|
-
onRemove: attachments.removeAttachment,
|
|
528
|
-
disabled
|
|
529
|
-
}
|
|
530
|
-
),
|
|
531
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex justify-between items-center gap-2 flex-wrap", children: [
|
|
532
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [
|
|
533
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
534
|
-
_chunkZGTDUPTWcjs.ChatAttachmentAddButton,
|
|
535
|
-
{
|
|
536
|
-
attachmentsEnabled: !supportSystemDown,
|
|
537
|
-
attachmentsCount: attachments.attachments.length,
|
|
538
|
-
onAddFiles: attachments.addFiles,
|
|
539
|
-
disabled
|
|
540
|
-
}
|
|
541
|
-
),
|
|
542
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-xs text-ods-text-secondary", children: "Attach files" })
|
|
543
|
-
] }),
|
|
544
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2", children: [
|
|
545
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
546
|
-
_chunkXDPSSE4Ocjs.Button,
|
|
547
|
-
{
|
|
548
|
-
type: "button",
|
|
549
|
-
variant: "transparent",
|
|
550
|
-
onClick: () => setCloseDialogOpen(true),
|
|
551
|
-
disabled,
|
|
552
|
-
className: "bg-ods-error hover:bg-ods-error-hover text-white border-transparent",
|
|
553
|
-
children: "Close ticket"
|
|
554
|
-
}
|
|
555
|
-
),
|
|
556
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
557
|
-
_chunkXDPSSE4Ocjs.Button,
|
|
558
|
-
{
|
|
559
|
-
type: "button",
|
|
560
|
-
onClick: () => void sendMessage(),
|
|
561
|
-
disabled: !canSend,
|
|
562
|
-
loading: busy,
|
|
563
|
-
children: "Send reply"
|
|
564
|
-
}
|
|
565
|
-
)
|
|
566
|
-
] })
|
|
567
|
-
] })
|
|
568
|
-
] }),
|
|
569
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZGTDUPTWcjs.AlertDialog, { open: closeDialogOpen, onOpenChange: setCloseDialogOpen, children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkZGTDUPTWcjs.AlertDialogContent, { className: "bg-ods-card border-ods-border", children: [
|
|
570
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkZGTDUPTWcjs.AlertDialogHeader, { children: [
|
|
571
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZGTDUPTWcjs.AlertDialogTitle, { className: "text-ods-text-primary font-['DM_Sans'] text-[20px] font-semibold", children: "Close this ticket?" }),
|
|
572
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZGTDUPTWcjs.AlertDialogDescription, { className: "text-ods-text-secondary font-['DM_Sans'] text-[14px]", children: "Add an optional resolution note below. You can reopen the ticket later if needed." })
|
|
573
|
-
] }),
|
|
574
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
575
|
-
_chunkZGTDUPTWcjs.Textarea,
|
|
576
|
-
{
|
|
577
|
-
value: resolution,
|
|
578
|
-
onChange: (e) => setResolution(e.target.value),
|
|
579
|
-
placeholder: "Resolution (optional)",
|
|
580
|
-
rows: 3,
|
|
581
|
-
maxLength: TICKET_TEXT_MAX_CHARS,
|
|
582
|
-
className: "mt-2"
|
|
583
|
-
}
|
|
584
|
-
),
|
|
585
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkZGTDUPTWcjs.AlertDialogFooter, { children: [
|
|
586
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
587
|
-
_chunkZGTDUPTWcjs.AlertDialogCancel,
|
|
588
|
-
{
|
|
589
|
-
disabled: busy,
|
|
590
|
-
className: "bg-transparent border-ods-border text-ods-text-primary hover:bg-ods-border",
|
|
591
|
-
children: "Cancel"
|
|
592
|
-
}
|
|
593
|
-
),
|
|
594
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
595
|
-
_chunkZGTDUPTWcjs.AlertDialogAction,
|
|
596
|
-
{
|
|
597
|
-
onClick: () => void confirmClose(),
|
|
598
|
-
disabled: busy,
|
|
599
|
-
className: "bg-ods-error hover:bg-ods-error-hover text-white",
|
|
600
|
-
children: "Close ticket"
|
|
601
|
-
}
|
|
602
|
-
)
|
|
603
|
-
] })
|
|
604
|
-
] }) })
|
|
605
|
-
] });
|
|
606
|
-
}
|
|
607
638
|
function ReplyFailureBanner({
|
|
608
639
|
error,
|
|
609
640
|
onDismiss
|
|
@@ -634,18 +665,18 @@ function ReplyFailureBanner({
|
|
|
634
665
|
function AssignedAgentRow({
|
|
635
666
|
assignedOwner
|
|
636
667
|
}) {
|
|
637
|
-
const trimmedName = _optionalChain([assignedOwner, 'optionalAccess',
|
|
638
|
-
const emailFallback = _optionalChain([assignedOwner, 'optionalAccess',
|
|
668
|
+
const trimmedName = _optionalChain([assignedOwner, 'optionalAccess', _34 => _34.name, 'optionalAccess', _35 => _35.trim, 'call', _36 => _36()]) || null;
|
|
669
|
+
const emailFallback = _optionalChain([assignedOwner, 'optionalAccess', _37 => _37.email, 'optionalAccess', _38 => _38.trim, 'call', _39 => _39()]) || null;
|
|
639
670
|
const displayLabel = _nullishCoalesce(trimmedName, () => ( (emailFallback ? emailFallback.split("@")[0] : null)));
|
|
640
671
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2 text-xs", children: [
|
|
641
672
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-ods-text-secondary uppercase tracking-wider font-medium", children: "Assigned to" }),
|
|
642
673
|
displayLabel ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-center gap-1.5 text-ods-text-primary font-medium", children: [
|
|
643
674
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
644
|
-
|
|
675
|
+
_chunkYWDC5BXMcjs.SquareAvatar,
|
|
645
676
|
{
|
|
646
677
|
size: "sm",
|
|
647
678
|
variant: "round",
|
|
648
|
-
src: _nullishCoalesce(_optionalChain([assignedOwner, 'optionalAccess',
|
|
679
|
+
src: _nullishCoalesce(_optionalChain([assignedOwner, 'optionalAccess', _40 => _40.avatarUrl]), () => ( void 0)),
|
|
649
680
|
alt: displayLabel,
|
|
650
681
|
fallback: displayLabel
|
|
651
682
|
}
|
|
@@ -672,7 +703,7 @@ function TicketRow({
|
|
|
672
703
|
const rowRef = _react.useRef.call(void 0, null);
|
|
673
704
|
const handleClick = _react.useCallback.call(void 0, () => {
|
|
674
705
|
onToggle(ticket.id);
|
|
675
|
-
|
|
706
|
+
_chunkYWDC5BXMcjs.scrollElementIntoView.call(void 0, rowRef.current, {
|
|
676
707
|
adjustTargetY: (raw) => {
|
|
677
708
|
if (!rowRef.current) return raw;
|
|
678
709
|
const expandedDrawer = document.querySelector(
|
|
@@ -698,7 +729,7 @@ function TicketRow({
|
|
|
698
729
|
// so the badge accurately reflects "Closed" with a checkmark.
|
|
699
730
|
statusLabel: _nullishCoalesce(ticket.pipeline_stage_label, () => ( void 0)),
|
|
700
731
|
category: _nullishCoalesce(ticket.customer_company, () => ( void 0)),
|
|
701
|
-
timeAgo: ticket.hubspot_updated_at ?
|
|
732
|
+
timeAgo: ticket.hubspot_updated_at ? _chunkYWDC5BXMcjs.formatRelativeTime.call(void 0, ticket.hubspot_updated_at) : void 0,
|
|
702
733
|
// Linked-work chip: surfaced whenever the ticket has a linked
|
|
703
734
|
// ClickUp task. Uses the linked task's own status so the chip text
|
|
704
735
|
// reads "Working" / "Waiting on version release" / etc. — useful
|
|
@@ -713,7 +744,7 @@ function TicketRow({
|
|
|
713
744
|
className: "border-b border-ods-border last:border-b-0",
|
|
714
745
|
children: [
|
|
715
746
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
716
|
-
|
|
747
|
+
_chunkYWDC5BXMcjs.ChatTicketItem,
|
|
717
748
|
{
|
|
718
749
|
ticket: tileData,
|
|
719
750
|
onClick: optimistic ? void 0 : handleClick,
|
|
@@ -758,6 +789,7 @@ function useTicketsList(filters) {
|
|
|
758
789
|
const pageSize = Math.max(1, Math.min(100, Math.floor(_nullishCoalesce(filters.pageSize, () => ( DEFAULT_PAGE_SIZE))) || DEFAULT_PAGE_SIZE));
|
|
759
790
|
const enabled = !!customerEmail;
|
|
760
791
|
const identityKey = customerEmail || "anon";
|
|
792
|
+
const refetchInterval = _nullishCoalesce(filters.refetchInterval, () => ( false));
|
|
761
793
|
const query = _reactquery.useQuery.call(void 0, {
|
|
762
794
|
queryKey: ["tickets", "self", identityKey, search, statusFilter, page, pageSize],
|
|
763
795
|
enabled,
|
|
@@ -770,6 +802,10 @@ function useTicketsList(filters) {
|
|
|
770
802
|
gcTime: 0,
|
|
771
803
|
refetchOnMount: "always",
|
|
772
804
|
refetchOnWindowFocus: true,
|
|
805
|
+
// Live status: poll while the caller opts in (drawer open). Defaults to
|
|
806
|
+
// false. `refetchIntervalInBackground` stays false (the default) so polling
|
|
807
|
+
// pauses on a hidden tab — no wasted requests when the user tabs away.
|
|
808
|
+
refetchInterval,
|
|
773
809
|
queryFn: async () => {
|
|
774
810
|
const body = {
|
|
775
811
|
query: search,
|
|
@@ -777,7 +813,7 @@ function useTicketsList(filters) {
|
|
|
777
813
|
pageSize
|
|
778
814
|
};
|
|
779
815
|
if (statusFilter) body.status = statusFilter;
|
|
780
|
-
const response = await
|
|
816
|
+
const response = await _chunkYWDC5BXMcjs.embedAuthedFetch.call(void 0, FIND_TICKET_ENDPOINT, {
|
|
781
817
|
method: "POST",
|
|
782
818
|
body: JSON.stringify(body)
|
|
783
819
|
});
|
|
@@ -789,12 +825,12 @@ function useTicketsList(filters) {
|
|
|
789
825
|
}
|
|
790
826
|
});
|
|
791
827
|
const data = query.data;
|
|
792
|
-
const totalCount = _nullishCoalesce(_nullishCoalesce(_optionalChain([data, 'optionalAccess',
|
|
793
|
-
const echoedPage = _nullishCoalesce(_optionalChain([data, 'optionalAccess',
|
|
794
|
-
const echoedPageSize = _nullishCoalesce(_optionalChain([data, 'optionalAccess',
|
|
795
|
-
const totalPages = _nullishCoalesce(_optionalChain([data, 'optionalAccess',
|
|
828
|
+
const totalCount = _nullishCoalesce(_nullishCoalesce(_optionalChain([data, 'optionalAccess', _41 => _41.totalCount]), () => ( _optionalChain([data, 'optionalAccess', _42 => _42.count]))), () => ( (_nullishCoalesce(_optionalChain([data, 'optionalAccess', _43 => _43.tickets, 'optionalAccess', _44 => _44.length]), () => ( 0)))));
|
|
829
|
+
const echoedPage = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _45 => _45.page]), () => ( page));
|
|
830
|
+
const echoedPageSize = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _46 => _46.pageSize]), () => ( pageSize));
|
|
831
|
+
const totalPages = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _47 => _47.totalPages]), () => ( Math.max(1, Math.ceil(totalCount / echoedPageSize))));
|
|
796
832
|
return {
|
|
797
|
-
tickets: _nullishCoalesce(_optionalChain([data, 'optionalAccess',
|
|
833
|
+
tickets: _nullishCoalesce(_optionalChain([data, 'optionalAccess', _48 => _48.tickets]), () => ( [])),
|
|
798
834
|
// Loading-state-truth = `data === undefined`. TanStack v5's
|
|
799
835
|
// `isPending` / `isLoading` flags can be `false` in transient
|
|
800
836
|
// windows where the query is enabled-but-fetch-not-yet-fired
|
|
@@ -816,7 +852,7 @@ function useTicketsList(filters) {
|
|
|
816
852
|
// - Background refetch with existing data: data !== undefined → no load
|
|
817
853
|
// - Filter-change refetch landing on empty results: data?.tickets===[]
|
|
818
854
|
// + isFetching → bridge skeleton (the `||` branch)
|
|
819
|
-
isLoading: enabled && (data === void 0 || query.isFetching && (_nullishCoalesce(_optionalChain([data, 'optionalAccess',
|
|
855
|
+
isLoading: enabled && (data === void 0 || query.isFetching && (_nullishCoalesce(_optionalChain([data, 'optionalAccess', _49 => _49.tickets]), () => ( []))).length === 0),
|
|
820
856
|
isFetching: query.isFetching,
|
|
821
857
|
error: _nullishCoalesce(query.error, () => ( null)),
|
|
822
858
|
refetch: () => {
|
|
@@ -891,7 +927,7 @@ function useTicketActions(options) {
|
|
|
891
927
|
}, []);
|
|
892
928
|
const executeTicketAction = _react.useCallback.call(void 0,
|
|
893
929
|
async (toolName, args) => {
|
|
894
|
-
const res = await
|
|
930
|
+
const res = await _chunkYWDC5BXMcjs.embedAuthedFetch.call(void 0, TICKET_ACTION_ENDPOINT, {
|
|
895
931
|
method: "POST",
|
|
896
932
|
body: JSON.stringify({ tool_name: toolName, args })
|
|
897
933
|
});
|
|
@@ -1003,7 +1039,7 @@ function useTicketActions(options) {
|
|
|
1003
1039
|
const result = await executeTicketAction("create_ticket", {
|
|
1004
1040
|
subject: input.subject.trim(),
|
|
1005
1041
|
content: input.content.trim(),
|
|
1006
|
-
..._optionalChain([input, 'access',
|
|
1042
|
+
..._optionalChain([input, 'access', _50 => _50.attachments, 'optionalAccess', _51 => _51.length]) ? { attachments: input.attachments } : {}
|
|
1007
1043
|
});
|
|
1008
1044
|
if (result.mirror_synced === false) {
|
|
1009
1045
|
toast2(TOAST_COPY.open_mirror_pending);
|
|
@@ -1115,7 +1151,7 @@ function useTicketActions(options) {
|
|
|
1115
1151
|
ticket,
|
|
1116
1152
|
{
|
|
1117
1153
|
status: "CLOSED",
|
|
1118
|
-
..._optionalChain([resolution, 'optionalAccess',
|
|
1154
|
+
..._optionalChain([resolution, 'optionalAccess', _52 => _52.trim, 'call', _53 => _53()]) ? { resolution: resolution.trim() } : {}
|
|
1119
1155
|
},
|
|
1120
1156
|
TOAST_COPY.close_success,
|
|
1121
1157
|
"close ticket"
|
|
@@ -1188,7 +1224,7 @@ function mapTicketActionError(err) {
|
|
|
1188
1224
|
removeRowFromCache: false
|
|
1189
1225
|
};
|
|
1190
1226
|
case "RATE_LIMITED": {
|
|
1191
|
-
const retryAfterRaw = _optionalChain([err, 'access',
|
|
1227
|
+
const retryAfterRaw = _optionalChain([err, 'access', _54 => _54.response, 'optionalAccess', _55 => _55.headers, 'access', _56 => _56.get, 'call', _57 => _57("Retry-After")]);
|
|
1192
1228
|
const retryAfterSeconds = retryAfterRaw ? parseInt(retryAfterRaw, 10) : void 0;
|
|
1193
1229
|
return {
|
|
1194
1230
|
code: err.code,
|
|
@@ -1276,13 +1312,13 @@ function resolveErrorCode(bodyCode, status) {
|
|
|
1276
1312
|
// src/components/tickets/ticket-center.tsx
|
|
1277
1313
|
|
|
1278
1314
|
function TicketCenter({ toast: toast2 = _chunk5V6MSE3Bcjs.toast } = {}) {
|
|
1279
|
-
const identity =
|
|
1315
|
+
const identity = _chunkYWDC5BXMcjs.useChatIdentity.call(void 0, );
|
|
1280
1316
|
if (identity.isLoading) {
|
|
1281
1317
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TicketCenterSkeleton, {});
|
|
1282
1318
|
}
|
|
1283
|
-
if (identity.authTier === "anon" || !_optionalChain([identity, 'access',
|
|
1319
|
+
if (identity.authTier === "anon" || !_optionalChain([identity, 'access', _58 => _58.user, 'optionalAccess', _59 => _59.email])) {
|
|
1284
1320
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1285
|
-
|
|
1321
|
+
_chunkG2HHSZ3Scjs.EmptyState,
|
|
1286
1322
|
{
|
|
1287
1323
|
type: "generic",
|
|
1288
1324
|
title: "Sign in to manage tickets",
|
|
@@ -1353,7 +1389,7 @@ function TicketCenterAuthed({
|
|
|
1353
1389
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-3 text-xs text-ods-text-secondary", children: [
|
|
1354
1390
|
lastUpdatedAt && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { children: [
|
|
1355
1391
|
"Updated ",
|
|
1356
|
-
|
|
1392
|
+
_chunkYWDC5BXMcjs.formatRelativeTime.call(void 0, new Date(lastUpdatedAt))
|
|
1357
1393
|
] }),
|
|
1358
1394
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1359
1395
|
_chunkXDPSSE4Ocjs.Button,
|
|
@@ -1369,15 +1405,15 @@ function TicketCenterAuthed({
|
|
|
1369
1405
|
)
|
|
1370
1406
|
] })
|
|
1371
1407
|
] }),
|
|
1372
|
-
isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TicketListSkeleton, {}) : merged.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1373
|
-
|
|
1408
|
+
isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TicketListSkeleton, {}) : merged.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.Card, { className: "p-6", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1409
|
+
_chunkG2HHSZ3Scjs.EmptyState,
|
|
1374
1410
|
{
|
|
1375
1411
|
type: "generic",
|
|
1376
1412
|
title: "No tickets yet",
|
|
1377
1413
|
description: "Open one above to start the conversation.",
|
|
1378
1414
|
showCTA: false
|
|
1379
1415
|
}
|
|
1380
|
-
) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1416
|
+
) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.Card, { className: "overflow-hidden", children: merged.map((ticket) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1381
1417
|
TicketRow,
|
|
1382
1418
|
{
|
|
1383
1419
|
ticket,
|
|
@@ -1397,7 +1433,7 @@ function TicketCenterAuthed({
|
|
|
1397
1433
|
}
|
|
1398
1434
|
function TicketCenterSkeleton() {
|
|
1399
1435
|
return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-6", children: [
|
|
1400
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1436
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkYWDC5BXMcjs.Card, { className: "p-6", children: [
|
|
1401
1437
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Skeleton, { className: "h-7 w-48 mb-4" }),
|
|
1402
1438
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Skeleton, { className: "h-10 w-full mb-3" }),
|
|
1403
1439
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Skeleton, { className: "h-24 w-full" })
|
|
@@ -1406,7 +1442,7 @@ function TicketCenterSkeleton() {
|
|
|
1406
1442
|
] });
|
|
1407
1443
|
}
|
|
1408
1444
|
function TicketListSkeleton() {
|
|
1409
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1445
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.Card, { className: "overflow-hidden", children: [0, 1, 2].map((i) => /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "h-20 px-4 flex items-center gap-4 border-b border-ods-border last:border-b-0", children: [
|
|
1410
1446
|
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex-1 flex flex-col gap-2", children: [
|
|
1411
1447
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Skeleton, { className: "h-4 w-2/3" }),
|
|
1412
1448
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Skeleton, { className: "h-3 w-full" })
|
|
@@ -1419,7 +1455,7 @@ function TicketListSkeleton() {
|
|
|
1419
1455
|
// src/components/tickets/help-center-list.tsx
|
|
1420
1456
|
|
|
1421
1457
|
|
|
1422
|
-
|
|
1458
|
+
_chunkG2HHSZ3Scjs.init_unified_pagination.call(void 0, );
|
|
1423
1459
|
|
|
1424
1460
|
// src/components/tickets/help-center-card.tsx
|
|
1425
1461
|
|
|
@@ -1441,7 +1477,7 @@ function HelpCenterCard({
|
|
|
1441
1477
|
const optimistic = isOptimistic(ticket);
|
|
1442
1478
|
const rawStatus = (_nullishCoalesce(ticket.status, () => ( "OPEN"))).toUpperCase();
|
|
1443
1479
|
const priority = (_nullishCoalesce(ticket.priority, () => ( ""))).toUpperCase();
|
|
1444
|
-
const relativeUpdated = ticket.hubspot_updated_at ?
|
|
1480
|
+
const relativeUpdated = ticket.hubspot_updated_at ? _chunkYWDC5BXMcjs.formatRelativeTime.call(void 0, ticket.hubspot_updated_at) : "recently";
|
|
1445
1481
|
const title = (ticket.subject || "").trim() || "(untitled)";
|
|
1446
1482
|
const subtitle = `UPDATED ${relativeUpdated}, #${ticket.external_id || "\u2014"}${ticket.pipeline_stage_label ? `, ${ticket.pipeline_stage_label}` : ""}`;
|
|
1447
1483
|
const description = _nullishCoalesce(_nullishCoalesce(ticket.preview, () => ( ticket.body)), () => ( ""));
|
|
@@ -1450,33 +1486,28 @@ function HelpCenterCard({
|
|
|
1450
1486
|
const rowRef = _react.useRef.call(void 0, null);
|
|
1451
1487
|
const handleClick = _react.useCallback.call(void 0, () => {
|
|
1452
1488
|
onToggle(ticket.id);
|
|
1453
|
-
_chunkZGTDUPTWcjs.scrollElementIntoView.call(void 0, rowRef.current, {
|
|
1454
|
-
headerOffset: STICKY_HEADER_OFFSET_PX,
|
|
1455
|
-
adjustTargetY: (raw) => {
|
|
1456
|
-
if (!rowRef.current) return raw;
|
|
1457
|
-
const expandedDrawer = document.querySelector(
|
|
1458
|
-
'div[id^="help-center-drawer-"]'
|
|
1459
|
-
);
|
|
1460
|
-
if (!(expandedDrawer instanceof HTMLElement)) return raw;
|
|
1461
|
-
const drawerRect = expandedDrawer.getBoundingClientRect();
|
|
1462
|
-
const myRect = rowRef.current.getBoundingClientRect();
|
|
1463
|
-
if (drawerRect.bottom > myRect.top) return raw;
|
|
1464
|
-
return raw - drawerRect.height;
|
|
1465
|
-
}
|
|
1466
|
-
});
|
|
1467
1489
|
}, [onToggle, ticket.id]);
|
|
1490
|
+
_react.useEffect.call(void 0, () => {
|
|
1491
|
+
if (!isExpanded) return;
|
|
1492
|
+
const raf = requestAnimationFrame(() => {
|
|
1493
|
+
_chunkYWDC5BXMcjs.scrollElementIntoView.call(void 0, rowRef.current, {
|
|
1494
|
+
headerOffset: STICKY_HEADER_OFFSET_PX
|
|
1495
|
+
});
|
|
1496
|
+
});
|
|
1497
|
+
return () => cancelAnimationFrame(raf);
|
|
1498
|
+
}, [isExpanded]);
|
|
1468
1499
|
const rightBadges = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
|
|
1469
1500
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1470
|
-
|
|
1501
|
+
_chunkYWDC5BXMcjs.StatusBadge,
|
|
1471
1502
|
{
|
|
1472
1503
|
text: rawStatus,
|
|
1473
|
-
colorScheme:
|
|
1504
|
+
colorScheme: _chunkYWDC5BXMcjs.getStatusColorScheme.call(void 0, rawStatus),
|
|
1474
1505
|
variant: "card",
|
|
1475
1506
|
className: "border border-ods-border"
|
|
1476
1507
|
}
|
|
1477
1508
|
),
|
|
1478
1509
|
priority && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1479
|
-
|
|
1510
|
+
_chunkYWDC5BXMcjs.StatusBadge,
|
|
1480
1511
|
{
|
|
1481
1512
|
text: priority,
|
|
1482
1513
|
colorScheme: mapPriorityScheme(priority),
|
|
@@ -1503,7 +1534,7 @@ function HelpCenterCard({
|
|
|
1503
1534
|
"aria-controls": isExpanded ? `help-center-drawer-${ticket.id}` : void 0,
|
|
1504
1535
|
className: "w-full text-left p-[12px] md:p-[16px] focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ods-accent focus-visible:ring-inset disabled:cursor-default",
|
|
1505
1536
|
children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1506
|
-
|
|
1537
|
+
_chunkTYIBMDUZcjs.DevCardRowContent,
|
|
1507
1538
|
{
|
|
1508
1539
|
title,
|
|
1509
1540
|
subtitle,
|
|
@@ -1578,7 +1609,7 @@ function HelpCenterCreateForm({
|
|
|
1578
1609
|
const [subject, setSubject] = _react.useState.call(void 0, "");
|
|
1579
1610
|
const [subjectError, setSubjectError] = _react.useState.call(void 0, null);
|
|
1580
1611
|
const subjectField = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col", children: [
|
|
1581
|
-
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0,
|
|
1612
|
+
/* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkYWDC5BXMcjs.Label, { htmlFor: "help-center-subject", children: [
|
|
1582
1613
|
"Subject",
|
|
1583
1614
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-ods-accent", children: "*" })
|
|
1584
1615
|
] }),
|
|
@@ -1610,7 +1641,7 @@ function HelpCenterCreateForm({
|
|
|
1610
1641
|
)
|
|
1611
1642
|
] });
|
|
1612
1643
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1613
|
-
|
|
1644
|
+
_chunk6DCKL73Fcjs.ContactForm,
|
|
1614
1645
|
{
|
|
1615
1646
|
title: "Open a new ticket",
|
|
1616
1647
|
footerText: "The support team typically responds within one business day.",
|
|
@@ -1648,20 +1679,21 @@ function HelpCenterCreateForm({
|
|
|
1648
1679
|
// src/components/tickets/help-center-list.tsx
|
|
1649
1680
|
|
|
1650
1681
|
function HelpCenterList({ toast: toast2 = _chunk5V6MSE3Bcjs.toast } = {}) {
|
|
1651
|
-
const identity =
|
|
1682
|
+
const identity = _chunkYWDC5BXMcjs.useChatIdentity.call(void 0, );
|
|
1652
1683
|
const searchParams = _chunkG7UE6RKVcjs.useSearchParams.call(void 0, );
|
|
1653
1684
|
const router = _chunkG7UE6RKVcjs.useRouter.call(void 0, );
|
|
1654
1685
|
const pathname = _chunkG7UE6RKVcjs.usePathname.call(void 0, );
|
|
1655
1686
|
const search = searchParams.get("search") || "";
|
|
1656
1687
|
const status = searchParams.get("status") || "all";
|
|
1688
|
+
const ticketParam = searchParams.get("ticket") || "";
|
|
1657
1689
|
const rawPage = Number(searchParams.get("page"));
|
|
1658
1690
|
const page = Number.isFinite(rawPage) && rawPage > 0 ? Math.floor(rawPage) : 1;
|
|
1659
1691
|
if (identity.isLoading) {
|
|
1660
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1692
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevSectionPage, { sectionKey: "tickets", preControls: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, HelpCenterCreateFormSkeleton, {}), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevCardRowSkeletonList, {}) });
|
|
1661
1693
|
}
|
|
1662
|
-
if (identity.authTier === "anon" || !_optionalChain([identity, 'access',
|
|
1663
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1664
|
-
|
|
1694
|
+
if (identity.authTier === "anon" || !_optionalChain([identity, 'access', _60 => _60.user, 'optionalAccess', _61 => _61.email])) {
|
|
1695
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevSectionPage, { sectionKey: "tickets", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1696
|
+
_chunkG2HHSZ3Scjs.EmptyState,
|
|
1665
1697
|
{
|
|
1666
1698
|
type: "generic",
|
|
1667
1699
|
title: "Sign in to manage tickets",
|
|
@@ -1670,7 +1702,7 @@ function HelpCenterList({ toast: toast2 = _chunk5V6MSE3Bcjs.toast } = {}) {
|
|
|
1670
1702
|
}
|
|
1671
1703
|
) });
|
|
1672
1704
|
}
|
|
1673
|
-
const sessionName = [_optionalChain([identity, 'access',
|
|
1705
|
+
const sessionName = [_optionalChain([identity, 'access', _62 => _62.user, 'optionalAccess', _63 => _63.firstName]), _optionalChain([identity, 'access', _64 => _64.user, 'optionalAccess', _65 => _65.lastName])].filter(Boolean).join(" ").trim() || _optionalChain([identity, 'access', _66 => _66.user, 'optionalAccess', _67 => _67.email, 'optionalAccess', _68 => _68.split, 'call', _69 => _69("@"), 'access', _70 => _70[0]]) || "Customer";
|
|
1674
1706
|
const sessionEmail = identity.user.email;
|
|
1675
1707
|
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1676
1708
|
HelpCenterListAuthed,
|
|
@@ -1678,6 +1710,7 @@ function HelpCenterList({ toast: toast2 = _chunk5V6MSE3Bcjs.toast } = {}) {
|
|
|
1678
1710
|
search,
|
|
1679
1711
|
status,
|
|
1680
1712
|
page,
|
|
1713
|
+
ticketParam,
|
|
1681
1714
|
searchParams,
|
|
1682
1715
|
router,
|
|
1683
1716
|
pathname,
|
|
@@ -1691,6 +1724,7 @@ function HelpCenterListAuthed({
|
|
|
1691
1724
|
search,
|
|
1692
1725
|
status,
|
|
1693
1726
|
page,
|
|
1727
|
+
ticketParam,
|
|
1694
1728
|
searchParams,
|
|
1695
1729
|
router,
|
|
1696
1730
|
pathname,
|
|
@@ -1699,6 +1733,18 @@ function HelpCenterListAuthed({
|
|
|
1699
1733
|
sessionEmail
|
|
1700
1734
|
}) {
|
|
1701
1735
|
const queryClient = _reactquery.useQueryClient.call(void 0, );
|
|
1736
|
+
const [optimisticTickets, setOptimisticTickets] = _react.useState.call(void 0, []);
|
|
1737
|
+
const [supportSystemDown, setSupportSystemDown] = _react.useState.call(void 0, false);
|
|
1738
|
+
const setOpenTicket = _react.useCallback.call(void 0,
|
|
1739
|
+
(externalId) => {
|
|
1740
|
+
const params = new URLSearchParams(searchParams.toString());
|
|
1741
|
+
if (externalId) params.set("ticket", externalId);
|
|
1742
|
+
else params.delete("ticket");
|
|
1743
|
+
const qs = params.toString();
|
|
1744
|
+
router.replace(qs ? `${pathname}?${qs}` : pathname, { scroll: false });
|
|
1745
|
+
},
|
|
1746
|
+
[searchParams, router, pathname]
|
|
1747
|
+
);
|
|
1702
1748
|
const { tickets, isLoading, isFetching, error, refetch, totalPages } = useTicketsList({
|
|
1703
1749
|
// `sessionEmail` is drilled in from the parent — see the same
|
|
1704
1750
|
// pattern + race-cause rationale documented in
|
|
@@ -1709,17 +1755,19 @@ function HelpCenterListAuthed({
|
|
|
1709
1755
|
customerEmail: sessionEmail,
|
|
1710
1756
|
search,
|
|
1711
1757
|
status,
|
|
1712
|
-
page
|
|
1758
|
+
page,
|
|
1759
|
+
// Live status: while a drawer is open, poll so an out-of-band HubSpot
|
|
1760
|
+
// status change (e.g. agent closes the ticket) flips the badge +
|
|
1761
|
+
// open/reopen affordance within one interval. Idle (no drawer) → no poll.
|
|
1762
|
+
// `ticketParam` (the open ticket's external_id) is the open signal.
|
|
1763
|
+
refetchInterval: ticketParam ? TICKET_LIVE_POLL_MS : false
|
|
1713
1764
|
});
|
|
1714
|
-
const [
|
|
1715
|
-
const [expandedTicketId, setExpandedTicketId] = _react.useState.call(void 0, null);
|
|
1716
|
-
const [supportSystemDown, setSupportSystemDown] = _react.useState.call(void 0, false);
|
|
1765
|
+
const expandedTicketId = ticketParam && _optionalChain([tickets, 'access', _71 => _71.find, 'call', _72 => _72((t) => t.external_id === ticketParam), 'optionalAccess', _73 => _73.id]) || null;
|
|
1717
1766
|
const prependOptimistic = _react.useCallback.call(void 0, (placeholder) => {
|
|
1718
1767
|
setOptimisticTickets((prev) => [placeholder, ...prev]);
|
|
1719
1768
|
}, []);
|
|
1720
1769
|
const removeOptimistic = _react.useCallback.call(void 0, (placeholderId) => {
|
|
1721
1770
|
setOptimisticTickets((prev) => prev.filter((t) => t.id !== placeholderId));
|
|
1722
|
-
setExpandedTicketId((prev) => prev === placeholderId ? null : prev);
|
|
1723
1771
|
}, []);
|
|
1724
1772
|
const removeTicketFromCache = _react.useCallback.call(void 0,
|
|
1725
1773
|
(ticketId) => {
|
|
@@ -1732,7 +1780,6 @@ function HelpCenterListAuthed({
|
|
|
1732
1780
|
return { ...prev, tickets: nextTickets };
|
|
1733
1781
|
}
|
|
1734
1782
|
);
|
|
1735
|
-
setExpandedTicketId((prev) => prev === ticketId ? null : prev);
|
|
1736
1783
|
},
|
|
1737
1784
|
[queryClient]
|
|
1738
1785
|
);
|
|
@@ -1743,9 +1790,14 @@ function HelpCenterListAuthed({
|
|
|
1743
1790
|
toast: toast2,
|
|
1744
1791
|
onSupportSystemDown: () => setSupportSystemDown(true)
|
|
1745
1792
|
});
|
|
1746
|
-
const toggleRow = _react.useCallback.call(void 0,
|
|
1747
|
-
|
|
1748
|
-
|
|
1793
|
+
const toggleRow = _react.useCallback.call(void 0,
|
|
1794
|
+
(id) => {
|
|
1795
|
+
const t = tickets.find((x) => x.id === id);
|
|
1796
|
+
if (!_optionalChain([t, 'optionalAccess', _74 => _74.external_id])) return;
|
|
1797
|
+
setOpenTicket(t.external_id === ticketParam ? null : t.external_id);
|
|
1798
|
+
},
|
|
1799
|
+
[tickets, ticketParam, setOpenTicket]
|
|
1800
|
+
);
|
|
1749
1801
|
const merged = [...optimisticTickets, ...tickets];
|
|
1750
1802
|
const hasActiveFilters = search !== "" || status !== "" && status !== "all";
|
|
1751
1803
|
const hasResults = merged.length > 0;
|
|
@@ -1766,15 +1818,15 @@ function HelpCenterListAuthed({
|
|
|
1766
1818
|
] }),
|
|
1767
1819
|
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Button, { type: "button", variant: "accent", onClick: () => refetch(), children: "Retry" })
|
|
1768
1820
|
] }),
|
|
1769
|
-
!error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full", children: isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1821
|
+
!error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full", children: isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevCardRowSkeletonList, {}) : !hasResults && isFetching ? (
|
|
1770
1822
|
// Bridge state — background refetch in flight and the
|
|
1771
1823
|
// optimistic placeholder was just removed by the mutation
|
|
1772
1824
|
// callback. Without this branch "No tickets yet" would flash
|
|
1773
1825
|
// for ~50ms between `removeOptimistic` and the server
|
|
1774
1826
|
// response landing.
|
|
1775
|
-
/* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1827
|
+
/* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevCardRowSkeletonList, { rows: 1 })
|
|
1776
1828
|
) : !hasResults ? hasActiveFilters ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1777
|
-
|
|
1829
|
+
_chunkG2HHSZ3Scjs.EmptyState,
|
|
1778
1830
|
{
|
|
1779
1831
|
type: "search",
|
|
1780
1832
|
title: "No tickets found",
|
|
@@ -1789,7 +1841,7 @@ function HelpCenterListAuthed({
|
|
|
1789
1841
|
}
|
|
1790
1842
|
}
|
|
1791
1843
|
) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1792
|
-
|
|
1844
|
+
_chunkG2HHSZ3Scjs.EmptyState,
|
|
1793
1845
|
{
|
|
1794
1846
|
type: "generic",
|
|
1795
1847
|
title: "No tickets yet",
|
|
@@ -1816,16 +1868,16 @@ function HelpCenterListAuthed({
|
|
|
1816
1868
|
onSendMessage: actions.sendMessage,
|
|
1817
1869
|
onClose: actions.closeTicket,
|
|
1818
1870
|
onReopen: actions.reopenTicket,
|
|
1819
|
-
onActionCollapsed: () =>
|
|
1871
|
+
onActionCollapsed: () => setOpenTicket(null),
|
|
1820
1872
|
replyError: actions.replyErrorFor(ticket.external_id),
|
|
1821
1873
|
onClearReplyError: () => actions.clearReplyError(ticket.external_id)
|
|
1822
1874
|
},
|
|
1823
1875
|
ticket.id
|
|
1824
1876
|
)) })
|
|
1825
1877
|
) }),
|
|
1826
|
-
!error && totalPages > 1 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1878
|
+
!error && totalPages > 1 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkG2HHSZ3Scjs.UnifiedPagination, { currentPage: page, totalPages })
|
|
1827
1879
|
] });
|
|
1828
|
-
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
|
|
1880
|
+
return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevSectionPage, { sectionKey: "tickets", preControls: form, children: body });
|
|
1829
1881
|
}
|
|
1830
1882
|
|
|
1831
1883
|
|
|
@@ -1843,5 +1895,6 @@ function HelpCenterListAuthed({
|
|
|
1843
1895
|
|
|
1844
1896
|
|
|
1845
1897
|
|
|
1846
|
-
|
|
1898
|
+
|
|
1899
|
+
exports.HelpCenterCard = HelpCenterCard; exports.HelpCenterCreateForm = HelpCenterCreateForm; exports.HelpCenterCreateFormSkeleton = HelpCenterCreateFormSkeleton; exports.HelpCenterList = HelpCenterList; exports.TICKET_TOAST_COPY = TOAST_COPY; exports.TicketCenter = TicketCenter; exports.TicketDetailDrawer = TicketDetailDrawer; exports.TicketLinkedDeliveryCard = TicketLinkedDeliveryCard; exports.TicketOpenForm = TicketOpenForm; exports.TicketReplyComposer = TicketReplyComposer; exports.TicketRow = TicketRow; exports.isOptimistic = isOptimistic; exports.mapTicketActionError = mapTicketActionError; exports.useTicketActions = useTicketActions; exports.useTicketEngagements = useTicketEngagements; exports.useTicketsList = useTicketsList;
|
|
1847
1900
|
//# sourceMappingURL=index.cjs.map
|