@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.
- package/dist/charts/empty-chart-state.d.ts +11 -0
- package/dist/charts/empty-chart-state.js +70 -0
- package/dist/charts/empty-chart-state.js.map +1 -0
- package/dist/charts/index.d.ts +1 -0
- package/dist/charts/index.js +1 -0
- package/dist/charts/index.js.map +1 -1
- package/dist/charts/pipeline-overview.d.ts +2 -1
- package/dist/charts/pipeline-overview.js +29 -1
- package/dist/charts/pipeline-overview.js.map +1 -1
- package/dist/components/actor-byline.d.ts +3 -0
- package/dist/components/actor-byline.js +5 -0
- package/dist/components/actor-byline.js.map +1 -0
- package/dist/components/days-open-cell.d.ts +16 -0
- package/dist/components/days-open-cell.js +73 -0
- package/dist/components/days-open-cell.js.map +1 -0
- package/dist/components/detail-drawer.d.ts +16 -0
- package/dist/components/detail-drawer.js +45 -0
- package/dist/components/detail-drawer.js.map +1 -0
- package/dist/components/insights-filter-bar.d.ts +2 -1
- package/dist/components/insights-filter-bar.js +13 -5
- package/dist/components/insights-filter-bar.js.map +1 -1
- package/dist/components/linked-entity-cell.d.ts +14 -0
- package/dist/components/linked-entity-cell.js +96 -0
- package/dist/components/linked-entity-cell.js.map +1 -0
- package/dist/components/metric-card.d.ts +14 -1
- package/dist/components/metric-card.js +86 -0
- package/dist/components/metric-card.js.map +1 -1
- package/dist/components/performance-metrics-table.d.ts +2 -1
- package/dist/components/performance-metrics-table.js +78 -46
- package/dist/components/performance-metrics-table.js.map +1 -1
- package/dist/components/pill.d.ts +26 -0
- package/dist/components/pill.js +77 -0
- package/dist/components/pill.js.map +1 -0
- package/dist/components/quick-segment.d.ts +13 -0
- package/dist/components/quick-segment.js +96 -0
- package/dist/components/quick-segment.js.map +1 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/charts/__tests__/insights-charts.test.tsx +62 -0
- package/src/charts/empty-chart-state.tsx +44 -0
- package/src/charts/index.ts +1 -0
- package/src/charts/pipeline-overview.tsx +38 -1
- package/src/components/__tests__/insights-primitives.test.tsx +117 -0
- package/src/components/__tests__/performance-metrics-table.test.tsx +54 -0
- package/src/components/__tests__/user-display.test.tsx +75 -0
- package/src/components/actor-byline.tsx +1 -0
- package/src/components/days-open-cell.tsx +50 -0
- package/src/components/detail-drawer.tsx +60 -0
- package/src/components/insights-filter-bar.tsx +13 -4
- package/src/components/linked-entity-cell.tsx +74 -0
- package/src/components/metric-card.tsx +82 -0
- package/src/components/performance-metrics-table.tsx +99 -63
- package/src/components/pill.tsx +67 -0
- package/src/components/quick-segment.tsx +68 -0
- package/src/index.ts +5 -0
- 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 = [
|
|
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(
|
|
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(
|
|
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
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
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
|
-
"
|
|
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":[]}
|