@handled-ai/design-system 0.8.0 → 0.9.0

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 (336) hide show
  1. package/README.md +14 -4
  2. package/dist/charts/bar-chart-component.d.ts +24 -0
  3. package/dist/charts/bar-chart-component.js +123 -0
  4. package/dist/charts/bar-chart-component.js.map +1 -0
  5. package/dist/charts/chart-tooltip.d.ts +26 -0
  6. package/dist/charts/chart-tooltip.js +69 -0
  7. package/dist/charts/chart-tooltip.js.map +1 -0
  8. package/dist/charts/chart.d.ts +64 -0
  9. package/dist/charts/chart.js +285 -0
  10. package/dist/charts/chart.js.map +1 -0
  11. package/dist/charts/donut-chart.d.ts +21 -0
  12. package/dist/charts/donut-chart.js +96 -0
  13. package/dist/charts/donut-chart.js.map +1 -0
  14. package/dist/charts/index.d.ts +11 -0
  15. package/dist/charts/index.js +10 -0
  16. package/dist/charts/index.js.map +1 -0
  17. package/dist/charts/pipeline-overview.d.ts +76 -0
  18. package/dist/charts/pipeline-overview.js +372 -0
  19. package/dist/charts/pipeline-overview.js.map +1 -0
  20. package/dist/charts/sankey-chart.d.ts +52 -0
  21. package/dist/charts/sankey-chart.js +219 -0
  22. package/dist/charts/sankey-chart.js.map +1 -0
  23. package/dist/charts/top-line-metrics.d.ts +26 -0
  24. package/dist/charts/top-line-metrics.js +224 -0
  25. package/dist/charts/top-line-metrics.js.map +1 -0
  26. package/dist/charts/trend-area-chart.d.ts +21 -0
  27. package/dist/charts/trend-area-chart.js +150 -0
  28. package/dist/charts/trend-area-chart.js.map +1 -0
  29. package/dist/charts/volume-analysis-chart.d.ts +19 -0
  30. package/dist/charts/volume-analysis-chart.js +121 -0
  31. package/dist/charts/volume-analysis-chart.js.map +1 -0
  32. package/dist/components/activity-detail.d.ts +38 -0
  33. package/dist/components/activity-detail.js +163 -0
  34. package/dist/components/activity-detail.js.map +1 -0
  35. package/dist/components/activity-log.d.ts +21 -0
  36. package/dist/components/activity-log.js +61 -0
  37. package/dist/components/activity-log.js.map +1 -0
  38. package/dist/components/agent-popover.d.ts +71 -0
  39. package/dist/components/agent-popover.js +282 -0
  40. package/dist/components/agent-popover.js.map +1 -0
  41. package/dist/components/agent-widget.d.ts +24 -0
  42. package/dist/components/agent-widget.js +117 -0
  43. package/dist/components/agent-widget.js.map +1 -0
  44. package/dist/components/avatar.d.ts +13 -0
  45. package/dist/components/avatar.js +140 -0
  46. package/dist/components/avatar.js.map +1 -0
  47. package/dist/components/badge.d.ts +12 -0
  48. package/dist/components/badge.js +75 -0
  49. package/dist/components/badge.js.map +1 -0
  50. package/dist/components/button.d.ts +13 -0
  51. package/dist/components/button.js +83 -0
  52. package/dist/components/button.js.map +1 -0
  53. package/dist/components/card.d.ts +11 -0
  54. package/dist/components/card.js +119 -0
  55. package/dist/components/card.js.map +1 -0
  56. package/dist/components/contact-list.d.ts +34 -0
  57. package/dist/components/contact-list.js +84 -0
  58. package/dist/components/contact-list.js.map +1 -0
  59. package/dist/components/dashboard-cards.d.ts +10 -0
  60. package/dist/components/dashboard-cards.js +164 -0
  61. package/dist/components/dashboard-cards.js.map +1 -0
  62. package/dist/components/data-table-display.d.ts +19 -0
  63. package/dist/components/data-table-display.js +109 -0
  64. package/dist/components/data-table-display.js.map +1 -0
  65. package/dist/components/data-table-filter.d.ts +18 -0
  66. package/dist/components/data-table-filter.js +107 -0
  67. package/dist/components/data-table-filter.js.map +1 -0
  68. package/dist/components/data-table-quick-views.d.ts +13 -0
  69. package/dist/components/data-table-quick-views.js +90 -0
  70. package/dist/components/data-table-quick-views.js.map +1 -0
  71. package/dist/components/data-table-toolbar.d.ts +18 -0
  72. package/dist/components/data-table-toolbar.js +45 -0
  73. package/dist/components/data-table-toolbar.js.map +1 -0
  74. package/dist/components/data-table.d.ts +39 -0
  75. package/dist/components/data-table.js +821 -0
  76. package/dist/components/data-table.js.map +1 -0
  77. package/dist/components/detail-view.d.ts +44 -0
  78. package/dist/components/detail-view.js +165 -0
  79. package/dist/components/detail-view.js.map +1 -0
  80. package/dist/components/dialog.d.ts +19 -0
  81. package/dist/components/dialog.js +188 -0
  82. package/dist/components/dialog.js.map +1 -0
  83. package/dist/components/dropdown-menu.d.ts +27 -0
  84. package/dist/components/dropdown-menu.js +279 -0
  85. package/dist/components/dropdown-menu.js.map +1 -0
  86. package/dist/components/entity-panel.d.ts +69 -0
  87. package/dist/components/entity-panel.js +584 -0
  88. package/dist/components/entity-panel.js.map +1 -0
  89. package/dist/components/inbox-row.d.ts +27 -0
  90. package/dist/components/inbox-row.js +139 -0
  91. package/dist/components/inbox-row.js.map +1 -0
  92. package/dist/components/inbox-toolbar.d.ts +21 -0
  93. package/dist/components/inbox-toolbar.js +203 -0
  94. package/dist/components/inbox-toolbar.js.map +1 -0
  95. package/dist/components/input.d.ts +5 -0
  96. package/dist/components/input.js +50 -0
  97. package/dist/components/input.js.map +1 -0
  98. package/dist/components/insights-filter-bar.d.ts +21 -0
  99. package/dist/components/insights-filter-bar.js +99 -0
  100. package/dist/components/insights-filter-bar.js.map +1 -0
  101. package/dist/components/item-list-display.d.ts +22 -0
  102. package/dist/components/item-list-display.js +240 -0
  103. package/dist/components/item-list-display.js.map +1 -0
  104. package/dist/components/item-list-filter.d.ts +16 -0
  105. package/dist/components/item-list-filter.js +87 -0
  106. package/dist/components/item-list-filter.js.map +1 -0
  107. package/dist/components/item-list-toolbar.d.ts +25 -0
  108. package/dist/components/item-list-toolbar.js +79 -0
  109. package/dist/components/item-list-toolbar.js.map +1 -0
  110. package/dist/components/item-list.d.ts +20 -0
  111. package/dist/components/item-list.js +702 -0
  112. package/dist/components/item-list.js.map +1 -0
  113. package/dist/components/label.d.ts +6 -0
  114. package/dist/components/label.js +55 -0
  115. package/dist/components/label.js.map +1 -0
  116. package/dist/components/message.d.ts +23 -0
  117. package/dist/components/message.js +117 -0
  118. package/dist/components/message.js.map +1 -0
  119. package/dist/components/metric-card.d.ts +25 -0
  120. package/dist/components/metric-card.js +107 -0
  121. package/dist/components/metric-card.js.map +1 -0
  122. package/dist/components/performance-metrics-table.d.ts +38 -0
  123. package/dist/components/performance-metrics-table.js +342 -0
  124. package/dist/components/performance-metrics-table.js.map +1 -0
  125. package/dist/components/preview-list.d.ts +14 -0
  126. package/dist/components/preview-list.js +83 -0
  127. package/dist/components/preview-list.js.map +1 -0
  128. package/dist/components/progress.d.ts +6 -0
  129. package/dist/components/progress.js +69 -0
  130. package/dist/components/progress.js.map +1 -0
  131. package/dist/components/quick-action-chat-area.d.ts +24 -0
  132. package/dist/components/quick-action-chat-area.js +178 -0
  133. package/dist/components/quick-action-chat-area.js.map +1 -0
  134. package/dist/components/quick-action-modal.d.ts +30 -0
  135. package/dist/components/quick-action-modal.js +288 -0
  136. package/dist/components/quick-action-modal.js.map +1 -0
  137. package/dist/components/quick-action-sidebar-nav.d.ts +51 -0
  138. package/dist/components/quick-action-sidebar-nav.js +528 -0
  139. package/dist/components/quick-action-sidebar-nav.js.map +1 -0
  140. package/dist/components/recommended-actions-section.d.ts +23 -0
  141. package/dist/components/recommended-actions-section.js +215 -0
  142. package/dist/components/recommended-actions-section.js.map +1 -0
  143. package/dist/components/report-card.d.ts +26 -0
  144. package/dist/components/report-card.js +69 -0
  145. package/dist/components/report-card.js.map +1 -0
  146. package/dist/components/score-analysis-modal.d.ts +26 -0
  147. package/dist/components/score-analysis-modal.js +141 -0
  148. package/dist/components/score-analysis-modal.js.map +1 -0
  149. package/dist/components/score-breakdown.d.ts +17 -0
  150. package/dist/components/score-breakdown.js +162 -0
  151. package/dist/components/score-breakdown.js.map +1 -0
  152. package/dist/components/score-feedback.d.ts +40 -0
  153. package/dist/components/score-feedback.js +209 -0
  154. package/dist/components/score-feedback.js.map +1 -0
  155. package/dist/components/score-ring.d.ts +14 -0
  156. package/dist/components/score-ring.js +79 -0
  157. package/dist/components/score-ring.js.map +1 -0
  158. package/dist/components/scroll-area.d.ts +7 -0
  159. package/dist/components/scroll-area.js +101 -0
  160. package/dist/components/scroll-area.js.map +1 -0
  161. package/dist/components/select.d.ts +17 -0
  162. package/dist/components/select.js +228 -0
  163. package/dist/components/select.js.map +1 -0
  164. package/dist/components/separator.d.ts +6 -0
  165. package/dist/components/separator.js +61 -0
  166. package/dist/components/separator.js.map +1 -0
  167. package/dist/components/sheet.d.ts +16 -0
  168. package/dist/components/sheet.js +168 -0
  169. package/dist/components/sheet.js.map +1 -0
  170. package/dist/components/sidebar.d.ts +73 -0
  171. package/dist/components/sidebar.js +723 -0
  172. package/dist/components/sidebar.js.map +1 -0
  173. package/dist/components/signal-feedback-inline.d.ts +51 -0
  174. package/dist/components/signal-feedback-inline.js +548 -0
  175. package/dist/components/signal-feedback-inline.js.map +1 -0
  176. package/dist/components/simple-data-table.d.ts +15 -0
  177. package/dist/components/simple-data-table.js +91 -0
  178. package/dist/components/simple-data-table.js.map +1 -0
  179. package/dist/components/skeleton.d.ts +5 -0
  180. package/dist/components/skeleton.js +44 -0
  181. package/dist/components/skeleton.js.map +1 -0
  182. package/dist/components/status-badge.d.ts +10 -0
  183. package/dist/components/status-badge.js +82 -0
  184. package/dist/components/status-badge.js.map +1 -0
  185. package/dist/components/styled-bar-list.d.ts +20 -0
  186. package/dist/components/styled-bar-list.js +59 -0
  187. package/dist/components/styled-bar-list.js.map +1 -0
  188. package/dist/components/suggested-actions.d.ts +110 -0
  189. package/dist/components/suggested-actions.js +1538 -0
  190. package/dist/components/suggested-actions.js.map +1 -0
  191. package/dist/components/table.d.ts +12 -0
  192. package/dist/components/table.js +147 -0
  193. package/dist/components/table.js.map +1 -0
  194. package/dist/components/tabs.d.ts +14 -0
  195. package/dist/components/tabs.js +129 -0
  196. package/dist/components/tabs.js.map +1 -0
  197. package/dist/components/textarea.d.ts +5 -0
  198. package/dist/components/textarea.js +47 -0
  199. package/dist/components/textarea.js.map +1 -0
  200. package/dist/components/timeline-activity.d.ts +34 -0
  201. package/dist/components/timeline-activity.js +181 -0
  202. package/dist/components/timeline-activity.js.map +1 -0
  203. package/dist/components/tooltip.d.ts +9 -0
  204. package/dist/components/tooltip.js +93 -0
  205. package/dist/components/tooltip.js.map +1 -0
  206. package/dist/components/view-mode-toggle.d.ts +16 -0
  207. package/dist/components/view-mode-toggle.js +24 -0
  208. package/dist/components/view-mode-toggle.js.map +1 -0
  209. package/dist/hooks/use-mobile.d.ts +3 -0
  210. package/dist/hooks/use-mobile.js +21 -0
  211. package/dist/hooks/use-mobile.js.map +1 -0
  212. package/dist/index.d.ts +68 -1878
  213. package/dist/index.js +69 -10918
  214. package/dist/index.js.map +1 -1
  215. package/dist/lib/icons.d.ts +18 -0
  216. package/dist/lib/icons.js +21 -0
  217. package/dist/lib/icons.js.map +1 -0
  218. package/dist/lib/utils.d.ts +5 -0
  219. package/dist/lib/utils.js +9 -0
  220. package/dist/lib/utils.js.map +1 -0
  221. package/dist/prototype/index.d.ts +20 -0
  222. package/dist/prototype/index.js +8 -0
  223. package/dist/prototype/index.js.map +1 -0
  224. package/dist/prototype/prototype-accounts-view.d.ts +22 -0
  225. package/dist/prototype/prototype-accounts-view.js +70 -0
  226. package/dist/prototype/prototype-accounts-view.js.map +1 -0
  227. package/dist/prototype/prototype-admin-view.d.ts +21 -0
  228. package/dist/prototype/prototype-admin-view.js +53 -0
  229. package/dist/prototype/prototype-admin-view.js.map +1 -0
  230. package/dist/prototype/prototype-config.d.ts +226 -0
  231. package/dist/prototype/prototype-config.js +1 -0
  232. package/dist/prototype/prototype-config.js.map +1 -0
  233. package/dist/prototype/prototype-inbox-view.d.ts +48 -0
  234. package/dist/prototype/prototype-inbox-view.js +701 -0
  235. package/dist/prototype/prototype-inbox-view.js.map +1 -0
  236. package/dist/prototype/prototype-insights-view.d.ts +23 -0
  237. package/dist/prototype/prototype-insights-view.js +335 -0
  238. package/dist/prototype/prototype-insights-view.js.map +1 -0
  239. package/dist/prototype/prototype-shell.d.ts +40 -0
  240. package/dist/prototype/prototype-shell.js +190 -0
  241. package/dist/prototype/prototype-shell.js.map +1 -0
  242. package/dist/prototype/prototype-work-queue-view.d.ts +8 -0
  243. package/dist/prototype/prototype-work-queue-view.js +17 -0
  244. package/dist/prototype/prototype-work-queue-view.js.map +1 -0
  245. package/dist/three/agent-orb.d.ts +39 -0
  246. package/dist/three/agent-orb.js +500 -0
  247. package/dist/three/agent-orb.js.map +1 -0
  248. package/dist/three/index.d.ts +2 -0
  249. package/dist/three/index.js +2 -0
  250. package/dist/three/index.js.map +1 -0
  251. package/package.json +98 -17
  252. package/src/charts/bar-chart-component.tsx +150 -0
  253. package/src/charts/chart-tooltip.tsx +86 -0
  254. package/src/charts/chart.tsx +371 -0
  255. package/src/charts/donut-chart.tsx +112 -0
  256. package/src/charts/index.ts +13 -0
  257. package/src/charts/pipeline-overview.tsx +476 -0
  258. package/src/charts/sankey-chart.tsx +290 -0
  259. package/src/charts/top-line-metrics.tsx +261 -0
  260. package/src/charts/trend-area-chart.tsx +150 -0
  261. package/src/charts/volume-analysis-chart.tsx +124 -0
  262. package/src/components/activity-detail.tsx +233 -0
  263. package/src/components/activity-log.tsx +89 -0
  264. package/src/components/agent-popover.tsx +373 -0
  265. package/src/components/agent-widget.tsx +163 -0
  266. package/src/components/avatar.tsx +109 -0
  267. package/src/components/badge.tsx +48 -0
  268. package/src/components/button.tsx +59 -0
  269. package/src/components/card.tsx +92 -0
  270. package/src/components/contact-list.tsx +121 -0
  271. package/src/components/dashboard-cards.tsx +170 -0
  272. package/src/components/data-table-display.tsx +139 -0
  273. package/src/components/data-table-filter.tsx +138 -0
  274. package/src/components/data-table-quick-views.tsx +103 -0
  275. package/src/components/data-table-toolbar.tsx +56 -0
  276. package/src/components/data-table.tsx +915 -0
  277. package/src/components/detail-view.tsx +237 -0
  278. package/src/components/dialog.tsx +158 -0
  279. package/src/components/dropdown-menu.tsx +257 -0
  280. package/src/components/entity-panel.tsx +767 -0
  281. package/src/components/inbox-row.tsx +132 -0
  282. package/src/components/inbox-toolbar.tsx +213 -0
  283. package/src/components/input.tsx +21 -0
  284. package/src/components/insights-filter-bar.tsx +132 -0
  285. package/src/components/item-list-display.tsx +278 -0
  286. package/src/components/item-list-filter.tsx +118 -0
  287. package/src/components/item-list-toolbar.tsx +97 -0
  288. package/src/components/item-list.tsx +843 -0
  289. package/src/components/label.tsx +24 -0
  290. package/src/components/message.tsx +83 -0
  291. package/src/components/metric-card.tsx +178 -0
  292. package/src/components/performance-metrics-table.tsx +442 -0
  293. package/src/components/preview-list.tsx +62 -0
  294. package/src/components/progress.tsx +31 -0
  295. package/src/components/quick-action-chat-area.tsx +156 -0
  296. package/src/components/quick-action-modal.tsx +331 -0
  297. package/src/components/quick-action-sidebar-nav.tsx +592 -0
  298. package/src/components/recommended-actions-section.tsx +258 -0
  299. package/src/components/report-card.tsx +106 -0
  300. package/src/components/score-analysis-modal.tsx +172 -0
  301. package/src/components/score-breakdown.tsx +179 -0
  302. package/src/components/score-feedback.tsx +288 -0
  303. package/src/components/score-ring.tsx +87 -0
  304. package/src/components/scroll-area.tsx +58 -0
  305. package/src/components/select.tsx +190 -0
  306. package/src/components/separator.tsx +28 -0
  307. package/src/components/sheet.tsx +143 -0
  308. package/src/components/sidebar.tsx +726 -0
  309. package/src/components/signal-feedback-inline.tsx +591 -0
  310. package/src/components/simple-data-table.tsx +124 -0
  311. package/src/components/skeleton.tsx +15 -0
  312. package/src/components/status-badge.tsx +63 -0
  313. package/src/components/styled-bar-list.tsx +70 -0
  314. package/src/components/suggested-actions.tsx +1985 -0
  315. package/src/components/table.tsx +116 -0
  316. package/src/components/tabs.tsx +91 -0
  317. package/src/components/textarea.tsx +18 -0
  318. package/src/components/timeline-activity.tsx +234 -0
  319. package/src/components/tooltip.tsx +57 -0
  320. package/src/components/view-mode-toggle.tsx +39 -0
  321. package/src/hooks/use-mobile.ts +21 -0
  322. package/src/index.ts +77 -0
  323. package/src/lib/icons.ts +18 -0
  324. package/src/lib/utils.ts +6 -0
  325. package/src/prototype/index.ts +11 -0
  326. package/src/prototype/prototype-accounts-view.tsx +112 -0
  327. package/src/prototype/prototype-admin-view.tsx +67 -0
  328. package/src/prototype/prototype-config.ts +243 -0
  329. package/src/prototype/prototype-inbox-view.tsx +810 -0
  330. package/src/prototype/prototype-insights-view.tsx +379 -0
  331. package/src/prototype/prototype-shell.tsx +219 -0
  332. package/src/prototype/prototype-work-queue-view.tsx +30 -0
  333. package/src/styles/globals.css +299 -0
  334. package/src/three/agent-orb.tsx +557 -0
  335. package/src/three/index.ts +5 -0
  336. package/src/types/r3f.d.ts +8 -0
@@ -0,0 +1,162 @@
1
+ "use client"
2
+
3
+ "use client";
4
+ var __defProp = Object.defineProperty;
5
+ var __defProps = Object.defineProperties;
6
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
7
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
10
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
11
+ var __spreadValues = (a, b) => {
12
+ for (var prop in b || (b = {}))
13
+ if (__hasOwnProp.call(b, prop))
14
+ __defNormalProp(a, prop, b[prop]);
15
+ if (__getOwnPropSymbols)
16
+ for (var prop of __getOwnPropSymbols(b)) {
17
+ if (__propIsEnum.call(b, prop))
18
+ __defNormalProp(a, prop, b[prop]);
19
+ }
20
+ return a;
21
+ };
22
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
23
+ import { jsx, jsxs } from "react/jsx-runtime";
24
+ import * as React from "react";
25
+ import { ThumbsUp, ThumbsDown } from "lucide-react";
26
+ import { cn } from "../lib/utils.js";
27
+ function getFactorBarColor(score) {
28
+ if (score >= 70) return "bg-emerald-500";
29
+ if (score >= 40) return "bg-amber-500";
30
+ return "bg-red-500";
31
+ }
32
+ function getRiskBadgeStyle(risk) {
33
+ switch (risk) {
34
+ case "Low":
35
+ return "text-emerald-600 bg-emerald-50 dark:text-emerald-400 dark:bg-emerald-950/30";
36
+ case "Medium":
37
+ return "text-amber-600 bg-amber-50 dark:text-amber-400 dark:bg-amber-950/30";
38
+ case "High":
39
+ return "text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-950/30";
40
+ }
41
+ }
42
+ function ScoreBreakdown({ factors, onFactorFeedback, className }) {
43
+ const [feedback, setFeedback] = React.useState({});
44
+ const [feedbackText, setFeedbackText] = React.useState({});
45
+ const [savedText, setSavedText] = React.useState({});
46
+ const [editingKey, setEditingKey] = React.useState(null);
47
+ const handleFeedback = (factorKey, type) => {
48
+ const newState = feedback[factorKey] === type ? null : type;
49
+ setFeedback((prev) => __spreadProps(__spreadValues({}, prev), { [factorKey]: newState }));
50
+ if (newState === null) {
51
+ setEditingKey(null);
52
+ onFactorFeedback == null ? void 0 : onFactorFeedback(factorKey, null);
53
+ } else {
54
+ setEditingKey(factorKey);
55
+ onFactorFeedback == null ? void 0 : onFactorFeedback(factorKey, newState);
56
+ }
57
+ };
58
+ const submitFeedbackText = (factorKey) => {
59
+ var _a;
60
+ const text = ((_a = feedbackText[factorKey]) != null ? _a : "").trim();
61
+ if (feedback[factorKey]) {
62
+ onFactorFeedback == null ? void 0 : onFactorFeedback(factorKey, feedback[factorKey], text || void 0);
63
+ if (text) {
64
+ setSavedText((prev) => __spreadProps(__spreadValues({}, prev), { [factorKey]: text }));
65
+ }
66
+ }
67
+ setEditingKey(null);
68
+ };
69
+ return /* @__PURE__ */ jsx("div", { className: cn("border border-border rounded-lg overflow-hidden divide-y divide-border/40", className), children: factors.map((factor) => {
70
+ var _a;
71
+ const feedbackState = feedback[factor.key];
72
+ const hasThumb = feedbackState === "up" || feedbackState === "down";
73
+ const isEditing = editingKey === factor.key;
74
+ const saved = savedText[factor.key];
75
+ return /* @__PURE__ */ jsxs("div", { className: "px-3 py-3 space-y-1.5", children: [
76
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
77
+ /* @__PURE__ */ jsx("span", { className: "text-[13px] font-semibold text-foreground flex-1", children: factor.label }),
78
+ factor.score !== null ? /* @__PURE__ */ jsxs("span", { className: "text-xs font-bold text-foreground tabular-nums", children: [
79
+ factor.score,
80
+ /* @__PURE__ */ jsx("span", { className: "text-muted-foreground font-normal", children: "/100" })
81
+ ] }) : factor.risk ? /* @__PURE__ */ jsx(
82
+ "span",
83
+ {
84
+ className: cn(
85
+ "text-[11px] font-semibold px-1.5 py-0.5 rounded",
86
+ getRiskBadgeStyle(factor.risk)
87
+ ),
88
+ children: factor.risk
89
+ }
90
+ ) : null,
91
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-0.5 ml-1", children: [
92
+ /* @__PURE__ */ jsx(
93
+ "button",
94
+ {
95
+ type: "button",
96
+ onClick: () => handleFeedback(factor.key, "up"),
97
+ className: cn(
98
+ "p-1 rounded transition-colors",
99
+ feedbackState === "up" ? "text-emerald-600 bg-emerald-50 dark:text-emerald-400 dark:bg-emerald-950/30" : "text-muted-foreground/40 hover:text-emerald-600 hover:bg-emerald-50/50"
100
+ ),
101
+ title: "This factor is accurate",
102
+ children: /* @__PURE__ */ jsx(ThumbsUp, { className: "w-3 h-3" })
103
+ }
104
+ ),
105
+ /* @__PURE__ */ jsx(
106
+ "button",
107
+ {
108
+ type: "button",
109
+ onClick: () => handleFeedback(factor.key, "down"),
110
+ className: cn(
111
+ "p-1 rounded transition-colors",
112
+ feedbackState === "down" ? "text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-950/30" : "text-muted-foreground/40 hover:text-red-600 hover:bg-red-50/50"
113
+ ),
114
+ title: "Report issue with this factor",
115
+ children: /* @__PURE__ */ jsx(ThumbsDown, { className: "w-3 h-3" })
116
+ }
117
+ )
118
+ ] })
119
+ ] }),
120
+ factor.score !== null && /* @__PURE__ */ jsx("div", { className: "w-full h-1 bg-muted rounded-full overflow-hidden", children: /* @__PURE__ */ jsx(
121
+ "div",
122
+ {
123
+ className: cn("h-full rounded-full", getFactorBarColor(factor.score)),
124
+ style: { width: `${factor.score}%` }
125
+ }
126
+ ) }),
127
+ /* @__PURE__ */ jsx("p", { className: "text-xs text-muted-foreground leading-relaxed", children: factor.why }),
128
+ hasThumb && isEditing && /* @__PURE__ */ jsx(
129
+ "input",
130
+ {
131
+ type: "text",
132
+ autoFocus: true,
133
+ value: (_a = feedbackText[factor.key]) != null ? _a : "",
134
+ onChange: (e) => setFeedbackText((prev) => __spreadProps(__spreadValues({}, prev), { [factor.key]: e.target.value })),
135
+ onKeyDown: (e) => {
136
+ if (e.key === "Enter") submitFeedbackText(factor.key);
137
+ if (e.key === "Escape") setEditingKey(null);
138
+ },
139
+ onBlur: () => submitFeedbackText(factor.key),
140
+ placeholder: feedbackState === "up" ? "What\u2019s accurate? (optional)" : "What\u2019s wrong? (optional)",
141
+ className: "w-full h-6 rounded border border-border bg-background px-2 text-[11px] text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:ring-1 focus:ring-ring"
142
+ }
143
+ ),
144
+ hasThumb && !isEditing && saved && /* @__PURE__ */ jsx(
145
+ "button",
146
+ {
147
+ type: "button",
148
+ onClick: () => {
149
+ setFeedbackText((prev) => __spreadProps(__spreadValues({}, prev), { [factor.key]: saved }));
150
+ setEditingKey(factor.key);
151
+ },
152
+ className: "text-[11px] text-muted-foreground/70 hover:text-muted-foreground transition-colors text-left leading-snug",
153
+ children: saved
154
+ }
155
+ )
156
+ ] }, factor.key);
157
+ }) });
158
+ }
159
+ export {
160
+ ScoreBreakdown
161
+ };
162
+ //# sourceMappingURL=score-breakdown.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/score-breakdown.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ThumbsUp, ThumbsDown } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nexport interface ScoreFactor {\n key: string\n label: string\n score: number | null\n risk?: \"Low\" | \"Medium\" | \"High\"\n why: string\n}\n\nfunction getFactorBarColor(score: number) {\n if (score >= 70) return \"bg-emerald-500\"\n if (score >= 40) return \"bg-amber-500\"\n return \"bg-red-500\"\n}\n\nfunction getRiskBadgeStyle(risk: \"Low\" | \"Medium\" | \"High\") {\n switch (risk) {\n case \"Low\":\n return \"text-emerald-600 bg-emerald-50 dark:text-emerald-400 dark:bg-emerald-950/30\"\n case \"Medium\":\n return \"text-amber-600 bg-amber-50 dark:text-amber-400 dark:bg-amber-950/30\"\n case \"High\":\n return \"text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-950/30\"\n }\n}\n\ninterface ScoreBreakdownProps {\n factors: ScoreFactor[]\n onFactorFeedback?: (factorKey: string, type: \"up\" | \"down\" | null, detail?: string) => void\n className?: string\n}\n\nfunction ScoreBreakdown({ factors, onFactorFeedback, className }: ScoreBreakdownProps) {\n const [feedback, setFeedback] = React.useState<Record<string, \"up\" | \"down\" | null>>({})\n const [feedbackText, setFeedbackText] = React.useState<Record<string, string>>({})\n const [savedText, setSavedText] = React.useState<Record<string, string>>({})\n const [editingKey, setEditingKey] = React.useState<string | null>(null)\n\n const handleFeedback = (factorKey: string, type: \"up\" | \"down\") => {\n const newState = feedback[factorKey] === type ? null : type\n setFeedback((prev) => ({ ...prev, [factorKey]: newState }))\n if (newState === null) {\n setEditingKey(null)\n onFactorFeedback?.(factorKey, null)\n } else {\n setEditingKey(factorKey)\n onFactorFeedback?.(factorKey, newState)\n }\n }\n\n const submitFeedbackText = (factorKey: string) => {\n const text = (feedbackText[factorKey] ?? \"\").trim()\n if (feedback[factorKey]) {\n onFactorFeedback?.(factorKey, feedback[factorKey]!, text || undefined)\n if (text) {\n setSavedText((prev) => ({ ...prev, [factorKey]: text }))\n }\n }\n setEditingKey(null)\n }\n\n return (\n <div className={cn(\"border border-border rounded-lg overflow-hidden divide-y divide-border/40\", className)}>\n {factors.map((factor) => {\n const feedbackState = feedback[factor.key]\n const hasThumb = feedbackState === \"up\" || feedbackState === \"down\"\n const isEditing = editingKey === factor.key\n const saved = savedText[factor.key]\n\n return (\n <div key={factor.key} className=\"px-3 py-3 space-y-1.5\">\n {/* Row 1: Factor name + score + rating buttons */}\n <div className=\"flex items-center gap-2\">\n <span className=\"text-[13px] font-semibold text-foreground flex-1\">{factor.label}</span>\n\n {factor.score !== null ? (\n <span className=\"text-xs font-bold text-foreground tabular-nums\">{factor.score}<span className=\"text-muted-foreground font-normal\">/100</span></span>\n ) : factor.risk ? (\n <span\n className={cn(\n \"text-[11px] font-semibold px-1.5 py-0.5 rounded\",\n getRiskBadgeStyle(factor.risk)\n )}\n >\n {factor.risk}\n </span>\n ) : null}\n\n <div className=\"flex items-center gap-0.5 ml-1\">\n <button\n type=\"button\"\n onClick={() => handleFeedback(factor.key, \"up\")}\n className={cn(\n \"p-1 rounded transition-colors\",\n feedbackState === \"up\"\n ? \"text-emerald-600 bg-emerald-50 dark:text-emerald-400 dark:bg-emerald-950/30\"\n : \"text-muted-foreground/40 hover:text-emerald-600 hover:bg-emerald-50/50\"\n )}\n title=\"This factor is accurate\"\n >\n <ThumbsUp className=\"w-3 h-3\" />\n </button>\n <button\n type=\"button\"\n onClick={() => handleFeedback(factor.key, \"down\")}\n className={cn(\n \"p-1 rounded transition-colors\",\n feedbackState === \"down\"\n ? \"text-red-600 bg-red-50 dark:text-red-400 dark:bg-red-950/30\"\n : \"text-muted-foreground/40 hover:text-red-600 hover:bg-red-50/50\"\n )}\n title=\"Report issue with this factor\"\n >\n <ThumbsDown className=\"w-3 h-3\" />\n </button>\n </div>\n </div>\n\n {/* Row 2: Score bar */}\n {factor.score !== null && (\n <div className=\"w-full h-1 bg-muted rounded-full overflow-hidden\">\n <div\n className={cn(\"h-full rounded-full\", getFactorBarColor(factor.score))}\n style={{ width: `${factor.score}%` }}\n />\n </div>\n )}\n\n {/* Row 3: Why text */}\n <p className=\"text-xs text-muted-foreground leading-relaxed\">{factor.why}</p>\n\n {/* Feedback input */}\n {hasThumb && isEditing && (\n <input\n type=\"text\"\n autoFocus\n value={feedbackText[factor.key] ?? \"\"}\n onChange={(e) =>\n setFeedbackText((prev) => ({ ...prev, [factor.key]: e.target.value }))\n }\n onKeyDown={(e) => {\n if (e.key === \"Enter\") submitFeedbackText(factor.key)\n if (e.key === \"Escape\") setEditingKey(null)\n }}\n onBlur={() => submitFeedbackText(factor.key)}\n placeholder={\n feedbackState === \"up\"\n ? \"What\\u2019s accurate? (optional)\"\n : \"What\\u2019s wrong? (optional)\"\n }\n className=\"w-full h-6 rounded border border-border bg-background px-2 text-[11px] text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n )}\n {hasThumb && !isEditing && saved && (\n <button\n type=\"button\"\n onClick={() => {\n setFeedbackText((prev) => ({ ...prev, [factor.key]: saved }))\n setEditingKey(factor.key)\n }}\n className=\"text-[11px] text-muted-foreground/70 hover:text-muted-foreground transition-colors text-left leading-snug\"\n >\n {saved}\n </button>\n )}\n </div>\n )\n })}\n </div>\n )\n}\n\nexport { ScoreBreakdown }\nexport type { ScoreBreakdownProps }\n"],"mappings":"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","names":[]}
@@ -0,0 +1,40 @@
1
+ import * as React from 'react';
2
+
3
+ interface SubmittedScoreFeedback {
4
+ type: "up" | "down";
5
+ pills: string[];
6
+ detail: string;
7
+ }
8
+ interface ScoreFeedbackState {
9
+ thumbState: "up" | "down" | null;
10
+ selectedPills: string[];
11
+ detailText: string;
12
+ notedType: "up" | "down" | null;
13
+ submittedFeedback: SubmittedScoreFeedback | null;
14
+ otherSelected: boolean;
15
+ hasRequiredInput: boolean;
16
+ handleThumbClick: (type: "up" | "down") => void;
17
+ togglePill: (pill: string) => void;
18
+ setDetailText: (text: string) => void;
19
+ handleSubmit: () => void;
20
+ editSubmitted: () => void;
21
+ }
22
+ declare function useScoreFeedback(): ScoreFeedbackState;
23
+ interface RootProps {
24
+ children: React.ReactNode;
25
+ onSubmitFeedback?: (type: "up" | "down", pills: string[], detail: string) => void;
26
+ }
27
+ declare function Root({ children, onSubmitFeedback }: RootProps): React.JSX.Element;
28
+ declare function Trigger({ className }: {
29
+ className?: string;
30
+ }): React.JSX.Element;
31
+ declare function Panel({ className }: {
32
+ className?: string;
33
+ }): React.JSX.Element | null;
34
+ declare const ScoreFeedback: {
35
+ Root: typeof Root;
36
+ Trigger: typeof Trigger;
37
+ Panel: typeof Panel;
38
+ };
39
+
40
+ export { ScoreFeedback, useScoreFeedback };
@@ -0,0 +1,209 @@
1
+ "use client"
2
+
3
+ "use client";
4
+ import { jsx, jsxs } from "react/jsx-runtime";
5
+ import * as React from "react";
6
+ import { ThumbsUp, ThumbsDown, Check } from "lucide-react";
7
+ import { cn } from "../lib/utils.js";
8
+ const positivePills = [
9
+ "Right timing",
10
+ "Accurate data",
11
+ "Good prospect fit",
12
+ "Actionable"
13
+ ];
14
+ const negativePills = [
15
+ "Bad timing",
16
+ "Inaccurate data",
17
+ "Wrong prospect",
18
+ "Already handled",
19
+ "Not actionable",
20
+ "Other"
21
+ ];
22
+ const ScoreFeedbackCtx = React.createContext(null);
23
+ function useScoreFeedback() {
24
+ const ctx = React.useContext(ScoreFeedbackCtx);
25
+ if (!ctx) throw new Error("Must be used within ScoreFeedback.Root");
26
+ return ctx;
27
+ }
28
+ function Root({ children, onSubmitFeedback }) {
29
+ const [thumbState, setThumbState] = React.useState(null);
30
+ const [selectedPills, setSelectedPills] = React.useState([]);
31
+ const [detailText, setDetailTextState] = React.useState("");
32
+ const [notedType, setNotedType] = React.useState(null);
33
+ const [submittedFeedback, setSubmittedFeedback] = React.useState(null);
34
+ const otherSelected = selectedPills.includes("Other");
35
+ const hasRequiredInput = thumbState === "down" ? selectedPills.length > 0 && (!otherSelected || detailText.trim().length > 0) : selectedPills.length > 0 || detailText.trim().length > 0;
36
+ const togglePill = React.useCallback((pill) => {
37
+ setSelectedPills(
38
+ (prev) => prev.includes(pill) ? prev.filter((p) => p !== pill) : [...prev, pill]
39
+ );
40
+ }, []);
41
+ const handleThumbClick = React.useCallback((type) => {
42
+ setThumbState((prev) => prev === type ? null : type);
43
+ setSelectedPills([]);
44
+ setDetailTextState("");
45
+ }, []);
46
+ const handleSubmit = React.useCallback(() => {
47
+ if (!thumbState) return;
48
+ onSubmitFeedback == null ? void 0 : onSubmitFeedback(thumbState, selectedPills, detailText);
49
+ setSubmittedFeedback({ type: thumbState, pills: [...selectedPills], detail: detailText.trim() });
50
+ setNotedType(thumbState);
51
+ setThumbState(null);
52
+ setSelectedPills([]);
53
+ setDetailTextState("");
54
+ setTimeout(() => setNotedType(null), 3e3);
55
+ }, [thumbState, selectedPills, detailText, onSubmitFeedback]);
56
+ const editSubmitted = React.useCallback(() => {
57
+ if (!submittedFeedback) return;
58
+ setThumbState(submittedFeedback.type);
59
+ setSelectedPills([...submittedFeedback.pills]);
60
+ setDetailTextState(submittedFeedback.detail);
61
+ setNotedType(null);
62
+ }, [submittedFeedback]);
63
+ return /* @__PURE__ */ jsx(
64
+ ScoreFeedbackCtx.Provider,
65
+ {
66
+ value: {
67
+ thumbState,
68
+ selectedPills,
69
+ detailText,
70
+ notedType,
71
+ submittedFeedback,
72
+ otherSelected,
73
+ hasRequiredInput,
74
+ handleThumbClick,
75
+ togglePill,
76
+ setDetailText: setDetailTextState,
77
+ handleSubmit,
78
+ editSubmitted
79
+ },
80
+ children
81
+ }
82
+ );
83
+ }
84
+ function Trigger({ className }) {
85
+ const { thumbState, notedType, submittedFeedback, handleThumbClick, editSubmitted } = useScoreFeedback();
86
+ if (notedType || submittedFeedback && !thumbState) {
87
+ return /* @__PURE__ */ jsxs("div", { className: cn("shrink-0", className), children: [
88
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-1", children: [
89
+ /* @__PURE__ */ jsx(Check, { className: "w-3 h-3 text-emerald-500" }),
90
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] text-muted-foreground", children: notedType ? notedType === "up" ? "Noted" : "Recorded" : (submittedFeedback == null ? void 0 : submittedFeedback.type) === "up" ? "Noted" : "Recorded" })
91
+ ] }),
92
+ submittedFeedback && (submittedFeedback.pills.length > 0 || submittedFeedback.detail) && /* @__PURE__ */ jsxs(
93
+ "button",
94
+ {
95
+ type: "button",
96
+ onClick: editSubmitted,
97
+ className: "mt-1.5 w-full text-left space-y-1 group cursor-pointer",
98
+ children: [
99
+ submittedFeedback.pills.length > 0 && /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1", children: submittedFeedback.pills.map((p) => /* @__PURE__ */ jsx(
100
+ "span",
101
+ {
102
+ className: cn(
103
+ "rounded-full border px-2 py-0.5 text-[10px] font-medium transition-colors group-hover:opacity-80",
104
+ submittedFeedback.type === "up" ? "border-emerald-200/60 bg-emerald-50/50 text-emerald-700/70" : "border-red-200/60 bg-red-50/50 text-red-700/70"
105
+ ),
106
+ children: p
107
+ },
108
+ p
109
+ )) }),
110
+ submittedFeedback.detail && /* @__PURE__ */ jsx("p", { className: "text-[11px] text-muted-foreground/70 leading-snug group-hover:text-muted-foreground transition-colors", children: submittedFeedback.detail })
111
+ ]
112
+ }
113
+ )
114
+ ] });
115
+ }
116
+ return /* @__PURE__ */ jsxs("div", { className: cn("flex gap-0.5 shrink-0", className), children: [
117
+ /* @__PURE__ */ jsx(
118
+ "button",
119
+ {
120
+ type: "button",
121
+ onClick: () => handleThumbClick("up"),
122
+ className: cn(
123
+ "p-1.5 rounded transition-colors",
124
+ thumbState === "up" ? "bg-emerald-100 text-emerald-600 dark:bg-emerald-900/30 dark:text-emerald-400" : "hover:bg-muted text-muted-foreground hover:text-foreground"
125
+ ),
126
+ children: /* @__PURE__ */ jsx(ThumbsUp, { className: "w-3.5 h-3.5", fill: thumbState === "up" ? "currentColor" : "none" })
127
+ }
128
+ ),
129
+ /* @__PURE__ */ jsx(
130
+ "button",
131
+ {
132
+ type: "button",
133
+ onClick: () => handleThumbClick("down"),
134
+ className: cn(
135
+ "p-1.5 rounded transition-colors",
136
+ thumbState === "down" ? "bg-red-100 text-red-600 dark:bg-red-900/30 dark:text-red-400" : "hover:bg-muted text-muted-foreground hover:text-foreground"
137
+ ),
138
+ children: /* @__PURE__ */ jsx(ThumbsDown, { className: "w-3.5 h-3.5", fill: thumbState === "down" ? "currentColor" : "none" })
139
+ }
140
+ )
141
+ ] });
142
+ }
143
+ function Panel({ className }) {
144
+ const {
145
+ thumbState,
146
+ selectedPills,
147
+ detailText,
148
+ otherSelected,
149
+ hasRequiredInput,
150
+ togglePill,
151
+ setDetailText,
152
+ handleSubmit
153
+ } = useScoreFeedback();
154
+ if (!thumbState) return null;
155
+ return /* @__PURE__ */ jsx("div", { className: cn("overflow-hidden", className), children: /* @__PURE__ */ jsxs("div", { className: "mt-4 pt-4 pb-1 space-y-3 border-t border-border/60", children: [
156
+ /* @__PURE__ */ jsx("span", { className: "text-[11px] font-bold text-muted-foreground/70 uppercase tracking-wider", children: "How's this score?" }),
157
+ /* @__PURE__ */ jsxs("div", { children: [
158
+ /* @__PURE__ */ jsx("span", { className: "text-xs text-muted-foreground mb-2 block font-medium", children: thumbState === "up" ? "What was useful?" : "What\u2019s the issue?" }),
159
+ /* @__PURE__ */ jsx("div", { className: "flex flex-wrap gap-1.5", children: (thumbState === "up" ? positivePills : negativePills).map((pill) => /* @__PURE__ */ jsx(
160
+ "button",
161
+ {
162
+ type: "button",
163
+ onClick: () => togglePill(pill),
164
+ className: cn(
165
+ "px-2.5 py-1 rounded-full text-[11px] font-medium border transition-colors",
166
+ selectedPills.includes(pill) ? thumbState === "up" ? "bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-300 dark:border-emerald-800" : "bg-red-100 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-300 dark:border-red-800" : "bg-background text-muted-foreground border-border hover:bg-muted/50 hover:text-foreground"
167
+ ),
168
+ children: pill
169
+ },
170
+ pill
171
+ )) })
172
+ ] }),
173
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
174
+ /* @__PURE__ */ jsx(
175
+ "input",
176
+ {
177
+ type: "text",
178
+ value: detailText,
179
+ onChange: (e) => setDetailText(e.target.value),
180
+ onKeyDown: (e) => {
181
+ if (e.key === "Enter" && hasRequiredInput) handleSubmit();
182
+ },
183
+ placeholder: thumbState === "up" ? "Tell us more (optional)" : "e.g., The risk factors are outdated",
184
+ className: "w-full text-xs bg-background border border-border rounded-md px-2.5 py-2 text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:ring-1 focus:ring-ring"
185
+ }
186
+ ),
187
+ otherSelected && detailText.trim().length === 0 && /* @__PURE__ */ jsx("span", { className: "text-[10px] text-red-500", children: "Please describe when \u201COther\u201D is selected" })
188
+ ] }),
189
+ /* @__PURE__ */ jsx("div", { className: "flex items-center gap-2 pt-1", children: /* @__PURE__ */ jsx(
190
+ "button",
191
+ {
192
+ type: "button",
193
+ onClick: handleSubmit,
194
+ disabled: !hasRequiredInput,
195
+ className: cn(
196
+ "flex-1 py-1.5 rounded-md text-xs font-semibold transition-colors",
197
+ hasRequiredInput ? "bg-foreground text-background hover:bg-foreground/90" : "bg-muted text-muted-foreground cursor-not-allowed"
198
+ ),
199
+ children: "Submit"
200
+ }
201
+ ) })
202
+ ] }) });
203
+ }
204
+ const ScoreFeedback = { Root, Trigger, Panel };
205
+ export {
206
+ ScoreFeedback,
207
+ useScoreFeedback
208
+ };
209
+ //# sourceMappingURL=score-feedback.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/score-feedback.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ThumbsUp, ThumbsDown, Check } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nconst positivePills = [\n \"Right timing\",\n \"Accurate data\",\n \"Good prospect fit\",\n \"Actionable\",\n]\n\nconst negativePills = [\n \"Bad timing\",\n \"Inaccurate data\",\n \"Wrong prospect\",\n \"Already handled\",\n \"Not actionable\",\n \"Other\",\n]\n\ninterface SubmittedScoreFeedback {\n type: \"up\" | \"down\"\n pills: string[]\n detail: string\n}\n\ninterface ScoreFeedbackState {\n thumbState: \"up\" | \"down\" | null\n selectedPills: string[]\n detailText: string\n notedType: \"up\" | \"down\" | null\n submittedFeedback: SubmittedScoreFeedback | null\n otherSelected: boolean\n hasRequiredInput: boolean\n handleThumbClick: (type: \"up\" | \"down\") => void\n togglePill: (pill: string) => void\n setDetailText: (text: string) => void\n handleSubmit: () => void\n editSubmitted: () => void\n}\n\nconst ScoreFeedbackCtx = React.createContext<ScoreFeedbackState | null>(null)\n\nfunction useScoreFeedback() {\n const ctx = React.useContext(ScoreFeedbackCtx)\n if (!ctx) throw new Error(\"Must be used within ScoreFeedback.Root\")\n return ctx\n}\n\ninterface RootProps {\n children: React.ReactNode\n onSubmitFeedback?: (type: \"up\" | \"down\", pills: string[], detail: string) => void\n}\n\nfunction Root({ children, onSubmitFeedback }: RootProps) {\n const [thumbState, setThumbState] = React.useState<\"up\" | \"down\" | null>(null)\n const [selectedPills, setSelectedPills] = React.useState<string[]>([])\n const [detailText, setDetailTextState] = React.useState(\"\")\n const [notedType, setNotedType] = React.useState<\"up\" | \"down\" | null>(null)\n const [submittedFeedback, setSubmittedFeedback] = React.useState<SubmittedScoreFeedback | null>(null)\n\n const otherSelected = selectedPills.includes(\"Other\")\n\n const hasRequiredInput =\n thumbState === \"down\"\n ? selectedPills.length > 0 && (!otherSelected || detailText.trim().length > 0)\n : selectedPills.length > 0 || detailText.trim().length > 0\n\n const togglePill = React.useCallback((pill: string) => {\n setSelectedPills((prev) =>\n prev.includes(pill) ? prev.filter((p) => p !== pill) : [...prev, pill]\n )\n }, [])\n\n const handleThumbClick = React.useCallback((type: \"up\" | \"down\") => {\n setThumbState((prev) => (prev === type ? null : type))\n setSelectedPills([])\n setDetailTextState(\"\")\n }, [])\n\n const handleSubmit = React.useCallback(() => {\n if (!thumbState) return\n onSubmitFeedback?.(thumbState, selectedPills, detailText)\n setSubmittedFeedback({ type: thumbState, pills: [...selectedPills], detail: detailText.trim() })\n setNotedType(thumbState)\n setThumbState(null)\n setSelectedPills([])\n setDetailTextState(\"\")\n setTimeout(() => setNotedType(null), 3000)\n }, [thumbState, selectedPills, detailText, onSubmitFeedback])\n\n const editSubmitted = React.useCallback(() => {\n if (!submittedFeedback) return\n setThumbState(submittedFeedback.type)\n setSelectedPills([...submittedFeedback.pills])\n setDetailTextState(submittedFeedback.detail)\n setNotedType(null)\n }, [submittedFeedback])\n\n return (\n <ScoreFeedbackCtx.Provider\n value={{\n thumbState,\n selectedPills,\n detailText,\n notedType,\n submittedFeedback,\n otherSelected,\n hasRequiredInput,\n handleThumbClick,\n togglePill,\n setDetailText: setDetailTextState,\n handleSubmit,\n editSubmitted,\n }}\n >\n {children}\n </ScoreFeedbackCtx.Provider>\n )\n}\n\nfunction Trigger({ className }: { className?: string }) {\n const { thumbState, notedType, submittedFeedback, handleThumbClick, editSubmitted } = useScoreFeedback()\n\n if (notedType || (submittedFeedback && !thumbState)) {\n return (\n <div className={cn(\"shrink-0\", className)}>\n <div className=\"flex items-center gap-1\">\n <Check className=\"w-3 h-3 text-emerald-500\" />\n <span className=\"text-[11px] text-muted-foreground\">\n {notedType\n ? notedType === \"up\" ? \"Noted\" : \"Recorded\"\n : submittedFeedback?.type === \"up\" ? \"Noted\" : \"Recorded\"}\n </span>\n </div>\n {submittedFeedback && (submittedFeedback.pills.length > 0 || submittedFeedback.detail) && (\n <button\n type=\"button\"\n onClick={editSubmitted}\n className=\"mt-1.5 w-full text-left space-y-1 group cursor-pointer\"\n >\n {submittedFeedback.pills.length > 0 && (\n <div className=\"flex flex-wrap gap-1\">\n {submittedFeedback.pills.map((p) => (\n <span\n key={p}\n className={cn(\n \"rounded-full border px-2 py-0.5 text-[10px] font-medium transition-colors group-hover:opacity-80\",\n submittedFeedback.type === \"up\"\n ? \"border-emerald-200/60 bg-emerald-50/50 text-emerald-700/70\"\n : \"border-red-200/60 bg-red-50/50 text-red-700/70\"\n )}\n >\n {p}\n </span>\n ))}\n </div>\n )}\n {submittedFeedback.detail && (\n <p className=\"text-[11px] text-muted-foreground/70 leading-snug group-hover:text-muted-foreground transition-colors\">{submittedFeedback.detail}</p>\n )}\n </button>\n )}\n </div>\n )\n }\n\n return (\n <div className={cn(\"flex gap-0.5 shrink-0\", className)}>\n <button\n type=\"button\"\n onClick={() => handleThumbClick(\"up\")}\n className={cn(\n \"p-1.5 rounded transition-colors\",\n thumbState === \"up\"\n ? \"bg-emerald-100 text-emerald-600 dark:bg-emerald-900/30 dark:text-emerald-400\"\n : \"hover:bg-muted text-muted-foreground hover:text-foreground\"\n )}\n >\n <ThumbsUp className=\"w-3.5 h-3.5\" fill={thumbState === \"up\" ? \"currentColor\" : \"none\"} />\n </button>\n <button\n type=\"button\"\n onClick={() => handleThumbClick(\"down\")}\n className={cn(\n \"p-1.5 rounded transition-colors\",\n thumbState === \"down\"\n ? \"bg-red-100 text-red-600 dark:bg-red-900/30 dark:text-red-400\"\n : \"hover:bg-muted text-muted-foreground hover:text-foreground\"\n )}\n >\n <ThumbsDown className=\"w-3.5 h-3.5\" fill={thumbState === \"down\" ? \"currentColor\" : \"none\"} />\n </button>\n </div>\n )\n}\n\nfunction Panel({ className }: { className?: string }) {\n const {\n thumbState,\n selectedPills,\n detailText,\n otherSelected,\n hasRequiredInput,\n togglePill,\n setDetailText,\n handleSubmit,\n } = useScoreFeedback()\n\n if (!thumbState) return null\n\n return (\n <div className={cn(\"overflow-hidden\", className)}>\n <div className=\"mt-4 pt-4 pb-1 space-y-3 border-t border-border/60\">\n <span className=\"text-[11px] font-bold text-muted-foreground/70 uppercase tracking-wider\">\n How&apos;s this score?\n </span>\n <div>\n <span className=\"text-xs text-muted-foreground mb-2 block font-medium\">\n {thumbState === \"up\" ? \"What was useful?\" : \"What\\u2019s the issue?\"}\n </span>\n <div className=\"flex flex-wrap gap-1.5\">\n {(thumbState === \"up\" ? positivePills : negativePills).map((pill) => (\n <button\n key={pill}\n type=\"button\"\n onClick={() => togglePill(pill)}\n className={cn(\n \"px-2.5 py-1 rounded-full text-[11px] font-medium border transition-colors\",\n selectedPills.includes(pill)\n ? thumbState === \"up\"\n ? \"bg-emerald-100 text-emerald-700 border-emerald-200 dark:bg-emerald-900/30 dark:text-emerald-300 dark:border-emerald-800\"\n : \"bg-red-100 text-red-700 border-red-200 dark:bg-red-900/30 dark:text-red-300 dark:border-red-800\"\n : \"bg-background text-muted-foreground border-border hover:bg-muted/50 hover:text-foreground\"\n )}\n >\n {pill}\n </button>\n ))}\n </div>\n </div>\n\n <div className=\"space-y-1\">\n <input\n type=\"text\"\n value={detailText}\n onChange={(e) => setDetailText(e.target.value)}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && hasRequiredInput) handleSubmit()\n }}\n placeholder={\n thumbState === \"up\"\n ? \"Tell us more (optional)\"\n : \"e.g., The risk factors are outdated\"\n }\n className=\"w-full text-xs bg-background border border-border rounded-md px-2.5 py-2 text-foreground placeholder:text-muted-foreground/50 focus:outline-none focus:ring-1 focus:ring-ring\"\n />\n {otherSelected && detailText.trim().length === 0 && (\n <span className=\"text-[10px] text-red-500\">\n Please describe when &ldquo;Other&rdquo; is selected\n </span>\n )}\n </div>\n\n <div className=\"flex items-center gap-2 pt-1\">\n <button\n type=\"button\"\n onClick={handleSubmit}\n disabled={!hasRequiredInput}\n className={cn(\n \"flex-1 py-1.5 rounded-md text-xs font-semibold transition-colors\",\n hasRequiredInput\n ? \"bg-foreground text-background hover:bg-foreground/90\"\n : \"bg-muted text-muted-foreground cursor-not-allowed\"\n )}\n >\n Submit\n </button>\n </div>\n </div>\n </div>\n )\n}\n\nexport const ScoreFeedback = { Root, Trigger, Panel }\nexport { useScoreFeedback }\n"],"mappings":"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","names":[]}
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+
3
+ declare function getScoreColor(score: number, denominator: number): "text-emerald-500" | "text-amber-500" | "text-red-500";
4
+ interface ScoreRingProps {
5
+ score: number;
6
+ denominator?: number;
7
+ size?: number;
8
+ strokeWidth?: number;
9
+ className?: string;
10
+ showLabel?: boolean;
11
+ }
12
+ declare function ScoreRing({ score, denominator, size, strokeWidth, className, showLabel, }: ScoreRingProps): React.JSX.Element;
13
+
14
+ export { ScoreRing, type ScoreRingProps, getScoreColor };
@@ -0,0 +1,79 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import { cn } from "../lib/utils.js";
3
+ function getScoreColor(score, denominator) {
4
+ const pct = score / denominator * 100;
5
+ if (pct >= 70) return "text-emerald-500";
6
+ if (pct >= 40) return "text-amber-500";
7
+ return "text-red-500";
8
+ }
9
+ function getScoreTrackColor(score, denominator) {
10
+ const pct = score / denominator * 100;
11
+ if (pct >= 70) return "text-emerald-500/15";
12
+ if (pct >= 40) return "text-amber-500/15";
13
+ return "text-red-500/15";
14
+ }
15
+ function ScoreRing({
16
+ score,
17
+ denominator = 100,
18
+ size = 120,
19
+ strokeWidth = 10,
20
+ className,
21
+ showLabel = true
22
+ }) {
23
+ const radius = (size - strokeWidth) / 2;
24
+ const circumference = 2 * Math.PI * radius;
25
+ const pct = Math.min(score / denominator, 1);
26
+ const offset = circumference * (1 - pct);
27
+ return /* @__PURE__ */ jsxs("div", { className: cn("relative inline-flex items-center justify-center", className), children: [
28
+ /* @__PURE__ */ jsxs(
29
+ "svg",
30
+ {
31
+ width: size,
32
+ height: size,
33
+ viewBox: `0 0 ${size} ${size}`,
34
+ className: "-rotate-90",
35
+ children: [
36
+ /* @__PURE__ */ jsx(
37
+ "circle",
38
+ {
39
+ cx: size / 2,
40
+ cy: size / 2,
41
+ r: radius,
42
+ fill: "none",
43
+ stroke: "currentColor",
44
+ strokeWidth,
45
+ className: getScoreTrackColor(score, denominator)
46
+ }
47
+ ),
48
+ /* @__PURE__ */ jsx(
49
+ "circle",
50
+ {
51
+ cx: size / 2,
52
+ cy: size / 2,
53
+ r: radius,
54
+ fill: "none",
55
+ stroke: "currentColor",
56
+ strokeWidth,
57
+ strokeLinecap: "round",
58
+ strokeDasharray: circumference,
59
+ strokeDashoffset: offset,
60
+ className: cn("transition-all duration-500", getScoreColor(score, denominator))
61
+ }
62
+ )
63
+ ]
64
+ }
65
+ ),
66
+ showLabel && /* @__PURE__ */ jsxs("div", { className: "absolute inset-0 flex flex-col items-center justify-center", children: [
67
+ /* @__PURE__ */ jsx("span", { className: "text-2xl font-bold text-foreground leading-none", children: score }),
68
+ /* @__PURE__ */ jsxs("span", { className: "text-xs text-muted-foreground mt-0.5", children: [
69
+ "/",
70
+ denominator
71
+ ] })
72
+ ] })
73
+ ] });
74
+ }
75
+ export {
76
+ ScoreRing,
77
+ getScoreColor
78
+ };
79
+ //# sourceMappingURL=score-ring.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/score-ring.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { cn } from \"../lib/utils\"\n\nfunction getScoreColor(score: number, denominator: number) {\n const pct = (score / denominator) * 100\n if (pct >= 70) return \"text-emerald-500\"\n if (pct >= 40) return \"text-amber-500\"\n return \"text-red-500\"\n}\n\nfunction getScoreTrackColor(score: number, denominator: number) {\n const pct = (score / denominator) * 100\n if (pct >= 70) return \"text-emerald-500/15\"\n if (pct >= 40) return \"text-amber-500/15\"\n return \"text-red-500/15\"\n}\n\ninterface ScoreRingProps {\n score: number\n denominator?: number\n size?: number\n strokeWidth?: number\n className?: string\n showLabel?: boolean\n}\n\nfunction ScoreRing({\n score,\n denominator = 100,\n size = 120,\n strokeWidth = 10,\n className,\n showLabel = true,\n}: ScoreRingProps) {\n const radius = (size - strokeWidth) / 2\n const circumference = 2 * Math.PI * radius\n const pct = Math.min(score / denominator, 1)\n const offset = circumference * (1 - pct)\n\n return (\n <div className={cn(\"relative inline-flex items-center justify-center\", className)}>\n <svg\n width={size}\n height={size}\n viewBox={`0 0 ${size} ${size}`}\n className=\"-rotate-90\"\n >\n {/* Track */}\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n className={getScoreTrackColor(score, denominator)}\n />\n {/* Fill */}\n <circle\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke=\"currentColor\"\n strokeWidth={strokeWidth}\n strokeLinecap=\"round\"\n strokeDasharray={circumference}\n strokeDashoffset={offset}\n className={cn(\"transition-all duration-500\", getScoreColor(score, denominator))}\n />\n </svg>\n {showLabel && (\n <div className=\"absolute inset-0 flex flex-col items-center justify-center\">\n <span className=\"text-2xl font-bold text-foreground leading-none\">\n {score}\n </span>\n <span className=\"text-xs text-muted-foreground mt-0.5\">\n /{denominator}\n </span>\n </div>\n )}\n </div>\n )\n}\n\nexport { ScoreRing, getScoreColor }\nexport type { ScoreRingProps }\n"],"mappings":"AAyCM,SAOE,KAPF;AAxCN,SAAS,UAAU;AAEnB,SAAS,cAAc,OAAe,aAAqB;AACzD,QAAM,MAAO,QAAQ,cAAe;AACpC,MAAI,OAAO,GAAI,QAAO;AACtB,MAAI,OAAO,GAAI,QAAO;AACtB,SAAO;AACT;AAEA,SAAS,mBAAmB,OAAe,aAAqB;AAC9D,QAAM,MAAO,QAAQ,cAAe;AACpC,MAAI,OAAO,GAAI,QAAO;AACtB,MAAI,OAAO,GAAI,QAAO;AACtB,SAAO;AACT;AAWA,SAAS,UAAU;AAAA,EACjB;AAAA,EACA,cAAc;AAAA,EACd,OAAO;AAAA,EACP,cAAc;AAAA,EACd;AAAA,EACA,YAAY;AACd,GAAmB;AACjB,QAAM,UAAU,OAAO,eAAe;AACtC,QAAM,gBAAgB,IAAI,KAAK,KAAK;AACpC,QAAM,MAAM,KAAK,IAAI,QAAQ,aAAa,CAAC;AAC3C,QAAM,SAAS,iBAAiB,IAAI;AAEpC,SACE,qBAAC,SAAI,WAAW,GAAG,oDAAoD,SAAS,GAC9E;AAAA;AAAA,MAAC;AAAA;AAAA,QACC,OAAO;AAAA,QACP,QAAQ;AAAA,QACR,SAAS,OAAO,IAAI,IAAI,IAAI;AAAA,QAC5B,WAAU;AAAA,QAGV;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,IAAI,OAAO;AAAA,cACX,IAAI,OAAO;AAAA,cACX,GAAG;AAAA,cACH,MAAK;AAAA,cACL,QAAO;AAAA,cACP;AAAA,cACA,WAAW,mBAAmB,OAAO,WAAW;AAAA;AAAA,UAClD;AAAA,UAEA;AAAA,YAAC;AAAA;AAAA,cACC,IAAI,OAAO;AAAA,cACX,IAAI,OAAO;AAAA,cACX,GAAG;AAAA,cACH,MAAK;AAAA,cACL,QAAO;AAAA,cACP;AAAA,cACA,eAAc;AAAA,cACd,iBAAiB;AAAA,cACjB,kBAAkB;AAAA,cAClB,WAAW,GAAG,+BAA+B,cAAc,OAAO,WAAW,CAAC;AAAA;AAAA,UAChF;AAAA;AAAA;AAAA,IACF;AAAA,IACC,aACC,qBAAC,SAAI,WAAU,8DACb;AAAA,0BAAC,UAAK,WAAU,mDACb,iBACH;AAAA,MACA,qBAAC,UAAK,WAAU,wCAAuC;AAAA;AAAA,QACnD;AAAA,SACJ;AAAA,OACF;AAAA,KAEJ;AAEJ;","names":[]}
@@ -0,0 +1,7 @@
1
+ import * as React from 'react';
2
+ import { ScrollArea as ScrollArea$1 } from 'radix-ui';
3
+
4
+ declare function ScrollArea({ className, children, ...props }: React.ComponentProps<typeof ScrollArea$1.Root>): React.JSX.Element;
5
+ declare function ScrollBar({ className, orientation, ...props }: React.ComponentProps<typeof ScrollArea$1.ScrollAreaScrollbar>): React.JSX.Element;
6
+
7
+ export { ScrollArea, ScrollBar };