@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.
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 +309 -256
  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 +376 -323
  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 +41 -5
  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,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 = _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 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 _chunkZGTDUPTWcjs.embedAuthedFetch.call(void 0, LIST_ENGAGEMENTS_ENDPOINT, {
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
- isLoading: queryEnabled && query.isLoading,
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
- _chunkK4DFAVSOcjs.DeliveryRow,
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
- OpenActions,
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 = _chunkZGTDUPTWcjs.useChatIdentity.call(void 0, );
513
+ const identity = _chunkYWDC5BXMcjs.useChatIdentity.call(void 0, );
363
514
  const externalId = isOptimistic(ticket) ? null : ticket.external_id;
364
- const { engagements, isLoading } = useTicketEngagements(externalId, !!externalId);
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 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));
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', _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;
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
- _chunkK4DFAVSOcjs.EmptyState,
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.jsxs.call(void 0, "div", { className: "bg-ods-card border border-ods-border rounded-[6px] overflow-hidden w-full", children: [
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
- _chunkIH76P5R6cjs.ConversationCardRow,
552
+ _chunkYWDC5BXMcjs.ChatMessageRow,
393
553
  {
394
- author: customerName,
395
- role,
396
- avatarSrc: customerAvatar,
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', _22 => _22.user, 'optionalAccess', _23 => _23.email]) && eng.authorId.toLowerCase() === identity.user.email.toLowerCase();
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', _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));
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
- _chunkIH76P5R6cjs.ConversationCardRow,
581
+ _chunkYWDC5BXMcjs.ChatMessageRow,
423
582
  {
424
- author,
425
- role: "Reply",
426
- avatarSrc,
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
- attachments: mapEngagementAttachments(eng.attachments),
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
- 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
- ] });
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', _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;
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
- _chunkZGTDUPTWcjs.SquareAvatar,
675
+ _chunkYWDC5BXMcjs.SquareAvatar,
645
676
  {
646
677
  size: "sm",
647
678
  variant: "round",
648
- src: _nullishCoalesce(_optionalChain([assignedOwner, 'optionalAccess', _36 => _36.avatarUrl]), () => ( void 0)),
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
- _chunkZGTDUPTWcjs.scrollElementIntoView.call(void 0, rowRef.current, {
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 ? _chunkZGTDUPTWcjs.formatRelativeTime.call(void 0, ticket.hubspot_updated_at) : void 0,
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
- _chunkZGTDUPTWcjs.ChatTicketItem,
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 _chunkZGTDUPTWcjs.embedAuthedFetch.call(void 0, FIND_TICKET_ENDPOINT, {
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', _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))));
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', _44 => _44.tickets]), () => ( [])),
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', _45 => _45.tickets]), () => ( []))).length === 0),
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 _chunkZGTDUPTWcjs.embedAuthedFetch.call(void 0, TICKET_ACTION_ENDPOINT, {
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', _46 => _46.attachments, 'optionalAccess', _47 => _47.length]) ? { attachments: input.attachments } : {}
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', _48 => _48.trim, 'call', _49 => _49()]) ? { resolution: resolution.trim() } : {}
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', _50 => _50.response, 'optionalAccess', _51 => _51.headers, 'access', _52 => _52.get, 'call', _53 => _53("Retry-After")]);
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 = _chunkZGTDUPTWcjs.useChatIdentity.call(void 0, );
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', _54 => _54.user, 'optionalAccess', _55 => _55.email])) {
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
- _chunkK4DFAVSOcjs.EmptyState,
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
- _chunkZGTDUPTWcjs.formatRelativeTime.call(void 0, new Date(lastUpdatedAt))
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, _chunkZGTDUPTWcjs.Card, { className: "p-6", children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1373
- _chunkK4DFAVSOcjs.EmptyState,
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, _chunkZGTDUPTWcjs.Card, { className: "overflow-hidden", children: merged.map((ticket) => /* @__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, _chunkZGTDUPTWcjs.Card, { className: "p-6", children: [
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, _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: [
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
- _chunkK4DFAVSOcjs.init_unified_pagination.call(void 0, );
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 ? _chunkZGTDUPTWcjs.formatRelativeTime.call(void 0, ticket.hubspot_updated_at) : "recently";
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
- _chunkZGTDUPTWcjs.StatusBadge,
1501
+ _chunkYWDC5BXMcjs.StatusBadge,
1471
1502
  {
1472
1503
  text: rawStatus,
1473
- colorScheme: _chunkZGTDUPTWcjs.getStatusColorScheme.call(void 0, rawStatus),
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
- _chunkZGTDUPTWcjs.StatusBadge,
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
- _chunkIH76P5R6cjs.DevCardRowContent,
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, _chunkZGTDUPTWcjs.Label, { htmlFor: "help-center-subject", children: [
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
- _chunkSMCG2CCCcjs.ContactForm,
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 = _chunkZGTDUPTWcjs.useChatIdentity.call(void 0, );
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, _chunkIH76P5R6cjs.DevSectionPage, { sectionKey: "tickets", preControls: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, HelpCenterCreateFormSkeleton, {}), children: /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkIH76P5R6cjs.DevCardRowSkeletonList, {}) });
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', _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,
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', _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";
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 [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);
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, (id) => {
1747
- setExpandedTicketId((prev) => prev === id ? null : id);
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, _chunkIH76P5R6cjs.DevCardRowSkeletonList, {}) : !hasResults && isFetching ? (
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, _chunkIH76P5R6cjs.DevCardRowSkeletonList, { rows: 1 })
1827
+ /* @__PURE__ */ _jsxruntime.jsx.call(void 0, _chunkTYIBMDUZcjs.DevCardRowSkeletonList, { rows: 1 })
1776
1828
  ) : !hasResults ? hasActiveFilters ? /* @__PURE__ */ _jsxruntime.jsx.call(void 0,
1777
- _chunkK4DFAVSOcjs.EmptyState,
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
- _chunkK4DFAVSOcjs.EmptyState,
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: () => setExpandedTicketId(null),
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, _chunkK4DFAVSOcjs.UnifiedPagination, { currentPage: page, totalPages })
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, _chunkIH76P5R6cjs.DevSectionPage, { sectionKey: "tickets", preControls: form, children: body });
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
- 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;
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