fix: aggregate anomaly data per day instead of taking latest — fixes missing returned bars when multiple syncs per day
This commit is contained in:
@@ -226,34 +226,44 @@ export default function IvantiCountsChart() {
|
||||
);
|
||||
|
||||
// Build archive activity data aligned to the same date axis as the main chart.
|
||||
// Aggregate anomaly rows by date (take the last sync per day, matching the
|
||||
// counts history pattern), then merge onto the chartData date set.
|
||||
// Aggregate anomaly rows by date — sum archived/returned counts and merge
|
||||
// classifications across all syncs that day, then align to the chartData dates.
|
||||
const archiveData = useMemo(() => {
|
||||
if (!anomalies.length || !chartData.length) return [];
|
||||
|
||||
// Group anomalies by date, keep the latest per day
|
||||
// Aggregate all anomaly rows per date (sum counts, merge classifications)
|
||||
const byDate = {};
|
||||
for (const a of anomalies) {
|
||||
const rawDate = extractDate(a.sync_timestamp);
|
||||
const dateKey = fmtDate(rawDate);
|
||||
// anomaly/history returns newest first, so first seen per date is the latest
|
||||
if (!byDate[dateKey]) {
|
||||
byDate[dateKey] = a;
|
||||
byDate[dateKey] = {
|
||||
archived: 0,
|
||||
returned: 0,
|
||||
classification: {},
|
||||
return_classification: {},
|
||||
is_significant: false,
|
||||
};
|
||||
}
|
||||
const entry = byDate[dateKey];
|
||||
entry.archived += (a.newly_archived_count || 0);
|
||||
entry.returned += (a.returned_count || 0);
|
||||
if (a.is_significant) entry.is_significant = true;
|
||||
|
||||
// Merge classification counts
|
||||
for (const [key, val] of Object.entries(a.classification || {})) {
|
||||
entry.classification[key] = (entry.classification[key] || 0) + (val || 0);
|
||||
}
|
||||
for (const [key, val] of Object.entries(a.return_classification || {})) {
|
||||
entry.return_classification[key] = (entry.return_classification[key] || 0) + (val || 0);
|
||||
}
|
||||
}
|
||||
|
||||
// Map onto the chart date axis so both charts share the same X positions
|
||||
return chartData.map(point => {
|
||||
const anomaly = byDate[point.date];
|
||||
if (anomaly) {
|
||||
return {
|
||||
date: point.date,
|
||||
archived: anomaly.newly_archived_count || 0,
|
||||
returned: anomaly.returned_count || 0,
|
||||
classification: anomaly.classification || {},
|
||||
return_classification: anomaly.return_classification || {},
|
||||
is_significant: anomaly.is_significant,
|
||||
};
|
||||
const agg = byDate[point.date];
|
||||
if (agg) {
|
||||
return { date: point.date, ...agg };
|
||||
}
|
||||
return { date: point.date, archived: 0, returned: 0, classification: {}, return_classification: {}, is_significant: false };
|
||||
});
|
||||
@@ -377,13 +387,13 @@ export default function IvantiCountsChart() {
|
||||
}}>
|
||||
Archive Activity
|
||||
</div>
|
||||
<ResponsiveContainer width="100%" height={64}>
|
||||
<ResponsiveContainer width="100%" height={80}>
|
||||
<BarChart data={archiveData} margin={{ top: 2, right: 12, bottom: 0, left: -12 }}>
|
||||
<CartesianGrid {...GRID_STYLE} />
|
||||
<XAxis dataKey="date" tick={false} axisLine={false} />
|
||||
<YAxis tick={AXIS_STYLE} allowDecimals={false} width={30} />
|
||||
<Tooltip content={<ArchiveTooltip />} />
|
||||
<Bar dataKey="archived" name="Archived" stackId="a" maxBarSize={12}>
|
||||
<Bar dataKey="archived" name="Archived" stackId="a" maxBarSize={14}>
|
||||
{archiveData.map((entry, idx) => (
|
||||
<Cell
|
||||
key={`arch-${idx}`}
|
||||
@@ -391,7 +401,14 @@ export default function IvantiCountsChart() {
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
<Bar dataKey="returned" name="Returned" stackId="a" fill={TEAL} maxBarSize={12} />
|
||||
<Bar dataKey="returned" name="Returned" stackId="a" maxBarSize={14}>
|
||||
{archiveData.map((entry, idx) => (
|
||||
<Cell
|
||||
key={`ret-${idx}`}
|
||||
fill={TEAL}
|
||||
/>
|
||||
))}
|
||||
</Bar>
|
||||
</BarChart>
|
||||
</ResponsiveContainer>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user