@handled-ai/design-system 0.20.0 → 0.20.2

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 (33) hide show
  1. package/dist/components/conversation-panel.d.ts +1 -1
  2. package/dist/components/conversation-panel.js +282 -15
  3. package/dist/components/conversation-panel.js.map +1 -1
  4. package/dist/components/owner-chips.d.ts +3 -4
  5. package/dist/components/owner-chips.js +77 -41
  6. package/dist/components/owner-chips.js.map +1 -1
  7. package/dist/components/score-why-chips.d.ts +1 -1
  8. package/dist/components/signal-priority-popover.d.ts +1 -1
  9. package/dist/components/timeline-activity.d.ts +4 -2
  10. package/dist/components/timeline-activity.js +366 -154
  11. package/dist/components/timeline-activity.js.map +1 -1
  12. package/dist/index.d.ts +2 -2
  13. package/dist/prototype/index.d.ts +1 -1
  14. package/dist/prototype/prototype-accounts-view.d.ts +1 -1
  15. package/dist/prototype/prototype-admin-view.d.ts +1 -1
  16. package/dist/prototype/prototype-config.d.ts +1 -1
  17. package/dist/prototype/prototype-inbox-view.d.ts +9 -3
  18. package/dist/prototype/prototype-inbox-view.js +94 -47
  19. package/dist/prototype/prototype-inbox-view.js.map +1 -1
  20. package/dist/prototype/prototype-insights-view.d.ts +1 -1
  21. package/dist/prototype/prototype-shell.d.ts +1 -1
  22. package/dist/{signal-priority-popover-Cg9XPJsp.d.ts → signal-priority-popover-BJHd07dU.d.ts} +6 -0
  23. package/package.json +1 -1
  24. package/src/components/__tests__/conversation-panel.test.tsx +276 -0
  25. package/src/components/__tests__/owner-chips.test.tsx +137 -17
  26. package/src/components/__tests__/timeline-activity.test.tsx +92 -1
  27. package/src/components/conversation-panel.tsx +358 -21
  28. package/src/components/owner-chips.tsx +98 -63
  29. package/src/components/timeline-activity.tsx +452 -160
  30. package/src/prototype/__tests__/detail-view-case-panel-v2.test.tsx +181 -0
  31. package/src/prototype/__tests__/detail-view-timeline-system-events.test.tsx +16 -2
  32. package/src/prototype/prototype-config.ts +6 -0
  33. package/src/prototype/prototype-inbox-view.tsx +128 -51
@@ -0,0 +1,181 @@
1
+ import { cleanup, render, screen } from "@testing-library/react"
2
+ import { afterEach, describe, expect, it } from "vitest"
3
+
4
+ import { DetailView, type DetailViewProps } from "../prototype-inbox-view"
5
+ import type { QueueItem, SignalScoreData } from "../prototype-config"
6
+ import type { TimelineEvent } from "../../components/timeline-activity"
7
+
8
+ const baseItem: QueueItem = {
9
+ id: "case-1",
10
+ title: "Churn risk case title",
11
+ details: "Case details",
12
+ statusColor: "red",
13
+ time: "1h ago",
14
+ company: "Acme Corp",
15
+ tag1: "Signal",
16
+ }
17
+
18
+ const timelineEvents: TimelineEvent[] = [
19
+ {
20
+ id: "timeline-1",
21
+ icon: <span aria-hidden="true">•</span>,
22
+ title: "Timeline marker event",
23
+ time: "10m ago",
24
+ },
25
+ ]
26
+
27
+ const FOLLOWS = Node.DOCUMENT_POSITION_FOLLOWING
28
+
29
+ afterEach(() => {
30
+ cleanup()
31
+ })
32
+
33
+ function makeSignalScore(overrides: Partial<SignalScoreData> = {}): SignalScoreData {
34
+ return {
35
+ score: 84,
36
+ factors: [],
37
+ whyNow: "The why copy: customer replied after the escalation window opened.",
38
+ evidence: ["Evidence line"],
39
+ confidence: 91,
40
+ urgencyLabel: "High",
41
+ urgencyExplanation: "Urgency explanation copy.",
42
+ signalBrief: "Signal brief copy: account activity needs review.",
43
+ timeChipLabel: "3 days left",
44
+ explanationBuckets: [
45
+ {
46
+ key: "cash-movement",
47
+ label: "Cash movement",
48
+ kind: "signal",
49
+ signals: [{ id: "sig-1", label: "Large outgoing transfer" }],
50
+ },
51
+ ],
52
+ ...overrides,
53
+ }
54
+ }
55
+
56
+ function renderDetailView(
57
+ overrides: Partial<DetailViewProps> = {},
58
+ scoreOverrides: Partial<SignalScoreData> = {},
59
+ ) {
60
+ const props: DetailViewProps = {
61
+ item: baseItem,
62
+ sections: { signalBrief: true, suggestedActions: false, timeline: true },
63
+ getSignalScore: () => makeSignalScore(scoreOverrides),
64
+ buildSuggestedActions: () => [],
65
+ buildSourceItems: () => [],
66
+ getTimelineEvents: () => timelineEvents,
67
+ accountContacts: [],
68
+ emailSignature: "",
69
+ iconMap: {},
70
+ ...overrides,
71
+ }
72
+
73
+ return render(<DetailView {...props} />)
74
+ }
75
+
76
+ function expectInDocumentOrder(...nodes: HTMLElement[]) {
77
+ nodes.forEach((node, index) => {
78
+ const next = nodes[index + 1]
79
+ if (!next) return
80
+ expect(node.compareDocumentPosition(next) & FOLLOWS).toBeTruthy()
81
+ })
82
+ }
83
+
84
+ describe("DetailView case-panel-v2 section layout", () => {
85
+ it("keeps the default layout unchanged and ignores v2-only slots", () => {
86
+ renderDetailView({
87
+ renderMetadataExtra: () => <span>Metadata marker</span>,
88
+ renderAfterScore: () => <section>After-score marker</section>,
89
+ renderPrimaryAction: () => <section>Primary action marker</section>,
90
+ renderCommentArea: () => <section>Comment area marker</section>,
91
+ renderDetailExtra: () => <section>Legacy detail extra marker</section>,
92
+ })
93
+
94
+ expect(screen.queryByText("Primary action marker")).toBeNull()
95
+ expect(screen.queryByText("Comment area marker")).toBeNull()
96
+ expect(screen.getByText("Legacy detail extra marker")).toBeTruthy()
97
+
98
+ expectInDocumentOrder(
99
+ screen.getByText("Metadata marker"),
100
+ screen.getByText("Signal brief copy: account activity needs review."),
101
+ screen.getByText("Cash movement"),
102
+ screen.getByText("Approve action"),
103
+ screen.getByText("After-score marker"),
104
+ screen.getByText("Activity timeline"),
105
+ screen.getByText("Legacy detail extra marker"),
106
+ )
107
+ })
108
+
109
+ it("renders v2 sections in the approved order", () => {
110
+ renderDetailView({
111
+ sectionLayout: "case-panel-v2",
112
+ renderMetadataExtra: () => <span>Metadata marker</span>,
113
+ renderBeforeScore: () => <section>Before-score status marker</section>,
114
+ renderAfterScore: () => <section>Opportunity marker</section>,
115
+ renderPrimaryAction: () => <section>Primary action marker</section>,
116
+ renderCommentArea: () => <section>Comment area marker</section>,
117
+ })
118
+
119
+ expectInDocumentOrder(
120
+ screen.getByText("Signal brief copy: account activity needs review."),
121
+ screen.getByText("Metadata marker"),
122
+ screen.getByText("Before-score status marker"),
123
+ screen.getByText("The why"),
124
+ screen.getByText("The why copy: customer replied after the escalation window opened."),
125
+ screen.getByText("Cash movement"),
126
+ screen.getByText("Approve action"),
127
+ screen.getByText("Opportunity marker"),
128
+ screen.getByText("Primary action marker"),
129
+ screen.getByText("Comment area marker"),
130
+ screen.getByText("Activity timeline"),
131
+ )
132
+ })
133
+
134
+ it("renders the primary slot in the v2 layout", () => {
135
+ renderDetailView({
136
+ sectionLayout: "case-panel-v2",
137
+ renderPrimaryAction: (item) => (
138
+ <section aria-label="primary slot">Primary action for {item.id}</section>
139
+ ),
140
+ })
141
+
142
+ expect(screen.getByText("Primary action for case-1")).toBeTruthy()
143
+ })
144
+
145
+ it("places the comment slot before the timeline in the v2 layout", () => {
146
+ renderDetailView({
147
+ sectionLayout: "case-panel-v2",
148
+ renderCommentArea: () => <section>Comment composer marker</section>,
149
+ })
150
+
151
+ expectInDocumentOrder(
152
+ screen.getByText("Comment composer marker"),
153
+ screen.getByText("Activity timeline"),
154
+ )
155
+ })
156
+
157
+ it("renders signal brief and The why separately when both copies exist", () => {
158
+ renderDetailView({ sectionLayout: "case-panel-v2" })
159
+
160
+ expectInDocumentOrder(
161
+ screen.getByText("Signal brief copy: account activity needs review."),
162
+ screen.getByText("The why"),
163
+ screen.getByText("The why copy: customer replied after the escalation window opened."),
164
+ )
165
+ })
166
+
167
+ it("falls back to whyNow in the brief and suppresses the separate The why block when signalBrief is missing", () => {
168
+ renderDetailView(
169
+ { sectionLayout: "case-panel-v2" },
170
+ {
171
+ signalBrief: undefined,
172
+ whyNow: "Fallback why copy shown in the brief.",
173
+ urgencyExplanation: "Urgency copy should not render as a separate why block.",
174
+ },
175
+ )
176
+
177
+ expect(screen.getAllByText("Fallback why copy shown in the brief.")).toHaveLength(1)
178
+ expect(screen.queryByText("The why")).toBeNull()
179
+ expect(screen.queryByText("Urgency copy should not render as a separate why block.")).toBeNull()
180
+ })
181
+ })
@@ -1,3 +1,4 @@
1
+ import "@testing-library/jest-dom/vitest"
1
2
  import { describe, it, expect, vi, beforeEach } from "vitest"
2
3
  import React from "react"
3
4
  import { render, fireEvent } from "@testing-library/react"
@@ -181,14 +182,19 @@ describe("DetailView timeline system-events toggle", () => {
181
182
  '[data-testid="system-events-toggle"]',
182
183
  ) as HTMLElement
183
184
  fireEvent.click(toggle)
185
+ expect(toggle).toHaveAttribute("aria-pressed", "true")
184
186
  expect(container.textContent).toContain("Score updated +3")
185
187
 
186
188
  // Collapse the timeline
187
189
  expandTimeline(container)
190
+ expect(toggle).toHaveAttribute("aria-pressed", "true")
191
+ expect(container.textContent).not.toContain("Score updated +3")
192
+
188
193
  // Re-expand
189
194
  expandTimeline(container)
190
195
 
191
196
  // System events should still be visible (toggle didn't change)
197
+ expect(toggle).toHaveAttribute("aria-pressed", "true")
192
198
  expect(container.textContent).toContain("Score updated +3")
193
199
  })
194
200
 
@@ -203,15 +209,20 @@ describe("DetailView timeline system-events toggle", () => {
203
209
  const badge = container.querySelector('[data-testid="hidden-count-badge"]')
204
210
  expect(badge).not.toBeNull()
205
211
  expect(badge?.textContent).toBe("2")
212
+ expect(badge).toHaveClass("min-w-[18px]")
206
213
  })
207
214
 
208
- it("calls localStorage.setItem when toggle changes", () => {
215
+ it("calls localStorage.setItem when toggle changes and shows a stronger pressed style", () => {
209
216
  const { container } = render(<DetailView {...baseProps()} />)
210
217
  expandTimeline(container)
211
218
  const toggle = container.querySelector(
212
219
  '[data-testid="system-events-toggle"]',
213
220
  ) as HTMLElement
221
+ expect(toggle).toHaveAttribute("aria-pressed", "false")
214
222
  fireEvent.click(toggle)
223
+ expect(toggle).toHaveAttribute("aria-pressed", "true")
224
+ expect(toggle.className).toContain("border-primary/40")
225
+ expect(toggle.className).toContain("bg-primary/10")
215
226
  expect(localStorageMock.setItem).toHaveBeenCalledWith(
216
227
  "test-show-score-changes",
217
228
  "true",
@@ -301,12 +312,15 @@ describe("DetailView timeline system-events toggle", () => {
301
312
  expect(toggle).toBeNull()
302
313
  })
303
314
 
304
- it("shows footer hint when timeline is expanded and system events are hidden", () => {
315
+ it("shows footer hint below the case-panel timeline when timeline is expanded and system events are hidden", () => {
305
316
  const { container } = render(<DetailView {...baseProps()} />)
306
317
  expandTimeline(container)
318
+ const timeline = container.querySelector('[data-variant="case-panel"]')
307
319
  const hint = container.querySelector('[data-testid="timeline-footer-hint"]')
320
+ expect(timeline).not.toBeNull()
308
321
  expect(hint).not.toBeNull()
309
322
  expect(hint?.textContent).toBe("Score changes are hidden.")
323
+ expect(timeline?.compareDocumentPosition(hint as Node)).toBe(Node.DOCUMENT_POSITION_FOLLOWING)
310
324
  })
311
325
 
312
326
  it("shows visible footer hint with count when system events are shown", () => {
@@ -224,12 +224,18 @@ export interface InboxViewConfig {
224
224
  * - "prominent": standard foreground color, text-base size for the brief
225
225
  */
226
226
  briefStyleVariant?: BriefStyleVariant
227
+ /** Opt-in detail panel section ordering. Defaults to the existing layout. */
228
+ sectionLayout?: "default" | "case-panel-v2"
227
229
  /** Render extra content at the end of the detail view, below the suggested actions section. */
228
230
  renderDetailExtra?: (item: QueueItem) => React.ReactNode
229
231
  /** Render content between the signal brief text and the signal score bar (e.g. "Signals on Case" chips). */
230
232
  renderBeforeScore?: (item: QueueItem) => React.ReactNode
231
233
  /** Render content between the signal score section and the activity timeline (e.g. OpportunityPanel). */
232
234
  renderAfterScore?: (item: QueueItem) => React.ReactNode
235
+ /** Render primary case-panel content between the opportunity section and comment area. */
236
+ renderPrimaryAction?: (item: QueueItem) => React.ReactNode
237
+ /** Render case-panel comment content before the activity timeline. */
238
+ renderCommentArea?: (item: QueueItem) => React.ReactNode
233
239
  /** Formatted string for "Last activity X ago" in the collapsed timeline header. If omitted, falls back to the first event's time. */
234
240
  lastActivityTime?: string
235
241
  /** Configuration for the system-noise events toggle (score changes, etc.). */
@@ -48,6 +48,7 @@ import {
48
48
  type SuggestedContact,
49
49
  } from "../components/suggested-actions"
50
50
  import { TimelineActivity, type TimelineEvent } from "../components/timeline-activity"
51
+ import { cn } from "../lib/utils"
51
52
  import type {
52
53
  QueueItem,
53
54
  InboxViewConfig,
@@ -155,11 +156,17 @@ export interface DetailViewProps {
155
156
  hideApproveButton?: boolean
156
157
  signalBriefCopy?: InboxViewConfig["signalBriefCopy"]
157
158
  briefStyleVariant?: BriefStyleVariant
159
+ /** Opt-in detail panel section ordering. Defaults to the existing layout. */
160
+ sectionLayout?: InboxViewConfig["sectionLayout"]
158
161
  renderDetailExtra?: (item: QueueItem) => React.ReactNode
159
162
  /** Render content between the signal brief text and the signal score bar (e.g. "Signals on Case" chips). */
160
163
  renderBeforeScore?: (item: QueueItem) => React.ReactNode
161
164
  /** Render content between the signal score section and the activity timeline. */
162
165
  renderAfterScore?: (item: QueueItem) => React.ReactNode
166
+ /** Render primary case-panel content between the opportunity section and comment area. */
167
+ renderPrimaryAction?: (item: QueueItem) => React.ReactNode
168
+ /** Render case-panel comment content before the activity timeline. */
169
+ renderCommentArea?: (item: QueueItem) => React.ReactNode
163
170
  lastActivityTime?: string
164
171
  /** Render extra content inline with the detail title. */
165
172
  renderTitleExtra?: (item: QueueItem) => React.ReactNode
@@ -290,13 +297,23 @@ function TimelineSection({
290
297
  <button
291
298
  type="button"
292
299
  onClick={() => setShowSystemEvents((prev) => !prev)}
293
- className="flex shrink-0 items-center gap-1.5 rounded-full border border-border bg-background px-2.5 py-1 text-[11px] font-medium text-muted-foreground transition-colors hover:bg-muted/40 hover:text-foreground cursor-pointer"
300
+ className={cn(
301
+ "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",
302
+ showSystemEvents
303
+ ? "border-primary/40 bg-primary/10 text-primary shadow-sm hover:bg-primary/15"
304
+ : "border-border bg-background text-muted-foreground hover:bg-muted/40"
305
+ )}
294
306
  aria-pressed={showSystemEvents}
295
307
  data-testid="system-events-toggle"
296
308
  >
297
309
  {toggleLabel}
298
310
  <span
299
- className="inline-flex items-center justify-center rounded-full bg-muted px-1.5 text-[10px] font-semibold min-w-[18px] tabular-nums"
311
+ className={cn(
312
+ "inline-flex min-w-[18px] items-center justify-center rounded-full px-1.5 text-[10px] font-semibold tabular-nums",
313
+ showSystemEvents
314
+ ? "bg-primary/15 text-primary ring-1 ring-primary/30"
315
+ : "bg-muted text-muted-foreground ring-1 ring-border/70"
316
+ )}
300
317
  data-testid="hidden-count-badge"
301
318
  >
302
319
  {hiddenCount}
@@ -308,7 +325,7 @@ function TimelineSection({
308
325
  {/* Timeline body */}
309
326
  {showTimeline && visibleEvents.length > 0 && (
310
327
  <div className="mt-3">
311
- <TimelineActivity events={visibleEvents} />
328
+ <TimelineActivity events={visibleEvents} variant="case-panel" />
312
329
  </div>
313
330
  )}
314
331
 
@@ -351,9 +368,12 @@ export function DetailView({
351
368
  hideApproveButton,
352
369
  signalBriefCopy,
353
370
  briefStyleVariant = "default",
371
+ sectionLayout = "default",
354
372
  renderDetailExtra,
355
373
  renderBeforeScore,
356
374
  renderAfterScore,
375
+ renderPrimaryAction,
376
+ renderCommentArea,
357
377
  lastActivityTime,
358
378
  renderTitleExtra,
359
379
  renderTitleActionRow,
@@ -481,6 +501,11 @@ export function DetailView({
481
501
  ? "border-amber-300 bg-amber-50 text-amber-700 hover:bg-amber-50"
482
502
  : "hover:bg-muted/50"
483
503
 
504
+ const isCasePanelV2 = sectionLayout === "case-panel-v2"
505
+ const v2WhyText = signalData.signalBrief
506
+ ? signalData.whyNow || signalData.urgencyExplanation
507
+ : undefined
508
+
484
509
  // The metadata chips row (priority · deadline · account · renderMetadataExtra). Rendered above
485
510
  // the brief by default, or beneath it when `metadataLayout === "below-brief"` (case-panel redesign).
486
511
  const metadataChips = (
@@ -551,6 +576,38 @@ export function DetailView({
551
576
  </>
552
577
  )
553
578
 
579
+ const timelineSection = sections.timeline && timelineEvents.length > 0 ? (
580
+ <TimelineSection
581
+ timelineEvents={timelineEvents}
582
+ showTimeline={showTimeline}
583
+ setShowTimeline={setShowTimeline}
584
+ showSystemEvents={showSystemEvents}
585
+ setShowSystemEvents={setShowSystemEvents}
586
+ attentionCount={attentionCount}
587
+ sysEvtConfig={sysEvtConfig}
588
+ lastActivityTime={lastActivityTime}
589
+ />
590
+ ) : null
591
+
592
+ const suggestedActionsSection = sections.suggestedActions ? (
593
+ <SignalApproval.Gate>
594
+ <SuggestedActions
595
+ actions={suggestedActions}
596
+ accountContacts={accountContacts}
597
+ signature={emailSignature}
598
+ iconMap={iconMap}
599
+ onDismiss={(id) => console.log("Dismiss action:", id)}
600
+ onSend={(id) => console.log("Send action:", id)}
601
+ onSaveDraft={(id) => console.log("Save draft:", id)}
602
+ onDuplicate={handleDuplicate}
603
+ onOpenAccountDetails={onOpenEntityPanel}
604
+ onOpenRecentActivity={onOpenRecentActivity}
605
+ onMarkComplete={(id) => console.log("Mark complete:", id)}
606
+ onDispatchAgent={(id) => console.log("Dispatch agent:", id)}
607
+ />
608
+ </SignalApproval.Gate>
609
+ ) : null
610
+
554
611
  return (
555
612
  <SignalApproval.Root
556
613
  key={item.id}
@@ -592,7 +649,7 @@ export function DetailView({
592
649
  ) : null}
593
650
  </div>
594
651
 
595
- {metadataLayout === "default" ? (
652
+ {!isCasePanelV2 && metadataLayout === "default" ? (
596
653
  <div className="mb-6 flex flex-wrap items-center gap-2">{metadataChips}</div>
597
654
  ) : null}
598
655
 
@@ -637,63 +694,77 @@ export function DetailView({
637
694
  )}
638
695
 
639
696
  {/* Metadata chips relocated beneath the brief (case-panel redesign). */}
640
- {metadataLayout === "below-brief" ? (
697
+ {isCasePanelV2 || metadataLayout === "below-brief" ? (
641
698
  <div className="mb-4 flex flex-wrap items-center gap-2">{metadataChips}</div>
642
699
  ) : null}
643
700
 
644
- {/* Before-score content slot (e.g. "Signals on Case" chips) */}
645
- {renderBeforeScore?.(item)}
646
-
647
- <ScoreWhyChips
648
- item={item}
649
- signalData={signalData}
650
- onOpenSignalBucket={onOpenSignalBucket}
651
- />
652
- <div className="mt-4">
653
- <SignalApproval.Actions />
654
- </div>
701
+ {!isCasePanelV2 ? (
702
+ <>
703
+ {/* Before-score content slot (e.g. "Signals on Case" chips) */}
704
+ {renderBeforeScore?.(item)}
705
+
706
+ <ScoreWhyChips
707
+ item={item}
708
+ signalData={signalData}
709
+ onOpenSignalBucket={onOpenSignalBucket}
710
+ />
711
+ <div className="mt-4">
712
+ <SignalApproval.Actions />
713
+ </div>
714
+ </>
715
+ ) : null}
655
716
  </div>
656
717
  )
657
718
  })()}
658
719
 
659
- {/* After-score content slot (e.g. OpportunityPanel) */}
660
- {renderAfterScore?.(item)}
661
-
662
- {/* Activity Timeline */}
663
- {sections.timeline && timelineEvents.length > 0 && (
664
- <TimelineSection
665
- timelineEvents={timelineEvents}
666
- showTimeline={showTimeline}
667
- setShowTimeline={setShowTimeline}
668
- showSystemEvents={showSystemEvents}
669
- setShowSystemEvents={setShowSystemEvents}
670
- attentionCount={attentionCount}
671
- sysEvtConfig={sysEvtConfig}
672
- lastActivityTime={lastActivityTime}
673
- />
720
+ {isCasePanelV2 ? (
721
+ <>
722
+ {sections.signalBrief ? (
723
+ <>
724
+ {/* Before-score content slot (e.g. status/attention pills) */}
725
+ {renderBeforeScore?.(item)}
726
+
727
+ {v2WhyText ? (
728
+ <div className="mb-8">
729
+ <h3 className="text-xs font-bold text-muted-foreground uppercase tracking-wider mb-3">The why</h3>
730
+ <p className="text-sm text-foreground/90 leading-relaxed mb-4">
731
+ {v2WhyText}
732
+ </p>
733
+ </div>
734
+ ) : null}
735
+
736
+ <div className="mb-8">
737
+ <ScoreWhyChips
738
+ item={item}
739
+ signalData={signalData}
740
+ onOpenSignalBucket={onOpenSignalBucket}
741
+ />
742
+ <div className="mt-4">
743
+ <SignalApproval.Actions />
744
+ </div>
745
+ </div>
746
+ </>
747
+ ) : null}
748
+
749
+ {/* After-score content slot (e.g. OpportunityPanel) */}
750
+ {renderAfterScore?.(item)}
751
+ {renderPrimaryAction?.(item)}
752
+ {renderCommentArea?.(item)}
753
+ {timelineSection}
754
+ </>
755
+ ) : (
756
+ <>
757
+ {/* After-score content slot (e.g. OpportunityPanel) */}
758
+ {renderAfterScore?.(item)}
759
+
760
+ {/* Activity Timeline */}
761
+ {timelineSection}
762
+ </>
674
763
  )}
675
764
  </div>
676
765
 
677
- {/* Suggested Actions */}
678
- {sections.suggestedActions && (
679
- <SignalApproval.Gate>
680
- <SuggestedActions
681
- actions={suggestedActions}
682
- accountContacts={accountContacts}
683
- signature={emailSignature}
684
- iconMap={iconMap}
685
- onDismiss={(id) => console.log("Dismiss action:", id)}
686
- onSend={(id) => console.log("Send action:", id)}
687
- onSaveDraft={(id) => console.log("Save draft:", id)}
688
- onDuplicate={handleDuplicate}
689
- onOpenAccountDetails={onOpenEntityPanel}
690
- onOpenRecentActivity={onOpenRecentActivity}
691
- onMarkComplete={(id) => console.log("Mark complete:", id)}
692
- onDispatchAgent={(id) => console.log("Dispatch agent:", id)}
693
- />
694
- </SignalApproval.Gate>
695
- )}
696
- {renderDetailExtra?.(item)}
766
+ {!isCasePanelV2 ? suggestedActionsSection : null}
767
+ {!isCasePanelV2 ? renderDetailExtra?.(item) : null}
697
768
  </div>
698
769
  </SignalApproval.Root>
699
770
  )
@@ -735,9 +806,12 @@ export function PrototypeInboxView({
735
806
  hideApproveButton,
736
807
  signalBriefCopy,
737
808
  briefStyleVariant,
809
+ sectionLayout,
738
810
  renderDetailExtra,
739
811
  renderBeforeScore,
740
812
  renderAfterScore,
813
+ renderPrimaryAction,
814
+ renderCommentArea,
741
815
  lastActivityTime,
742
816
  timelineSystemEventsConfig,
743
817
  attentionCount,
@@ -980,9 +1054,12 @@ export function PrototypeInboxView({
980
1054
  hideApproveButton,
981
1055
  signalBriefCopy,
982
1056
  briefStyleVariant,
1057
+ sectionLayout,
983
1058
  renderDetailExtra,
984
1059
  renderBeforeScore,
985
1060
  renderAfterScore,
1061
+ renderPrimaryAction,
1062
+ renderCommentArea,
986
1063
  lastActivityTime,
987
1064
  timelineSystemEventsConfig,
988
1065
  attentionCount,