@flamingo-stack/openframe-frontend-core 0.0.216 → 0.0.217

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.
Files changed (103) hide show
  1. package/dist/{chunk-SMCG2CCC.cjs → chunk-6DCKL73F.cjs} +24 -24
  2. package/dist/{chunk-SMCG2CCC.cjs.map → chunk-6DCKL73F.cjs.map} +1 -1
  3. package/dist/{chunk-QTKU6ULP.js → chunk-BVFRD34B.js} +2 -2
  4. package/dist/{chunk-CDLYRFDE.js → chunk-ENBGG2K2.js} +3767 -3610
  5. package/dist/chunk-ENBGG2K2.js.map +1 -0
  6. package/dist/{chunk-K4DFAVSO.cjs → chunk-G2HHSZ3S.cjs} +9 -9
  7. package/dist/{chunk-K4DFAVSO.cjs.map → chunk-G2HHSZ3S.cjs.map} +1 -1
  8. package/dist/{chunk-2V4SACHE.js → chunk-L6IBKPVM.js} +2 -2
  9. package/dist/{chunk-572WQWIX.cjs → chunk-MVQ3OODK.cjs} +9 -9
  10. package/dist/{chunk-572WQWIX.cjs.map → chunk-MVQ3OODK.cjs.map} +1 -1
  11. package/dist/{chunk-GVNQAGXB.js → chunk-N5IKPYRL.js} +3 -81
  12. package/dist/chunk-N5IKPYRL.js.map +1 -0
  13. package/dist/{chunk-VC3ND5RB.js → chunk-SWZUZYWR.js} +2 -2
  14. package/dist/{chunk-IH76P5R6.cjs → chunk-TYIBMDUZ.cjs} +8 -86
  15. package/dist/chunk-TYIBMDUZ.cjs.map +1 -0
  16. package/dist/{chunk-ZGTDUPTW.cjs → chunk-YWDC5BXM.cjs} +382 -225
  17. package/dist/chunk-YWDC5BXM.cjs.map +1 -0
  18. package/dist/components/chat/chat-attachment-bar.d.ts +13 -2
  19. package/dist/components/chat/chat-attachment-bar.d.ts.map +1 -1
  20. package/dist/components/chat/chat-input.d.ts.map +1 -1
  21. package/dist/components/chat/chat-message-row.d.ts +25 -0
  22. package/dist/components/chat/chat-message-row.d.ts.map +1 -0
  23. package/dist/components/chat/index.cjs +6 -2
  24. package/dist/components/chat/index.cjs.map +1 -1
  25. package/dist/components/chat/index.d.ts +1 -0
  26. package/dist/components/chat/index.d.ts.map +1 -1
  27. package/dist/components/chat/index.js +5 -1
  28. package/dist/components/chat/types/component.types.d.ts +8 -1
  29. package/dist/components/chat/types/component.types.d.ts.map +1 -1
  30. package/dist/components/contact/index.cjs +3 -3
  31. package/dist/components/contact/index.js +2 -2
  32. package/dist/components/features/index.cjs +2 -2
  33. package/dist/components/features/index.js +1 -1
  34. package/dist/components/form.d.ts +1 -1
  35. package/dist/components/form.d.ts.map +1 -1
  36. package/dist/components/index.cjs +56 -52
  37. package/dist/components/index.cjs.map +1 -1
  38. package/dist/components/index.js +9 -5
  39. package/dist/components/index.js.map +1 -1
  40. package/dist/components/navigation/index.cjs +2 -2
  41. package/dist/components/navigation/index.js +1 -1
  42. package/dist/components/onboarding-guides/index.cjs +18 -18
  43. package/dist/components/onboarding-guides/index.js +3 -3
  44. package/dist/components/shared/dev-section/dev-card-row.d.ts +5 -45
  45. package/dist/components/shared/dev-section/dev-card-row.d.ts.map +1 -1
  46. package/dist/components/shared/legal-document/use-legal-docs.d.ts.map +1 -1
  47. package/dist/components/tickets/help-center-card.d.ts.map +1 -1
  48. package/dist/components/tickets/help-center-list.d.ts.map +1 -1
  49. package/dist/components/tickets/hooks/use-ticket-engagements.d.ts +9 -1
  50. package/dist/components/tickets/hooks/use-ticket-engagements.d.ts.map +1 -1
  51. package/dist/components/tickets/hooks/use-tickets-list.d.ts +7 -0
  52. package/dist/components/tickets/hooks/use-tickets-list.d.ts.map +1 -1
  53. package/dist/components/tickets/index.cjs +294 -255
  54. package/dist/components/tickets/index.cjs.map +1 -1
  55. package/dist/components/tickets/index.d.ts +1 -0
  56. package/dist/components/tickets/index.d.ts.map +1 -1
  57. package/dist/components/tickets/index.js +360 -321
  58. package/dist/components/tickets/index.js.map +1 -1
  59. package/dist/components/tickets/ticket-detail-drawer.d.ts.map +1 -1
  60. package/dist/components/tickets/ticket-reply-composer.d.ts +33 -0
  61. package/dist/components/tickets/ticket-reply-composer.d.ts.map +1 -0
  62. package/dist/components/tickets/types.d.ts +13 -0
  63. package/dist/components/tickets/types.d.ts.map +1 -1
  64. package/dist/components/ui/index.cjs +6 -2
  65. package/dist/components/ui/index.cjs.map +1 -1
  66. package/dist/components/ui/index.js +5 -1
  67. package/dist/components/ui/ticket-attachments-list.d.ts +5 -1
  68. package/dist/components/ui/ticket-attachments-list.d.ts.map +1 -1
  69. package/dist/index.cjs +6 -2
  70. package/dist/index.cjs.map +1 -1
  71. package/dist/index.js +5 -1
  72. package/dist/utils/index.cjs +59 -4
  73. package/dist/utils/index.cjs.map +1 -1
  74. package/dist/utils/index.js +59 -4
  75. package/dist/utils/index.js.map +1 -1
  76. package/dist/utils/scroll-into-view.d.ts +43 -48
  77. package/dist/utils/scroll-into-view.d.ts.map +1 -1
  78. package/package.json +1 -1
  79. package/src/components/chat/chat-attachment-bar.tsx +58 -22
  80. package/src/components/chat/chat-input.tsx +68 -29
  81. package/src/components/chat/chat-message-row.tsx +124 -0
  82. package/src/components/chat/index.ts +1 -0
  83. package/src/components/chat/types/component.types.ts +8 -1
  84. package/src/components/shared/dev-section/dev-card-row.tsx +5 -183
  85. package/src/components/shared/legal-document/use-legal-docs.ts +5 -1
  86. package/src/components/tickets/help-center-card.tsx +26 -29
  87. package/src/components/tickets/help-center-list.tsx +57 -10
  88. package/src/components/tickets/hooks/use-ticket-engagements.ts +17 -1
  89. package/src/components/tickets/hooks/use-tickets-list.ts +13 -0
  90. package/src/components/tickets/index.ts +4 -0
  91. package/src/components/tickets/ticket-detail-drawer.tsx +144 -200
  92. package/src/components/tickets/ticket-reply-composer.tsx +195 -0
  93. package/src/components/tickets/types.ts +14 -0
  94. package/src/components/ui/ticket-attachments-list.tsx +26 -8
  95. package/src/styles/app-globals.css +13 -0
  96. package/src/utils/scroll-into-view.ts +127 -53
  97. package/dist/chunk-CDLYRFDE.js.map +0 -1
  98. package/dist/chunk-GVNQAGXB.js.map +0 -1
  99. package/dist/chunk-IH76P5R6.cjs.map +0 -1
  100. package/dist/chunk-ZGTDUPTW.cjs.map +0 -1
  101. /package/dist/{chunk-QTKU6ULP.js.map → chunk-BVFRD34B.js.map} +0 -0
  102. /package/dist/{chunk-2V4SACHE.js.map → chunk-L6IBKPVM.js.map} +0 -0
  103. /package/dist/{chunk-VC3ND5RB.js.map → chunk-SWZUZYWR.js.map} +0 -0
@@ -4,18 +4,19 @@
4
4
 
5
5
 
6
6
 
7
- var _chunkK4DFAVSOcjs = require('../../chunk-K4DFAVSO.cjs');
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 _chunkIH76P5R6cjs = require('../../chunk-IH76P5R6.cjs');
15
- require('../../chunk-OB45JHDY.cjs');
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
- var _chunkZGTDUPTWcjs = require('../../chunk-ZGTDUPTW.cjs');
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 } = _chunkZGTDUPTWcjs.useChatAttachments.call(void 0, );
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, _chunkZGTDUPTWcjs.Card, { className: "p-6", children: /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "form", { onSubmit: handleSubmit, className: "flex flex-col md:flex-row gap-6", children: [
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
- _chunkZGTDUPTWcjs.Textarea,
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
- _chunkZGTDUPTWcjs.ChatAttachmentChipStrip,
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
- _chunkZGTDUPTWcjs.ChatAttachmentAddButton,
200
+ _chunkYWDC5BXMcjs.ChatAttachmentAddButton,
198
201
  {
199
202
  attachmentsEnabled: !supportSystemDown,
200
203
  attachmentsCount: attachments.length,
@@ -226,15 +229,15 @@ 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 = _chunkZGTDUPTWcjs.useChatIdentity.call(void 0, );
236
- const identityKey = _nullishCoalesce(_optionalChain([identity, 'access', _ => _.user, 'optionalAccess', _2 => _2.email]), () => ( "anon"));
237
- const queryEnabled = enabled && identity.authTier !== "anon" && !!_optionalChain([identity, 'access', _3 => _3.user, 'optionalAccess', _4 => _4.email]) && !!externalTicketId && !externalTicketId.startsWith("temp-");
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 queryEnabled = enabled && identity.authTier !== "anon" && !!_optionalChain([identity, 'access', _4 => _4.user, 'optionalAccess', _5 => _5.email]) && !!externalTicketId && !externalTicketId.startsWith("temp-");
238
241
  const query = _reactquery.useQuery.call(void 0, {
239
242
  queryKey: ["ticket-engagements", externalTicketId, identityKey],
240
243
  enabled: queryEnabled,
@@ -245,8 +248,13 @@ function useTicketEngagements(externalTicketId, enabled = true) {
245
248
  gcTime: 0,
246
249
  refetchOnMount: "always",
247
250
  refetchOnWindowFocus: true,
251
+ // Live conversation: poll while the caller opts in (drawer open). New
252
+ // agent replies + attachments appear within one interval without a
253
+ // manual refresh. `refetchIntervalInBackground` stays false (default)
254
+ // so polling pauses on a hidden tab.
255
+ refetchInterval,
248
256
  queryFn: async () => {
249
- const response = await _chunkZGTDUPTWcjs.embedAuthedFetch.call(void 0, LIST_ENGAGEMENTS_ENDPOINT, {
257
+ const response = await _chunkYWDC5BXMcjs.embedAuthedFetch.call(void 0, LIST_ENGAGEMENTS_ENDPOINT, {
250
258
  method: "POST",
251
259
  body: JSON.stringify({ ticket_id: externalTicketId })
252
260
  });
@@ -294,7 +302,7 @@ function TicketLinkedDeliveryCard({
294
302
  {
295
303
  className: `rounded-md border border-ods-border bg-ods-bg overflow-hidden ${_nullishCoalesce(className, () => ( ""))}`,
296
304
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
297
- _chunkK4DFAVSOcjs.DeliveryRow,
305
+ _chunkG2HHSZ3Scjs.DeliveryRow,
298
306
  {
299
307
  item,
300
308
  href: clickup.delivery_href,
@@ -305,6 +313,132 @@ function TicketLinkedDeliveryCard({
305
313
  );
306
314
  }
307
315
 
316
+ // src/components/tickets/ticket-reply-composer.tsx
317
+ _chunkXDPSSE4Ocjs.init_button2.call(void 0, );
318
+
319
+
320
+ function TicketReplyComposer({
321
+ ticket,
322
+ busy,
323
+ supportSystemDown,
324
+ onSendMessage,
325
+ onClose
326
+ }) {
327
+ const [resolution, setResolution] = _react.useState.call(void 0, "");
328
+ const [closeDialogOpen, setCloseDialogOpen] = _react.useState.call(void 0, false);
329
+ const attachments = _chunkYWDC5BXMcjs.useChatAttachments.call(void 0, );
330
+ const ticketRef = { id: ticket.id, external_id: ticket.external_id };
331
+ const hasReadyFiles = attachments.readyAttachments.length > 0;
332
+ const handleSend = _react.useCallback.call(void 0,
333
+ async (text) => {
334
+ const ref = { id: ticket.id, external_id: ticket.external_id };
335
+ const ok = await onSendMessage(ref, text.trim(), attachments.readyAttachments);
336
+ if (ok) attachments.clear();
337
+ return ok;
338
+ },
339
+ // Depend on the reactive projections, not the whole bag (a fresh object each
340
+ // render). `readyAttachments` is memo-stable; `clear` is callback-stable.
341
+ [
342
+ onSendMessage,
343
+ ticket.id,
344
+ ticket.external_id,
345
+ attachments.readyAttachments,
346
+ attachments.clear
347
+ ]
348
+ );
349
+ const confirmClose = async () => {
350
+ setCloseDialogOpen(false);
351
+ await onClose(ticketRef, resolution.trim() || void 0);
352
+ setResolution("");
353
+ };
354
+ const disabled = busy || supportSystemDown;
355
+ return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-2", children: [
356
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
357
+ _chunkYWDC5BXMcjs.ChatAttachmentChipStrip,
358
+ {
359
+ attachments: attachments.attachments,
360
+ onRemove: attachments.removeAttachment,
361
+ disabled,
362
+ size: "compact"
363
+ }
364
+ ),
365
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
366
+ _chunkYWDC5BXMcjs.ChatInput,
367
+ {
368
+ fullWidth: true,
369
+ autoFocus: true,
370
+ placeholder: "Type a reply\u2026",
371
+ sending: busy || attachments.hasInflightUploads,
372
+ disabled: supportSystemDown,
373
+ allowEmptySend: hasReadyFiles,
374
+ maxLength: TICKET_TEXT_MAX_CHARS,
375
+ onSend: handleSend
376
+ }
377
+ ),
378
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2 w-full", children: [
379
+ !supportSystemDown && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
380
+ _chunkYWDC5BXMcjs.ChatAttachmentAddButton,
381
+ {
382
+ attachmentsEnabled: true,
383
+ attachmentsCount: attachments.attachments.length,
384
+ onAddFiles: attachments.addFiles,
385
+ disabled,
386
+ size: "compact"
387
+ }
388
+ ),
389
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "flex-1 min-w-0" }),
390
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
391
+ _chunkXDPSSE4Ocjs.Button,
392
+ {
393
+ type: "button",
394
+ variant: "transparent",
395
+ size: "small",
396
+ onClick: () => setCloseDialogOpen(true),
397
+ disabled,
398
+ className: "text-ods-text-secondary hover:text-ods-text-primary",
399
+ children: "Close ticket"
400
+ }
401
+ )
402
+ ] }),
403
+ /* @__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: [
404
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkYWDC5BXMcjs.AlertDialogHeader, { children: [
405
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.AlertDialogTitle, { className: "text-ods-text-primary", children: "Close this ticket?" }),
406
+ /* @__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." })
407
+ ] }),
408
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
409
+ _chunkYWDC5BXMcjs.Textarea,
410
+ {
411
+ value: resolution,
412
+ onChange: (e) => setResolution(e.target.value),
413
+ placeholder: "Resolution (optional)",
414
+ rows: 3,
415
+ maxLength: TICKET_TEXT_MAX_CHARS,
416
+ className: "mt-2"
417
+ }
418
+ ),
419
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkYWDC5BXMcjs.AlertDialogFooter, { children: [
420
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
421
+ _chunkYWDC5BXMcjs.AlertDialogCancel,
422
+ {
423
+ disabled: busy,
424
+ className: "bg-transparent border-ods-border text-ods-text-primary hover:bg-ods-border",
425
+ children: "Cancel"
426
+ }
427
+ ),
428
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
429
+ _chunkYWDC5BXMcjs.AlertDialogAction,
430
+ {
431
+ onClick: () => void confirmClose(),
432
+ disabled: busy,
433
+ className: "bg-ods-accent text-ods-text-on-accent hover:bg-ods-accent-hover",
434
+ children: "Close ticket"
435
+ }
436
+ )
437
+ ] })
438
+ ] }) })
439
+ ] });
440
+ }
441
+
308
442
  // src/components/tickets/ticket-detail-drawer.tsx
309
443
 
310
444
  function TicketDetailDrawer({
@@ -344,37 +478,49 @@ function TicketDetailDrawer({
344
478
  onActionCollapsed
345
479
  }
346
480
  ) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
347
- OpenActions,
481
+ TicketReplyComposer,
348
482
  {
349
483
  ticket,
350
484
  busy,
351
485
  supportSystemDown,
352
486
  onSendMessage,
353
- onClose,
354
- onActionCollapsed
487
+ onClose
355
488
  }
356
489
  )
357
490
  ] })
358
491
  ] });
359
492
  }
360
493
  var TURN_SEPARATOR_RE = /[\s]{1,16}---[\s]{1,16}/g;
494
+ var TICKET_FEED_FRAME = "bg-ods-card border border-ods-border rounded-[6px] overflow-y-auto w-full";
495
+ var TICKET_FEED_HEIGHT = "h-[60vh] md:h-[420px]";
496
+ var TICKET_FEED_INNER = "flex flex-col gap-4 md:gap-6 px-4 md:px-6 py-4 md:py-6";
497
+ var TICKET_FEED_SKELETON_ROWS = 6;
361
498
  function TicketTimelinePanel({ ticket }) {
362
- const identity = _chunkZGTDUPTWcjs.useChatIdentity.call(void 0, );
499
+ const identity = _chunkYWDC5BXMcjs.useChatIdentity.call(void 0, );
363
500
  const externalId = isOptimistic(ticket) ? null : ticket.external_id;
364
- const { engagements, isLoading } = useTicketEngagements(externalId, !!externalId);
501
+ const { engagements, isLoading } = useTicketEngagements(
502
+ externalId,
503
+ !!externalId,
504
+ TICKET_LIVE_POLL_MS
505
+ );
506
+ const { scrollRef, contentRef } = _usesticktobottom.useStickToBottom.call(void 0, { initial: "instant", resize: "smooth" });
365
507
  const bodyTurns = ticket.body ? ticket.body.split(TURN_SEPARATOR_RE).map((t) => t.trim()).filter(Boolean) : [];
366
- const sessionEmailLower = _nullishCoalesce(_optionalChain([identity, 'access', _5 => _5.user, 'optionalAccess', _6 => _6.email, 'optionalAccess', _7 => _7.trim, 'call', _8 => _8(), 'access', _9 => _9.toLowerCase, 'call', _10 => _10()]), () => ( null));
508
+ const customerEngagementBodies = new Set(
509
+ engagements.filter((e) => e.authorRole === "customer").map((e) => (_nullishCoalesce(e.body, () => ( ""))).trim()).filter(Boolean)
510
+ );
511
+ const suppressBodyTurnZero = bodyTurns.length > 0 && customerEngagementBodies.has(bodyTurns[0]);
512
+ 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
513
  const isViewerTheCustomer = !!sessionEmailLower && ticket.customer_emails.some((e) => e.trim().toLowerCase() === sessionEmailLower);
368
- const viewerName = _optionalChain([identity, 'access', _11 => _11.user, 'optionalAccess', _12 => _12.name, 'optionalAccess', _13 => _13.trim, 'call', _14 => _14()]) || null;
369
- const ticketCustomerName = _optionalChain([ticket, 'access', _15 => _15.customer_name, 'optionalAccess', _16 => _16.trim, 'call', _17 => _17()]) || null;
370
- const customerName = (isViewerTheCustomer ? viewerName : null) || ticketCustomerName || viewerName || _optionalChain([identity, 'access', _18 => _18.user, 'optionalAccess', _19 => _19.email]) || "You";
371
- const customerAvatar = isViewerTheCustomer ? _nullishCoalesce(_optionalChain([identity, 'access', _20 => _20.user, 'optionalAccess', _21 => _21.avatarUrl]), () => ( void 0)) : void 0;
514
+ const viewerName = _optionalChain([identity, 'access', _12 => _12.user, 'optionalAccess', _13 => _13.name, 'optionalAccess', _14 => _14.trim, 'call', _15 => _15()]) || null;
515
+ const ticketCustomerName = _optionalChain([ticket, 'access', _16 => _16.customer_name, 'optionalAccess', _17 => _17.trim, 'call', _18 => _18()]) || null;
516
+ const customerName = (isViewerTheCustomer ? viewerName : null) || ticketCustomerName || viewerName || _optionalChain([identity, 'access', _19 => _19.user, 'optionalAccess', _20 => _20.email]) || "You";
517
+ const customerAvatar = isViewerTheCustomer ? _nullishCoalesce(_optionalChain([identity, 'access', _21 => _21.user, 'optionalAccess', _22 => _22.avatarUrl]), () => ( void 0)) : void 0;
518
+ if (isLoading) {
519
+ 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)) }) });
520
+ }
372
521
  if (bodyTurns.length === 0 && engagements.length === 0) {
373
- if (isLoading) {
374
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIH76P5R6cjs.ConversationCardRowSkeletonList, { rows: 2 });
375
- }
376
522
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
377
- _chunkK4DFAVSOcjs.EmptyState,
523
+ _chunkG2HHSZ3Scjs.EmptyState,
378
524
  {
379
525
  type: "generic",
380
526
  title: "No conversation yet",
@@ -383,31 +529,29 @@ function TicketTimelinePanel({ ticket }) {
383
529
  }
384
530
  );
385
531
  }
386
- return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "bg-ods-card border border-ods-border rounded-[6px] overflow-hidden w-full", children: [
532
+ 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
533
  bodyTurns.map((turn, i) => {
534
+ if (i === 0 && suppressBodyTurnZero) return null;
388
535
  const isResolution = turn.startsWith("[Resolution]");
389
- const role = i === 0 ? "Original message" : isResolution ? "Resolution" : `Update ${i}`;
390
536
  const text = isResolution ? turn.replace(/^\[Resolution\]\s*/, "") : turn;
391
537
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
392
- _chunkIH76P5R6cjs.ConversationCardRow,
538
+ _chunkYWDC5BXMcjs.ChatMessageRow,
393
539
  {
394
- author: customerName,
395
- role,
396
- avatarSrc: customerAvatar,
397
- body: text,
398
- variant: "current-user"
540
+ displayName: customerName,
541
+ avatarUrl: customerAvatar,
542
+ body: text
399
543
  },
400
544
  `body-${i}-${turn.slice(0, 24)}`
401
545
  );
402
546
  }),
403
547
  engagements.map((eng) => {
404
548
  const isCustomer = eng.authorRole === "customer";
405
- const isOwnReply = isCustomer && !!eng.authorId && !!_optionalChain([identity, 'access', _22 => _22.user, 'optionalAccess', _23 => _23.email]) && eng.authorId.toLowerCase() === identity.user.email.toLowerCase();
549
+ const isOwnReply = isCustomer && !!eng.authorId && !!_optionalChain([identity, 'access', _23 => _23.user, 'optionalAccess', _24 => _24.email]) && eng.authorId.toLowerCase() === identity.user.email.toLowerCase();
406
550
  let author;
407
551
  let avatarSrc;
408
552
  if (isCustomer && isOwnReply) {
409
- author = _optionalChain([identity, 'access', _24 => _24.user, 'optionalAccess', _25 => _25.name, 'optionalAccess', _26 => _26.trim, 'call', _27 => _27()]) || customerName;
410
- avatarSrc = _nullishCoalesce(_optionalChain([identity, 'access', _28 => _28.user, 'optionalAccess', _29 => _29.avatarUrl]), () => ( void 0));
553
+ author = _optionalChain([identity, 'access', _25 => _25.user, 'optionalAccess', _26 => _26.name, 'optionalAccess', _27 => _27.trim, 'call', _28 => _28()]) || customerName;
554
+ avatarSrc = _nullishCoalesce(_optionalChain([identity, 'access', _29 => _29.user, 'optionalAccess', _30 => _30.avatarUrl]), () => ( void 0));
411
555
  } else if (isCustomer) {
412
556
  author = ticketCustomerName || "Customer";
413
557
  avatarSrc = void 0;
@@ -418,32 +562,30 @@ function TicketTimelinePanel({ ticket }) {
418
562
  author = "Support team";
419
563
  avatarSrc = void 0;
420
564
  }
565
+ const engAttachments = mapEngagementAttachments(eng.attachments);
421
566
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
422
- _chunkIH76P5R6cjs.ConversationCardRow,
567
+ _chunkYWDC5BXMcjs.ChatMessageRow,
423
568
  {
424
- author,
425
- role: "Reply",
426
- avatarSrc,
427
- timestamp: eng.createdAt,
569
+ displayName: author,
570
+ avatarUrl: avatarSrc,
571
+ timeLabel: eng.createdAt ? _chunkYWDC5BXMcjs.formatRelativeTime.call(void 0, eng.createdAt) : null,
428
572
  body: stripAttachmentsPreamble(_nullishCoalesce(eng.body, () => ( ""))),
429
- attachments: mapEngagementAttachments(eng.attachments),
430
- variant: isCustomer ? "current-user" : "support"
573
+ 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
574
  },
432
575
  eng.id
433
576
  );
434
- }),
435
- isLoading && // Trailing single-row skeleton when the panel already has
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
- ] });
577
+ })
578
+ ] }) });
441
579
  }
442
580
  function mapEngagementAttachments(files) {
443
581
  return files.map((f) => ({
444
582
  id: f.id,
445
583
  fileName: _nullishCoalesce(f.name, () => ( `file-${f.id}`)),
446
584
  fileSize: f.size ? formatBytes(f.size) : "",
585
+ // Show an inline thumbnail for image attachments (the signed `url` is a
586
+ // viewable URL). Non-images fall back to the file-type icon. SquareAvatar
587
+ // degrades to initials on a broken/expired image URL.
588
+ thumbnailSrc: f.url && (_nullishCoalesce(_optionalChain([f, 'access', _31 => _31.mime, 'optionalAccess', _32 => _32.startsWith, 'call', _33 => _33("image/")]), () => ( false))) ? f.url : void 0,
447
589
  onDownload: f.url ? () => window.open(f.url, "_blank", "noopener,noreferrer") : void 0
448
590
  }));
449
591
  }
@@ -470,6 +612,8 @@ function ReopenAction({
470
612
  _chunkXDPSSE4Ocjs.Button,
471
613
  {
472
614
  type: "button",
615
+ variant: "outline",
616
+ size: "small",
473
617
  onClick: () => void handleReopen(),
474
618
  disabled: busy || supportSystemDown,
475
619
  loading: busy,
@@ -477,133 +621,6 @@ function ReopenAction({
477
621
  }
478
622
  ) });
479
623
  }
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
624
  function ReplyFailureBanner({
608
625
  error,
609
626
  onDismiss
@@ -634,18 +651,18 @@ function ReplyFailureBanner({
634
651
  function AssignedAgentRow({
635
652
  assignedOwner
636
653
  }) {
637
- const trimmedName = _optionalChain([assignedOwner, 'optionalAccess', _30 => _30.name, 'optionalAccess', _31 => _31.trim, 'call', _32 => _32()]) || null;
638
- const emailFallback = _optionalChain([assignedOwner, 'optionalAccess', _33 => _33.email, 'optionalAccess', _34 => _34.trim, 'call', _35 => _35()]) || null;
654
+ const trimmedName = _optionalChain([assignedOwner, 'optionalAccess', _34 => _34.name, 'optionalAccess', _35 => _35.trim, 'call', _36 => _36()]) || null;
655
+ const emailFallback = _optionalChain([assignedOwner, 'optionalAccess', _37 => _37.email, 'optionalAccess', _38 => _38.trim, 'call', _39 => _39()]) || null;
639
656
  const displayLabel = _nullishCoalesce(trimmedName, () => ( (emailFallback ? emailFallback.split("@")[0] : null)));
640
657
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-2 text-xs", children: [
641
658
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-ods-text-secondary uppercase tracking-wider font-medium", children: "Assigned to" }),
642
659
  displayLabel ? /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { className: "flex items-center gap-1.5 text-ods-text-primary font-medium", children: [
643
660
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
644
- _chunkZGTDUPTWcjs.SquareAvatar,
661
+ _chunkYWDC5BXMcjs.SquareAvatar,
645
662
  {
646
663
  size: "sm",
647
664
  variant: "round",
648
- src: _nullishCoalesce(_optionalChain([assignedOwner, 'optionalAccess', _36 => _36.avatarUrl]), () => ( void 0)),
665
+ src: _nullishCoalesce(_optionalChain([assignedOwner, 'optionalAccess', _40 => _40.avatarUrl]), () => ( void 0)),
649
666
  alt: displayLabel,
650
667
  fallback: displayLabel
651
668
  }
@@ -672,7 +689,7 @@ function TicketRow({
672
689
  const rowRef = _react.useRef.call(void 0, null);
673
690
  const handleClick = _react.useCallback.call(void 0, () => {
674
691
  onToggle(ticket.id);
675
- _chunkZGTDUPTWcjs.scrollElementIntoView.call(void 0, rowRef.current, {
692
+ _chunkYWDC5BXMcjs.scrollElementIntoView.call(void 0, rowRef.current, {
676
693
  adjustTargetY: (raw) => {
677
694
  if (!rowRef.current) return raw;
678
695
  const expandedDrawer = document.querySelector(
@@ -698,7 +715,7 @@ function TicketRow({
698
715
  // so the badge accurately reflects "Closed" with a checkmark.
699
716
  statusLabel: _nullishCoalesce(ticket.pipeline_stage_label, () => ( void 0)),
700
717
  category: _nullishCoalesce(ticket.customer_company, () => ( void 0)),
701
- timeAgo: ticket.hubspot_updated_at ? _chunkZGTDUPTWcjs.formatRelativeTime.call(void 0, ticket.hubspot_updated_at) : void 0,
718
+ timeAgo: ticket.hubspot_updated_at ? _chunkYWDC5BXMcjs.formatRelativeTime.call(void 0, ticket.hubspot_updated_at) : void 0,
702
719
  // Linked-work chip: surfaced whenever the ticket has a linked
703
720
  // ClickUp task. Uses the linked task's own status so the chip text
704
721
  // reads "Working" / "Waiting on version release" / etc. — useful
@@ -713,7 +730,7 @@ function TicketRow({
713
730
  className: "border-b border-ods-border last:border-b-0",
714
731
  children: [
715
732
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
716
- _chunkZGTDUPTWcjs.ChatTicketItem,
733
+ _chunkYWDC5BXMcjs.ChatTicketItem,
717
734
  {
718
735
  ticket: tileData,
719
736
  onClick: optimistic ? void 0 : handleClick,
@@ -758,6 +775,7 @@ function useTicketsList(filters) {
758
775
  const pageSize = Math.max(1, Math.min(100, Math.floor(_nullishCoalesce(filters.pageSize, () => ( DEFAULT_PAGE_SIZE))) || DEFAULT_PAGE_SIZE));
759
776
  const enabled = !!customerEmail;
760
777
  const identityKey = customerEmail || "anon";
778
+ const refetchInterval = _nullishCoalesce(filters.refetchInterval, () => ( false));
761
779
  const query = _reactquery.useQuery.call(void 0, {
762
780
  queryKey: ["tickets", "self", identityKey, search, statusFilter, page, pageSize],
763
781
  enabled,
@@ -770,6 +788,10 @@ function useTicketsList(filters) {
770
788
  gcTime: 0,
771
789
  refetchOnMount: "always",
772
790
  refetchOnWindowFocus: true,
791
+ // Live status: poll while the caller opts in (drawer open). Defaults to
792
+ // false. `refetchIntervalInBackground` stays false (the default) so polling
793
+ // pauses on a hidden tab — no wasted requests when the user tabs away.
794
+ refetchInterval,
773
795
  queryFn: async () => {
774
796
  const body = {
775
797
  query: search,
@@ -777,7 +799,7 @@ function useTicketsList(filters) {
777
799
  pageSize
778
800
  };
779
801
  if (statusFilter) body.status = statusFilter;
780
- const response = await _chunkZGTDUPTWcjs.embedAuthedFetch.call(void 0, FIND_TICKET_ENDPOINT, {
802
+ const response = await _chunkYWDC5BXMcjs.embedAuthedFetch.call(void 0, FIND_TICKET_ENDPOINT, {
781
803
  method: "POST",
782
804
  body: JSON.stringify(body)
783
805
  });
@@ -789,12 +811,12 @@ function useTicketsList(filters) {
789
811
  }
790
812
  });
791
813
  const data = query.data;
792
- const totalCount = _nullishCoalesce(_nullishCoalesce(_optionalChain([data, 'optionalAccess', _37 => _37.totalCount]), () => ( _optionalChain([data, 'optionalAccess', _38 => _38.count]))), () => ( (_nullishCoalesce(_optionalChain([data, 'optionalAccess', _39 => _39.tickets, 'optionalAccess', _40 => _40.length]), () => ( 0)))));
793
- const echoedPage = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _41 => _41.page]), () => ( page));
794
- const echoedPageSize = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _42 => _42.pageSize]), () => ( pageSize));
795
- const totalPages = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _43 => _43.totalPages]), () => ( Math.max(1, Math.ceil(totalCount / echoedPageSize))));
814
+ 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)))));
815
+ const echoedPage = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _45 => _45.page]), () => ( page));
816
+ const echoedPageSize = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _46 => _46.pageSize]), () => ( pageSize));
817
+ const totalPages = _nullishCoalesce(_optionalChain([data, 'optionalAccess', _47 => _47.totalPages]), () => ( Math.max(1, Math.ceil(totalCount / echoedPageSize))));
796
818
  return {
797
- tickets: _nullishCoalesce(_optionalChain([data, 'optionalAccess', _44 => _44.tickets]), () => ( [])),
819
+ tickets: _nullishCoalesce(_optionalChain([data, 'optionalAccess', _48 => _48.tickets]), () => ( [])),
798
820
  // Loading-state-truth = `data === undefined`. TanStack v5's
799
821
  // `isPending` / `isLoading` flags can be `false` in transient
800
822
  // windows where the query is enabled-but-fetch-not-yet-fired
@@ -816,7 +838,7 @@ function useTicketsList(filters) {
816
838
  // - Background refetch with existing data: data !== undefined → no load
817
839
  // - Filter-change refetch landing on empty results: data?.tickets===[]
818
840
  // + isFetching → bridge skeleton (the `||` branch)
819
- isLoading: enabled && (data === void 0 || query.isFetching && (_nullishCoalesce(_optionalChain([data, 'optionalAccess', _45 => _45.tickets]), () => ( []))).length === 0),
841
+ isLoading: enabled && (data === void 0 || query.isFetching && (_nullishCoalesce(_optionalChain([data, 'optionalAccess', _49 => _49.tickets]), () => ( []))).length === 0),
820
842
  isFetching: query.isFetching,
821
843
  error: _nullishCoalesce(query.error, () => ( null)),
822
844
  refetch: () => {
@@ -891,7 +913,7 @@ function useTicketActions(options) {
891
913
  }, []);
892
914
  const executeTicketAction = _react.useCallback.call(void 0,
893
915
  async (toolName, args) => {
894
- const res = await _chunkZGTDUPTWcjs.embedAuthedFetch.call(void 0, TICKET_ACTION_ENDPOINT, {
916
+ const res = await _chunkYWDC5BXMcjs.embedAuthedFetch.call(void 0, TICKET_ACTION_ENDPOINT, {
895
917
  method: "POST",
896
918
  body: JSON.stringify({ tool_name: toolName, args })
897
919
  });
@@ -1003,7 +1025,7 @@ function useTicketActions(options) {
1003
1025
  const result = await executeTicketAction("create_ticket", {
1004
1026
  subject: input.subject.trim(),
1005
1027
  content: input.content.trim(),
1006
- ..._optionalChain([input, 'access', _46 => _46.attachments, 'optionalAccess', _47 => _47.length]) ? { attachments: input.attachments } : {}
1028
+ ..._optionalChain([input, 'access', _50 => _50.attachments, 'optionalAccess', _51 => _51.length]) ? { attachments: input.attachments } : {}
1007
1029
  });
1008
1030
  if (result.mirror_synced === false) {
1009
1031
  toast2(TOAST_COPY.open_mirror_pending);
@@ -1115,7 +1137,7 @@ function useTicketActions(options) {
1115
1137
  ticket,
1116
1138
  {
1117
1139
  status: "CLOSED",
1118
- ..._optionalChain([resolution, 'optionalAccess', _48 => _48.trim, 'call', _49 => _49()]) ? { resolution: resolution.trim() } : {}
1140
+ ..._optionalChain([resolution, 'optionalAccess', _52 => _52.trim, 'call', _53 => _53()]) ? { resolution: resolution.trim() } : {}
1119
1141
  },
1120
1142
  TOAST_COPY.close_success,
1121
1143
  "close ticket"
@@ -1188,7 +1210,7 @@ function mapTicketActionError(err) {
1188
1210
  removeRowFromCache: false
1189
1211
  };
1190
1212
  case "RATE_LIMITED": {
1191
- const retryAfterRaw = _optionalChain([err, 'access', _50 => _50.response, 'optionalAccess', _51 => _51.headers, 'access', _52 => _52.get, 'call', _53 => _53("Retry-After")]);
1213
+ const retryAfterRaw = _optionalChain([err, 'access', _54 => _54.response, 'optionalAccess', _55 => _55.headers, 'access', _56 => _56.get, 'call', _57 => _57("Retry-After")]);
1192
1214
  const retryAfterSeconds = retryAfterRaw ? parseInt(retryAfterRaw, 10) : void 0;
1193
1215
  return {
1194
1216
  code: err.code,
@@ -1276,13 +1298,13 @@ function resolveErrorCode(bodyCode, status) {
1276
1298
  // src/components/tickets/ticket-center.tsx
1277
1299
 
1278
1300
  function TicketCenter({ toast: toast2 = _chunk5V6MSE3Bcjs.toast } = {}) {
1279
- const identity = _chunkZGTDUPTWcjs.useChatIdentity.call(void 0, );
1301
+ const identity = _chunkYWDC5BXMcjs.useChatIdentity.call(void 0, );
1280
1302
  if (identity.isLoading) {
1281
1303
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TicketCenterSkeleton, {});
1282
1304
  }
1283
- if (identity.authTier === "anon" || !_optionalChain([identity, 'access', _54 => _54.user, 'optionalAccess', _55 => _55.email])) {
1305
+ if (identity.authTier === "anon" || !_optionalChain([identity, 'access', _58 => _58.user, 'optionalAccess', _59 => _59.email])) {
1284
1306
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1285
- _chunkK4DFAVSOcjs.EmptyState,
1307
+ _chunkG2HHSZ3Scjs.EmptyState,
1286
1308
  {
1287
1309
  type: "generic",
1288
1310
  title: "Sign in to manage tickets",
@@ -1353,7 +1375,7 @@ function TicketCenterAuthed({
1353
1375
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex items-center gap-3 text-xs text-ods-text-secondary", children: [
1354
1376
  lastUpdatedAt && /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "span", { children: [
1355
1377
  "Updated ",
1356
- _chunkZGTDUPTWcjs.formatRelativeTime.call(void 0, new Date(lastUpdatedAt))
1378
+ _chunkYWDC5BXMcjs.formatRelativeTime.call(void 0, new Date(lastUpdatedAt))
1357
1379
  ] }),
1358
1380
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1359
1381
  _chunkXDPSSE4Ocjs.Button,
@@ -1369,15 +1391,15 @@ function TicketCenterAuthed({
1369
1391
  )
1370
1392
  ] })
1371
1393
  ] }),
1372
- isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, TicketListSkeleton, {}) : merged.length === 0 ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZGTDUPTWcjs.Card, { className: "p-6", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1373
- _chunkK4DFAVSOcjs.EmptyState,
1394
+ 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,
1395
+ _chunkG2HHSZ3Scjs.EmptyState,
1374
1396
  {
1375
1397
  type: "generic",
1376
1398
  title: "No tickets yet",
1377
1399
  description: "Open one above to start the conversation.",
1378
1400
  showCTA: false
1379
1401
  }
1380
- ) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZGTDUPTWcjs.Card, { className: "overflow-hidden", children: merged.map((ticket) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1402
+ ) }) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkYWDC5BXMcjs.Card, { className: "overflow-hidden", children: merged.map((ticket) => /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1381
1403
  TicketRow,
1382
1404
  {
1383
1405
  ticket,
@@ -1397,7 +1419,7 @@ function TicketCenterAuthed({
1397
1419
  }
1398
1420
  function TicketCenterSkeleton() {
1399
1421
  return /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col gap-6", children: [
1400
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkZGTDUPTWcjs.Card, { className: "p-6", children: [
1422
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkYWDC5BXMcjs.Card, { className: "p-6", children: [
1401
1423
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Skeleton, { className: "h-7 w-48 mb-4" }),
1402
1424
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Skeleton, { className: "h-10 w-full mb-3" }),
1403
1425
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Skeleton, { className: "h-24 w-full" })
@@ -1406,7 +1428,7 @@ function TicketCenterSkeleton() {
1406
1428
  ] });
1407
1429
  }
1408
1430
  function TicketListSkeleton() {
1409
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkZGTDUPTWcjs.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: [
1431
+ 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
1432
  /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex-1 flex flex-col gap-2", children: [
1411
1433
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Skeleton, { className: "h-4 w-2/3" }),
1412
1434
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Skeleton, { className: "h-3 w-full" })
@@ -1419,7 +1441,7 @@ function TicketListSkeleton() {
1419
1441
  // src/components/tickets/help-center-list.tsx
1420
1442
 
1421
1443
 
1422
- _chunkK4DFAVSOcjs.init_unified_pagination.call(void 0, );
1444
+ _chunkG2HHSZ3Scjs.init_unified_pagination.call(void 0, );
1423
1445
 
1424
1446
  // src/components/tickets/help-center-card.tsx
1425
1447
 
@@ -1441,7 +1463,7 @@ function HelpCenterCard({
1441
1463
  const optimistic = isOptimistic(ticket);
1442
1464
  const rawStatus = (_nullishCoalesce(ticket.status, () => ( "OPEN"))).toUpperCase();
1443
1465
  const priority = (_nullishCoalesce(ticket.priority, () => ( ""))).toUpperCase();
1444
- const relativeUpdated = ticket.hubspot_updated_at ? _chunkZGTDUPTWcjs.formatRelativeTime.call(void 0, ticket.hubspot_updated_at) : "recently";
1466
+ const relativeUpdated = ticket.hubspot_updated_at ? _chunkYWDC5BXMcjs.formatRelativeTime.call(void 0, ticket.hubspot_updated_at) : "recently";
1445
1467
  const title = (ticket.subject || "").trim() || "(untitled)";
1446
1468
  const subtitle = `UPDATED ${relativeUpdated}, #${ticket.external_id || "\u2014"}${ticket.pipeline_stage_label ? `, ${ticket.pipeline_stage_label}` : ""}`;
1447
1469
  const description = _nullishCoalesce(_nullishCoalesce(ticket.preview, () => ( ticket.body)), () => ( ""));
@@ -1450,33 +1472,28 @@ function HelpCenterCard({
1450
1472
  const rowRef = _react.useRef.call(void 0, null);
1451
1473
  const handleClick = _react.useCallback.call(void 0, () => {
1452
1474
  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
1475
  }, [onToggle, ticket.id]);
1476
+ _react.useEffect.call(void 0, () => {
1477
+ if (!isExpanded) return;
1478
+ const raf = requestAnimationFrame(() => {
1479
+ _chunkYWDC5BXMcjs.scrollElementIntoView.call(void 0, rowRef.current, {
1480
+ headerOffset: STICKY_HEADER_OFFSET_PX
1481
+ });
1482
+ });
1483
+ return () => cancelAnimationFrame(raf);
1484
+ }, [isExpanded]);
1468
1485
  const rightBadges = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _jsxruntime.Fragment, { children: [
1469
1486
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1470
- _chunkZGTDUPTWcjs.StatusBadge,
1487
+ _chunkYWDC5BXMcjs.StatusBadge,
1471
1488
  {
1472
1489
  text: rawStatus,
1473
- colorScheme: _chunkZGTDUPTWcjs.getStatusColorScheme.call(void 0, rawStatus),
1490
+ colorScheme: _chunkYWDC5BXMcjs.getStatusColorScheme.call(void 0, rawStatus),
1474
1491
  variant: "card",
1475
1492
  className: "border border-ods-border"
1476
1493
  }
1477
1494
  ),
1478
1495
  priority && /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1479
- _chunkZGTDUPTWcjs.StatusBadge,
1496
+ _chunkYWDC5BXMcjs.StatusBadge,
1480
1497
  {
1481
1498
  text: priority,
1482
1499
  colorScheme: mapPriorityScheme(priority),
@@ -1503,7 +1520,7 @@ function HelpCenterCard({
1503
1520
  "aria-controls": isExpanded ? `help-center-drawer-${ticket.id}` : void 0,
1504
1521
  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
1522
  children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1506
- _chunkIH76P5R6cjs.DevCardRowContent,
1523
+ _chunkTYIBMDUZcjs.DevCardRowContent,
1507
1524
  {
1508
1525
  title,
1509
1526
  subtitle,
@@ -1578,7 +1595,7 @@ function HelpCenterCreateForm({
1578
1595
  const [subject, setSubject] = _react.useState.call(void 0, "");
1579
1596
  const [subjectError, setSubjectError] = _react.useState.call(void 0, null);
1580
1597
  const subjectField = /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, "div", { className: "flex flex-col", children: [
1581
- /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkZGTDUPTWcjs.Label, { htmlFor: "help-center-subject", children: [
1598
+ /* @__PURE__ */ _jsxruntime.jsxs.call(void 0, _chunkYWDC5BXMcjs.Label, { htmlFor: "help-center-subject", children: [
1582
1599
  "Subject",
1583
1600
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "span", { className: "text-ods-accent", children: "*" })
1584
1601
  ] }),
@@ -1610,7 +1627,7 @@ function HelpCenterCreateForm({
1610
1627
  )
1611
1628
  ] });
1612
1629
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1613
- _chunkSMCG2CCCcjs.ContactForm,
1630
+ _chunk6DCKL73Fcjs.ContactForm,
1614
1631
  {
1615
1632
  title: "Open a new ticket",
1616
1633
  footerText: "The support team typically responds within one business day.",
@@ -1648,20 +1665,21 @@ function HelpCenterCreateForm({
1648
1665
  // src/components/tickets/help-center-list.tsx
1649
1666
 
1650
1667
  function HelpCenterList({ toast: toast2 = _chunk5V6MSE3Bcjs.toast } = {}) {
1651
- const identity = _chunkZGTDUPTWcjs.useChatIdentity.call(void 0, );
1668
+ const identity = _chunkYWDC5BXMcjs.useChatIdentity.call(void 0, );
1652
1669
  const searchParams = _chunkG7UE6RKVcjs.useSearchParams.call(void 0, );
1653
1670
  const router = _chunkG7UE6RKVcjs.useRouter.call(void 0, );
1654
1671
  const pathname = _chunkG7UE6RKVcjs.usePathname.call(void 0, );
1655
1672
  const search = searchParams.get("search") || "";
1656
1673
  const status = searchParams.get("status") || "all";
1674
+ const ticketParam = searchParams.get("ticket") || "";
1657
1675
  const rawPage = Number(searchParams.get("page"));
1658
1676
  const page = Number.isFinite(rawPage) && rawPage > 0 ? Math.floor(rawPage) : 1;
1659
1677
  if (identity.isLoading) {
1660
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIH76P5R6cjs.DevSectionPage, { sectionKey: "tickets", preControls: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, HelpCenterCreateFormSkeleton, {}), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIH76P5R6cjs.DevCardRowSkeletonList, {}) });
1678
+ 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
1679
  }
1662
- if (identity.authTier === "anon" || !_optionalChain([identity, 'access', _56 => _56.user, 'optionalAccess', _57 => _57.email])) {
1663
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIH76P5R6cjs.DevSectionPage, { sectionKey: "tickets", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1664
- _chunkK4DFAVSOcjs.EmptyState,
1680
+ if (identity.authTier === "anon" || !_optionalChain([identity, 'access', _60 => _60.user, 'optionalAccess', _61 => _61.email])) {
1681
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevSectionPage, { sectionKey: "tickets", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1682
+ _chunkG2HHSZ3Scjs.EmptyState,
1665
1683
  {
1666
1684
  type: "generic",
1667
1685
  title: "Sign in to manage tickets",
@@ -1670,7 +1688,7 @@ function HelpCenterList({ toast: toast2 = _chunk5V6MSE3Bcjs.toast } = {}) {
1670
1688
  }
1671
1689
  ) });
1672
1690
  }
1673
- const sessionName = [_optionalChain([identity, 'access', _58 => _58.user, 'optionalAccess', _59 => _59.firstName]), _optionalChain([identity, 'access', _60 => _60.user, 'optionalAccess', _61 => _61.lastName])].filter(Boolean).join(" ").trim() || _optionalChain([identity, 'access', _62 => _62.user, 'optionalAccess', _63 => _63.email, 'optionalAccess', _64 => _64.split, 'call', _65 => _65("@"), 'access', _66 => _66[0]]) || "Customer";
1691
+ 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
1692
  const sessionEmail = identity.user.email;
1675
1693
  return /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1676
1694
  HelpCenterListAuthed,
@@ -1678,6 +1696,7 @@ function HelpCenterList({ toast: toast2 = _chunk5V6MSE3Bcjs.toast } = {}) {
1678
1696
  search,
1679
1697
  status,
1680
1698
  page,
1699
+ ticketParam,
1681
1700
  searchParams,
1682
1701
  router,
1683
1702
  pathname,
@@ -1691,6 +1710,7 @@ function HelpCenterListAuthed({
1691
1710
  search,
1692
1711
  status,
1693
1712
  page,
1713
+ ticketParam,
1694
1714
  searchParams,
1695
1715
  router,
1696
1716
  pathname,
@@ -1699,6 +1719,18 @@ function HelpCenterListAuthed({
1699
1719
  sessionEmail
1700
1720
  }) {
1701
1721
  const queryClient = _reactquery.useQueryClient.call(void 0, );
1722
+ const [optimisticTickets, setOptimisticTickets] = _react.useState.call(void 0, []);
1723
+ const [supportSystemDown, setSupportSystemDown] = _react.useState.call(void 0, false);
1724
+ const setOpenTicket = _react.useCallback.call(void 0,
1725
+ (externalId) => {
1726
+ const params = new URLSearchParams(searchParams.toString());
1727
+ if (externalId) params.set("ticket", externalId);
1728
+ else params.delete("ticket");
1729
+ const qs = params.toString();
1730
+ router.replace(qs ? `${pathname}?${qs}` : pathname, { scroll: false });
1731
+ },
1732
+ [searchParams, router, pathname]
1733
+ );
1702
1734
  const { tickets, isLoading, isFetching, error, refetch, totalPages } = useTicketsList({
1703
1735
  // `sessionEmail` is drilled in from the parent — see the same
1704
1736
  // pattern + race-cause rationale documented in
@@ -1709,17 +1741,19 @@ function HelpCenterListAuthed({
1709
1741
  customerEmail: sessionEmail,
1710
1742
  search,
1711
1743
  status,
1712
- page
1744
+ page,
1745
+ // Live status: while a drawer is open, poll so an out-of-band HubSpot
1746
+ // status change (e.g. agent closes the ticket) flips the badge +
1747
+ // open/reopen affordance within one interval. Idle (no drawer) → no poll.
1748
+ // `ticketParam` (the open ticket's external_id) is the open signal.
1749
+ refetchInterval: ticketParam ? TICKET_LIVE_POLL_MS : false
1713
1750
  });
1714
- const [optimisticTickets, setOptimisticTickets] = _react.useState.call(void 0, []);
1715
- const [expandedTicketId, setExpandedTicketId] = _react.useState.call(void 0, null);
1716
- const [supportSystemDown, setSupportSystemDown] = _react.useState.call(void 0, false);
1751
+ const expandedTicketId = ticketParam && _optionalChain([tickets, 'access', _71 => _71.find, 'call', _72 => _72((t) => t.external_id === ticketParam), 'optionalAccess', _73 => _73.id]) || null;
1717
1752
  const prependOptimistic = _react.useCallback.call(void 0, (placeholder) => {
1718
1753
  setOptimisticTickets((prev) => [placeholder, ...prev]);
1719
1754
  }, []);
1720
1755
  const removeOptimistic = _react.useCallback.call(void 0, (placeholderId) => {
1721
1756
  setOptimisticTickets((prev) => prev.filter((t) => t.id !== placeholderId));
1722
- setExpandedTicketId((prev) => prev === placeholderId ? null : prev);
1723
1757
  }, []);
1724
1758
  const removeTicketFromCache = _react.useCallback.call(void 0,
1725
1759
  (ticketId) => {
@@ -1732,7 +1766,6 @@ function HelpCenterListAuthed({
1732
1766
  return { ...prev, tickets: nextTickets };
1733
1767
  }
1734
1768
  );
1735
- setExpandedTicketId((prev) => prev === ticketId ? null : prev);
1736
1769
  },
1737
1770
  [queryClient]
1738
1771
  );
@@ -1743,9 +1776,14 @@ function HelpCenterListAuthed({
1743
1776
  toast: toast2,
1744
1777
  onSupportSystemDown: () => setSupportSystemDown(true)
1745
1778
  });
1746
- const toggleRow = _react.useCallback.call(void 0, (id) => {
1747
- setExpandedTicketId((prev) => prev === id ? null : id);
1748
- }, []);
1779
+ const toggleRow = _react.useCallback.call(void 0,
1780
+ (id) => {
1781
+ const t = tickets.find((x) => x.id === id);
1782
+ if (!_optionalChain([t, 'optionalAccess', _74 => _74.external_id])) return;
1783
+ setOpenTicket(t.external_id === ticketParam ? null : t.external_id);
1784
+ },
1785
+ [tickets, ticketParam, setOpenTicket]
1786
+ );
1749
1787
  const merged = [...optimisticTickets, ...tickets];
1750
1788
  const hasActiveFilters = search !== "" || status !== "" && status !== "all";
1751
1789
  const hasResults = merged.length > 0;
@@ -1766,15 +1804,15 @@ function HelpCenterListAuthed({
1766
1804
  ] }),
1767
1805
  /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkXDPSSE4Ocjs.Button, { type: "button", variant: "accent", onClick: () => refetch(), children: "Retry" })
1768
1806
  ] }),
1769
- !error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full", children: isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIH76P5R6cjs.DevCardRowSkeletonList, {}) : !hasResults && isFetching ? (
1807
+ !error && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, "div", { className: "w-full", children: isLoading ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevCardRowSkeletonList, {}) : !hasResults && isFetching ? (
1770
1808
  // Bridge state — background refetch in flight and the
1771
1809
  // optimistic placeholder was just removed by the mutation
1772
1810
  // callback. Without this branch "No tickets yet" would flash
1773
1811
  // for ~50ms between `removeOptimistic` and the server
1774
1812
  // response landing.
1775
- /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIH76P5R6cjs.DevCardRowSkeletonList, { rows: 1 })
1813
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevCardRowSkeletonList, { rows: 1 })
1776
1814
  ) : !hasResults ? hasActiveFilters ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1777
- _chunkK4DFAVSOcjs.EmptyState,
1815
+ _chunkG2HHSZ3Scjs.EmptyState,
1778
1816
  {
1779
1817
  type: "search",
1780
1818
  title: "No tickets found",
@@ -1789,7 +1827,7 @@ function HelpCenterListAuthed({
1789
1827
  }
1790
1828
  }
1791
1829
  ) : /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1792
- _chunkK4DFAVSOcjs.EmptyState,
1830
+ _chunkG2HHSZ3Scjs.EmptyState,
1793
1831
  {
1794
1832
  type: "generic",
1795
1833
  title: "No tickets yet",
@@ -1816,16 +1854,16 @@ function HelpCenterListAuthed({
1816
1854
  onSendMessage: actions.sendMessage,
1817
1855
  onClose: actions.closeTicket,
1818
1856
  onReopen: actions.reopenTicket,
1819
- onActionCollapsed: () => setExpandedTicketId(null),
1857
+ onActionCollapsed: () => setOpenTicket(null),
1820
1858
  replyError: actions.replyErrorFor(ticket.external_id),
1821
1859
  onClearReplyError: () => actions.clearReplyError(ticket.external_id)
1822
1860
  },
1823
1861
  ticket.id
1824
1862
  )) })
1825
1863
  ) }),
1826
- !error && totalPages > 1 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkK4DFAVSOcjs.UnifiedPagination, { currentPage: page, totalPages })
1864
+ !error && totalPages > 1 && /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkG2HHSZ3Scjs.UnifiedPagination, { currentPage: page, totalPages })
1827
1865
  ] });
1828
- return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIH76P5R6cjs.DevSectionPage, { sectionKey: "tickets", preControls: form, children: body });
1866
+ return /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevSectionPage, { sectionKey: "tickets", preControls: form, children: body });
1829
1867
  }
1830
1868
 
1831
1869
 
@@ -1843,5 +1881,6 @@ function HelpCenterListAuthed({
1843
1881
 
1844
1882
 
1845
1883
 
1846
- 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.TicketRow = TicketRow; exports.isOptimistic = isOptimistic; exports.mapTicketActionError = mapTicketActionError; exports.useTicketActions = useTicketActions; exports.useTicketEngagements = useTicketEngagements; exports.useTicketsList = useTicketsList;
1884
+
1885
+ 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
1886
  //# sourceMappingURL=index.cjs.map