@handled-ai/design-system 0.20.13 → 0.20.14
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/comment-composer.js +69 -49
- package/dist/components/comment-composer.js.map +1 -1
- package/dist/components/conversation-panel.js +5 -4
- package/dist/components/conversation-panel.js.map +1 -1
- package/dist/components/email-body.js +39 -1
- package/dist/components/email-body.js.map +1 -1
- package/dist/prototype/prototype-inbox-view.js +107 -62
- package/dist/prototype/prototype-inbox-view.js.map +1 -1
- package/package.json +1 -1
- package/src/components/__tests__/comment-composer.test.tsx +7 -3
- package/src/components/__tests__/conversation-panel.test.tsx +118 -0
- package/src/components/__tests__/email-body.test.tsx +32 -0
- package/src/components/comment-composer.tsx +26 -14
- package/src/components/conversation-panel.tsx +14 -7
- package/src/components/email-body.tsx +46 -1
- package/src/prototype/__tests__/detail-view-case-panel-v2.test.tsx +3 -3
- package/src/prototype/__tests__/detail-view-timeline-system-events.test.tsx +17 -14
- package/src/prototype/prototype-inbox-view.tsx +72 -44
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/components/email-body.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { sanitizeHtml } from \"../internal/safe-html\"\nimport { cn } from \"../lib/utils\"\nimport {\n decodeEmailDisplayText,\n splitEmailHtmlForDisplay,\n splitEmailTextForDisplay,\n} from \"./email-display-helpers\"\n\nexport interface EmailBodyProps {\n html?: string | null\n text?: string | null\n detailsHtml?: string | null\n detailsText?: string | null\n collapseDetails?: boolean\n defaultDetailsOpen?: boolean\n variant?: \"history\" | \"preview\"\n className?: string\n}\n\nconst PROSE = cn(\n \"break-words leading-[1.62]\",\n \"[&_p]:my-2 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0\",\n \"[&_a]:text-[#1a73e8] [&_a]:underline-offset-2 hover:[&_a]:underline\",\n \"[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-5\",\n \"[&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground\",\n \"[&_table]:my-2 [&_table]:max-w-full [&_table]:border-collapse\",\n \"[&_td]:align-top [&_td]:pr-2 [&_th]:align-top [&_th]:pr-2\",\n \"[&_img]:max-w-full [&_img]:h-auto\",\n \"[&_sup]:align-super [&_sup]:text-[0.75em] [&_sub]:align-sub [&_sub]:text-[0.75em]\",\n)\n\nfunction PlainTextBlock({ text, className, slot }: { text: string; className?: string; slot: string }) {\n return (\n <div data-slot={slot} className={cn(PROSE, \"whitespace-pre-line\", className)}>\n {decodeEmailDisplayText(text)}\n </div>\n )\n}\n\nfunction HtmlBlock({ html, className, slot }: { html: string; className?: string; slot: string }) {\n return (\n <div\n data-slot={slot}\n className={cn(PROSE, className)}\n dangerouslySetInnerHTML={{ __html: sanitizeHtml(html) }}\n />\n )\n}\n\nexport function EmailBody({\n html,\n text,\n detailsHtml,\n detailsText,\n collapseDetails = false,\n defaultDetailsOpen = false,\n variant = \"history\",\n className,\n}: EmailBodyProps) {\n const [detailsOpen, setDetailsOpen] = React.useState(defaultDetailsOpen)\n\n const htmlParts = html ? splitEmailHtmlForDisplay(html) : null\n const textParts = html ? null : splitEmailTextForDisplay(text ?? \"\")\n\n const bodyHtml = htmlParts?.bodyHtml ?? \"\"\n const bodyText = textParts?.bodyText ?? \"\"\n const combinedDetailsHtml = [htmlParts?.detailsHtml, detailsHtml].filter(Boolean).join(\"\")\n const combinedDetailsText = [textParts?.detailsText, detailsText].filter(Boolean).join(\"\")\n const hasBody = Boolean(bodyHtml.trim() || bodyText.trim())\n const hasDetails = Boolean(combinedDetailsHtml.trim() || combinedDetailsText.trim())\n const shouldCollapseDetails = collapseDetails && hasDetails\n const showDetails = hasDetails && (!shouldCollapseDetails || detailsOpen)\n\n return (\n <div\n data-slot=\"email-body\"\n data-variant={variant}\n className={cn(\n \"text-sm text-foreground/90\",\n variant === \"preview\" && \"text-[13.5px]\",\n className,\n )}\n >\n {hasBody ? (\n htmlParts ? (\n <HtmlBlock slot=\"email-body-content\" html={bodyHtml} />\n ) : (\n <PlainTextBlock slot=\"email-body-content\" text={bodyText} />\n )\n ) : null}\n\n {shouldCollapseDetails ? (\n <button\n type=\"button\"\n onClick={() => setDetailsOpen((value) => !value)}\n className=\"text-muted-foreground hover:text-foreground hover:bg-muted mt-2 rounded px-1.5 text-xs leading-5\"\n aria-expanded={detailsOpen}\n title={detailsOpen ? \"Hide historical details\" : \"Show historical details\"}\n >\n •••\n </button>\n ) : null}\n\n {showDetails ? (\n <div\n data-slot=\"email-body-details\"\n className={cn(\n \"mt-2 text-muted-foreground\",\n shouldCollapseDetails && \"border-border border-l-2 pl-3\",\n variant === \"preview\" && \"border-border/50 border-t pt-3\",\n )}\n >\n {combinedDetailsHtml ? (\n <HtmlBlock slot=\"email-body-details-content\" html={combinedDetailsHtml} />\n ) : (\n <PlainTextBlock slot=\"email-body-details-content\" text={combinedDetailsText} />\n )}\n </div>\n ) : null}\n </div>\n )\n}\n"],"mappings":";
|
|
1
|
+
{"version":3,"sources":["../../src/components/email-body.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\n\nimport { sanitizeHtml } from \"../internal/safe-html\"\nimport { cn } from \"../lib/utils\"\nimport {\n decodeEmailDisplayText,\n splitEmailHtmlForDisplay,\n splitEmailTextForDisplay,\n} from \"./email-display-helpers\"\n\nexport interface EmailBodyProps {\n html?: string | null\n text?: string | null\n detailsHtml?: string | null\n detailsText?: string | null\n collapseDetails?: boolean\n defaultDetailsOpen?: boolean\n variant?: \"history\" | \"preview\"\n className?: string\n}\n\nconst PROSE = cn(\n \"break-words leading-[1.62]\",\n \"[&_p]:my-2 [&_p:first-child]:mt-0 [&_p:last-child]:mb-0\",\n \"[&_a]:text-[#1a73e8] [&_a]:underline-offset-2 hover:[&_a]:underline\",\n \"[&_ul]:my-2 [&_ul]:list-disc [&_ul]:pl-5 [&_ol]:my-2 [&_ol]:list-decimal [&_ol]:pl-5\",\n \"[&_blockquote]:my-2 [&_blockquote]:border-l-2 [&_blockquote]:border-border [&_blockquote]:pl-3 [&_blockquote]:text-muted-foreground\",\n \"[&_table]:my-2 [&_table]:max-w-full [&_table]:border-collapse\",\n \"[&_td]:align-top [&_td]:pr-2 [&_th]:align-top [&_th]:pr-2\",\n \"[&_img]:max-w-full [&_img]:h-auto\",\n \"[&_sup]:align-super [&_sup]:text-[0.75em] [&_sub]:align-sub [&_sub]:text-[0.75em]\",\n)\n\nconst PLAIN_TEXT_LINK_RE = /https?:\\/\\/[^\\s<>\"']+|www\\.[^\\s<>\"']+|[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}/gi\nconst TRAILING_LINK_PUNCTUATION_RE = /[),.;:!?]+$/\n\nfunction plainTextHref(value: string): string | null {\n if (/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}$/i.test(value)) {\n return `mailto:${value}`\n }\n const href = value.toLowerCase().startsWith(\"www.\") ? `https://${value}` : value\n try {\n const url = new URL(href)\n return url.protocol === \"http:\" || url.protocol === \"https:\" ? href : null\n } catch {\n return null\n }\n}\n\nfunction linkifyPlainText(text: string): React.ReactNode[] {\n const nodes: React.ReactNode[] = []\n let cursor = 0\n\n for (const match of text.matchAll(PLAIN_TEXT_LINK_RE)) {\n const value = match[0]\n const index = match.index ?? 0\n if (index > cursor) nodes.push(text.slice(cursor, index))\n\n const trailing = value.match(TRAILING_LINK_PUNCTUATION_RE)?.[0] ?? \"\"\n const linkText = trailing ? value.slice(0, -trailing.length) : value\n const href = plainTextHref(linkText)\n if (href) {\n nodes.push(\n <a key={`${index}-${linkText}`} href={href} target=\"_blank\" rel=\"noreferrer noopener\">\n {linkText}\n </a>,\n )\n } else {\n nodes.push(linkText)\n }\n if (trailing) nodes.push(trailing)\n cursor = index + value.length\n }\n\n if (cursor < text.length) nodes.push(text.slice(cursor))\n return nodes\n}\n\nfunction PlainTextBlock({ text, className, slot }: { text: string; className?: string; slot: string }) {\n return (\n <div data-slot={slot} className={cn(PROSE, \"whitespace-pre-line\", className)}>\n {linkifyPlainText(decodeEmailDisplayText(text))}\n </div>\n )\n}\n\nfunction HtmlBlock({ html, className, slot }: { html: string; className?: string; slot: string }) {\n return (\n <div\n data-slot={slot}\n className={cn(PROSE, className)}\n dangerouslySetInnerHTML={{ __html: sanitizeHtml(html) }}\n />\n )\n}\n\nexport function EmailBody({\n html,\n text,\n detailsHtml,\n detailsText,\n collapseDetails = false,\n defaultDetailsOpen = false,\n variant = \"history\",\n className,\n}: EmailBodyProps) {\n const [detailsOpen, setDetailsOpen] = React.useState(defaultDetailsOpen)\n\n const htmlParts = html ? splitEmailHtmlForDisplay(html) : null\n const textParts = html ? null : splitEmailTextForDisplay(text ?? \"\")\n\n const bodyHtml = htmlParts?.bodyHtml ?? \"\"\n const bodyText = textParts?.bodyText ?? \"\"\n const combinedDetailsHtml = [htmlParts?.detailsHtml, detailsHtml].filter(Boolean).join(\"\")\n const combinedDetailsText = [textParts?.detailsText, detailsText].filter(Boolean).join(\"\")\n const hasBody = Boolean(bodyHtml.trim() || bodyText.trim())\n const hasDetails = Boolean(combinedDetailsHtml.trim() || combinedDetailsText.trim())\n const shouldCollapseDetails = collapseDetails && hasDetails\n const showDetails = hasDetails && (!shouldCollapseDetails || detailsOpen)\n\n return (\n <div\n data-slot=\"email-body\"\n data-variant={variant}\n className={cn(\n \"text-sm text-foreground/90\",\n variant === \"preview\" && \"text-[13.5px]\",\n className,\n )}\n >\n {hasBody ? (\n htmlParts ? (\n <HtmlBlock slot=\"email-body-content\" html={bodyHtml} />\n ) : (\n <PlainTextBlock slot=\"email-body-content\" text={bodyText} />\n )\n ) : null}\n\n {shouldCollapseDetails ? (\n <button\n type=\"button\"\n onClick={() => setDetailsOpen((value) => !value)}\n className=\"text-muted-foreground hover:text-foreground hover:bg-muted mt-2 rounded px-1.5 text-xs leading-5\"\n aria-expanded={detailsOpen}\n title={detailsOpen ? \"Hide historical details\" : \"Show historical details\"}\n >\n •••\n </button>\n ) : null}\n\n {showDetails ? (\n <div\n data-slot=\"email-body-details\"\n className={cn(\n \"mt-2 text-muted-foreground\",\n shouldCollapseDetails && \"border-border border-l-2 pl-3\",\n variant === \"preview\" && \"border-border/50 border-t pt-3\",\n )}\n >\n {combinedDetailsHtml ? (\n <HtmlBlock slot=\"email-body-details-content\" html={combinedDetailsHtml} />\n ) : (\n <PlainTextBlock slot=\"email-body-details-content\" text={combinedDetailsText} />\n )}\n </div>\n ) : null}\n </div>\n )\n}\n"],"mappings":"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","names":[]}
|
|
@@ -113,9 +113,10 @@ function TimelineSection({
|
|
|
113
113
|
setShowSystemEvents,
|
|
114
114
|
attentionCount,
|
|
115
115
|
sysEvtConfig,
|
|
116
|
-
lastActivityTime
|
|
116
|
+
lastActivityTime,
|
|
117
|
+
isCasePanel = false
|
|
117
118
|
}) {
|
|
118
|
-
var _a;
|
|
119
|
+
var _a, _b, _c, _d;
|
|
119
120
|
const visibleEvents = [];
|
|
120
121
|
let hiddenCount = 0;
|
|
121
122
|
for (const e of timelineEvents) {
|
|
@@ -124,74 +125,117 @@ function TimelineSection({
|
|
|
124
125
|
}
|
|
125
126
|
const hasSystemNoise = hiddenCount > 0;
|
|
126
127
|
const toggleLabel = (_a = sysEvtConfig == null ? void 0 : sysEvtConfig.toggleLabel) != null ? _a : "System events";
|
|
128
|
+
const toggleHelp = showSystemEvents ? (_c = (_b = sysEvtConfig == null ? void 0 : sysEvtConfig.visibleHint) == null ? void 0 : _b.replace("{count}", String(hiddenCount))) != null ? _c : "Hide system events" : (_d = sysEvtConfig == null ? void 0 : sysEvtConfig.hiddenHint) != null ? _d : "Show system events";
|
|
127
129
|
const firstVisibleTime = (!hasSystemNoise || showSystemEvents) && lastActivityTime ? lastActivityTime : visibleEvents.length > 0 ? visibleEvents[0].time : "";
|
|
128
130
|
const visibleCount = visibleEvents.length;
|
|
129
131
|
const eventCountLabel = `${visibleCount} ${visibleCount === 1 ? "event" : "events"}`;
|
|
130
|
-
return /* @__PURE__ */ jsxs(
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
"
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
onClick: () => setShowSystemEvents((prev) => !prev),
|
|
166
|
-
className: cn(
|
|
167
|
-
"flex shrink-0 cursor-pointer items-center gap-1.5 rounded-full border px-2.5 py-1 text-[11px] font-medium transition-colors hover:text-foreground",
|
|
168
|
-
showSystemEvents ? "border-primary/40 bg-primary/10 text-primary shadow-sm hover:bg-primary/15" : "border-border bg-background text-muted-foreground hover:bg-muted/40"
|
|
132
|
+
return /* @__PURE__ */ jsxs(
|
|
133
|
+
"div",
|
|
134
|
+
{
|
|
135
|
+
className: cn(
|
|
136
|
+
isCasePanel ? "mt-8 border-t border-border pt-8 pb-8" : "mb-8"
|
|
137
|
+
),
|
|
138
|
+
children: [
|
|
139
|
+
/* @__PURE__ */ jsxs(
|
|
140
|
+
"div",
|
|
141
|
+
{
|
|
142
|
+
className: cn(
|
|
143
|
+
"flex w-full items-center justify-between",
|
|
144
|
+
isCasePanel ? "gap-4 border-b border-border pb-5" : "group/timeline gap-2 rounded-md py-2 transition-colors hover:bg-muted/40 -mx-2 px-2"
|
|
145
|
+
),
|
|
146
|
+
"data-testid": "timeline-header",
|
|
147
|
+
children: [
|
|
148
|
+
/* @__PURE__ */ jsxs(
|
|
149
|
+
"button",
|
|
150
|
+
{
|
|
151
|
+
type: "button",
|
|
152
|
+
onClick: () => setShowTimeline((prev) => !prev),
|
|
153
|
+
className: "flex min-w-0 cursor-pointer items-center gap-2 border-0 bg-transparent p-0 text-left",
|
|
154
|
+
"data-testid": "timeline-collapse-btn",
|
|
155
|
+
children: [
|
|
156
|
+
/* @__PURE__ */ jsx("h3", { className: "text-xs font-bold uppercase tracking-[0.16em] text-muted-foreground transition-colors group-hover/timeline:text-foreground", children: "ACTIVITY TIMELINE" }),
|
|
157
|
+
!showTimeline && attentionCount != null && attentionCount > 0 && /* @__PURE__ */ jsxs("span", { className: "inline-flex items-center gap-1 rounded-full bg-destructive/10 px-1.5 py-0.5 text-[10px] font-semibold text-destructive border border-destructive/20", children: [
|
|
158
|
+
attentionCount,
|
|
159
|
+
" new"
|
|
160
|
+
] }),
|
|
161
|
+
!isCasePanel && !showTimeline && firstVisibleTime && /* @__PURE__ */ jsxs("span", { className: "text-[11px] text-muted-foreground/60", "data-testid": "last-activity-hint", children: [
|
|
162
|
+
"\xB7 Last activity ",
|
|
163
|
+
firstVisibleTime
|
|
164
|
+
] })
|
|
165
|
+
]
|
|
166
|
+
}
|
|
169
167
|
),
|
|
170
|
-
"
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
toggleLabel,
|
|
174
|
-
/* @__PURE__ */ jsx(
|
|
175
|
-
"span",
|
|
168
|
+
/* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center gap-4", children: [
|
|
169
|
+
hasSystemNoise && /* @__PURE__ */ jsxs(
|
|
170
|
+
"button",
|
|
176
171
|
{
|
|
172
|
+
type: "button",
|
|
173
|
+
onClick: () => setShowSystemEvents((prev) => !prev),
|
|
177
174
|
className: cn(
|
|
178
|
-
"inline-flex
|
|
179
|
-
showSystemEvents ? "bg-
|
|
175
|
+
"inline-flex shrink-0 cursor-pointer items-center gap-3 rounded-full border px-3.5 py-2 text-sm font-semibold transition-colors",
|
|
176
|
+
showSystemEvents ? "border-foreground bg-foreground text-background shadow-sm hover:bg-foreground/90" : "border-border bg-background text-muted-foreground shadow-sm hover:bg-muted/40 hover:text-foreground"
|
|
180
177
|
),
|
|
181
|
-
"
|
|
182
|
-
|
|
178
|
+
"aria-pressed": showSystemEvents,
|
|
179
|
+
"aria-label": toggleHelp,
|
|
180
|
+
title: toggleHelp,
|
|
181
|
+
"data-testid": "system-events-toggle",
|
|
182
|
+
children: [
|
|
183
|
+
/* @__PURE__ */ jsx(
|
|
184
|
+
"span",
|
|
185
|
+
{
|
|
186
|
+
className: cn(
|
|
187
|
+
"relative inline-flex h-4 w-8 shrink-0 items-center rounded-full p-0.5 transition-colors",
|
|
188
|
+
showSystemEvents ? "bg-teal-600" : "bg-muted-foreground/30"
|
|
189
|
+
),
|
|
190
|
+
"aria-hidden": "true",
|
|
191
|
+
"data-testid": "system-events-indicator",
|
|
192
|
+
children: /* @__PURE__ */ jsx(
|
|
193
|
+
"span",
|
|
194
|
+
{
|
|
195
|
+
className: cn(
|
|
196
|
+
"block h-3 w-3 rounded-full bg-white shadow-sm transition-transform",
|
|
197
|
+
showSystemEvents ? "translate-x-4" : "translate-x-0"
|
|
198
|
+
)
|
|
199
|
+
}
|
|
200
|
+
)
|
|
201
|
+
}
|
|
202
|
+
),
|
|
203
|
+
/* @__PURE__ */ jsx("span", { children: toggleLabel }),
|
|
204
|
+
!showSystemEvents ? /* @__PURE__ */ jsx(
|
|
205
|
+
"span",
|
|
206
|
+
{
|
|
207
|
+
className: "inline-flex min-w-[22px] items-center justify-center rounded-full bg-muted px-1.5 text-xs font-bold tabular-nums text-muted-foreground",
|
|
208
|
+
"data-testid": "hidden-count-badge",
|
|
209
|
+
children: hiddenCount
|
|
210
|
+
}
|
|
211
|
+
) : null
|
|
212
|
+
]
|
|
213
|
+
}
|
|
214
|
+
),
|
|
215
|
+
/* @__PURE__ */ jsxs(
|
|
216
|
+
"button",
|
|
217
|
+
{
|
|
218
|
+
type: "button",
|
|
219
|
+
onClick: () => setShowTimeline((prev) => !prev),
|
|
220
|
+
className: cn(
|
|
221
|
+
"inline-flex shrink-0 cursor-pointer items-center border-0 bg-transparent p-0 text-muted-foreground transition-colors hover:text-foreground",
|
|
222
|
+
isCasePanel ? "gap-3 text-sm" : "gap-1.5 text-[11px]"
|
|
223
|
+
),
|
|
224
|
+
"aria-label": showTimeline ? "Collapse activity timeline" : "Expand activity timeline",
|
|
225
|
+
children: [
|
|
226
|
+
/* @__PURE__ */ jsx("span", { className: "font-medium", "data-testid": "event-count", children: eventCountLabel }),
|
|
227
|
+
/* @__PURE__ */ jsx(ChevronDown, { className: `h-3.5 w-3.5 transition-transform duration-200 ${showTimeline ? "rotate-180" : ""}` })
|
|
228
|
+
]
|
|
183
229
|
}
|
|
184
230
|
)
|
|
185
|
-
]
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
showTimeline && showSystemEvents && (sysEvtConfig == null ? void 0 : sysEvtConfig.visibleHint) && hasSystemNoise && /* @__PURE__ */ jsx("p", { className: "mt-2 text-[11px] text-muted-foreground/60 border-t border-dashed border-border pt-2", "data-testid": "timeline-footer-hint", children: sysEvtConfig.visibleHint.replace("{count}", String(hiddenCount)) })
|
|
194
|
-
] });
|
|
231
|
+
] })
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
),
|
|
235
|
+
showTimeline && visibleEvents.length > 0 && /* @__PURE__ */ jsx("div", { className: "mt-6", children: /* @__PURE__ */ jsx(TimelineActivity, { events: visibleEvents, variant: "case-panel" }) })
|
|
236
|
+
]
|
|
237
|
+
}
|
|
238
|
+
);
|
|
195
239
|
}
|
|
196
240
|
function DetailView({
|
|
197
241
|
item,
|
|
@@ -387,7 +431,8 @@ function DetailView({
|
|
|
387
431
|
setShowSystemEvents,
|
|
388
432
|
attentionCount,
|
|
389
433
|
sysEvtConfig,
|
|
390
|
-
lastActivityTime
|
|
434
|
+
lastActivityTime,
|
|
435
|
+
isCasePanel: isCasePanelV2
|
|
391
436
|
}
|
|
392
437
|
) : null;
|
|
393
438
|
const suggestedActionsSection = sections.suggestedActions ? /* @__PURE__ */ jsx(SignalApproval.Gate, { children: /* @__PURE__ */ jsx(
|