@handled-ai/design-system 0.18.22 → 0.18.23

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 (70) hide show
  1. package/dist/components/case-panel-activity-timeline.d.ts +100 -0
  2. package/dist/components/case-panel-activity-timeline.js +270 -0
  3. package/dist/components/case-panel-activity-timeline.js.map +1 -0
  4. package/dist/components/case-panel-detail.d.ts +60 -0
  5. package/dist/components/case-panel-detail.js +129 -0
  6. package/dist/components/case-panel-detail.js.map +1 -0
  7. package/dist/components/case-panel-email-composer.d.ts +61 -0
  8. package/dist/components/case-panel-email-composer.js +304 -0
  9. package/dist/components/case-panel-email-composer.js.map +1 -0
  10. package/dist/components/case-panel-why.d.ts +35 -0
  11. package/dist/components/case-panel-why.js +149 -0
  12. package/dist/components/case-panel-why.js.map +1 -0
  13. package/dist/components/contextual-quick-action-launcher.d.ts +7 -3
  14. package/dist/components/contextual-quick-action-launcher.js +99 -27
  15. package/dist/components/contextual-quick-action-launcher.js.map +1 -1
  16. package/dist/components/data-table.js +0 -1
  17. package/dist/components/data-table.js.map +1 -1
  18. package/dist/components/pill.d.ts +1 -1
  19. package/dist/components/score-analysis-modal.d.ts +2 -8
  20. package/dist/components/score-analysis-modal.js +6 -19
  21. package/dist/components/score-analysis-modal.js.map +1 -1
  22. package/dist/components/score-breakdown.d.ts +1 -3
  23. package/dist/components/score-breakdown.js +6 -5
  24. package/dist/components/score-breakdown.js.map +1 -1
  25. package/dist/components/score-ring.d.ts +3 -6
  26. package/dist/components/score-ring.js +14 -11
  27. package/dist/components/score-ring.js.map +1 -1
  28. package/dist/components/score-why-chips.d.ts +2 -3
  29. package/dist/components/score-why-chips.js +21 -10
  30. package/dist/components/score-why-chips.js.map +1 -1
  31. package/dist/components/signal-priority-popover.d.ts +0 -1
  32. package/dist/components/signal-priority-popover.js +20 -20
  33. package/dist/components/signal-priority-popover.js.map +1 -1
  34. package/dist/index.d.ts +7 -4
  35. package/dist/index.js +4 -1
  36. package/dist/index.js.map +1 -1
  37. package/dist/prototype/index.d.ts +0 -1
  38. package/dist/prototype/prototype-accounts-view.d.ts +0 -1
  39. package/dist/prototype/prototype-admin-view.d.ts +0 -1
  40. package/dist/prototype/prototype-config.d.ts +0 -1
  41. package/dist/prototype/prototype-inbox-view.d.ts +0 -1
  42. package/dist/prototype/prototype-insights-view.d.ts +0 -1
  43. package/dist/prototype/prototype-shell.d.ts +0 -1
  44. package/package.json +1 -1
  45. package/src/components/__tests__/case-panel-activity-timeline.test.tsx +152 -0
  46. package/src/components/__tests__/case-panel-detail.test.tsx +138 -0
  47. package/src/components/__tests__/case-panel-email-composer.test.tsx +171 -0
  48. package/src/components/__tests__/case-panel-why.test.tsx +152 -0
  49. package/src/components/__tests__/contextual-quick-action-launcher.test.tsx +87 -0
  50. package/src/components/__tests__/signal-priority-popover.test.tsx +5 -7
  51. package/src/components/case-panel-activity-timeline.tsx +414 -0
  52. package/src/components/case-panel-detail.tsx +228 -0
  53. package/src/components/case-panel-email-composer.tsx +341 -0
  54. package/src/components/case-panel-why.tsx +214 -0
  55. package/src/components/contextual-quick-action-launcher.tsx +92 -15
  56. package/src/components/data-table.tsx +0 -1
  57. package/src/components/score-analysis-modal.tsx +5 -22
  58. package/src/components/score-breakdown.tsx +6 -7
  59. package/src/components/score-ring.tsx +13 -11
  60. package/src/components/score-why-chips.tsx +23 -12
  61. package/src/components/signal-priority-popover.tsx +21 -21
  62. package/src/index.ts +4 -1
  63. package/dist/components/score-semantics.d.ts +0 -27
  64. package/dist/components/score-semantics.js +0 -173
  65. package/dist/components/score-semantics.js.map +0 -1
  66. package/src/components/__tests__/score-analysis-modal.test.tsx +0 -55
  67. package/src/components/__tests__/score-breakdown-intent.test.tsx +0 -47
  68. package/src/components/__tests__/score-ring.test.tsx +0 -43
  69. package/src/components/__tests__/score-semantics.test.ts +0 -107
  70. package/src/components/score-semantics.ts +0 -187
@@ -1,7 +1,7 @@
1
1
  "use client"
2
2
 
3
3
  import * as React from "react"
4
- import { ArrowRight, ChevronDown, Zap } from "lucide-react"
4
+ import { ChevronDown, Zap } from "lucide-react"
5
5
 
6
6
  import { cn } from "../lib/utils"
7
7
  import {
@@ -19,11 +19,15 @@ export interface ContextualQuickActionItem {
19
19
  icon?: React.ReactNode
20
20
  disabled?: boolean
21
21
  disabledReason?: string
22
+ meta?: React.ReactNode
22
23
  }
23
24
 
25
+ export type ContextualQuickActionLauncherVariant = "default" | "case-panel"
26
+
24
27
  export interface ContextualQuickActionContextLabelProps {
25
28
  contextLabel: string
26
29
  contextSecondary?: string
30
+ variant?: ContextualQuickActionLauncherVariant
27
31
  className?: string
28
32
  }
29
33
 
@@ -35,6 +39,7 @@ export interface ContextualQuickActionLauncherProps {
35
39
  onBrowseAll?: () => void
36
40
  showHint?: boolean
37
41
  align?: "start" | "end"
42
+ variant?: ContextualQuickActionLauncherVariant
38
43
  className?: string
39
44
  open?: boolean
40
45
  defaultOpen?: boolean
@@ -44,19 +49,35 @@ export interface ContextualQuickActionLauncherProps {
44
49
  function ContextualQuickActionContextLabel({
45
50
  contextLabel,
46
51
  contextSecondary,
52
+ variant = "default",
47
53
  className,
48
54
  }: ContextualQuickActionContextLabelProps) {
55
+ const isCasePanel = variant === "case-panel"
56
+
49
57
  return (
50
58
  <div
51
59
  data-slot="contextual-quick-action-context-label"
60
+ data-variant={variant}
52
61
  className={cn(
53
- "-mx-1 -mt-1 mb-1 flex items-center gap-1.5 border-b px-3 py-2 text-[11px] text-muted-foreground",
62
+ isCasePanel
63
+ ? "-mx-2 -mt-2 mb-1 flex items-center gap-1.5 whitespace-nowrap border-b px-3 py-2.5 text-[13px] text-muted-foreground"
64
+ : "-mx-1 -mt-1 mb-1 flex items-center gap-1.5 border-b px-3 py-2 text-[11px] text-muted-foreground",
54
65
  className
55
66
  )}
56
67
  >
57
- <Zap className="h-3 w-3 shrink-0" strokeWidth={2.25} aria-hidden="true" />
68
+ <Zap
69
+ className={cn(isCasePanel ? "h-3.5 w-3.5 shrink-0" : "h-3 w-3 shrink-0")}
70
+ strokeWidth={2.25}
71
+ aria-hidden="true"
72
+ />
58
73
  <span>Acting on</span>
59
- <strong className="max-w-[180px] truncate font-semibold text-foreground">
74
+ <strong
75
+ className={cn(
76
+ isCasePanel
77
+ ? "max-w-[260px] truncate font-semibold text-foreground"
78
+ : "max-w-[180px] truncate font-semibold text-foreground"
79
+ )}
80
+ >
60
81
  {contextLabel}
61
82
  </strong>
62
83
  {contextSecondary ? (
@@ -80,12 +101,14 @@ function ContextualQuickActionLauncher({
80
101
  onBrowseAll,
81
102
  showHint = false,
82
103
  align = "start",
104
+ variant = "default",
83
105
  className,
84
106
  open,
85
107
  defaultOpen,
86
108
  onOpenChange,
87
109
  }: ContextualQuickActionLauncherProps) {
88
110
  const [uncontrolledOpen, setUncontrolledOpen] = React.useState(defaultOpen ?? false)
111
+ const isCasePanel = variant === "case-panel"
89
112
  const isControlled = open !== undefined
90
113
  const isOpen = isControlled ? open : uncontrolledOpen
91
114
 
@@ -154,11 +177,16 @@ function ContextualQuickActionLauncher({
154
177
  align={align}
155
178
  side="bottom"
156
179
  sideOffset={6}
157
- className="pointer-events-auto w-[308px] rounded-[10px] p-1.5 text-[12.5px] shadow-[0_12px_28px_rgba(0,0,0,0.12),0_2px_6px_rgba(0,0,0,0.04)]"
180
+ className={cn(
181
+ isCasePanel
182
+ ? "pointer-events-auto w-[432px] rounded-[13px] border border-border bg-popover p-2 text-[13px] shadow-[0_18px_44px_rgba(0,0,0,0.14),0_2px_10px_rgba(0,0,0,0.06)]"
183
+ : "pointer-events-auto w-[308px] rounded-[10px] p-1.5 text-[12.5px] shadow-[0_12px_28px_rgba(0,0,0,0.12),0_2px_6px_rgba(0,0,0,0.04)]"
184
+ )}
158
185
  >
159
186
  <ContextualQuickActionContextLabel
160
187
  contextLabel={contextLabel}
161
188
  contextSecondary={contextSecondary}
189
+ variant={variant}
162
190
  />
163
191
 
164
192
  {items.map((item) => (
@@ -167,48 +195,97 @@ function ContextualQuickActionLauncher({
167
195
  disabled={item.disabled}
168
196
  onSelect={(event) => handleSelect(item, event)}
169
197
  className={cn(
170
- "flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-left outline-none focus:bg-accent data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100",
198
+ isCasePanel
199
+ ? "grid cursor-pointer grid-cols-[34px_minmax(0,1fr)_auto] items-center gap-3 rounded-[9px] px-2.5 py-[9px] text-left outline-none focus:bg-muted/60 data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100"
200
+ : "flex cursor-pointer items-center gap-2.5 rounded-md px-2 py-2 text-left outline-none focus:bg-accent data-[disabled]:cursor-not-allowed data-[disabled]:opacity-100",
171
201
  item.disabled && "text-muted-foreground"
172
202
  )}
173
203
  >
174
204
  <span
175
205
  data-slot="contextual-quick-action-item-icon"
176
206
  className={cn(
177
- "flex h-[26px] w-[26px] shrink-0 items-center justify-center rounded-md bg-secondary text-foreground",
207
+ isCasePanel
208
+ ? "flex h-[34px] w-[34px] shrink-0 items-center justify-center rounded-[9px] border border-border/70 bg-muted/60 text-foreground [&_svg]:h-[18px] [&_svg]:w-[18px]"
209
+ : "flex h-[26px] w-[26px] shrink-0 items-center justify-center rounded-md bg-secondary text-foreground",
178
210
  item.disabled && "opacity-60"
179
211
  )}
180
212
  >
181
213
  {item.icon ?? <DefaultActionIcon />}
182
214
  </span>
183
215
  <span className="min-w-0 flex-1">
184
- <span className="block truncate text-[12.5px] font-medium leading-tight text-current">
216
+ <span
217
+ className={cn(
218
+ isCasePanel
219
+ ? "block truncate text-[13.5px] font-semibold leading-tight text-current"
220
+ : "block truncate text-[12.5px] font-medium leading-tight text-current"
221
+ )}
222
+ >
185
223
  {item.label}
186
224
  </span>
187
225
  {item.description ? (
188
- <span className="mt-0.5 block truncate text-[11px] leading-tight text-muted-foreground">
226
+ <span
227
+ className={cn(
228
+ isCasePanel
229
+ ? "mt-1 block truncate text-[12px] leading-tight text-muted-foreground"
230
+ : "mt-0.5 block truncate text-[11px] leading-tight text-muted-foreground"
231
+ )}
232
+ >
189
233
  {item.description}
190
234
  </span>
191
235
  ) : null}
192
236
  </span>
193
237
  {item.disabled && item.disabledReason ? (
194
- <span className="ml-auto shrink-0 text-[10.5px] italic text-muted-foreground/80">
238
+ <span
239
+ data-slot="contextual-quick-action-item-disabled-meta"
240
+ className={cn(
241
+ isCasePanel
242
+ ? "ml-auto shrink-0 whitespace-nowrap text-[12px] italic text-muted-foreground/70"
243
+ : "ml-auto shrink-0 text-[10.5px] italic text-muted-foreground/80"
244
+ )}
245
+ >
195
246
  {item.disabledReason}
196
247
  </span>
248
+ ) : item.meta ? (
249
+ <span
250
+ data-slot="contextual-quick-action-item-meta"
251
+ className={cn(
252
+ isCasePanel
253
+ ? "ml-auto shrink-0 whitespace-nowrap text-[12px] italic text-muted-foreground/70"
254
+ : "ml-auto shrink-0 text-[10.5px] text-muted-foreground/80"
255
+ )}
256
+ >
257
+ {item.meta}
258
+ </span>
197
259
  ) : null}
198
260
  </DropdownMenuItem>
199
261
  ))}
200
262
 
201
- <DropdownMenuSeparator className="-mx-1.5 my-1" />
202
- <div className="flex items-center justify-between px-2 py-1.5 text-[11px] text-muted-foreground">
263
+ <DropdownMenuSeparator className={cn(isCasePanel ? "-mx-2 mt-1 mb-1" : "-mx-1.5 my-1")} />
264
+ <div
265
+ className={cn(
266
+ isCasePanel
267
+ ? "flex items-center justify-between whitespace-nowrap px-2.5 py-2 text-[13px] text-muted-foreground"
268
+ : "flex items-center justify-between px-2 py-1.5 text-[11px] text-muted-foreground"
269
+ )}
270
+ >
203
271
  <button
204
272
  type="button"
205
- className="inline-flex items-center gap-1 font-medium text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
273
+ className={cn(
274
+ isCasePanel
275
+ ? "inline-flex items-center gap-1 font-semibold text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
276
+ : "inline-flex items-center gap-1 font-medium text-foreground hover:underline focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2"
277
+ )}
206
278
  onClick={handleBrowseAll}
207
279
  >
208
280
  Browse all actions
209
- <ArrowRight className="h-3 w-3" strokeWidth={2} aria-hidden="true" />
210
281
  </button>
211
- <span className="inline-flex items-center rounded border border-border px-1 py-0.5 text-[10px] font-medium leading-none text-muted-foreground">
282
+ <span
283
+ className={cn(
284
+ isCasePanel
285
+ ? "inline-flex items-center rounded-[5px] border border-border bg-muted/50 px-1.5 py-0.5 font-mono text-[11px] font-medium leading-none text-muted-foreground"
286
+ : "inline-flex items-center rounded border border-border px-1 py-0.5 text-[10px] font-medium leading-none text-muted-foreground"
287
+ )}
288
+ >
212
289
  ⌘K
213
290
  </span>
214
291
  </div>
@@ -892,7 +892,6 @@ export function DataTable({
892
892
  title={data.title}
893
893
  description={data.description}
894
894
  score={scoreModal.type === "Risk" ? scoreModal.row.riskScore : scoreModal.row.expansionScore}
895
- scoreIntent={scoreModal.type === "Risk" ? "risk" : "positive"}
896
895
  whyNow={data.whyNow}
897
896
  evidence={data.evidence}
898
897
  factors={data.factors}
@@ -7,7 +7,6 @@ import { ScoreRing } from "./score-ring"
7
7
  import { ScoreBreakdown, type ScoreFactor } from "./score-breakdown"
8
8
  import { SignalApproval } from "./signal-feedback-inline"
9
9
  import { X } from "lucide-react"
10
- import type { ScoreIntent } from "./score-semantics"
11
10
 
12
11
  interface ScoreAnalysisModalProps {
13
12
  open: boolean
@@ -27,25 +26,10 @@ interface ScoreAnalysisModalProps {
27
26
  onDismiss?: (reasons: string[], detail: string) => void
28
27
  /** When true, renders as an absolute-positioned inline panel instead of a Radix Sheet portal. Useful when the component is inside a container that should not be escaped. */
29
28
  useInlinePanel?: boolean
30
- scoreIntent?: ScoreIntent
31
29
  }
32
30
 
33
- function getScoreLabel(score: number, denominator: number, intent: ScoreIntent = "positive") {
31
+ function getScoreLabel(score: number, denominator: number) {
34
32
  const pct = (score / denominator) * 100
35
-
36
- if (intent === "urgency") {
37
- if (pct >= 80) return { text: "URGENT", className: "text-red-600" }
38
- if (pct >= 60) return { text: "HIGH", className: "text-orange-600" }
39
- if (pct >= 35) return { text: "MEDIUM", className: "text-amber-600" }
40
- return { text: "LOW", className: "text-neutral-600" }
41
- }
42
-
43
- if (intent === "risk") {
44
- if (pct >= 70) return { text: "High Risk", className: "text-red-600" }
45
- if (pct >= 40) return { text: "Medium Risk", className: "text-amber-600" }
46
- return { text: "Low Risk", className: "text-neutral-600" }
47
- }
48
-
49
33
  if (pct >= 70) return { text: "HIGH", className: "text-emerald-600" }
50
34
  if (pct >= 40) return { text: "MEDIUM", className: "text-amber-600" }
51
35
  return { text: "LOW", className: "text-red-600" }
@@ -68,9 +52,8 @@ function ScoreAnalysisModal({
68
52
  onApproveFeedback,
69
53
  onDismiss,
70
54
  useInlinePanel = false,
71
- scoreIntent = "positive",
72
55
  }: ScoreAnalysisModalProps) {
73
- const label = getScoreLabel(score, denominator, scoreIntent)
56
+ const label = getScoreLabel(score, denominator)
74
57
 
75
58
  const panelContent = (
76
59
  <SignalApproval.Root
@@ -100,7 +83,7 @@ function ScoreAnalysisModal({
100
83
 
101
84
  <div className="space-y-6">
102
85
  <div className="flex flex-col items-center gap-3">
103
- <ScoreRing score={score} denominator={denominator} size={120} strokeWidth={10} intent={scoreIntent} />
86
+ <ScoreRing score={score} denominator={denominator} size={120} strokeWidth={10} />
104
87
  <Badge variant="outline">
105
88
  {Math.round((score / denominator) * 100)}% Score
106
89
  {" \u2014 "}
@@ -128,7 +111,7 @@ function ScoreAnalysisModal({
128
111
  {factors && factors.length > 0 && (
129
112
  <div>
130
113
  <h3 className="font-semibold mb-2 text-sm">Score Breakdown</h3>
131
- <ScoreBreakdown factors={factors} onFactorFeedback={onFactorFeedback} scoreIntent={scoreIntent} />
114
+ <ScoreBreakdown factors={factors} onFactorFeedback={onFactorFeedback} />
132
115
  </div>
133
116
  )}
134
117
  </div>
@@ -185,5 +168,5 @@ function ScoreAnalysisModal({
185
168
 
186
169
  const ScoreAnalysisPanel = ScoreAnalysisModal
187
170
 
188
- export { ScoreAnalysisModal, ScoreAnalysisPanel, getScoreLabel }
171
+ export { ScoreAnalysisModal, ScoreAnalysisPanel }
189
172
  export type { ScoreAnalysisModalProps }
@@ -3,8 +3,6 @@
3
3
  import * as React from "react"
4
4
  import { ThumbsUp, ThumbsDown } from "lucide-react"
5
5
  import { cn } from "../lib/utils"
6
- import type { ScoreIntent } from "./score-semantics"
7
- import { getScoreToneClasses } from "./score-semantics"
8
6
 
9
7
  export interface ScoreFactor {
10
8
  key: string
@@ -14,8 +12,10 @@ export interface ScoreFactor {
14
12
  why: string
15
13
  }
16
14
 
17
- function getFactorBarColor(score: number, intent: ScoreIntent = "positive") {
18
- return getScoreToneClasses(score, intent).bar
15
+ function getFactorBarColor(score: number) {
16
+ if (score >= 70) return "bg-emerald-500"
17
+ if (score >= 40) return "bg-amber-500"
18
+ return "bg-red-500"
19
19
  }
20
20
 
21
21
  function getRiskBadgeStyle(risk: "Low" | "Medium" | "High") {
@@ -34,7 +34,6 @@ interface ScoreBreakdownProps {
34
34
  onFactorFeedback?: (factorKey: string, type: "up" | "down" | null, detail?: string) => void
35
35
  className?: string
36
36
  initialFeedback?: Record<string, { type: "up" | "down"; detail: string }>
37
- scoreIntent?: ScoreIntent
38
37
  }
39
38
 
40
39
  function deriveInitialState<T>(
@@ -48,7 +47,7 @@ function deriveInitialState<T>(
48
47
  return Object.fromEntries(filtered.map(([k, v]) => [k, mapFn(v)]))
49
48
  }
50
49
 
51
- function ScoreBreakdown({ factors, onFactorFeedback, className, initialFeedback, scoreIntent = "positive" }: ScoreBreakdownProps) {
50
+ function ScoreBreakdown({ factors, onFactorFeedback, className, initialFeedback }: ScoreBreakdownProps) {
52
51
  const [feedback, setFeedback] = React.useState<Record<string, "up" | "down" | null>>(
53
52
  () => deriveInitialState(initialFeedback, (v) => v.type)
54
53
  )
@@ -171,7 +170,7 @@ function ScoreBreakdown({ factors, onFactorFeedback, className, initialFeedback,
171
170
  {factor.score !== null && (
172
171
  <div className="w-full h-1 bg-muted rounded-full overflow-hidden">
173
172
  <div
174
- className={cn("h-full rounded-full", getFactorBarColor(factor.score, scoreIntent))}
173
+ className={cn("h-full rounded-full", getFactorBarColor(factor.score))}
175
174
  style={{ width: `${factor.score}%` }}
176
175
  />
177
176
  </div>
@@ -1,14 +1,18 @@
1
1
  import * as React from "react"
2
2
  import { cn } from "../lib/utils"
3
- import type { ScoreIntent } from "./score-semantics"
4
- import { getScoreToneClasses } from "./score-semantics"
5
3
 
6
- function getScoreColor(score: number, denominator: number, intent: ScoreIntent = "positive") {
7
- return getScoreToneClasses((score / denominator) * 100, intent).text
4
+ function getScoreColor(score: number, denominator: number) {
5
+ const pct = (score / denominator) * 100
6
+ if (pct >= 70) return "text-emerald-500"
7
+ if (pct >= 40) return "text-amber-500"
8
+ return "text-red-500"
8
9
  }
9
10
 
10
- function getScoreTrackColor(score: number, denominator: number, intent: ScoreIntent = "positive") {
11
- return getScoreToneClasses((score / denominator) * 100, intent).track
11
+ function getScoreTrackColor(score: number, denominator: number) {
12
+ const pct = (score / denominator) * 100
13
+ if (pct >= 70) return "text-emerald-500/15"
14
+ if (pct >= 40) return "text-amber-500/15"
15
+ return "text-red-500/15"
12
16
  }
13
17
 
14
18
  interface ScoreRingProps {
@@ -18,7 +22,6 @@ interface ScoreRingProps {
18
22
  strokeWidth?: number
19
23
  className?: string
20
24
  showLabel?: boolean
21
- intent?: ScoreIntent
22
25
  }
23
26
 
24
27
  function ScoreRing({
@@ -28,7 +31,6 @@ function ScoreRing({
28
31
  strokeWidth = 10,
29
32
  className,
30
33
  showLabel = true,
31
- intent = "positive",
32
34
  }: ScoreRingProps) {
33
35
  const radius = (size - strokeWidth) / 2
34
36
  const circumference = 2 * Math.PI * radius
@@ -51,7 +53,7 @@ function ScoreRing({
51
53
  fill="none"
52
54
  stroke="currentColor"
53
55
  strokeWidth={strokeWidth}
54
- className={getScoreTrackColor(score, denominator, intent)}
56
+ className={getScoreTrackColor(score, denominator)}
55
57
  />
56
58
  {/* Fill */}
57
59
  <circle
@@ -64,7 +66,7 @@ function ScoreRing({
64
66
  strokeLinecap="round"
65
67
  strokeDasharray={circumference}
66
68
  strokeDashoffset={offset}
67
- className={cn("transition-all duration-500", getScoreColor(score, denominator, intent))}
69
+ className={cn("transition-all duration-500", getScoreColor(score, denominator))}
68
70
  />
69
71
  </svg>
70
72
  {showLabel && (
@@ -81,5 +83,5 @@ function ScoreRing({
81
83
  )
82
84
  }
83
85
 
84
- export { ScoreRing, getScoreColor, getScoreTrackColor }
86
+ export { ScoreRing, getScoreColor }
85
87
  export type { ScoreRingProps }
@@ -17,12 +17,6 @@ import type { LucideIcon } from "lucide-react"
17
17
  import { FeedbackFooter } from "./feedback-primitives"
18
18
  import type { FeedbackChipTree, FeedbackSubmitData, PersistedFeedbackData } from "./feedback-primitives"
19
19
  import { cn } from "../lib/utils"
20
- import {
21
- DEFAULT_SCORE_TONE_CLASS,
22
- SCORE_TONE_CLASSES,
23
- getUrgencyLevel,
24
- getUrgencyRange,
25
- } from "./score-semantics"
26
20
  import type {
27
21
  QueueItem,
28
22
  SignalScoreData,
@@ -39,11 +33,24 @@ export function getSignalScoreUrgencyLabel(
39
33
  score: number,
40
34
  providedLabel?: SignalScoreUrgencyLabel,
41
35
  ): SignalScoreUrgencyLabel {
42
- return providedLabel ?? getUrgencyLevel(score)
36
+ if (providedLabel) return providedLabel
37
+ if (score >= 80) return "Urgent"
38
+ if (score >= 60) return "High"
39
+ if (score >= 35) return "Medium"
40
+ return "Low"
43
41
  }
44
42
 
45
43
  export function scoreRangeForUrgency(label: SignalScoreUrgencyLabel): string {
46
- return getUrgencyRange(label)
44
+ switch (label) {
45
+ case "Urgent":
46
+ return "80-100"
47
+ case "High":
48
+ return "60-79"
49
+ case "Medium":
50
+ return "35-59"
51
+ case "Low":
52
+ return "0-34"
53
+ }
47
54
  }
48
55
 
49
56
  function makeDomId(...parts: Array<string | undefined>): string {
@@ -100,11 +107,15 @@ function resolveIcon(iconName?: string): LucideIcon {
100
107
  // Static tone class maps (REQUIRED for Tailwind v4 source scanning)
101
108
  // ---------------------------------------------------------------------------
102
109
 
103
- /** Shared tone-to-class map. Re-exported for backward compatibility. */
104
- export const SIGNAL_TONE_CLASSES: Record<string, string> = SCORE_TONE_CLASSES
110
+ /** Shared tone-to-class map. Re-exported for signal-priority-popover. */
111
+ export const SIGNAL_TONE_CLASSES: Record<string, string> = {
112
+ alert: "bg-red-50 text-red-600",
113
+ warn: "bg-amber-50 text-amber-600",
114
+ info: "bg-blue-50 text-blue-600",
115
+ }
105
116
 
106
- /** Default tone for missing/unknown tone values. Re-exported for backward compatibility. */
107
- export const DEFAULT_TONE_CLASS = DEFAULT_SCORE_TONE_CLASS
117
+ /** Default tone for missing/unknown tone values */
118
+ export const DEFAULT_TONE_CLASS = "bg-muted text-muted-foreground"
108
119
 
109
120
  // ---------------------------------------------------------------------------
110
121
  // Em-dash fallback for missing slot data
@@ -22,7 +22,7 @@ import { cn } from "../lib/utils"
22
22
  import { FeedbackFooter, InlineFeedbackControl } from "./feedback-primitives"
23
23
  import type { FeedbackChipTree, FeedbackSubmitData, PersistedFeedbackData } from "./feedback-primitives"
24
24
  import type { SignalScoreUrgencyLabel } from "../prototype/prototype-config"
25
- import { SCORE_TONE_CLASSES, URGENCY_SCORE_CLASSES, getUrgencyLevel, getUrgencyRange } from "./score-semantics"
25
+ import { getSignalScoreUrgencyLabel, scoreRangeForUrgency, SIGNAL_TONE_CLASSES } from "./score-why-chips"
26
26
 
27
27
  // ---------------------------------------------------------------------------
28
28
  // Types
@@ -75,28 +75,28 @@ export interface SignalPriorityPopoverProps {
75
75
  // ---------------------------------------------------------------------------
76
76
 
77
77
  const URGENCY_TRIGGER_DEFAULT: Record<SignalScoreUrgencyLabel, string> = {
78
- Urgent: URGENCY_SCORE_CLASSES.Urgent.trigger,
79
- High: URGENCY_SCORE_CLASSES.High.trigger,
80
- Medium: URGENCY_SCORE_CLASSES.Medium.trigger,
81
- Low: URGENCY_SCORE_CLASSES.Low.trigger,
78
+ Urgent: "border-red-200 bg-red-50 text-red-700",
79
+ High: "border-orange-200 bg-orange-50 text-orange-700",
80
+ Medium: "border-amber-200 bg-amber-50 text-amber-700",
81
+ Low: "border-blue-200 bg-blue-50 text-blue-700",
82
82
  }
83
83
 
84
84
  const URGENCY_TRIGGER_HOVER: Record<SignalScoreUrgencyLabel, string> = {
85
- Urgent: URGENCY_SCORE_CLASSES.Urgent.hover,
86
- High: URGENCY_SCORE_CLASSES.High.hover,
87
- Medium: URGENCY_SCORE_CLASSES.Medium.hover,
88
- Low: URGENCY_SCORE_CLASSES.Low.hover,
85
+ Urgent: "hover:bg-red-100",
86
+ High: "hover:bg-orange-100",
87
+ Medium: "hover:bg-amber-100",
88
+ Low: "hover:bg-blue-100",
89
89
  }
90
90
 
91
91
  const URGENCY_TRIGGER_OPEN: Record<SignalScoreUrgencyLabel, string> = {
92
- Urgent: URGENCY_SCORE_CLASSES.Urgent.open,
93
- High: URGENCY_SCORE_CLASSES.High.open,
94
- Medium: URGENCY_SCORE_CLASSES.Medium.open,
95
- Low: URGENCY_SCORE_CLASSES.Low.open,
92
+ Urgent: "bg-red-100",
93
+ High: "bg-orange-100",
94
+ Medium: "bg-amber-100",
95
+ Low: "bg-blue-100",
96
96
  }
97
97
 
98
- /** Re-use shared tone classes from score-semantics. */
99
- const TONE_ICON_CLASSES: Record<PriorityFactor["tone"], string> = SCORE_TONE_CLASSES as Record<PriorityFactor["tone"], string>
98
+ /** Re-use shared tone classes from score-why-chips. */
99
+ const TONE_ICON_CLASSES: Record<PriorityFactor["tone"], string> = SIGNAL_TONE_CLASSES as Record<PriorityFactor["tone"], string>
100
100
 
101
101
  const DIRECTION_CLASSES: Record<PriorityFactor["direction"], string> = {
102
102
  raises: "text-red-600",
@@ -124,10 +124,10 @@ const FACTOR_ICONS: Record<string, LucideIcon> = {
124
124
  // ---------------------------------------------------------------------------
125
125
 
126
126
  const URGENCY_DOT_CLASSES: Record<SignalScoreUrgencyLabel, string> = {
127
- Urgent: URGENCY_SCORE_CLASSES.Urgent.dot,
128
- High: URGENCY_SCORE_CLASSES.High.dot,
129
- Medium: URGENCY_SCORE_CLASSES.Medium.dot,
130
- Low: URGENCY_SCORE_CLASSES.Low.dot,
127
+ Urgent: "bg-red-500",
128
+ High: "bg-orange-500",
129
+ Medium: "bg-amber-500",
130
+ Low: "bg-blue-500",
131
131
  }
132
132
 
133
133
  // ---------------------------------------------------------------------------
@@ -283,8 +283,8 @@ export function SignalPriorityPopover({
283
283
  onFactorFeedback,
284
284
  initialPriorityFeedback,
285
285
  }: SignalPriorityPopoverProps) {
286
- const urgencyLabel = providedLabel ?? getUrgencyLevel(score)
287
- const scoreRange = getUrgencyRange(urgencyLabel)
286
+ const urgencyLabel = getSignalScoreUrgencyLabel(score, providedLabel)
287
+ const scoreRange = scoreRangeForUrgency(urgencyLabel)
288
288
 
289
289
  const [open, setOpen] = React.useState(false)
290
290
  const [feedback, setFeedback] = React.useState<"positive" | "negative" | null>(null)
package/src/index.ts CHANGED
@@ -20,6 +20,10 @@ export * from "./components/avatar"
20
20
  export * from "./components/badge"
21
21
  export * from "./components/button"
22
22
  export * from "./components/card"
23
+ export * from "./components/case-panel-email-composer"
24
+ export * from "./components/case-panel-activity-timeline"
25
+ export * from "./components/case-panel-detail"
26
+ export * from "./components/case-panel-why"
23
27
  export { CollapsibleSection, type CollapsibleSectionProps } from "./components/collapsible-section"
24
28
  export * from "./components/compliance-badge"
25
29
  export * from "./components/contact-chip"
@@ -77,7 +81,6 @@ export * from "./components/rich-text-toolbar"
77
81
  export * from "./components/score-analysis-modal"
78
82
  export * from "./components/score-breakdown"
79
83
  export * from "./components/score-feedback"
80
- export * from "./components/score-semantics"
81
84
  export * from "./components/score-why-chips"
82
85
  export * from "./components/score-ring"
83
86
  export * from "./components/scroll-area"
@@ -1,27 +0,0 @@
1
- type ScoreIntent = "urgency" | "risk" | "positive";
2
- type UrgencyLevel = "Low" | "Medium" | "High" | "Urgent";
3
- type RiskLevel = "Low Risk" | "Medium Risk" | "High Risk";
4
- type PositiveLevel = "Low" | "Medium" | "High";
5
- type ScoreSemanticClasses = {
6
- solid: string;
7
- outline: string;
8
- dot: string;
9
- trigger: string;
10
- hover: string;
11
- open: string;
12
- text: string;
13
- track: string;
14
- bar: string;
15
- };
16
- declare const URGENCY_SCORE_CLASSES: Record<UrgencyLevel, ScoreSemanticClasses>;
17
- declare const RISK_SCORE_CLASSES: Record<RiskLevel, ScoreSemanticClasses>;
18
- declare const POSITIVE_SCORE_CLASSES: Record<PositiveLevel, ScoreSemanticClasses>;
19
- declare const SCORE_TONE_CLASSES: Record<string, string>;
20
- declare const DEFAULT_SCORE_TONE_CLASS = "bg-muted text-muted-foreground";
21
- declare function getUrgencyLevel(score: number): UrgencyLevel;
22
- declare function getUrgencyRange(label: UrgencyLevel): string;
23
- declare function getRiskLevel(score: number): RiskLevel;
24
- declare function getPositiveLevel(score: number): PositiveLevel;
25
- declare function getScoreToneClasses(score: number, intent: ScoreIntent): ScoreSemanticClasses;
26
-
27
- export { DEFAULT_SCORE_TONE_CLASS, POSITIVE_SCORE_CLASSES, type PositiveLevel, RISK_SCORE_CLASSES, type RiskLevel, SCORE_TONE_CLASSES, type ScoreIntent, type ScoreSemanticClasses, URGENCY_SCORE_CLASSES, type UrgencyLevel, getPositiveLevel, getRiskLevel, getScoreToneClasses, getUrgencyLevel, getUrgencyRange };