Skip to content
Open
5,096 changes: 3,626 additions & 1,470 deletions dashboard/package-lock.json

Large diffs are not rendered by default.

12 changes: 3 additions & 9 deletions dashboard/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,11 @@
"react-hook-form": "7.53.0",
"react-idle-timer": "5.7.2",
"react-querybuilder": "8.0.0",
"react-quill-new": "3.4.1",
"react-quill-new": "3.8.3",
"react-redux": "9.1.0",
"react-router-dom": "6.30.4",
"react-toastify": "10.0.5",
"recharts": "2.15.1",
"recharts": "3.10.1",
Comment thread
Brijesh619 marked this conversation as resolved.
"sanitize-html": "2.17.4"
},
"devDependencies": {
Expand Down Expand Up @@ -112,12 +112,6 @@
"@swc/core-win32-x64-msvc": "1.15.43"
Comment thread
Brijesh619 marked this conversation as resolved.
},
"overrides": {
"semver": "7.5.4",
"minimatch": "9.0.7",
"brace-expansion": "5.0.6",
"d3-color": "3.1.0",
"lodash": "4.18.1",
"lodash-es": "4.18.1",
"js-yaml": "4.3.0"
"d3-color": "3.1.0"
}
}
188 changes: 188 additions & 0 deletions dashboard/src/styles/stats.scss
Original file line number Diff line number Diff line change
Expand Up @@ -60,3 +60,191 @@
.classification-name-cell {
max-width: 400px;
}

.chart-cursor-pointer {
cursor: pointer;
}

.chart-cursor-default {
cursor: default;
}

.chart-label-list {
font-size: 12px;
font-weight: 500;
fill: #1976d2;
}

.legend-button {
display: flex;
flex-direction: row;
align-items: center;
gap: 4px;
background: none;
padding: 0;
margin: 0;
border-radius: 4px;
}

.legend-color-box {
width: 12px;
height: 12px;
border-radius: 2px;
}

.legend-typography {
font-size: 0.875rem;
}

.legend-inactive {
color: #d3d3d3;
}

.legend-active {
color: #333;
}

.chart-card {
Comment thread
Brijesh619 marked this conversation as resolved.
padding: 16px;
border-radius: 8px;
min-height: 200px;
transition: box-shadow 0.3s ease;
}

.chart-card:hover {
box-shadow: 0px 4px 6px -1px rgba(0,0,0,0.1), 0px 2px 4px -1px rgba(0,0,0,0.06);
}

.chart-card-min-340 {
Comment thread
Brijesh619 marked this conversation as resolved.
Outdated
min-height: 340px;
}

.chart-card-header {
padding-bottom: 16px;
border-bottom: 1px solid rgba(0, 0, 0, 0.12);
}

.chart-card-title {
font-size: 1rem;
font-weight: 600;
color: #1a1a1a;
}

.donut-status-button {
display: flex;
align-items: center;
gap: 12px;
cursor: pointer;
background: none;
border: none;
padding: 0;
margin: 0;
font: inherit;
text-align: left;
}

.donut-status-button:hover {
opacity: 0.85;
}

.donut-status-box {
width: 12px;
height: 12px;
border-radius: 50%;
flex-shrink: 0;
}

.donut-status-text {
font-size: 0.875rem;
color: #374151;
}

.donut-status-stack {
padding-top: 16px;
}

.classification-card-wrapper {
padding: 16px;
border-radius: 8px;
min-height: 340px;
min-width: 0;
width: 100%;
height: 100%;
box-sizing: border-box;
transition: box-shadow 0.3s ease;
}

.classification-card-wrapper:hover {
box-shadow: 0px 4px 6px -1px rgba(0,0,0,0.1), 0px 2px 4px -1px rgba(0,0,0,0.06);
}

.chart-view-all-link {
font-size: 0.875rem;
cursor: pointer;
text-decoration: none;
color: #1976d2;
}

.classification-assoc-total {
color: #6c757d;
margin-top: 16px;
line-height: 1.4;
}

.classification-caption {
color: #868e96;
display: block;
margin-top: 6px;
line-height: 1.4;
}

.classification-chart-container {
margin-top: 16px;
min-height: 260px;
height: 260px;
width: 100%;
min-width: 280px;
}

.chart-tooltip-box {
padding: 12px;
background-color: #fff;
border-radius: 4px;
box-shadow: 0px 2px 4px -1px rgba(0,0,0,0.1), 0px 4px 5px 0px rgba(0,0,0,0.07), 0px 1px 10px 0px rgba(0,0,0,0.06);
min-width: 140px;
}

.chart-tooltip-title {
margin-bottom: 4px;
font-weight: 600;
}

.entity-type-deleted-text {
color: #ef4444;
}

.chart-legend-container {
display: flex;
flex-wrap: wrap;
margin-bottom: 8px;
gap: 16px;
}

.chart-legend-item-text {
color: #6c757d;
font-size: 0.8125rem;
}

.chart-legend-dot-active {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #1976d2;
}

.chart-legend-dot-deleted {
width: 10px;
height: 10px;
border-radius: 50%;
background-color: #ef4444;
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,12 @@ interface ClassificationDistributionCardProps {
isLoading?: boolean;
}

interface RechartsEventPayload {
payload?: {
name: string;
};
}

const ClassificationDistributionCard = memo(({ tag, isLoading }: ClassificationDistributionCardProps) => {
const navigate = useNavigate();
const data = getClassificationDistribution(tag, 5);
Expand All @@ -60,8 +66,13 @@ const ClassificationDistributionCard = memo(({ tag, isLoading }: ClassificationD
);

const handleBarClick = useCallback(
(entry: { name: string }) => {
navigateToClassificationSearch(navigate, entry.name);
(barProps: unknown) => {
Comment thread
Brijesh619 marked this conversation as resolved.
if (!barProps || typeof barProps !== "object") return;
const rec = barProps as RechartsEventPayload;
const name = rec.payload?.name;
if (name) {
navigateToClassificationSearch(navigate, name);
}
},
[navigate]
);
Expand All @@ -83,8 +94,8 @@ const ClassificationDistributionCard = memo(({ tag, isLoading }: ClassificationD
const row = p.payload[0]?.payload;
if (!row) return null;
return (
<Box sx={{ p: 1.5, bgcolor: "background.paper", borderRadius: 1, boxShadow: 2, minWidth: 140 }}>
<Typography variant="body2" fontWeight={600} sx={{ mb: 0.5 }}>
<Box className="chart-tooltip-box">
<Typography variant="body2" className="chart-tooltip-title">
{row.name}
</Typography>
<Typography variant="caption" display="block">
Expand All @@ -97,40 +108,27 @@ const ClassificationDistributionCard = memo(({ tag, isLoading }: ClassificationD
if (isLoading) return null;

return (
<Paper
elevation={1}
sx={{
padding: 2,
borderRadius: 2,
minHeight: 340,
minWidth: 0,
width: "100%",
height: "100%",
boxSizing: "border-box",
transition: "box-shadow 0.3s ease",
"&:hover": { boxShadow: 4 }
}}
>
<Box sx={{ pb: 2, borderBottom: "1px solid", borderColor: "divider" }}>
<Paper elevation={1} className="classification-card-wrapper">
<Box className="chart-card-header">
<Stack direction="row" justifyContent="space-between" alignItems="center">
<Typography sx={{ fontSize: "1rem", fontWeight: 600, color: "#1a1a1a" }}>
<Typography className="chart-card-title">
Classification Distribution
</Typography>
<Link
component="button"
onClick={handleViewAll}
sx={{ fontSize: "0.875rem", cursor: "pointer", textDecoration: "none", color: "primary.main" }}
className="chart-view-all-link"
aria-label="View all classifications"
>
View All
</Link>
</Stack>
</Box>
<Typography variant="body2" sx={{ color: "#6c757d", mt: 2, lineHeight: 1.4 }}>
<Typography variant="body2" className="classification-assoc-total">
<strong>Tag–entity associations (total):</strong>{" "}
{numberFormatWithComma(associationTotal)}
</Typography>
<Typography variant="caption" sx={{ color: "#868e96", display: "block", mt: 0.75, lineHeight: 1.4 }}>
<Typography variant="caption" className="classification-caption">
The chart shows the top 5 classifications by number of entities in use.
</Typography>
{data.length === 0 ? (
Expand All @@ -140,8 +138,8 @@ const ClassificationDistributionCard = memo(({ tag, isLoading }: ClassificationD
</Typography>
</Stack>
) : (
<Box sx={{ mt: 2, minHeight: 260, height: 260, width: "100%", minWidth: 280 }}>
<ResponsiveContainer width="100%" height="100%" style={{ cursor: "pointer" }}>
<Box className="classification-chart-container">
<ResponsiveContainer width="100%" height="100%" className="chart-cursor-pointer">
<BarChart
data={data}
layout="vertical"
Expand Down Expand Up @@ -174,7 +172,7 @@ const ClassificationDistributionCard = memo(({ tag, isLoading }: ClassificationD
<g
transform={`translate(${x},${y})`}
onClick={() => (value ? handleLabelClick(value) : undefined)}
style={{ cursor: value ? "pointer" : "default" }}
className={value ? "chart-cursor-pointer" : "chart-cursor-default"}
role={value ? "button" : undefined}
tabIndex={value ? 0 : undefined}
aria-label={value || undefined}
Expand Down Expand Up @@ -203,19 +201,15 @@ const ClassificationDistributionCard = memo(({ tag, isLoading }: ClassificationD
name="Entities"
fill={BAR_COLOR}
radius={[0, 4, 4, 0]}
onClick={(entry) => handleBarClick(entry)}
onClick={handleBarClick}
cursor="pointer"
>
<LabelList
dataKey="count"
position="right"
offset={10}
formatter={(v: number) => numberFormatWithComma(v)}
style={{
fontSize: 12,
fontWeight: 500,
fill: BAR_COLOR,
}}
formatter={(v: unknown) => numberFormatWithComma(Number(v))}
className="chart-label-list"
/>
{data.map((_, index) => <Cell key={index} fill={BAR_COLOR} />)}
</Bar>
Expand Down
Loading
Loading