@handled-ai/design-system 0.17.2 → 0.18.1

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 (58) hide show
  1. package/dist/charts/empty-chart-state.d.ts +11 -0
  2. package/dist/charts/empty-chart-state.js +70 -0
  3. package/dist/charts/empty-chart-state.js.map +1 -0
  4. package/dist/charts/index.d.ts +1 -0
  5. package/dist/charts/index.js +1 -0
  6. package/dist/charts/index.js.map +1 -1
  7. package/dist/charts/pipeline-overview.d.ts +2 -1
  8. package/dist/charts/pipeline-overview.js +29 -1
  9. package/dist/charts/pipeline-overview.js.map +1 -1
  10. package/dist/components/actor-byline.d.ts +3 -0
  11. package/dist/components/actor-byline.js +5 -0
  12. package/dist/components/actor-byline.js.map +1 -0
  13. package/dist/components/days-open-cell.d.ts +16 -0
  14. package/dist/components/days-open-cell.js +73 -0
  15. package/dist/components/days-open-cell.js.map +1 -0
  16. package/dist/components/detail-drawer.d.ts +16 -0
  17. package/dist/components/detail-drawer.js +45 -0
  18. package/dist/components/detail-drawer.js.map +1 -0
  19. package/dist/components/insights-filter-bar.d.ts +2 -1
  20. package/dist/components/insights-filter-bar.js +13 -5
  21. package/dist/components/insights-filter-bar.js.map +1 -1
  22. package/dist/components/linked-entity-cell.d.ts +14 -0
  23. package/dist/components/linked-entity-cell.js +96 -0
  24. package/dist/components/linked-entity-cell.js.map +1 -0
  25. package/dist/components/metric-card.d.ts +14 -1
  26. package/dist/components/metric-card.js +86 -0
  27. package/dist/components/metric-card.js.map +1 -1
  28. package/dist/components/performance-metrics-table.d.ts +2 -1
  29. package/dist/components/performance-metrics-table.js +78 -46
  30. package/dist/components/performance-metrics-table.js.map +1 -1
  31. package/dist/components/pill.d.ts +26 -0
  32. package/dist/components/pill.js +77 -0
  33. package/dist/components/pill.js.map +1 -0
  34. package/dist/components/quick-segment.d.ts +13 -0
  35. package/dist/components/quick-segment.js +96 -0
  36. package/dist/components/quick-segment.js.map +1 -0
  37. package/dist/index.d.ts +7 -1
  38. package/dist/index.js +5 -0
  39. package/dist/index.js.map +1 -1
  40. package/package.json +1 -1
  41. package/src/charts/__tests__/insights-charts.test.tsx +62 -0
  42. package/src/charts/empty-chart-state.tsx +44 -0
  43. package/src/charts/index.ts +1 -0
  44. package/src/charts/pipeline-overview.tsx +38 -1
  45. package/src/components/__tests__/insights-primitives.test.tsx +117 -0
  46. package/src/components/__tests__/performance-metrics-table.test.tsx +54 -0
  47. package/src/components/__tests__/user-display.test.tsx +75 -0
  48. package/src/components/actor-byline.tsx +1 -0
  49. package/src/components/days-open-cell.tsx +50 -0
  50. package/src/components/detail-drawer.tsx +60 -0
  51. package/src/components/insights-filter-bar.tsx +13 -4
  52. package/src/components/linked-entity-cell.tsx +74 -0
  53. package/src/components/metric-card.tsx +82 -0
  54. package/src/components/performance-metrics-table.tsx +99 -63
  55. package/src/components/pill.tsx +67 -0
  56. package/src/components/quick-segment.tsx +68 -0
  57. package/src/index.ts +5 -0
  58. package/src/lib/__tests__/user-display.test.ts +53 -11
@@ -0,0 +1,96 @@
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
+ var __objRest = (source, exclude) => {
24
+ var target = {};
25
+ for (var prop in source)
26
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
27
+ target[prop] = source[prop];
28
+ if (source != null && __getOwnPropSymbols)
29
+ for (var prop of __getOwnPropSymbols(source)) {
30
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
31
+ target[prop] = source[prop];
32
+ }
33
+ return target;
34
+ };
35
+ import { Fragment, jsx, jsxs } from "react/jsx-runtime";
36
+ import { ExternalLink } from "lucide-react";
37
+ import { cn } from "../lib/utils.js";
38
+ function LinkedEntityCell(_a) {
39
+ var _b = _a, {
40
+ name,
41
+ href,
42
+ subtitle,
43
+ meta,
44
+ icon,
45
+ external = false,
46
+ onNavigate,
47
+ className
48
+ } = _b, props = __objRest(_b, [
49
+ "name",
50
+ "href",
51
+ "subtitle",
52
+ "meta",
53
+ "icon",
54
+ "external",
55
+ "onNavigate",
56
+ "className"
57
+ ]);
58
+ const content = /* @__PURE__ */ jsxs(Fragment, { children: [
59
+ /* @__PURE__ */ jsx("span", { className: "truncate", children: name }),
60
+ external ? /* @__PURE__ */ jsx(ExternalLink, { className: "h-3 w-3 shrink-0 opacity-60", "aria-hidden": "true" }) : null
61
+ ] });
62
+ return /* @__PURE__ */ jsxs(
63
+ "div",
64
+ __spreadProps(__spreadValues({
65
+ "data-slot": "linked-entity-cell",
66
+ className: cn("flex min-w-0 items-center gap-2", className)
67
+ }, props), {
68
+ children: [
69
+ icon ? /* @__PURE__ */ jsx("span", { "data-slot": "linked-entity-cell-icon", className: "shrink-0 text-muted-foreground", children: icon }) : null,
70
+ /* @__PURE__ */ jsxs("div", { className: "min-w-0 flex-1", children: [
71
+ href ? /* @__PURE__ */ jsx(
72
+ "a",
73
+ {
74
+ "data-slot": "linked-entity-cell-link",
75
+ href,
76
+ target: external ? "_blank" : void 0,
77
+ rel: external ? "noreferrer" : void 0,
78
+ onClick: onNavigate,
79
+ className: "inline-flex max-w-full items-center gap-1 truncate font-medium text-foreground underline-offset-4 hover:text-primary hover:underline",
80
+ children: content
81
+ }
82
+ ) : /* @__PURE__ */ jsx("span", { "data-slot": "linked-entity-cell-name", className: "block truncate font-medium text-foreground", children: name }),
83
+ subtitle || meta ? /* @__PURE__ */ jsxs("div", { "data-slot": "linked-entity-cell-meta", className: "mt-0.5 truncate text-xs text-muted-foreground", children: [
84
+ subtitle,
85
+ subtitle && meta ? /* @__PURE__ */ jsx("span", { className: "px-1", children: "\xB7" }) : null,
86
+ meta
87
+ ] }) : null
88
+ ] })
89
+ ]
90
+ })
91
+ );
92
+ }
93
+ export {
94
+ LinkedEntityCell
95
+ };
96
+ //# sourceMappingURL=linked-entity-cell.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/components/linked-entity-cell.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport { ExternalLink } from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\n\nexport interface LinkedEntityCellProps extends React.HTMLAttributes<HTMLDivElement> {\n name: React.ReactNode\n href?: string\n subtitle?: React.ReactNode\n meta?: React.ReactNode\n icon?: React.ReactNode\n external?: boolean\n onNavigate?: () => void\n}\n\nexport function LinkedEntityCell({\n name,\n href,\n subtitle,\n meta,\n icon,\n external = false,\n onNavigate,\n className,\n ...props\n}: LinkedEntityCellProps) {\n const content = (\n <>\n <span className=\"truncate\">{name}</span>\n {external ? <ExternalLink className=\"h-3 w-3 shrink-0 opacity-60\" aria-hidden=\"true\" /> : null}\n </>\n )\n\n return (\n <div\n data-slot=\"linked-entity-cell\"\n className={cn(\"flex min-w-0 items-center gap-2\", className)}\n {...props}\n >\n {icon ? (\n <span data-slot=\"linked-entity-cell-icon\" className=\"shrink-0 text-muted-foreground\">\n {icon}\n </span>\n ) : null}\n <div className=\"min-w-0 flex-1\">\n {href ? (\n <a\n data-slot=\"linked-entity-cell-link\"\n href={href}\n target={external ? \"_blank\" : undefined}\n rel={external ? \"noreferrer\" : undefined}\n onClick={onNavigate}\n className=\"inline-flex max-w-full items-center gap-1 truncate font-medium text-foreground underline-offset-4 hover:text-primary hover:underline\"\n >\n {content}\n </a>\n ) : (\n <span data-slot=\"linked-entity-cell-name\" className=\"block truncate font-medium text-foreground\">\n {name}\n </span>\n )}\n {subtitle || meta ? (\n <div data-slot=\"linked-entity-cell-meta\" className=\"mt-0.5 truncate text-xs text-muted-foreground\">\n {subtitle}\n {subtitle && meta ? <span className=\"px-1\">·</span> : null}\n {meta}\n </div>\n ) : null}\n </div>\n </div>\n )\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA6BI,mBACE,KADF;AA1BJ,SAAS,oBAAoB;AAE7B,SAAS,UAAU;AAYZ,SAAS,iBAAiB,IAUP;AAVO,eAC/B;AAAA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,WAAW;AAAA,IACX;AAAA,IACA;AAAA,EAzBF,IAiBiC,IAS5B,kBAT4B,IAS5B;AAAA,IARH;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA;AAGA,QAAM,UACJ,iCACE;AAAA,wBAAC,UAAK,WAAU,YAAY,gBAAK;AAAA,IAChC,WAAW,oBAAC,gBAAa,WAAU,+BAA8B,eAAY,QAAO,IAAK;AAAA,KAC5F;AAGF,SACE;AAAA,IAAC;AAAA;AAAA,MACC,aAAU;AAAA,MACV,WAAW,GAAG,mCAAmC,SAAS;AAAA,OACtD,QAHL;AAAA,MAKE;AAAA,eACC,oBAAC,UAAK,aAAU,2BAA0B,WAAU,kCACjD,gBACH,IACE;AAAA,QACJ,qBAAC,SAAI,WAAU,kBACZ;AAAA,iBACC;AAAA,YAAC;AAAA;AAAA,cACC,aAAU;AAAA,cACV;AAAA,cACA,QAAQ,WAAW,WAAW;AAAA,cAC9B,KAAK,WAAW,eAAe;AAAA,cAC/B,SAAS;AAAA,cACT,WAAU;AAAA,cAET;AAAA;AAAA,UACH,IAEA,oBAAC,UAAK,aAAU,2BAA0B,WAAU,8CACjD,gBACH;AAAA,UAED,YAAY,OACX,qBAAC,SAAI,aAAU,2BAA0B,WAAU,iDAChD;AAAA;AAAA,YACA,YAAY,OAAO,oBAAC,UAAK,WAAU,QAAO,kBAAC,IAAU;AAAA,YACrD;AAAA,aACH,IACE;AAAA,WACN;AAAA;AAAA;AAAA,EACF;AAEJ;","names":[]}
@@ -20,6 +20,19 @@ interface MetricCardProps {
20
20
  showExternalLink?: boolean;
21
21
  showInfo?: boolean;
22
22
  }
23
+ interface KpiStripItem {
24
+ id?: string;
25
+ label: React.ReactNode;
26
+ value: React.ReactNode;
27
+ unit?: React.ReactNode;
28
+ subtitle?: React.ReactNode;
29
+ change?: MetricCardProps["change"];
30
+ }
31
+ interface KpiStripProps extends React.HTMLAttributes<HTMLDivElement> {
32
+ items: KpiStripItem[];
33
+ columns?: 2 | 3 | 4;
34
+ }
35
+ declare function KpiStrip({ items, columns, className, ...props }: KpiStripProps): React.JSX.Element;
23
36
  declare function MetricCard({ title, value, unit, subtitle, change, footerText, dataPoints, showExternalLink, showInfo, }: MetricCardProps): React.JSX.Element;
24
37
 
25
- export { MetricCard, type MetricCardProps, type MetricDataPoint };
38
+ export { KpiStrip, type KpiStripItem, type KpiStripProps, MetricCard, type MetricCardProps, type MetricDataPoint };
@@ -1,6 +1,91 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __defProps = Object.defineProperties;
3
+ var __getOwnPropDescs = Object.getOwnPropertyDescriptors;
4
+ var __getOwnPropSymbols = Object.getOwnPropertySymbols;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __propIsEnum = Object.prototype.propertyIsEnumerable;
7
+ var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: true, configurable: true, writable: true, value }) : obj[key] = value;
8
+ var __spreadValues = (a, b) => {
9
+ for (var prop in b || (b = {}))
10
+ if (__hasOwnProp.call(b, prop))
11
+ __defNormalProp(a, prop, b[prop]);
12
+ if (__getOwnPropSymbols)
13
+ for (var prop of __getOwnPropSymbols(b)) {
14
+ if (__propIsEnum.call(b, prop))
15
+ __defNormalProp(a, prop, b[prop]);
16
+ }
17
+ return a;
18
+ };
19
+ var __spreadProps = (a, b) => __defProps(a, __getOwnPropDescs(b));
20
+ var __objRest = (source, exclude) => {
21
+ var target = {};
22
+ for (var prop in source)
23
+ if (__hasOwnProp.call(source, prop) && exclude.indexOf(prop) < 0)
24
+ target[prop] = source[prop];
25
+ if (source != null && __getOwnPropSymbols)
26
+ for (var prop of __getOwnPropSymbols(source)) {
27
+ if (exclude.indexOf(prop) < 0 && __propIsEnum.call(source, prop))
28
+ target[prop] = source[prop];
29
+ }
30
+ return target;
31
+ };
1
32
  import { jsx, jsxs } from "react/jsx-runtime";
2
33
  import { ArrowUp, ArrowDown, Info, ExternalLink } from "lucide-react";
3
34
  import { cn } from "../lib/utils.js";
35
+ function KpiStrip(_a) {
36
+ var _b = _a, { items, columns = 4, className } = _b, props = __objRest(_b, ["items", "columns", "className"]);
37
+ return /* @__PURE__ */ jsx(
38
+ "div",
39
+ __spreadProps(__spreadValues({
40
+ "data-slot": "kpi-strip",
41
+ className: cn(
42
+ "grid gap-3 rounded-xl border border-border bg-card p-3 shadow-sm",
43
+ columns === 2 && "sm:grid-cols-2",
44
+ columns === 3 && "sm:grid-cols-3",
45
+ columns === 4 && "sm:grid-cols-2 lg:grid-cols-4",
46
+ className
47
+ )
48
+ }, props), {
49
+ children: items.map((item, index) => {
50
+ var _a2, _b2;
51
+ const isGoodDirection = item.change ? item.change.isGood !== void 0 ? item.change.isGood : item.change.direction === "up" : false;
52
+ const ChangeIcon = ((_a2 = item.change) == null ? void 0 : _a2.direction) === "down" ? ArrowDown : ArrowUp;
53
+ return /* @__PURE__ */ jsxs(
54
+ "div",
55
+ {
56
+ "data-slot": "kpi-strip-item",
57
+ className: "min-w-0 rounded-lg bg-muted/40 px-3 py-2",
58
+ children: [
59
+ /* @__PURE__ */ jsx("div", { "data-slot": "kpi-strip-label", className: "truncate text-xs font-medium text-muted-foreground", children: item.label }),
60
+ /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-baseline gap-1", children: [
61
+ /* @__PURE__ */ jsx("span", { "data-slot": "kpi-strip-value", className: "truncate text-2xl font-bold tracking-tight text-foreground", children: item.value }),
62
+ item.unit ? /* @__PURE__ */ jsx("span", { "data-slot": "kpi-strip-unit", className: "text-sm font-semibold text-muted-foreground", children: item.unit }) : null
63
+ ] }),
64
+ item.subtitle || item.change ? /* @__PURE__ */ jsxs("div", { className: "mt-1 flex items-center gap-2 text-xs", children: [
65
+ item.change ? /* @__PURE__ */ jsxs(
66
+ "span",
67
+ {
68
+ "data-slot": "kpi-strip-change",
69
+ className: cn(
70
+ "inline-flex items-center gap-0.5 font-semibold",
71
+ isGoodDirection ? "text-emerald-600" : "text-red-600"
72
+ ),
73
+ children: [
74
+ /* @__PURE__ */ jsx(ChangeIcon, { className: "h-3 w-3 stroke-[3]" }),
75
+ item.change.value
76
+ ]
77
+ }
78
+ ) : null,
79
+ item.subtitle ? /* @__PURE__ */ jsx("span", { "data-slot": "kpi-strip-subtitle", className: "truncate text-muted-foreground", children: item.subtitle }) : null
80
+ ] }) : null
81
+ ]
82
+ },
83
+ (_b2 = item.id) != null ? _b2 : index
84
+ );
85
+ })
86
+ })
87
+ );
88
+ }
4
89
  function MetricCard({
5
90
  title,
6
91
  value,
@@ -102,6 +187,7 @@ function MetricCard({
102
187
  ] });
103
188
  }
104
189
  export {
190
+ KpiStrip,
105
191
  MetricCard
106
192
  };
107
193
  //# sourceMappingURL=metric-card.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/metric-card.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { ArrowUp, ArrowDown, Info, ExternalLink } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nexport interface MetricDataPoint {\n label: string\n value: number | string\n color?: string\n}\n\nexport interface MetricCardProps {\n title: string\n value?: string | number\n unit?: string\n subtitle?: string\n change?: { \n value: string\n direction: \"up\" | \"down\" | \"neutral\"\n isGood?: boolean // if true, up is green. if false, up is red (e.g. churn).\n }\n footerText?: string\n dataPoints?: MetricDataPoint[]\n showExternalLink?: boolean\n showInfo?: boolean\n}\n\nexport function MetricCard({\n title,\n value,\n unit,\n subtitle,\n change,\n footerText,\n dataPoints,\n showExternalLink,\n showInfo = true,\n}: MetricCardProps) {\n // SVG Donut Chart logic for variants with dataPoints\n const renderDonut = () => {\n if (!dataPoints || dataPoints.length === 0 || value === undefined) return null\n\n // Simple pseudo-donut chart logic assuming specific colors from the image\n // In a real prod environment we'd use recharts/visx, but for this standalone component\n // we can draw an SVG circle with stroke-dasharray based on the data\n const size = 80\n const strokeWidth = 12\n const radius = (size - strokeWidth) / 2\n const circumference = 2 * Math.PI * radius\n \n // Calculate total to distribute the circle\n const total = dataPoints.reduce((sum, dp) => sum + (typeof dp.value === 'number' ? dp.value : 0), 0)\n let currentOffset = 0\n\n return (\n <div className=\"relative\" style={{ width: size, height: size }}>\n <svg width={size} height={size} className=\"transform -rotate-90\">\n {dataPoints.map((dp, i) => {\n const val = typeof dp.value === 'number' ? dp.value : 0\n const percentage = val / total\n const strokeLength = percentage * circumference\n const offset = currentOffset\n currentOffset += strokeLength\n\n // Fallback colors matching the image's teal/green palette\n const colors = [\"#166534\", \"#22c55e\", \"#6ee7b7\", \"#ccfbf1\", \"#f1f5f9\"]\n const color = dp.color || colors[i % colors.length]\n\n return (\n <circle\n key={dp.label}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeDasharray={`${Math.max(strokeLength - 2, 0)} ${circumference}`}\n strokeDashoffset={-offset}\n className=\"transition-all duration-300\"\n />\n )\n })}\n </svg>\n <div className=\"absolute inset-0 flex items-center justify-center flex-col\">\n <span className=\"text-xl font-bold text-foreground leading-none\">{value}</span>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col rounded-xl border border-border bg-card p-5 shadow-sm h-full w-full\">\n <div className={cn(\"flex justify-between items-start\", title ? \"mb-4\" : \"mb-4\")}>\n {title ? (\n <h3 className=\"font-semibold text-sm text-foreground/80\">{title}</h3>\n ) : (\n <div className=\"flex flex-col\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-3xl font-bold tracking-tight text-foreground\">{value}</span>\n {unit && <span className=\"text-2xl font-bold tracking-tight text-foreground\">{unit}</span>}\n </div>\n {subtitle && (\n <p className=\"text-sm font-medium text-muted-foreground mt-2\">{subtitle}</p>\n )}\n </div>\n )}\n <div className=\"flex items-center gap-1.5 text-muted-foreground shrink-0 mt-0.5\">\n {showExternalLink && <ExternalLink className=\"w-3.5 h-3.5 cursor-pointer hover:text-foreground transition-colors\" />}\n {showInfo && <Info className=\"w-3.5 h-3.5 cursor-pointer hover:text-foreground transition-colors\" />}\n </div>\n </div>\n\n <div className=\"flex-1 flex flex-col min-w-0\">\n {dataPoints && dataPoints.length > 0 ? (\n // Donut Chart Variant\n <div className=\"flex items-center gap-4 mt-2 mb-6\">\n <div className=\"shrink-0\">\n {renderDonut()}\n </div>\n <div className=\"flex flex-col gap-2 flex-1 min-w-0\">\n {dataPoints.slice(0, 5).map((dp, i) => {\n const colors = [\"bg-[#166534]\", \"bg-[#22c55e]\", \"bg-[#6ee7b7]\", \"bg-[#ccfbf1]\", \"bg-[#f1f5f9]\"]\n return (\n <div key={dp.label} className=\"flex items-center justify-between gap-2 text-[11px] font-medium min-w-0\">\n <div className=\"flex items-center gap-1.5 text-muted-foreground min-w-0\">\n <div className={cn(\"w-1.5 h-1.5 rounded-full shrink-0\", dp.color ? \"\" : colors[i % colors.length])} style={dp.color ? { backgroundColor: dp.color } : {}} />\n <span className=\"whitespace-nowrap\">{dp.label}</span>\n </div>\n <span className=\"text-foreground font-semibold shrink-0\">{dp.value}</span>\n </div>\n )\n })}\n </div>\n </div>\n ) : title && (\n // Standard Big Number Variant (only if title exists)\n <div className=\"mb-6\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-4xl font-bold tracking-tight text-foreground\">{value}</span>\n {unit && <span className=\"text-2xl font-bold tracking-tight text-foreground\">{unit}</span>}\n </div>\n {subtitle && (\n <p className=\"text-sm font-medium text-muted-foreground mt-1\">{subtitle}</p>\n )}\n </div>\n )}\n\n {/* Footer section (Change indicator & extra text) */}\n <div className=\"mt-auto flex flex-col gap-1.5\">\n {change && (\n <div className=\"flex items-center gap-1\">\n {(() => {\n // Determine color based on isGood property\n // By default, up is green (good), down is red (bad)\n const isGoodDirection = change.isGood !== undefined \n ? change.isGood \n : change.direction === \"up\";\n \n const colorClass = isGoodDirection ? \"text-emerald-600\" : \"text-red-600\";\n const Icon = change.direction === \"down\" ? ArrowDown : ArrowUp;\n \n return (\n <span className={cn(\"text-xs font-semibold flex items-center gap-0.5\", colorClass)}>\n <Icon className=\"w-3 h-3 stroke-[3]\" />\n {change.value}\n </span>\n )\n })()}\n </div>\n )}\n {footerText && (\n <span className=\"text-[11px] text-muted-foreground font-medium\">{footerText}</span>\n )}\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/metric-card.tsx"],"sourcesContent":["import * as React from \"react\"\nimport { ArrowUp, ArrowDown, Info, ExternalLink } from \"lucide-react\"\nimport { cn } from \"../lib/utils\"\n\nexport interface MetricDataPoint {\n label: string\n value: number | string\n color?: string\n}\n\nexport interface MetricCardProps {\n title: string\n value?: string | number\n unit?: string\n subtitle?: string\n change?: { \n value: string\n direction: \"up\" | \"down\" | \"neutral\"\n isGood?: boolean // if true, up is green. if false, up is red (e.g. churn).\n }\n footerText?: string\n dataPoints?: MetricDataPoint[]\n showExternalLink?: boolean\n showInfo?: boolean\n}\n\nexport interface KpiStripItem {\n id?: string\n label: React.ReactNode\n value: React.ReactNode\n unit?: React.ReactNode\n subtitle?: React.ReactNode\n change?: MetricCardProps[\"change\"]\n}\n\nexport interface KpiStripProps extends React.HTMLAttributes<HTMLDivElement> {\n items: KpiStripItem[]\n columns?: 2 | 3 | 4\n}\n\nexport function KpiStrip({ items, columns = 4, className, ...props }: KpiStripProps) {\n return (\n <div\n data-slot=\"kpi-strip\"\n className={cn(\n \"grid gap-3 rounded-xl border border-border bg-card p-3 shadow-sm\",\n columns === 2 && \"sm:grid-cols-2\",\n columns === 3 && \"sm:grid-cols-3\",\n columns === 4 && \"sm:grid-cols-2 lg:grid-cols-4\",\n className\n )}\n {...props}\n >\n {items.map((item, index) => {\n const isGoodDirection = item.change\n ? item.change.isGood !== undefined\n ? item.change.isGood\n : item.change.direction === \"up\"\n : false\n const ChangeIcon = item.change?.direction === \"down\" ? ArrowDown : ArrowUp\n\n return (\n <div\n key={item.id ?? index}\n data-slot=\"kpi-strip-item\"\n className=\"min-w-0 rounded-lg bg-muted/40 px-3 py-2\"\n >\n <div data-slot=\"kpi-strip-label\" className=\"truncate text-xs font-medium text-muted-foreground\">\n {item.label}\n </div>\n <div className=\"mt-1 flex items-baseline gap-1\">\n <span data-slot=\"kpi-strip-value\" className=\"truncate text-2xl font-bold tracking-tight text-foreground\">\n {item.value}\n </span>\n {item.unit ? (\n <span data-slot=\"kpi-strip-unit\" className=\"text-sm font-semibold text-muted-foreground\">\n {item.unit}\n </span>\n ) : null}\n </div>\n {item.subtitle || item.change ? (\n <div className=\"mt-1 flex items-center gap-2 text-xs\">\n {item.change ? (\n <span\n data-slot=\"kpi-strip-change\"\n className={cn(\n \"inline-flex items-center gap-0.5 font-semibold\",\n isGoodDirection ? \"text-emerald-600\" : \"text-red-600\"\n )}\n >\n <ChangeIcon className=\"h-3 w-3 stroke-[3]\" />\n {item.change.value}\n </span>\n ) : null}\n {item.subtitle ? (\n <span data-slot=\"kpi-strip-subtitle\" className=\"truncate text-muted-foreground\">\n {item.subtitle}\n </span>\n ) : null}\n </div>\n ) : null}\n </div>\n )\n })}\n </div>\n )\n}\n\nexport function MetricCard({\n title,\n value,\n unit,\n subtitle,\n change,\n footerText,\n dataPoints,\n showExternalLink,\n showInfo = true,\n}: MetricCardProps) {\n // SVG Donut Chart logic for variants with dataPoints\n const renderDonut = () => {\n if (!dataPoints || dataPoints.length === 0 || value === undefined) return null\n\n // Simple pseudo-donut chart logic assuming specific colors from the image\n // In a real prod environment we'd use recharts/visx, but for this standalone component\n // we can draw an SVG circle with stroke-dasharray based on the data\n const size = 80\n const strokeWidth = 12\n const radius = (size - strokeWidth) / 2\n const circumference = 2 * Math.PI * radius\n \n // Calculate total to distribute the circle\n const total = dataPoints.reduce((sum, dp) => sum + (typeof dp.value === 'number' ? dp.value : 0), 0)\n let currentOffset = 0\n\n return (\n <div className=\"relative\" style={{ width: size, height: size }}>\n <svg width={size} height={size} className=\"transform -rotate-90\">\n {dataPoints.map((dp, i) => {\n const val = typeof dp.value === 'number' ? dp.value : 0\n const percentage = val / total\n const strokeLength = percentage * circumference\n const offset = currentOffset\n currentOffset += strokeLength\n\n // Fallback colors matching the image's teal/green palette\n const colors = [\"#166534\", \"#22c55e\", \"#6ee7b7\", \"#ccfbf1\", \"#f1f5f9\"]\n const color = dp.color || colors[i % colors.length]\n\n return (\n <circle\n key={dp.label}\n cx={size / 2}\n cy={size / 2}\n r={radius}\n fill=\"none\"\n stroke={color}\n strokeWidth={strokeWidth}\n strokeDasharray={`${Math.max(strokeLength - 2, 0)} ${circumference}`}\n strokeDashoffset={-offset}\n className=\"transition-all duration-300\"\n />\n )\n })}\n </svg>\n <div className=\"absolute inset-0 flex items-center justify-center flex-col\">\n <span className=\"text-xl font-bold text-foreground leading-none\">{value}</span>\n </div>\n </div>\n )\n }\n\n return (\n <div className=\"flex flex-col rounded-xl border border-border bg-card p-5 shadow-sm h-full w-full\">\n <div className={cn(\"flex justify-between items-start\", title ? \"mb-4\" : \"mb-4\")}>\n {title ? (\n <h3 className=\"font-semibold text-sm text-foreground/80\">{title}</h3>\n ) : (\n <div className=\"flex flex-col\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-3xl font-bold tracking-tight text-foreground\">{value}</span>\n {unit && <span className=\"text-2xl font-bold tracking-tight text-foreground\">{unit}</span>}\n </div>\n {subtitle && (\n <p className=\"text-sm font-medium text-muted-foreground mt-2\">{subtitle}</p>\n )}\n </div>\n )}\n <div className=\"flex items-center gap-1.5 text-muted-foreground shrink-0 mt-0.5\">\n {showExternalLink && <ExternalLink className=\"w-3.5 h-3.5 cursor-pointer hover:text-foreground transition-colors\" />}\n {showInfo && <Info className=\"w-3.5 h-3.5 cursor-pointer hover:text-foreground transition-colors\" />}\n </div>\n </div>\n\n <div className=\"flex-1 flex flex-col min-w-0\">\n {dataPoints && dataPoints.length > 0 ? (\n // Donut Chart Variant\n <div className=\"flex items-center gap-4 mt-2 mb-6\">\n <div className=\"shrink-0\">\n {renderDonut()}\n </div>\n <div className=\"flex flex-col gap-2 flex-1 min-w-0\">\n {dataPoints.slice(0, 5).map((dp, i) => {\n const colors = [\"bg-[#166534]\", \"bg-[#22c55e]\", \"bg-[#6ee7b7]\", \"bg-[#ccfbf1]\", \"bg-[#f1f5f9]\"]\n return (\n <div key={dp.label} className=\"flex items-center justify-between gap-2 text-[11px] font-medium min-w-0\">\n <div className=\"flex items-center gap-1.5 text-muted-foreground min-w-0\">\n <div className={cn(\"w-1.5 h-1.5 rounded-full shrink-0\", dp.color ? \"\" : colors[i % colors.length])} style={dp.color ? { backgroundColor: dp.color } : {}} />\n <span className=\"whitespace-nowrap\">{dp.label}</span>\n </div>\n <span className=\"text-foreground font-semibold shrink-0\">{dp.value}</span>\n </div>\n )\n })}\n </div>\n </div>\n ) : title && (\n // Standard Big Number Variant (only if title exists)\n <div className=\"mb-6\">\n <div className=\"flex items-baseline gap-1\">\n <span className=\"text-4xl font-bold tracking-tight text-foreground\">{value}</span>\n {unit && <span className=\"text-2xl font-bold tracking-tight text-foreground\">{unit}</span>}\n </div>\n {subtitle && (\n <p className=\"text-sm font-medium text-muted-foreground mt-1\">{subtitle}</p>\n )}\n </div>\n )}\n\n {/* Footer section (Change indicator & extra text) */}\n <div className=\"mt-auto flex flex-col gap-1.5\">\n {change && (\n <div className=\"flex items-center gap-1\">\n {(() => {\n // Determine color based on isGood property\n // By default, up is green (good), down is red (bad)\n const isGoodDirection = change.isGood !== undefined \n ? change.isGood \n : change.direction === \"up\";\n \n const colorClass = isGoodDirection ? \"text-emerald-600\" : \"text-red-600\";\n const Icon = change.direction === \"down\" ? ArrowDown : ArrowUp;\n \n return (\n <span className={cn(\"text-xs font-semibold flex items-center gap-0.5\", colorClass)}>\n <Icon className=\"w-3 h-3 stroke-[3]\" />\n {change.value}\n </span>\n )\n })()}\n </div>\n )}\n {footerText && (\n <span className=\"text-[11px] text-muted-foreground font-medium\">{footerText}</span>\n )}\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":["_a","_b"]}
@@ -21,6 +21,7 @@ interface PerformanceMetricsTableProps {
21
21
  title?: string;
22
22
  entityColumnLabel?: string;
23
23
  primaryMetricColumnLabel?: string;
24
+ primaryMetricDisplayMode?: "progress" | "value";
24
25
  rateColumnLabel?: string;
25
26
  metricOneColumnLabel?: string;
26
27
  metricTwoColumnLabel?: string;
@@ -33,6 +34,6 @@ interface PerformanceMetricsTableProps {
33
34
  pageSize?: number;
34
35
  searchPlaceholder?: string;
35
36
  }
36
- declare function PerformanceMetricsTable({ title, entityColumnLabel, primaryMetricColumnLabel, rateColumnLabel, metricOneColumnLabel, metricTwoColumnLabel, metricThreeColumnLabel, metricFourColumnLabel, viewOptions, roleOptions, sortOptions, rows, pageSize, searchPlaceholder, }: PerformanceMetricsTableProps): React.JSX.Element;
37
+ declare function PerformanceMetricsTable({ title, entityColumnLabel, primaryMetricColumnLabel, primaryMetricDisplayMode, rateColumnLabel, metricOneColumnLabel, metricTwoColumnLabel, metricThreeColumnLabel, metricFourColumnLabel, viewOptions, roleOptions, sortOptions, rows, pageSize, searchPlaceholder, }: PerformanceMetricsTableProps): React.JSX.Element;
37
38
 
38
39
  export { PerformanceMetricsTable, type PerformanceMetricsTableRow, type PerformanceMetricsTableSortOption };
@@ -137,6 +137,37 @@ function getProgressStatus(value, target) {
137
137
  icon: /* @__PURE__ */ jsx(AlertTriangle, { className: "h-3.5 w-3.5 text-red-600" })
138
138
  };
139
139
  }
140
+ function PrimaryMetricCell({
141
+ row,
142
+ displayMode
143
+ }) {
144
+ if (displayMode === "value") {
145
+ return /* @__PURE__ */ jsx("div", { className: "text-sm font-bold text-foreground", children: row.primaryValue });
146
+ }
147
+ const percentage = row.primaryValue / row.primaryTarget * 100;
148
+ const progress = getProgressStatus(row.primaryValue, row.primaryTarget);
149
+ return /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
150
+ /* @__PURE__ */ jsx("span", { className: "shrink-0", children: progress.icon }),
151
+ /* @__PURE__ */ jsxs("div", { className: "w-full max-w-[180px]", children: [
152
+ /* @__PURE__ */ jsxs("div", { className: "mb-1 text-sm font-bold text-foreground", children: [
153
+ row.primaryValue,
154
+ "/",
155
+ row.primaryTarget
156
+ ] }),
157
+ /* @__PURE__ */ jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx(
158
+ "div",
159
+ {
160
+ className: cn("h-full rounded-full", progress.color),
161
+ style: { width: `${Math.min(100, percentage)}%` }
162
+ }
163
+ ) }),
164
+ /* @__PURE__ */ jsxs("div", { className: cn("mt-1 text-xs font-medium", progress.textColor), children: [
165
+ Math.round(percentage),
166
+ "%"
167
+ ] })
168
+ ] })
169
+ ] });
170
+ }
140
171
  function sortRows(rows, sortId) {
141
172
  const copy = [...rows];
142
173
  switch (sortId) {
@@ -155,13 +186,19 @@ function PerformanceMetricsTable({
155
186
  title = "Performance Table",
156
187
  entityColumnLabel = "Entity",
157
188
  primaryMetricColumnLabel = "Primary Goal",
189
+ primaryMetricDisplayMode = "progress",
158
190
  rateColumnLabel = "Rate",
159
191
  metricOneColumnLabel = "Metric One",
160
192
  metricTwoColumnLabel = "Metric Two",
161
193
  metricThreeColumnLabel = "Metric Three",
162
194
  metricFourColumnLabel = "Metric Four",
163
195
  viewOptions = ["By Entity"],
164
- roleOptions = ["All", "Senior Coordinator", "Coordinator", "Junior Coordinator"],
196
+ roleOptions = [
197
+ "All",
198
+ "Senior Coordinator",
199
+ "Coordinator",
200
+ "Junior Coordinator"
201
+ ],
165
202
  sortOptions = DEFAULT_SORT_OPTIONS,
166
203
  rows = DEFAULT_ROWS,
167
204
  pageSize = 6,
@@ -169,9 +206,7 @@ function PerformanceMetricsTable({
169
206
  }) {
170
207
  var _a, _b, _c, _d, _e, _f;
171
208
  const [view, setView] = React.useState((_a = viewOptions[0]) != null ? _a : "By Entity");
172
- const [sortId, setSortId] = React.useState(
173
- (_c = (_b = sortOptions[0]) == null ? void 0 : _b.id) != null ? _c : "primary-desc"
174
- );
209
+ const [sortId, setSortId] = React.useState((_c = (_b = sortOptions[0]) == null ? void 0 : _b.id) != null ? _c : "primary-desc");
175
210
  const [roleFilter, setRoleFilter] = React.useState((_d = roleOptions[0]) != null ? _d : "All");
176
211
  const [search, setSearch] = React.useState("");
177
212
  const [page, setPage] = React.useState(1);
@@ -218,7 +253,14 @@ function PerformanceMetricsTable({
218
253
  sortLabel.replace(/\s*\(.+\)/, ""),
219
254
  /* @__PURE__ */ jsx(ChevronDown, { className: "ml-2 h-3.5 w-3.5" })
220
255
  ] }) }),
221
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "w-56", children: sortOptions.map((option) => /* @__PURE__ */ jsx(DropdownMenuItem, { onClick: () => setSortId(option.id), children: option.label }, option.id)) })
256
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", className: "w-56", children: sortOptions.map((option) => /* @__PURE__ */ jsx(
257
+ DropdownMenuItem,
258
+ {
259
+ onClick: () => setSortId(option.id),
260
+ children: option.label
261
+ },
262
+ option.id
263
+ )) })
222
264
  ] }),
223
265
  /* @__PURE__ */ jsxs(DropdownMenu, { children: [
224
266
  /* @__PURE__ */ jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxs(Button, { variant: "outline", size: "sm", className: "h-8 text-xs", children: [
@@ -226,7 +268,14 @@ function PerformanceMetricsTable({
226
268
  roleFilter,
227
269
  /* @__PURE__ */ jsx(ChevronDown, { className: "ml-2 h-3.5 w-3.5" })
228
270
  ] }) }),
229
- /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: roleOptions.map((option) => /* @__PURE__ */ jsx(DropdownMenuItem, { onClick: () => setRoleFilter(option), children: option }, option)) })
271
+ /* @__PURE__ */ jsx(DropdownMenuContent, { align: "end", children: roleOptions.map((option) => /* @__PURE__ */ jsx(
272
+ DropdownMenuItem,
273
+ {
274
+ onClick: () => setRoleFilter(option),
275
+ children: option
276
+ },
277
+ option
278
+ )) })
230
279
  ] }),
231
280
  /* @__PURE__ */ jsx(
232
281
  Input,
@@ -250,45 +299,27 @@ function PerformanceMetricsTable({
250
299
  /* @__PURE__ */ jsx(TableHead, { className: "px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: metricThreeColumnLabel }),
251
300
  /* @__PURE__ */ jsx(TableHead, { className: "px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground", children: metricFourColumnLabel })
252
301
  ] }) }),
253
- /* @__PURE__ */ jsx(TableBody, { children: paginatedRows.map((row) => {
254
- const percentage = row.primaryValue / row.primaryTarget * 100;
255
- const progress = getProgressStatus(row.primaryValue, row.primaryTarget);
256
- return /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-muted/30", children: [
257
- /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
258
- /* @__PURE__ */ jsx(Avatar, { className: "h-8 w-8 border border-border", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-emerald-100 text-[11px] font-medium text-emerald-700", children: row.avatarFallback }) }),
259
- /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: row.label })
260
- ] }) }),
261
- /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
262
- /* @__PURE__ */ jsx("span", { className: "shrink-0", children: progress.icon }),
263
- /* @__PURE__ */ jsxs("div", { className: "w-full max-w-[180px]", children: [
264
- /* @__PURE__ */ jsxs("div", { className: "mb-1 text-sm font-bold text-foreground", children: [
265
- row.primaryValue,
266
- "/",
267
- row.primaryTarget
268
- ] }),
269
- /* @__PURE__ */ jsx("div", { className: "h-1.5 w-full overflow-hidden rounded-full bg-muted", children: /* @__PURE__ */ jsx(
270
- "div",
271
- {
272
- className: cn("h-full rounded-full", progress.color),
273
- style: { width: `${Math.min(100, percentage)}%` }
274
- }
275
- ) }),
276
- /* @__PURE__ */ jsxs("div", { className: cn("mt-1 text-xs font-medium", progress.textColor), children: [
277
- Math.round(percentage),
278
- "%"
279
- ] })
280
- ] })
281
- ] }) }),
282
- /* @__PURE__ */ jsxs(TableCell, { className: "px-4 py-3 text-right text-sm font-semibold text-emerald-600", children: [
283
- row.ratePercent,
284
- "%"
285
- ] }),
286
- /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3 text-right text-sm font-medium text-foreground", children: row.metricOne }),
287
- /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3 text-right text-sm text-muted-foreground", children: row.metricTwo }),
288
- /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3 text-right text-sm font-medium text-foreground", children: row.metricThree }),
289
- /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3 text-right text-sm font-medium text-foreground", children: row.metricFour })
290
- ] }, row.id);
291
- }) })
302
+ /* @__PURE__ */ jsx(TableBody, { children: paginatedRows.map((row) => /* @__PURE__ */ jsxs(TableRow, { className: "hover:bg-muted/30", children: [
303
+ /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3", children: /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-3", children: [
304
+ /* @__PURE__ */ jsx(Avatar, { className: "h-8 w-8 border border-border", children: /* @__PURE__ */ jsx(AvatarFallback, { className: "bg-emerald-100 text-[11px] font-medium text-emerald-700", children: row.avatarFallback }) }),
305
+ /* @__PURE__ */ jsx("span", { className: "text-sm font-medium text-foreground", children: row.label })
306
+ ] }) }),
307
+ /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3", children: /* @__PURE__ */ jsx(
308
+ PrimaryMetricCell,
309
+ {
310
+ row,
311
+ displayMode: primaryMetricDisplayMode
312
+ }
313
+ ) }),
314
+ /* @__PURE__ */ jsxs(TableCell, { className: "px-4 py-3 text-right text-sm font-semibold text-emerald-600", children: [
315
+ row.ratePercent,
316
+ "%"
317
+ ] }),
318
+ /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3 text-right text-sm font-medium text-foreground", children: row.metricOne }),
319
+ /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3 text-right text-sm text-muted-foreground", children: row.metricTwo }),
320
+ /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3 text-right text-sm font-medium text-foreground", children: row.metricThree }),
321
+ /* @__PURE__ */ jsx(TableCell, { className: "px-4 py-3 text-right text-sm font-medium text-foreground", children: row.metricFour })
322
+ ] }, row.id)) })
292
323
  ] }) }),
293
324
  /* @__PURE__ */ jsx(ScrollBar, { orientation: "horizontal" })
294
325
  ] }),
@@ -301,7 +332,8 @@ function PerformanceMetricsTable({
301
332
  Math.min(start + pageSize, sortedRows.length),
302
333
  " of ",
303
334
  sortedRows.length,
304
- " rows"
335
+ " ",
336
+ "rows"
305
337
  ] }),
306
338
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
307
339
  /* @__PURE__ */ jsxs(
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/components/performance-metrics-table.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n AlertTriangle,\n CheckCircle2,\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n} from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { Avatar, AvatarFallback } from \"./avatar\"\nimport { Button } from \"./button\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\nimport { Input } from \"./input\"\nimport { ScrollArea, ScrollBar } from \"./scroll-area\"\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"./table\"\n\nexport interface PerformanceMetricsTableRow {\n id: string\n label: string\n avatarFallback: string\n role?: string\n primaryValue: number\n primaryTarget: number\n ratePercent: number\n metricOne: number\n metricTwo: string\n metricThree: number\n metricFour: number\n}\n\nexport interface PerformanceMetricsTableSortOption {\n id: \"primary-desc\" | \"primary-asc\" | \"rate-desc\" | \"metric-four-desc\"\n label: string\n}\n\ninterface PerformanceMetricsTableProps {\n title?: string\n entityColumnLabel?: string\n primaryMetricColumnLabel?: string\n rateColumnLabel?: string\n metricOneColumnLabel?: string\n metricTwoColumnLabel?: string\n metricThreeColumnLabel?: string\n metricFourColumnLabel?: string\n viewOptions?: string[]\n roleOptions?: string[]\n sortOptions?: PerformanceMetricsTableSortOption[]\n rows?: PerformanceMetricsTableRow[]\n pageSize?: number\n searchPlaceholder?: string\n}\n\nconst DEFAULT_ROWS: PerformanceMetricsTableRow[] = [\n {\n id: \"member-1\",\n label: \"Jennifer Davis\",\n avatarFallback: \"JD\",\n role: \"Senior Coordinator\",\n primaryValue: 188,\n primaryTarget: 200,\n ratePercent: 78,\n metricOne: 256,\n metricTwo: \"8.5h\",\n metricThree: 401,\n metricFour: 42,\n },\n {\n id: \"member-2\",\n label: \"Robert Taylor\",\n avatarFallback: \"RT\",\n role: \"Coordinator\",\n primaryValue: 168,\n primaryTarget: 200,\n ratePercent: 70,\n metricOne: 210,\n metricTwo: \"7.4h\",\n metricThree: 330,\n metricFour: 36,\n },\n {\n id: \"member-3\",\n label: \"Karen Park\",\n avatarFallback: \"KP\",\n role: \"Coordinator\",\n primaryValue: 165,\n primaryTarget: 200,\n ratePercent: 68,\n metricOne: 195,\n metricTwo: \"6.9h\",\n metricThree: 298,\n metricFour: 33,\n },\n {\n id: \"member-4\",\n label: \"Alex Chen\",\n avatarFallback: \"AC\",\n role: \"Junior Coordinator\",\n primaryValue: 142,\n primaryTarget: 200,\n ratePercent: 65,\n metricOne: 201,\n metricTwo: \"7.2h\",\n metricThree: 315,\n metricFour: 29,\n },\n {\n id: \"member-5\",\n label: \"Sarah Mitchell\",\n avatarFallback: \"SM\",\n role: \"Senior Coordinator\",\n primaryValue: 130,\n primaryTarget: 200,\n ratePercent: 76,\n metricOne: 247,\n metricTwo: \"8.2h\",\n metricThree: 389,\n metricFour: 31,\n },\n {\n id: \"member-6\",\n label: \"Mike Rodriguez\",\n avatarFallback: \"MR\",\n role: \"Coordinator\",\n primaryValue: 115,\n primaryTarget: 200,\n ratePercent: 72,\n metricOne: 218,\n metricTwo: \"7.8h\",\n metricThree: 342,\n metricFour: 25,\n },\n]\n\nconst DEFAULT_SORT_OPTIONS: PerformanceMetricsTableSortOption[] = [\n { id: \"primary-desc\", label: \"Primary Metric (High to Low)\" },\n { id: \"primary-asc\", label: \"Primary Metric (Low to High)\" },\n { id: \"rate-desc\", label: \"Rate (High to Low)\" },\n { id: \"metric-four-desc\", label: \"Metric Four (High to Low)\" },\n]\n\nfunction getProgressStatus(value: number, target: number) {\n const percent = (value / target) * 100\n\n if (percent >= 80) {\n return {\n color: \"bg-emerald-500\",\n textColor: \"text-emerald-700\",\n icon: <CheckCircle2 className=\"h-3.5 w-3.5 text-emerald-600\" />,\n }\n }\n\n if (percent >= 65) {\n return {\n color: \"bg-amber-500\",\n textColor: \"text-amber-700\",\n icon: <AlertTriangle className=\"h-3.5 w-3.5 text-amber-600\" />,\n }\n }\n\n return {\n color: \"bg-red-500\",\n textColor: \"text-red-700\",\n icon: <AlertTriangle className=\"h-3.5 w-3.5 text-red-600\" />,\n }\n}\n\nfunction sortRows(\n rows: PerformanceMetricsTableRow[],\n sortId: PerformanceMetricsTableSortOption[\"id\"]\n) {\n const copy = [...rows]\n switch (sortId) {\n case \"primary-asc\":\n return copy.sort((a, b) => a.primaryValue - b.primaryValue)\n case \"rate-desc\":\n return copy.sort((a, b) => b.ratePercent - a.ratePercent)\n case \"metric-four-desc\":\n return copy.sort((a, b) => b.metricFour - a.metricFour)\n case \"primary-desc\":\n default:\n return copy.sort((a, b) => b.primaryValue - a.primaryValue)\n }\n}\n\nexport function PerformanceMetricsTable({\n title = \"Performance Table\",\n entityColumnLabel = \"Entity\",\n primaryMetricColumnLabel = \"Primary Goal\",\n rateColumnLabel = \"Rate\",\n metricOneColumnLabel = \"Metric One\",\n metricTwoColumnLabel = \"Metric Two\",\n metricThreeColumnLabel = \"Metric Three\",\n metricFourColumnLabel = \"Metric Four\",\n viewOptions = [\"By Entity\"],\n roleOptions = [\"All\", \"Senior Coordinator\", \"Coordinator\", \"Junior Coordinator\"],\n sortOptions = DEFAULT_SORT_OPTIONS,\n rows = DEFAULT_ROWS,\n pageSize = 6,\n searchPlaceholder = \"Search rows...\",\n}: PerformanceMetricsTableProps) {\n const [view, setView] = React.useState(viewOptions[0] ?? \"By Entity\")\n const [sortId, setSortId] =\n React.useState<PerformanceMetricsTableSortOption[\"id\"]>(\n sortOptions[0]?.id ?? \"primary-desc\"\n )\n const [roleFilter, setRoleFilter] = React.useState(roleOptions[0] ?? \"All\")\n const [search, setSearch] = React.useState(\"\")\n const [page, setPage] = React.useState(1)\n\n const filteredRows = React.useMemo(() => {\n const normalized = search.trim().toLowerCase()\n return rows.filter((row) => {\n if (roleFilter !== \"All\" && row.role !== roleFilter) {\n return false\n }\n if (!normalized) {\n return true\n }\n return row.label.toLowerCase().includes(normalized)\n })\n }, [roleFilter, rows, search])\n\n const sortedRows = React.useMemo(\n () => sortRows(filteredRows, sortId),\n [filteredRows, sortId]\n )\n\n const pageCount = Math.max(1, Math.ceil(sortedRows.length / pageSize))\n const start = (page - 1) * pageSize\n const paginatedRows = sortedRows.slice(start, start + pageSize)\n\n React.useEffect(() => {\n setPage(1)\n }, [search, roleFilter, sortId, view])\n\n React.useEffect(() => {\n setPage((previous) => Math.min(previous, pageCount))\n }, [pageCount])\n\n const sortLabel =\n sortOptions.find((option) => option.id === sortId)?.label ?? \"Sort\"\n\n return (\n <div className=\"overflow-hidden rounded-xl border border-border bg-card\">\n <div className=\"flex items-center justify-between border-b border-border px-4 py-3\">\n <h3 className=\"text-sm font-semibold text-foreground\">{title}</h3>\n <div className=\"flex items-center gap-2\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs\">\n {view}\n <ChevronDown className=\"ml-2 h-3.5 w-3.5\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n {viewOptions.map((option) => (\n <DropdownMenuItem key={option} onClick={() => setView(option)}>\n {option}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs\">\n Sort: {sortLabel.replace(/\\s*\\(.+\\)/, \"\")}\n <ChevronDown className=\"ml-2 h-3.5 w-3.5\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-56\">\n {sortOptions.map((option) => (\n <DropdownMenuItem key={option.id} onClick={() => setSortId(option.id)}>\n {option.label}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs\">\n Role: {roleFilter}\n <ChevronDown className=\"ml-2 h-3.5 w-3.5\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {roleOptions.map((option) => (\n <DropdownMenuItem key={option} onClick={() => setRoleFilter(option)}>\n {option}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n\n <Input\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder={searchPlaceholder}\n className=\"h-8 w-48 text-xs\"\n />\n </div>\n </div>\n\n <ScrollArea>\n <div className=\"min-w-[1180px]\">\n <Table>\n <TableHeader className=\"bg-muted/30\">\n <TableRow className=\"hover:bg-transparent\">\n <TableHead className=\"px-4 py-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {entityColumnLabel}\n </TableHead>\n <TableHead className=\"w-[260px] px-4 py-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {primaryMetricColumnLabel}\n </TableHead>\n <TableHead className=\"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {rateColumnLabel}\n </TableHead>\n <TableHead className=\"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {metricOneColumnLabel}\n </TableHead>\n <TableHead className=\"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {metricTwoColumnLabel}\n </TableHead>\n <TableHead className=\"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {metricThreeColumnLabel}\n </TableHead>\n <TableHead className=\"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {metricFourColumnLabel}\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {paginatedRows.map((row) => {\n const percentage = (row.primaryValue / row.primaryTarget) * 100\n const progress = getProgressStatus(row.primaryValue, row.primaryTarget)\n\n return (\n <TableRow key={row.id} className=\"hover:bg-muted/30\">\n <TableCell className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n <Avatar className=\"h-8 w-8 border border-border\">\n <AvatarFallback className=\"bg-emerald-100 text-[11px] font-medium text-emerald-700\">\n {row.avatarFallback}\n </AvatarFallback>\n </Avatar>\n <span className=\"text-sm font-medium text-foreground\">{row.label}</span>\n </div>\n </TableCell>\n\n <TableCell className=\"px-4 py-3\">\n <div className=\"flex items-center gap-2\">\n <span className=\"shrink-0\">{progress.icon}</span>\n <div className=\"w-full max-w-[180px]\">\n <div className=\"mb-1 text-sm font-bold text-foreground\">\n {row.primaryValue}/{row.primaryTarget}\n </div>\n <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n <div\n className={cn(\"h-full rounded-full\", progress.color)}\n style={{ width: `${Math.min(100, percentage)}%` }}\n />\n </div>\n <div className={cn(\"mt-1 text-xs font-medium\", progress.textColor)}>\n {Math.round(percentage)}%\n </div>\n </div>\n </div>\n </TableCell>\n\n <TableCell className=\"px-4 py-3 text-right text-sm font-semibold text-emerald-600\">\n {row.ratePercent}%\n </TableCell>\n <TableCell className=\"px-4 py-3 text-right text-sm font-medium text-foreground\">\n {row.metricOne}\n </TableCell>\n <TableCell className=\"px-4 py-3 text-right text-sm text-muted-foreground\">\n {row.metricTwo}\n </TableCell>\n <TableCell className=\"px-4 py-3 text-right text-sm font-medium text-foreground\">\n {row.metricThree}\n </TableCell>\n <TableCell className=\"px-4 py-3 text-right text-sm font-medium text-foreground\">\n {row.metricFour}\n </TableCell>\n </TableRow>\n )\n })}\n </TableBody>\n </Table>\n </div>\n <ScrollBar orientation=\"horizontal\" />\n </ScrollArea>\n\n <div className=\"flex items-center justify-between border-t border-border bg-muted/20 px-4 py-3\">\n <span className=\"text-xs text-muted-foreground\">\n Showing {sortedRows.length === 0 ? 0 : start + 1} to{\" \"}\n {Math.min(start + pageSize, sortedRows.length)} of {sortedRows.length} rows\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-7 text-xs\"\n disabled={page <= 1}\n onClick={() => setPage((previous) => Math.max(previous - 1, 1))}\n >\n <ChevronLeft className=\"mr-1 h-3.5 w-3.5\" />\n Previous\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-7 text-xs\"\n disabled={page >= pageCount}\n onClick={() =>\n setPage((previous) => Math.min(previous + 1, pageCount))\n }\n >\n Next\n <ChevronRight className=\"ml-1 h-3.5 w-3.5\" />\n </Button>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../../src/components/performance-metrics-table.tsx"],"sourcesContent":["\"use client\"\n\nimport * as React from \"react\"\nimport {\n AlertTriangle,\n CheckCircle2,\n ChevronDown,\n ChevronLeft,\n ChevronRight,\n} from \"lucide-react\"\n\nimport { cn } from \"../lib/utils\"\nimport { Avatar, AvatarFallback } from \"./avatar\"\nimport { Button } from \"./button\"\nimport {\n DropdownMenu,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuTrigger,\n} from \"./dropdown-menu\"\nimport { Input } from \"./input\"\nimport { ScrollArea, ScrollBar } from \"./scroll-area\"\nimport {\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n} from \"./table\"\n\nexport interface PerformanceMetricsTableRow {\n id: string\n label: string\n avatarFallback: string\n role?: string\n primaryValue: number\n primaryTarget: number\n ratePercent: number\n metricOne: number\n metricTwo: string\n metricThree: number\n metricFour: number\n}\n\nexport interface PerformanceMetricsTableSortOption {\n id: \"primary-desc\" | \"primary-asc\" | \"rate-desc\" | \"metric-four-desc\"\n label: string\n}\n\ninterface PerformanceMetricsTableProps {\n title?: string\n entityColumnLabel?: string\n primaryMetricColumnLabel?: string\n primaryMetricDisplayMode?: \"progress\" | \"value\"\n rateColumnLabel?: string\n metricOneColumnLabel?: string\n metricTwoColumnLabel?: string\n metricThreeColumnLabel?: string\n metricFourColumnLabel?: string\n viewOptions?: string[]\n roleOptions?: string[]\n sortOptions?: PerformanceMetricsTableSortOption[]\n rows?: PerformanceMetricsTableRow[]\n pageSize?: number\n searchPlaceholder?: string\n}\n\nconst DEFAULT_ROWS: PerformanceMetricsTableRow[] = [\n {\n id: \"member-1\",\n label: \"Jennifer Davis\",\n avatarFallback: \"JD\",\n role: \"Senior Coordinator\",\n primaryValue: 188,\n primaryTarget: 200,\n ratePercent: 78,\n metricOne: 256,\n metricTwo: \"8.5h\",\n metricThree: 401,\n metricFour: 42,\n },\n {\n id: \"member-2\",\n label: \"Robert Taylor\",\n avatarFallback: \"RT\",\n role: \"Coordinator\",\n primaryValue: 168,\n primaryTarget: 200,\n ratePercent: 70,\n metricOne: 210,\n metricTwo: \"7.4h\",\n metricThree: 330,\n metricFour: 36,\n },\n {\n id: \"member-3\",\n label: \"Karen Park\",\n avatarFallback: \"KP\",\n role: \"Coordinator\",\n primaryValue: 165,\n primaryTarget: 200,\n ratePercent: 68,\n metricOne: 195,\n metricTwo: \"6.9h\",\n metricThree: 298,\n metricFour: 33,\n },\n {\n id: \"member-4\",\n label: \"Alex Chen\",\n avatarFallback: \"AC\",\n role: \"Junior Coordinator\",\n primaryValue: 142,\n primaryTarget: 200,\n ratePercent: 65,\n metricOne: 201,\n metricTwo: \"7.2h\",\n metricThree: 315,\n metricFour: 29,\n },\n {\n id: \"member-5\",\n label: \"Sarah Mitchell\",\n avatarFallback: \"SM\",\n role: \"Senior Coordinator\",\n primaryValue: 130,\n primaryTarget: 200,\n ratePercent: 76,\n metricOne: 247,\n metricTwo: \"8.2h\",\n metricThree: 389,\n metricFour: 31,\n },\n {\n id: \"member-6\",\n label: \"Mike Rodriguez\",\n avatarFallback: \"MR\",\n role: \"Coordinator\",\n primaryValue: 115,\n primaryTarget: 200,\n ratePercent: 72,\n metricOne: 218,\n metricTwo: \"7.8h\",\n metricThree: 342,\n metricFour: 25,\n },\n]\n\nconst DEFAULT_SORT_OPTIONS: PerformanceMetricsTableSortOption[] = [\n { id: \"primary-desc\", label: \"Primary Metric (High to Low)\" },\n { id: \"primary-asc\", label: \"Primary Metric (Low to High)\" },\n { id: \"rate-desc\", label: \"Rate (High to Low)\" },\n { id: \"metric-four-desc\", label: \"Metric Four (High to Low)\" },\n]\n\nfunction getProgressStatus(value: number, target: number) {\n const percent = (value / target) * 100\n\n if (percent >= 80) {\n return {\n color: \"bg-emerald-500\",\n textColor: \"text-emerald-700\",\n icon: <CheckCircle2 className=\"h-3.5 w-3.5 text-emerald-600\" />,\n }\n }\n\n if (percent >= 65) {\n return {\n color: \"bg-amber-500\",\n textColor: \"text-amber-700\",\n icon: <AlertTriangle className=\"h-3.5 w-3.5 text-amber-600\" />,\n }\n }\n\n return {\n color: \"bg-red-500\",\n textColor: \"text-red-700\",\n icon: <AlertTriangle className=\"h-3.5 w-3.5 text-red-600\" />,\n }\n}\n\nfunction PrimaryMetricCell({\n row,\n displayMode,\n}: {\n row: PerformanceMetricsTableRow\n displayMode: \"progress\" | \"value\"\n}) {\n if (displayMode === \"value\") {\n return (\n <div className=\"text-sm font-bold text-foreground\">\n {row.primaryValue}\n </div>\n )\n }\n\n const percentage = (row.primaryValue / row.primaryTarget) * 100\n const progress = getProgressStatus(row.primaryValue, row.primaryTarget)\n\n return (\n <div className=\"flex items-center gap-2\">\n <span className=\"shrink-0\">{progress.icon}</span>\n <div className=\"w-full max-w-[180px]\">\n <div className=\"mb-1 text-sm font-bold text-foreground\">\n {row.primaryValue}/{row.primaryTarget}\n </div>\n <div className=\"h-1.5 w-full overflow-hidden rounded-full bg-muted\">\n <div\n className={cn(\"h-full rounded-full\", progress.color)}\n style={{ width: `${Math.min(100, percentage)}%` }}\n />\n </div>\n <div className={cn(\"mt-1 text-xs font-medium\", progress.textColor)}>\n {Math.round(percentage)}%\n </div>\n </div>\n </div>\n )\n}\n\nfunction sortRows(\n rows: PerformanceMetricsTableRow[],\n sortId: PerformanceMetricsTableSortOption[\"id\"],\n) {\n const copy = [...rows]\n switch (sortId) {\n case \"primary-asc\":\n return copy.sort((a, b) => a.primaryValue - b.primaryValue)\n case \"rate-desc\":\n return copy.sort((a, b) => b.ratePercent - a.ratePercent)\n case \"metric-four-desc\":\n return copy.sort((a, b) => b.metricFour - a.metricFour)\n case \"primary-desc\":\n default:\n return copy.sort((a, b) => b.primaryValue - a.primaryValue)\n }\n}\n\nexport function PerformanceMetricsTable({\n title = \"Performance Table\",\n entityColumnLabel = \"Entity\",\n primaryMetricColumnLabel = \"Primary Goal\",\n primaryMetricDisplayMode = \"progress\",\n rateColumnLabel = \"Rate\",\n metricOneColumnLabel = \"Metric One\",\n metricTwoColumnLabel = \"Metric Two\",\n metricThreeColumnLabel = \"Metric Three\",\n metricFourColumnLabel = \"Metric Four\",\n viewOptions = [\"By Entity\"],\n roleOptions = [\n \"All\",\n \"Senior Coordinator\",\n \"Coordinator\",\n \"Junior Coordinator\",\n ],\n sortOptions = DEFAULT_SORT_OPTIONS,\n rows = DEFAULT_ROWS,\n pageSize = 6,\n searchPlaceholder = \"Search rows...\",\n}: PerformanceMetricsTableProps) {\n const [view, setView] = React.useState(viewOptions[0] ?? \"By Entity\")\n const [sortId, setSortId] = React.useState<\n PerformanceMetricsTableSortOption[\"id\"]\n >(sortOptions[0]?.id ?? \"primary-desc\")\n const [roleFilter, setRoleFilter] = React.useState(roleOptions[0] ?? \"All\")\n const [search, setSearch] = React.useState(\"\")\n const [page, setPage] = React.useState(1)\n\n const filteredRows = React.useMemo(() => {\n const normalized = search.trim().toLowerCase()\n return rows.filter((row) => {\n if (roleFilter !== \"All\" && row.role !== roleFilter) {\n return false\n }\n if (!normalized) {\n return true\n }\n return row.label.toLowerCase().includes(normalized)\n })\n }, [roleFilter, rows, search])\n\n const sortedRows = React.useMemo(\n () => sortRows(filteredRows, sortId),\n [filteredRows, sortId],\n )\n\n const pageCount = Math.max(1, Math.ceil(sortedRows.length / pageSize))\n const start = (page - 1) * pageSize\n const paginatedRows = sortedRows.slice(start, start + pageSize)\n\n React.useEffect(() => {\n setPage(1)\n }, [search, roleFilter, sortId, view])\n\n React.useEffect(() => {\n setPage((previous) => Math.min(previous, pageCount))\n }, [pageCount])\n\n const sortLabel =\n sortOptions.find((option) => option.id === sortId)?.label ?? \"Sort\"\n\n return (\n <div className=\"overflow-hidden rounded-xl border border-border bg-card\">\n <div className=\"flex items-center justify-between border-b border-border px-4 py-3\">\n <h3 className=\"text-sm font-semibold text-foreground\">{title}</h3>\n <div className=\"flex items-center gap-2\">\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs\">\n {view}\n <ChevronDown className=\"ml-2 h-3.5 w-3.5\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent>\n {viewOptions.map((option) => (\n <DropdownMenuItem key={option} onClick={() => setView(option)}>\n {option}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs\">\n Sort: {sortLabel.replace(/\\s*\\(.+\\)/, \"\")}\n <ChevronDown className=\"ml-2 h-3.5 w-3.5\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\" className=\"w-56\">\n {sortOptions.map((option) => (\n <DropdownMenuItem\n key={option.id}\n onClick={() => setSortId(option.id)}\n >\n {option.label}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n <Button variant=\"outline\" size=\"sm\" className=\"h-8 text-xs\">\n Role: {roleFilter}\n <ChevronDown className=\"ml-2 h-3.5 w-3.5\" />\n </Button>\n </DropdownMenuTrigger>\n <DropdownMenuContent align=\"end\">\n {roleOptions.map((option) => (\n <DropdownMenuItem\n key={option}\n onClick={() => setRoleFilter(option)}\n >\n {option}\n </DropdownMenuItem>\n ))}\n </DropdownMenuContent>\n </DropdownMenu>\n\n <Input\n value={search}\n onChange={(event) => setSearch(event.target.value)}\n placeholder={searchPlaceholder}\n className=\"h-8 w-48 text-xs\"\n />\n </div>\n </div>\n\n <ScrollArea>\n <div className=\"min-w-[1180px]\">\n <Table>\n <TableHeader className=\"bg-muted/30\">\n <TableRow className=\"hover:bg-transparent\">\n <TableHead className=\"px-4 py-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {entityColumnLabel}\n </TableHead>\n <TableHead className=\"w-[260px] px-4 py-3 text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {primaryMetricColumnLabel}\n </TableHead>\n <TableHead className=\"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {rateColumnLabel}\n </TableHead>\n <TableHead className=\"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {metricOneColumnLabel}\n </TableHead>\n <TableHead className=\"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {metricTwoColumnLabel}\n </TableHead>\n <TableHead className=\"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {metricThreeColumnLabel}\n </TableHead>\n <TableHead className=\"px-4 py-3 text-right text-xs font-semibold uppercase tracking-wider text-muted-foreground\">\n {metricFourColumnLabel}\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {paginatedRows.map((row) => (\n <TableRow key={row.id} className=\"hover:bg-muted/30\">\n <TableCell className=\"px-4 py-3\">\n <div className=\"flex items-center gap-3\">\n <Avatar className=\"h-8 w-8 border border-border\">\n <AvatarFallback className=\"bg-emerald-100 text-[11px] font-medium text-emerald-700\">\n {row.avatarFallback}\n </AvatarFallback>\n </Avatar>\n <span className=\"text-sm font-medium text-foreground\">\n {row.label}\n </span>\n </div>\n </TableCell>\n\n <TableCell className=\"px-4 py-3\">\n <PrimaryMetricCell\n row={row}\n displayMode={primaryMetricDisplayMode}\n />\n </TableCell>\n\n <TableCell className=\"px-4 py-3 text-right text-sm font-semibold text-emerald-600\">\n {row.ratePercent}%\n </TableCell>\n <TableCell className=\"px-4 py-3 text-right text-sm font-medium text-foreground\">\n {row.metricOne}\n </TableCell>\n <TableCell className=\"px-4 py-3 text-right text-sm text-muted-foreground\">\n {row.metricTwo}\n </TableCell>\n <TableCell className=\"px-4 py-3 text-right text-sm font-medium text-foreground\">\n {row.metricThree}\n </TableCell>\n <TableCell className=\"px-4 py-3 text-right text-sm font-medium text-foreground\">\n {row.metricFour}\n </TableCell>\n </TableRow>\n ))}\n </TableBody>\n </Table>\n </div>\n <ScrollBar orientation=\"horizontal\" />\n </ScrollArea>\n\n <div className=\"flex items-center justify-between border-t border-border bg-muted/20 px-4 py-3\">\n <span className=\"text-xs text-muted-foreground\">\n Showing {sortedRows.length === 0 ? 0 : start + 1} to{\" \"}\n {Math.min(start + pageSize, sortedRows.length)} of {sortedRows.length}{\" \"}\n rows\n </span>\n <div className=\"flex items-center gap-2\">\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-7 text-xs\"\n disabled={page <= 1}\n onClick={() => setPage((previous) => Math.max(previous - 1, 1))}\n >\n <ChevronLeft className=\"mr-1 h-3.5 w-3.5\" />\n Previous\n </Button>\n <Button\n variant=\"outline\"\n size=\"sm\"\n className=\"h-7 text-xs\"\n disabled={page >= pageCount}\n onClick={() =>\n setPage((previous) => Math.min(previous + 1, pageCount))\n }\n >\n Next\n <ChevronRight className=\"ml-1 h-3.5 w-3.5\" />\n </Button>\n </div>\n </div>\n </div>\n )\n}\n"],"mappings":"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","names":[]}