diff --git a/frontend/src/components/git/ForgejoHeatmap.tsx b/frontend/src/components/git/ForgejoHeatmap.tsx index 910500e..bb2da9c 100644 --- a/frontend/src/components/git/ForgejoHeatmap.tsx +++ b/frontend/src/components/git/ForgejoHeatmap.tsx @@ -97,10 +97,11 @@ function toLevel(count: number): number { } // ── Line chart sub-component ─────────────────────────────────────────────── -function LineChart({ days, range, onRangeChange }: { +function LineChart({ days, range, onRangeChange, lastPush }: { days: ForgejoHeatmapDay[]; range: RangeKey; onRangeChange: (r: RangeKey) => void; + lastPush: ForgejoLastPush | null; }) { const { points, xLabels, yLabels, linePath, areaPath } = useMemo(() => { const lookup = new Map(days.map(d => [d.date, d.count])); @@ -218,13 +219,37 @@ function LineChart({ days, range, onRangeChange }: { ))} + + {/* Last push — centered at bottom of card */} + {lastPush && ( +
+ + {lastPush.sha} + +
+

+ {lastPush.message} +

+

+ {lastPush.author} pushed to{" "} + {lastPush.branch} + {" · "}{lastPush.repo}{" · "}{fmtRelative(lastPush.pushed_at)} +

+
+
+ )} ); } // ── Heatmap grid sub-component ───────────────────────────────────────────── -function HeatmapGrid({ days }: { days: ForgejoHeatmapDay[] }) { - const { weeks, monthLabels } = useMemo(() => { +function HeatmapGrid({ days, range, onRangeChange }: { + days: ForgejoHeatmapDay[]; + range: RangeKey; + onRangeChange: (r: RangeKey) => void; +}) { + const { weeks, monthLabels, totalEvents } = useMemo(() => { const lookup = new Map(days.map(d => [d.date, d.count])); const today = new Date(); today.setHours(0,0,0,0); const start = new Date(today); @@ -251,51 +276,72 @@ function HeatmapGrid({ days }: { days: ForgejoHeatmapDay[] }) { } builtWeeks.push(week); } - return { weeks: builtWeeks, monthLabels: monthLabelList }; - }, [days]); + + // contributions count for selected range + const numDays = RANGE_DAYS[range]; + const cutoff = new Date(today); cutoff.setDate(cutoff.getDate() - numDays); + const cutoffStr = isoDate(cutoff); + const totalEvents = days.filter(d => d.date >= cutoffStr).reduce((s,d) => s+d.count, 0); + + return { weeks: builtWeeks, monthLabels: monthLabelList, totalEvents }; + }, [days, range]); return ( -
+
{/* Header */} -
+
Activity Heatmap
- - Last 12 months - +
+ {RANGE_LABELS.map(r => ( + + ))} +
{/* SVG */} - {/* Month labels */} {monthLabels.map(({weekIdx, label}) => ( {label} ))} - {/* Day labels */} {(["Mon","Wed","Fri"] as const).map((label, i) => ( {label} ))} - {/* Cells */} {weeks.map((week, wi) => week.map((cell, di) => cell.future ? null : ( - {cell.date}{cell.count>0?`: ${cell.count} event${cell.count!==1?"s":""}`+"" : ": no activity"} + {cell.date}{cell.count>0?`: ${cell.count} event${cell.count!==1?"s":""}` : ": no activity"} )) )} - {/* Legend */} Less {HMAP_FILL.map((fill, i) => ( ))} More + + {/* Contributions summary */} +

+ + {totalEvents.toLocaleString()} + + + contributions across all tracked repositories in the last {range} + +

+
); } @@ -309,15 +355,8 @@ export function ForgejoHeatmap({ lastPush = null, isLoading = false, }: ForgejoHeatmapProps) { - const [lineRange, setLineRange] = useState("30d"); - - const totalEvents = useMemo(() => { - const numDays = RANGE_DAYS[lineRange]; - const today = new Date(); today.setHours(0,0,0,0); - const cutoff = new Date(today); cutoff.setDate(cutoff.getDate() - numDays); - const cutoffStr = isoDate(cutoff); - return days.filter(d => d.date >= cutoffStr).reduce((s,d) => s+d.count, 0); - }, [days, lineRange]); + const [lineRange, setLineRange] = useState("7d"); + const [heatRange, setHeatRange] = useState("7d"); if (isLoading) { return ( @@ -341,43 +380,13 @@ export function ForgejoHeatmap({ {/* ── Two cards ───────────────────────────────────────────── */}
- +
- +
- {/* ── Contributions summary ────────────────────────────────── */} -

- - {totalEvents.toLocaleString()} - - - contributions across all tracked repositories in the last {lineRange} - -

- - {/* ── Last push — no box, plain inline ────────────────────── */} - {lastPush && ( -
- - {lastPush.sha} - -
-

- {lastPush.message} -

-

- {lastPush.author} pushed to{" "} - {lastPush.branch} - {" · "}{lastPush.repo}{" · "}{fmtRelative(lastPush.pushed_at)} -

-
-
- )} - {/* ── Line stats — boxed ──────────────────────────────────── */} {hasLineStats ? (