feat: p2 batch 28-31 reminders lists comments bang stats
Calendar events support multiple reminder offsets with presets and per-user defaults. Lists index adds inline task entry and list property editing. Generic entity comments on list detail. New bangs.stats dashboard widget. Closes Gitea #28, #29, #30, #31. Migrations 0022 and 0023.
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
import type { BangAggregatesDto } from "../server/queries";
|
||||
|
||||
type Props = {
|
||||
aggregates: BangAggregatesDto;
|
||||
};
|
||||
|
||||
export function BangStatsWidget({ aggregates }: Props) {
|
||||
const recentMonths = aggregates.monthlyCounts.slice(-6);
|
||||
|
||||
return (
|
||||
<div className="flex flex-col gap-4">
|
||||
<div className="grid grid-cols-3 gap-3">
|
||||
<StatBlock label="This month" value={String(aggregates.thisMonth)} />
|
||||
<StatBlock label="This year" value={String(aggregates.thisYear)} />
|
||||
<StatBlock
|
||||
label="Avg gap"
|
||||
value={aggregates.averageDaysBetween == null ? "—" : `${aggregates.averageDaysBetween}d`}
|
||||
/>
|
||||
</div>
|
||||
|
||||
{recentMonths.length > 0 ? (
|
||||
<div className="space-y-2">
|
||||
<p className="text-xs font-medium text-[var(--ink-mute)]">Recent months</p>
|
||||
<div className="space-y-1.5">
|
||||
{recentMonths.map((row) => (
|
||||
<div key={row.month} className="flex items-center justify-between text-sm">
|
||||
<span>{formatMonth(row.month)}</span>
|
||||
<span className="tabular-nums font-medium">{row.count}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<p className="text-sm text-[var(--ink-mute)]">No bangs recorded yet.</p>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function StatBlock({ label, value }: { label: string; value: string }) {
|
||||
return (
|
||||
<div className="rounded-lg border border-[var(--hair)] px-3 py-2">
|
||||
<div className="text-[11px] uppercase tracking-wide text-[var(--ink-mute)]">{label}</div>
|
||||
<div className="text-2xl font-semibold tabular-nums">{value}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function formatMonth(monthKey: string): string {
|
||||
const [year, month] = monthKey.split("-").map(Number);
|
||||
return new Date(year!, month! - 1, 1).toLocaleDateString(undefined, {
|
||||
month: "short",
|
||||
year: "numeric",
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user