@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,173 +0,0 @@
1
- const URGENCY_SCORE_CLASSES = {
2
- Urgent: {
3
- solid: "bg-red-600 text-white",
4
- outline: "border-red-300 bg-red-50 text-red-800",
5
- dot: "bg-red-600",
6
- trigger: "border-red-300 bg-red-50 text-red-800",
7
- hover: "hover:bg-red-100",
8
- open: "bg-red-100",
9
- text: "text-red-600",
10
- track: "text-red-600/15",
11
- bar: "bg-red-600"
12
- },
13
- High: {
14
- solid: "bg-orange-500 text-white",
15
- outline: "border-orange-300 bg-orange-50 text-orange-800",
16
- dot: "bg-orange-500",
17
- trigger: "border-orange-300 bg-orange-50 text-orange-800",
18
- hover: "hover:bg-orange-100",
19
- open: "bg-orange-100",
20
- text: "text-orange-500",
21
- track: "text-orange-500/15",
22
- bar: "bg-orange-500"
23
- },
24
- Medium: {
25
- solid: "bg-amber-500 text-white",
26
- outline: "border-amber-300 bg-amber-50 text-amber-800",
27
- dot: "bg-amber-500",
28
- trigger: "border-amber-300 bg-amber-50 text-amber-800",
29
- hover: "hover:bg-amber-100",
30
- open: "bg-amber-100",
31
- text: "text-amber-500",
32
- track: "text-amber-500/15",
33
- bar: "bg-amber-500"
34
- },
35
- Low: {
36
- solid: "bg-neutral-300 text-neutral-900",
37
- outline: "border-neutral-200 bg-neutral-50 text-neutral-700",
38
- dot: "bg-neutral-400",
39
- trigger: "border-neutral-200 bg-neutral-50 text-neutral-700",
40
- hover: "hover:bg-neutral-100",
41
- open: "bg-neutral-100",
42
- text: "text-neutral-400",
43
- track: "text-neutral-400/15",
44
- bar: "bg-neutral-400"
45
- }
46
- };
47
- const RISK_SCORE_CLASSES = {
48
- "High Risk": {
49
- solid: "bg-red-600 text-white",
50
- outline: "border-red-300 bg-red-50 text-red-800",
51
- dot: "bg-red-600",
52
- trigger: "border-red-300 bg-red-50 text-red-800",
53
- hover: "hover:bg-red-100",
54
- open: "bg-red-100",
55
- text: "text-red-600",
56
- track: "text-red-600/15",
57
- bar: "bg-red-600"
58
- },
59
- "Medium Risk": {
60
- solid: "bg-amber-500 text-white",
61
- outline: "border-amber-300 bg-amber-50 text-amber-800",
62
- dot: "bg-amber-500",
63
- trigger: "border-amber-300 bg-amber-50 text-amber-800",
64
- hover: "hover:bg-amber-100",
65
- open: "bg-amber-100",
66
- text: "text-amber-500",
67
- track: "text-amber-500/15",
68
- bar: "bg-amber-500"
69
- },
70
- "Low Risk": {
71
- solid: "bg-neutral-300 text-neutral-900",
72
- outline: "border-neutral-200 bg-neutral-50 text-neutral-700",
73
- dot: "bg-neutral-400",
74
- trigger: "border-neutral-200 bg-neutral-50 text-neutral-700",
75
- hover: "hover:bg-neutral-100",
76
- open: "bg-neutral-100",
77
- text: "text-neutral-400",
78
- track: "text-neutral-400/15",
79
- bar: "bg-neutral-400"
80
- }
81
- };
82
- const POSITIVE_SCORE_CLASSES = {
83
- High: {
84
- solid: "bg-emerald-500 text-white",
85
- outline: "border-emerald-300 bg-emerald-50 text-emerald-800",
86
- dot: "bg-emerald-500",
87
- trigger: "border-emerald-300 bg-emerald-50 text-emerald-800",
88
- hover: "hover:bg-emerald-100",
89
- open: "bg-emerald-100",
90
- text: "text-emerald-500",
91
- track: "text-emerald-500/15",
92
- bar: "bg-emerald-500"
93
- },
94
- Medium: {
95
- solid: "bg-amber-500 text-white",
96
- outline: "border-amber-300 bg-amber-50 text-amber-800",
97
- dot: "bg-amber-500",
98
- trigger: "border-amber-300 bg-amber-50 text-amber-800",
99
- hover: "hover:bg-amber-100",
100
- open: "bg-amber-100",
101
- text: "text-amber-500",
102
- track: "text-amber-500/15",
103
- bar: "bg-amber-500"
104
- },
105
- Low: {
106
- solid: "bg-red-500 text-white",
107
- outline: "border-red-300 bg-red-50 text-red-800",
108
- dot: "bg-red-500",
109
- trigger: "border-red-300 bg-red-50 text-red-800",
110
- hover: "hover:bg-red-100",
111
- open: "bg-red-100",
112
- text: "text-red-500",
113
- track: "text-red-500/15",
114
- bar: "bg-red-500"
115
- }
116
- };
117
- const SCORE_TONE_CLASSES = {
118
- alert: "bg-red-50 text-red-600",
119
- warn: "bg-amber-50 text-amber-600",
120
- info: "bg-blue-50 text-blue-600"
121
- };
122
- const DEFAULT_SCORE_TONE_CLASS = "bg-muted text-muted-foreground";
123
- function getUrgencyLevel(score) {
124
- if (score >= 80) return "Urgent";
125
- if (score >= 60) return "High";
126
- if (score >= 35) return "Medium";
127
- return "Low";
128
- }
129
- function getUrgencyRange(label) {
130
- switch (label) {
131
- case "Urgent":
132
- return "80-100";
133
- case "High":
134
- return "60-79";
135
- case "Medium":
136
- return "35-59";
137
- case "Low":
138
- return "0-34";
139
- }
140
- }
141
- function getRiskLevel(score) {
142
- if (score >= 70) return "High Risk";
143
- if (score >= 40) return "Medium Risk";
144
- return "Low Risk";
145
- }
146
- function getPositiveLevel(score) {
147
- if (score >= 70) return "High";
148
- if (score >= 40) return "Medium";
149
- return "Low";
150
- }
151
- function getScoreToneClasses(score, intent) {
152
- switch (intent) {
153
- case "urgency":
154
- return URGENCY_SCORE_CLASSES[getUrgencyLevel(score)];
155
- case "risk":
156
- return RISK_SCORE_CLASSES[getRiskLevel(score)];
157
- case "positive":
158
- return POSITIVE_SCORE_CLASSES[getPositiveLevel(score)];
159
- }
160
- }
161
- export {
162
- DEFAULT_SCORE_TONE_CLASS,
163
- POSITIVE_SCORE_CLASSES,
164
- RISK_SCORE_CLASSES,
165
- SCORE_TONE_CLASSES,
166
- URGENCY_SCORE_CLASSES,
167
- getPositiveLevel,
168
- getRiskLevel,
169
- getScoreToneClasses,
170
- getUrgencyLevel,
171
- getUrgencyRange
172
- };
173
- //# sourceMappingURL=score-semantics.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/components/score-semantics.ts"],"sourcesContent":["export type ScoreIntent = \"urgency\" | \"risk\" | \"positive\"\n\nexport type UrgencyLevel = \"Low\" | \"Medium\" | \"High\" | \"Urgent\"\nexport type RiskLevel = \"Low Risk\" | \"Medium Risk\" | \"High Risk\"\nexport type PositiveLevel = \"Low\" | \"Medium\" | \"High\"\n\nexport type ScoreSemanticClasses = {\n solid: string\n outline: string\n dot: string\n trigger: string\n hover: string\n open: string\n text: string\n track: string\n bar: string\n}\n\nexport const URGENCY_SCORE_CLASSES: Record<UrgencyLevel, ScoreSemanticClasses> = {\n Urgent: {\n solid: \"bg-red-600 text-white\",\n outline: \"border-red-300 bg-red-50 text-red-800\",\n dot: \"bg-red-600\",\n trigger: \"border-red-300 bg-red-50 text-red-800\",\n hover: \"hover:bg-red-100\",\n open: \"bg-red-100\",\n text: \"text-red-600\",\n track: \"text-red-600/15\",\n bar: \"bg-red-600\",\n },\n High: {\n solid: \"bg-orange-500 text-white\",\n outline: \"border-orange-300 bg-orange-50 text-orange-800\",\n dot: \"bg-orange-500\",\n trigger: \"border-orange-300 bg-orange-50 text-orange-800\",\n hover: \"hover:bg-orange-100\",\n open: \"bg-orange-100\",\n text: \"text-orange-500\",\n track: \"text-orange-500/15\",\n bar: \"bg-orange-500\",\n },\n Medium: {\n solid: \"bg-amber-500 text-white\",\n outline: \"border-amber-300 bg-amber-50 text-amber-800\",\n dot: \"bg-amber-500\",\n trigger: \"border-amber-300 bg-amber-50 text-amber-800\",\n hover: \"hover:bg-amber-100\",\n open: \"bg-amber-100\",\n text: \"text-amber-500\",\n track: \"text-amber-500/15\",\n bar: \"bg-amber-500\",\n },\n Low: {\n solid: \"bg-neutral-300 text-neutral-900\",\n outline: \"border-neutral-200 bg-neutral-50 text-neutral-700\",\n dot: \"bg-neutral-400\",\n trigger: \"border-neutral-200 bg-neutral-50 text-neutral-700\",\n hover: \"hover:bg-neutral-100\",\n open: \"bg-neutral-100\",\n text: \"text-neutral-400\",\n track: \"text-neutral-400/15\",\n bar: \"bg-neutral-400\",\n },\n}\n\nexport const RISK_SCORE_CLASSES: Record<RiskLevel, ScoreSemanticClasses> = {\n \"High Risk\": {\n solid: \"bg-red-600 text-white\",\n outline: \"border-red-300 bg-red-50 text-red-800\",\n dot: \"bg-red-600\",\n trigger: \"border-red-300 bg-red-50 text-red-800\",\n hover: \"hover:bg-red-100\",\n open: \"bg-red-100\",\n text: \"text-red-600\",\n track: \"text-red-600/15\",\n bar: \"bg-red-600\",\n },\n \"Medium Risk\": {\n solid: \"bg-amber-500 text-white\",\n outline: \"border-amber-300 bg-amber-50 text-amber-800\",\n dot: \"bg-amber-500\",\n trigger: \"border-amber-300 bg-amber-50 text-amber-800\",\n hover: \"hover:bg-amber-100\",\n open: \"bg-amber-100\",\n text: \"text-amber-500\",\n track: \"text-amber-500/15\",\n bar: \"bg-amber-500\",\n },\n \"Low Risk\": {\n solid: \"bg-neutral-300 text-neutral-900\",\n outline: \"border-neutral-200 bg-neutral-50 text-neutral-700\",\n dot: \"bg-neutral-400\",\n trigger: \"border-neutral-200 bg-neutral-50 text-neutral-700\",\n hover: \"hover:bg-neutral-100\",\n open: \"bg-neutral-100\",\n text: \"text-neutral-400\",\n track: \"text-neutral-400/15\",\n bar: \"bg-neutral-400\",\n },\n}\n\nexport const POSITIVE_SCORE_CLASSES: Record<PositiveLevel, ScoreSemanticClasses> = {\n High: {\n solid: \"bg-emerald-500 text-white\",\n outline: \"border-emerald-300 bg-emerald-50 text-emerald-800\",\n dot: \"bg-emerald-500\",\n trigger: \"border-emerald-300 bg-emerald-50 text-emerald-800\",\n hover: \"hover:bg-emerald-100\",\n open: \"bg-emerald-100\",\n text: \"text-emerald-500\",\n track: \"text-emerald-500/15\",\n bar: \"bg-emerald-500\",\n },\n Medium: {\n solid: \"bg-amber-500 text-white\",\n outline: \"border-amber-300 bg-amber-50 text-amber-800\",\n dot: \"bg-amber-500\",\n trigger: \"border-amber-300 bg-amber-50 text-amber-800\",\n hover: \"hover:bg-amber-100\",\n open: \"bg-amber-100\",\n text: \"text-amber-500\",\n track: \"text-amber-500/15\",\n bar: \"bg-amber-500\",\n },\n Low: {\n solid: \"bg-red-500 text-white\",\n outline: \"border-red-300 bg-red-50 text-red-800\",\n dot: \"bg-red-500\",\n trigger: \"border-red-300 bg-red-50 text-red-800\",\n hover: \"hover:bg-red-100\",\n open: \"bg-red-100\",\n text: \"text-red-500\",\n track: \"text-red-500/15\",\n bar: \"bg-red-500\",\n },\n}\n\nexport const SCORE_TONE_CLASSES: Record<string, string> = {\n alert: \"bg-red-50 text-red-600\",\n warn: \"bg-amber-50 text-amber-600\",\n info: \"bg-blue-50 text-blue-600\",\n}\n\nexport const DEFAULT_SCORE_TONE_CLASS = \"bg-muted text-muted-foreground\"\n\nexport function getUrgencyLevel(score: number): UrgencyLevel {\n if (score >= 80) return \"Urgent\"\n if (score >= 60) return \"High\"\n if (score >= 35) return \"Medium\"\n return \"Low\"\n}\n\nexport function getUrgencyRange(label: UrgencyLevel): string {\n switch (label) {\n case \"Urgent\":\n return \"80-100\"\n case \"High\":\n return \"60-79\"\n case \"Medium\":\n return \"35-59\"\n case \"Low\":\n return \"0-34\"\n }\n}\n\nexport function getRiskLevel(score: number): RiskLevel {\n if (score >= 70) return \"High Risk\"\n if (score >= 40) return \"Medium Risk\"\n return \"Low Risk\"\n}\n\nexport function getPositiveLevel(score: number): PositiveLevel {\n if (score >= 70) return \"High\"\n if (score >= 40) return \"Medium\"\n return \"Low\"\n}\n\nexport function getScoreToneClasses(score: number, intent: ScoreIntent): ScoreSemanticClasses {\n switch (intent) {\n case \"urgency\":\n return URGENCY_SCORE_CLASSES[getUrgencyLevel(score)]\n case \"risk\":\n return RISK_SCORE_CLASSES[getRiskLevel(score)]\n case \"positive\":\n return POSITIVE_SCORE_CLASSES[getPositiveLevel(score)]\n }\n}\n"],"mappings":"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","names":[]}
@@ -1,55 +0,0 @@
1
- import { describe, expect, it, vi } from "vitest"
2
- import React from "react"
3
- import { render, screen } from "@testing-library/react"
4
- import { ScoreAnalysisModal, getScoreLabel } from "../score-analysis-modal"
5
-
6
- function renderModal(score: number, scoreIntent: React.ComponentProps<typeof ScoreAnalysisModal>["scoreIntent"]) {
7
- return render(
8
- <ScoreAnalysisModal
9
- open
10
- onOpenChange={vi.fn()}
11
- title="Score details"
12
- description="Detailed score analysis"
13
- score={score}
14
- scoreIntent={scoreIntent}
15
- whyNow="Because the score changed."
16
- evidence={["Signal evidence"]}
17
- useInlinePanel
18
- />,
19
- )
20
- }
21
-
22
- describe("ScoreAnalysisModal intent-aware score labels", () => {
23
- it("preserves positive labels and green high score ring", () => {
24
- expect(getScoreLabel(90, 100, "positive")).toEqual({ text: "HIGH", className: "text-emerald-600" })
25
-
26
- const { container } = renderModal(90, "positive")
27
-
28
- expect(screen.getByText("HIGH")).toBeDefined()
29
- expect(container.querySelectorAll("circle")[1]?.getAttribute("class")).toContain("text-emerald-500")
30
- })
31
-
32
- it("uses risk labels and never urgency copy for risk intent", () => {
33
- expect(getScoreLabel(90, 100, "risk")).toEqual({ text: "High Risk", className: "text-red-600" })
34
- expect(getScoreLabel(50, 100, "risk")).toEqual({ text: "Medium Risk", className: "text-amber-600" })
35
- expect(getScoreLabel(20, 100, "risk")).toEqual({ text: "Low Risk", className: "text-neutral-600" })
36
-
37
- const { container } = renderModal(90, "risk")
38
-
39
- expect(screen.getByText("High Risk")).toBeDefined()
40
- expect(screen.queryByText("URGENT")).toBeNull()
41
- expect(container.textContent).not.toContain("80-100")
42
- expect(container.querySelectorAll("circle")[1]?.getAttribute("class")).toContain("text-red-600")
43
- })
44
-
45
- it("uses neutral label/ring for low urgency instead of red", () => {
46
- expect(getScoreLabel(20, 100, "urgency")).toEqual({ text: "LOW", className: "text-neutral-600" })
47
-
48
- const { container } = renderModal(20, "urgency")
49
- const fillClass = container.querySelectorAll("circle")[1]?.getAttribute("class") ?? ""
50
-
51
- expect(screen.getByText("LOW")).toBeDefined()
52
- expect(fillClass).toContain("text-neutral-400")
53
- expect(fillClass).not.toContain("text-red")
54
- })
55
- })
@@ -1,47 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import React from "react"
3
- import { render, screen } from "@testing-library/react"
4
- import { ScoreBreakdown, type ScoreFactor } from "../score-breakdown"
5
-
6
- const factors: ScoreFactor[] = [
7
- { key: "high", label: "High Factor", score: 90, why: "High score" },
8
- { key: "low", label: "Low Factor", score: 20, why: "Low score" },
9
- ]
10
-
11
- function barClasses(scoreIntent: React.ComponentProps<typeof ScoreBreakdown>["scoreIntent"]) {
12
- const { container } = render(<ScoreBreakdown factors={factors} scoreIntent={scoreIntent} />)
13
- return Array.from(container.querySelectorAll(".h-full.rounded-full")).map((bar) => bar.getAttribute("class") ?? "")
14
- }
15
-
16
- describe("ScoreBreakdown intent-aware factor bars", () => {
17
- it("preserves positive high green and low red factor bars", () => {
18
- const [highClass, lowClass] = barClasses("positive")
19
-
20
- expect(highClass).toContain("bg-emerald-500")
21
- expect(lowClass).toContain("bg-red-500")
22
- })
23
-
24
- it("uses red for high risk factor bars", () => {
25
- const [highClass] = barClasses("risk")
26
-
27
- expect(highClass).toContain("bg-red-600")
28
- })
29
-
30
- it("uses neutral, not red, for low urgency factor bars", () => {
31
- const [, lowClass] = barClasses("urgency")
32
-
33
- expect(lowClass).toContain("bg-neutral-400")
34
- expect(lowClass).not.toContain("bg-red")
35
- })
36
-
37
- it("keeps explicit risk badge styling unchanged", () => {
38
- render(
39
- <ScoreBreakdown
40
- scoreIntent="risk"
41
- factors={[{ key: "badge", label: "Badge Factor", score: null, risk: "Low", why: "Risk badge" }]}
42
- />,
43
- )
44
-
45
- expect(screen.getByText("Low").getAttribute("class")).toContain("text-emerald-600")
46
- })
47
- })
@@ -1,43 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import React from "react"
3
- import { render } from "@testing-library/react"
4
- import { ScoreRing, getScoreColor, getScoreTrackColor } from "../score-ring"
5
-
6
- function renderRing(score: number, intent: React.ComponentProps<typeof ScoreRing>["intent"]) {
7
- const { container } = render(<ScoreRing score={score} intent={intent} />)
8
- const circles = container.querySelectorAll("circle")
9
- return {
10
- trackClass: circles[0]?.getAttribute("class") ?? "",
11
- fillClass: circles[1]?.getAttribute("class") ?? "",
12
- }
13
- }
14
-
15
- describe("ScoreRing intent-aware colors", () => {
16
- it("preserves positive high-is-good colors", () => {
17
- expect(getScoreColor(90, 100)).toBe("text-emerald-500")
18
- expect(getScoreColor(20, 100)).toBe("text-red-500")
19
-
20
- expect(renderRing(90, "positive").fillClass).toContain("text-emerald-500")
21
- expect(renderRing(20, "positive").fillClass).toContain("text-red-500")
22
- })
23
-
24
- it("uses red/orange/amber/neutral colors for urgency", () => {
25
- expect(getScoreColor(90, 100, "urgency")).toBe("text-red-600")
26
- expect(getScoreColor(65, 100, "urgency")).toBe("text-orange-500")
27
- expect(getScoreColor(45, 100, "urgency")).toBe("text-amber-500")
28
- expect(getScoreColor(20, 100, "urgency")).toBe("text-neutral-400")
29
- expect(getScoreTrackColor(20, 100, "urgency")).toBe("text-neutral-400/15")
30
-
31
- expect(renderRing(90, "urgency").fillClass).toContain("text-red-600")
32
- expect(renderRing(20, "urgency").fillClass).toContain("text-neutral-400")
33
- expect(renderRing(20, "urgency").fillClass).not.toContain("text-red")
34
- })
35
-
36
- it("uses red for high risk and neutral for low risk", () => {
37
- expect(getScoreColor(90, 100, "risk")).toBe("text-red-600")
38
- expect(getScoreColor(20, 100, "risk")).toBe("text-neutral-400")
39
-
40
- expect(renderRing(90, "risk").fillClass).toContain("text-red-600")
41
- expect(renderRing(20, "risk").trackClass).toContain("text-neutral-400/15")
42
- })
43
- })
@@ -1,107 +0,0 @@
1
- import { describe, expect, it } from "vitest"
2
- import {
3
- POSITIVE_SCORE_CLASSES,
4
- RISK_SCORE_CLASSES,
5
- URGENCY_SCORE_CLASSES,
6
- getPositiveLevel,
7
- getRiskLevel,
8
- getScoreToneClasses,
9
- getUrgencyLevel,
10
- getUrgencyRange,
11
- } from "../score-semantics"
12
-
13
- describe("score semantics", () => {
14
- it.each([
15
- [0, "Low"],
16
- [34, "Low"],
17
- [35, "Medium"],
18
- [59, "Medium"],
19
- [60, "High"],
20
- [79, "High"],
21
- [80, "Urgent"],
22
- [100, "Urgent"],
23
- ] as const)("maps urgency score %i to %s", (score, level) => {
24
- expect(getUrgencyLevel(score)).toBe(level)
25
- })
26
-
27
- it("returns urgency ranges", () => {
28
- expect(getUrgencyRange("Low")).toBe("0-34")
29
- expect(getUrgencyRange("Medium")).toBe("35-59")
30
- expect(getUrgencyRange("High")).toBe("60-79")
31
- expect(getUrgencyRange("Urgent")).toBe("80-100")
32
- })
33
-
34
- it("uses the approved urgency palette", () => {
35
- expect(URGENCY_SCORE_CLASSES.Urgent).toMatchObject({
36
- solid: "bg-red-600 text-white",
37
- outline: "border-red-300 bg-red-50 text-red-800",
38
- dot: "bg-red-600",
39
- trigger: "border-red-300 bg-red-50 text-red-800",
40
- hover: "hover:bg-red-100",
41
- open: "bg-red-100",
42
- text: "text-red-600",
43
- track: "text-red-600/15",
44
- bar: "bg-red-600",
45
- })
46
- expect(URGENCY_SCORE_CLASSES.High.trigger).toBe("border-orange-300 bg-orange-50 text-orange-800")
47
- expect(URGENCY_SCORE_CLASSES.Medium.dot).toBe("bg-amber-500")
48
- expect(URGENCY_SCORE_CLASSES.Low).toMatchObject({
49
- solid: "bg-neutral-300 text-neutral-900",
50
- outline: "border-neutral-200 bg-neutral-50 text-neutral-700",
51
- dot: "bg-neutral-400",
52
- trigger: "border-neutral-200 bg-neutral-50 text-neutral-700",
53
- hover: "hover:bg-neutral-100",
54
- open: "bg-neutral-100",
55
- text: "text-neutral-400",
56
- track: "text-neutral-400/15",
57
- bar: "bg-neutral-400",
58
- })
59
- })
60
-
61
- it("maps risk scores without urgency labels", () => {
62
- expect(getRiskLevel(0)).toBe("Low Risk")
63
- expect(getRiskLevel(39)).toBe("Low Risk")
64
- expect(getRiskLevel(40)).toBe("Medium Risk")
65
- expect(getRiskLevel(69)).toBe("Medium Risk")
66
- expect(getRiskLevel(70)).toBe("High Risk")
67
- expect(RISK_SCORE_CLASSES["High Risk"].solid).toBe("bg-red-600 text-white")
68
- expect(RISK_SCORE_CLASSES["Medium Risk"].dot).toBe("bg-amber-500")
69
- expect(RISK_SCORE_CLASSES["Low Risk"].trigger).toBe("border-neutral-200 bg-neutral-50 text-neutral-700")
70
- })
71
-
72
- it("preserves positive high-is-good semantics", () => {
73
- expect(getPositiveLevel(39)).toBe("Low")
74
- expect(getPositiveLevel(40)).toBe("Medium")
75
- expect(getPositiveLevel(70)).toBe("High")
76
- expect(POSITIVE_SCORE_CLASSES.High.solid).toBe("bg-emerald-500 text-white")
77
- expect(POSITIVE_SCORE_CLASSES.Medium.solid).toBe("bg-amber-500 text-white")
78
- expect(POSITIVE_SCORE_CLASSES.Low.solid).toBe("bg-red-500 text-white")
79
- })
80
-
81
- it("returns static urgency classes from the urgency map", () => {
82
- expect(getScoreToneClasses(0, "urgency")).toBe(URGENCY_SCORE_CLASSES.Low)
83
- expect(getScoreToneClasses(34, "urgency")).toBe(URGENCY_SCORE_CLASSES.Low)
84
- expect(getScoreToneClasses(35, "urgency")).toBe(URGENCY_SCORE_CLASSES.Medium)
85
- expect(getScoreToneClasses(59, "urgency")).toBe(URGENCY_SCORE_CLASSES.Medium)
86
- expect(getScoreToneClasses(60, "urgency")).toBe(URGENCY_SCORE_CLASSES.High)
87
- expect(getScoreToneClasses(79, "urgency")).toBe(URGENCY_SCORE_CLASSES.High)
88
- expect(getScoreToneClasses(80, "urgency")).toBe(URGENCY_SCORE_CLASSES.Urgent)
89
- expect(getScoreToneClasses(100, "urgency")).toBe(URGENCY_SCORE_CLASSES.Urgent)
90
- })
91
-
92
- it("returns static risk classes from risk labels only", () => {
93
- expect(getScoreToneClasses(39, "risk")).toBe(RISK_SCORE_CLASSES["Low Risk"])
94
- expect(getScoreToneClasses(40, "risk")).toBe(RISK_SCORE_CLASSES["Medium Risk"])
95
- expect(getScoreToneClasses(70, "risk")).toBe(RISK_SCORE_CLASSES["High Risk"])
96
- expect(RISK_SCORE_CLASSES).not.toHaveProperty("Low")
97
- expect(RISK_SCORE_CLASSES).not.toHaveProperty("Medium")
98
- expect(RISK_SCORE_CLASSES).not.toHaveProperty("High")
99
- expect(RISK_SCORE_CLASSES).not.toHaveProperty("Urgent")
100
- })
101
-
102
- it("returns static positive classes from high-is-good map", () => {
103
- expect(getScoreToneClasses(39, "positive")).toBe(POSITIVE_SCORE_CLASSES.Low)
104
- expect(getScoreToneClasses(40, "positive")).toBe(POSITIVE_SCORE_CLASSES.Medium)
105
- expect(getScoreToneClasses(70, "positive")).toBe(POSITIVE_SCORE_CLASSES.High)
106
- })
107
- })
@@ -1,187 +0,0 @@
1
- export type ScoreIntent = "urgency" | "risk" | "positive"
2
-
3
- export type UrgencyLevel = "Low" | "Medium" | "High" | "Urgent"
4
- export type RiskLevel = "Low Risk" | "Medium Risk" | "High Risk"
5
- export type PositiveLevel = "Low" | "Medium" | "High"
6
-
7
- export type ScoreSemanticClasses = {
8
- solid: string
9
- outline: string
10
- dot: string
11
- trigger: string
12
- hover: string
13
- open: string
14
- text: string
15
- track: string
16
- bar: string
17
- }
18
-
19
- export const URGENCY_SCORE_CLASSES: Record<UrgencyLevel, ScoreSemanticClasses> = {
20
- Urgent: {
21
- solid: "bg-red-600 text-white",
22
- outline: "border-red-300 bg-red-50 text-red-800",
23
- dot: "bg-red-600",
24
- trigger: "border-red-300 bg-red-50 text-red-800",
25
- hover: "hover:bg-red-100",
26
- open: "bg-red-100",
27
- text: "text-red-600",
28
- track: "text-red-600/15",
29
- bar: "bg-red-600",
30
- },
31
- High: {
32
- solid: "bg-orange-500 text-white",
33
- outline: "border-orange-300 bg-orange-50 text-orange-800",
34
- dot: "bg-orange-500",
35
- trigger: "border-orange-300 bg-orange-50 text-orange-800",
36
- hover: "hover:bg-orange-100",
37
- open: "bg-orange-100",
38
- text: "text-orange-500",
39
- track: "text-orange-500/15",
40
- bar: "bg-orange-500",
41
- },
42
- Medium: {
43
- solid: "bg-amber-500 text-white",
44
- outline: "border-amber-300 bg-amber-50 text-amber-800",
45
- dot: "bg-amber-500",
46
- trigger: "border-amber-300 bg-amber-50 text-amber-800",
47
- hover: "hover:bg-amber-100",
48
- open: "bg-amber-100",
49
- text: "text-amber-500",
50
- track: "text-amber-500/15",
51
- bar: "bg-amber-500",
52
- },
53
- Low: {
54
- solid: "bg-neutral-300 text-neutral-900",
55
- outline: "border-neutral-200 bg-neutral-50 text-neutral-700",
56
- dot: "bg-neutral-400",
57
- trigger: "border-neutral-200 bg-neutral-50 text-neutral-700",
58
- hover: "hover:bg-neutral-100",
59
- open: "bg-neutral-100",
60
- text: "text-neutral-400",
61
- track: "text-neutral-400/15",
62
- bar: "bg-neutral-400",
63
- },
64
- }
65
-
66
- export const RISK_SCORE_CLASSES: Record<RiskLevel, ScoreSemanticClasses> = {
67
- "High Risk": {
68
- solid: "bg-red-600 text-white",
69
- outline: "border-red-300 bg-red-50 text-red-800",
70
- dot: "bg-red-600",
71
- trigger: "border-red-300 bg-red-50 text-red-800",
72
- hover: "hover:bg-red-100",
73
- open: "bg-red-100",
74
- text: "text-red-600",
75
- track: "text-red-600/15",
76
- bar: "bg-red-600",
77
- },
78
- "Medium Risk": {
79
- solid: "bg-amber-500 text-white",
80
- outline: "border-amber-300 bg-amber-50 text-amber-800",
81
- dot: "bg-amber-500",
82
- trigger: "border-amber-300 bg-amber-50 text-amber-800",
83
- hover: "hover:bg-amber-100",
84
- open: "bg-amber-100",
85
- text: "text-amber-500",
86
- track: "text-amber-500/15",
87
- bar: "bg-amber-500",
88
- },
89
- "Low Risk": {
90
- solid: "bg-neutral-300 text-neutral-900",
91
- outline: "border-neutral-200 bg-neutral-50 text-neutral-700",
92
- dot: "bg-neutral-400",
93
- trigger: "border-neutral-200 bg-neutral-50 text-neutral-700",
94
- hover: "hover:bg-neutral-100",
95
- open: "bg-neutral-100",
96
- text: "text-neutral-400",
97
- track: "text-neutral-400/15",
98
- bar: "bg-neutral-400",
99
- },
100
- }
101
-
102
- export const POSITIVE_SCORE_CLASSES: Record<PositiveLevel, ScoreSemanticClasses> = {
103
- High: {
104
- solid: "bg-emerald-500 text-white",
105
- outline: "border-emerald-300 bg-emerald-50 text-emerald-800",
106
- dot: "bg-emerald-500",
107
- trigger: "border-emerald-300 bg-emerald-50 text-emerald-800",
108
- hover: "hover:bg-emerald-100",
109
- open: "bg-emerald-100",
110
- text: "text-emerald-500",
111
- track: "text-emerald-500/15",
112
- bar: "bg-emerald-500",
113
- },
114
- Medium: {
115
- solid: "bg-amber-500 text-white",
116
- outline: "border-amber-300 bg-amber-50 text-amber-800",
117
- dot: "bg-amber-500",
118
- trigger: "border-amber-300 bg-amber-50 text-amber-800",
119
- hover: "hover:bg-amber-100",
120
- open: "bg-amber-100",
121
- text: "text-amber-500",
122
- track: "text-amber-500/15",
123
- bar: "bg-amber-500",
124
- },
125
- Low: {
126
- solid: "bg-red-500 text-white",
127
- outline: "border-red-300 bg-red-50 text-red-800",
128
- dot: "bg-red-500",
129
- trigger: "border-red-300 bg-red-50 text-red-800",
130
- hover: "hover:bg-red-100",
131
- open: "bg-red-100",
132
- text: "text-red-500",
133
- track: "text-red-500/15",
134
- bar: "bg-red-500",
135
- },
136
- }
137
-
138
- export const SCORE_TONE_CLASSES: Record<string, string> = {
139
- alert: "bg-red-50 text-red-600",
140
- warn: "bg-amber-50 text-amber-600",
141
- info: "bg-blue-50 text-blue-600",
142
- }
143
-
144
- export const DEFAULT_SCORE_TONE_CLASS = "bg-muted text-muted-foreground"
145
-
146
- export function getUrgencyLevel(score: number): UrgencyLevel {
147
- if (score >= 80) return "Urgent"
148
- if (score >= 60) return "High"
149
- if (score >= 35) return "Medium"
150
- return "Low"
151
- }
152
-
153
- export function getUrgencyRange(label: UrgencyLevel): string {
154
- switch (label) {
155
- case "Urgent":
156
- return "80-100"
157
- case "High":
158
- return "60-79"
159
- case "Medium":
160
- return "35-59"
161
- case "Low":
162
- return "0-34"
163
- }
164
- }
165
-
166
- export function getRiskLevel(score: number): RiskLevel {
167
- if (score >= 70) return "High Risk"
168
- if (score >= 40) return "Medium Risk"
169
- return "Low Risk"
170
- }
171
-
172
- export function getPositiveLevel(score: number): PositiveLevel {
173
- if (score >= 70) return "High"
174
- if (score >= 40) return "Medium"
175
- return "Low"
176
- }
177
-
178
- export function getScoreToneClasses(score: number, intent: ScoreIntent): ScoreSemanticClasses {
179
- switch (intent) {
180
- case "urgency":
181
- return URGENCY_SCORE_CLASSES[getUrgencyLevel(score)]
182
- case "risk":
183
- return RISK_SCORE_CLASSES[getRiskLevel(score)]
184
- case "positive":
185
- return POSITIVE_SCORE_CLASSES[getPositiveLevel(score)]
186
- }
187
- }