fix: address reviewer feedback (#29)
- Replace semi-transparent backgrounds on sticky columns with opaque color-mix equivalents so scrolled content is fully hidden - Add opaque background to section header sticky td - Extract IIFE month options in ReportsPage into a useMemo
This commit is contained in:
parent
16c6d02e39
commit
2b2536bc80
2 changed files with 27 additions and 25 deletions
|
|
@ -206,8 +206,8 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps)
|
|||
const sectionYtdPct = sectionTotals.ytdBudget !== 0 ? sectionTotals.ytdVariation / Math.abs(sectionTotals.ytdBudget) : null;
|
||||
return (
|
||||
<Fragment key={section.type}>
|
||||
<tr className="bg-[var(--muted)]/50">
|
||||
<td colSpan={9} className="px-3 py-1.5 font-semibold text-[var(--muted-foreground)] uppercase text-xs tracking-wider sticky left-0">
|
||||
<tr className="bg-[var(--muted)]">
|
||||
<td colSpan={9} className="px-3 py-1.5 font-semibold text-[var(--muted-foreground)] uppercase text-xs tracking-wider sticky left-0 bg-[var(--muted)]">
|
||||
{section.label}
|
||||
</td>
|
||||
</tr>
|
||||
|
|
@ -220,11 +220,11 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps)
|
|||
<tr
|
||||
key={`${row.category_id}-${row.is_parent}-${depth}`}
|
||||
className={`border-b border-[var(--border)]/50 ${
|
||||
isParent && !isIntermediateParent ? "bg-[var(--muted)]/30 font-semibold" :
|
||||
isIntermediateParent ? "bg-[var(--muted)]/15 font-medium" : ""
|
||||
isParent && !isIntermediateParent ? "bg-[color-mix(in_srgb,var(--muted)_30%,var(--card))] font-semibold" :
|
||||
isIntermediateParent ? "bg-[color-mix(in_srgb,var(--muted)_15%,var(--card))] font-medium" : ""
|
||||
}`}
|
||||
>
|
||||
<td className={`py-1.5 sticky left-0 z-10 ${isParent && !isIntermediateParent ? "px-3 bg-[var(--muted)]/30" : isIntermediateParent ? "bg-[var(--muted)]/15" : "bg-[var(--card)]"} ${isParent && !isIntermediateParent ? "px-3" : paddingClass}`}>
|
||||
<td className={`py-1.5 sticky left-0 z-10 ${isParent && !isIntermediateParent ? "px-3 bg-[color-mix(in_srgb,var(--muted)_30%,var(--card))]" : isIntermediateParent ? "bg-[color-mix(in_srgb,var(--muted)_15%,var(--card))]" : "bg-[var(--card)]"} ${isParent && !isIntermediateParent ? "px-3" : paddingClass}`}>
|
||||
<span className="flex items-center gap-2">
|
||||
<span
|
||||
className="w-2.5 h-2.5 rounded-full shrink-0"
|
||||
|
|
@ -256,8 +256,8 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps)
|
|||
</tr>
|
||||
);
|
||||
})}
|
||||
<tr className="border-b border-[var(--border)] bg-[var(--muted)]/40 font-semibold text-sm">
|
||||
<td className="px-3 py-2.5 sticky left-0 bg-[var(--muted)]/40 z-10">{t(typeTotalKeys[section.type])}</td>
|
||||
<tr className="border-b border-[var(--border)] bg-[color-mix(in_srgb,var(--muted)_40%,var(--card))] font-semibold text-sm">
|
||||
<td className="px-3 py-2.5 sticky left-0 bg-[color-mix(in_srgb,var(--muted)_40%,var(--card))] z-10">{t(typeTotalKeys[section.type])}</td>
|
||||
<td className="text-right px-3 py-2.5 border-l border-[var(--border)]/50">
|
||||
{cadFormatter(sectionTotals.monthActual)}
|
||||
</td>
|
||||
|
|
@ -283,8 +283,8 @@ export default function BudgetVsActualTable({ data }: BudgetVsActualTableProps)
|
|||
);
|
||||
})}
|
||||
{/* Grand totals */}
|
||||
<tr className="border-t-2 border-[var(--border)] font-bold text-sm bg-[var(--muted)]/20">
|
||||
<td className="px-3 py-3 sticky left-0 bg-[var(--muted)]/20 z-10">{t("common.total")}</td>
|
||||
<tr className="border-t-2 border-[var(--border)] font-bold text-sm bg-[color-mix(in_srgb,var(--muted)_20%,var(--card))]">
|
||||
<td className="px-3 py-3 sticky left-0 bg-[color-mix(in_srgb,var(--muted)_20%,var(--card))] z-10">{t("common.total")}</td>
|
||||
<td className="text-right px-3 py-3 border-l border-[var(--border)]/50">
|
||||
{cadFormatter(totals.monthActual)}
|
||||
</td>
|
||||
|
|
|
|||
|
|
@ -92,6 +92,19 @@ export default function ReportsPage() {
|
|||
return [];
|
||||
}, [state.tab, state.categorySpending, state.categoryOverTime]);
|
||||
|
||||
const monthOptions = useMemo(() => {
|
||||
const now = new Date();
|
||||
const currentMonth = now.getMonth();
|
||||
const currentYear = now.getFullYear();
|
||||
return Array.from({ length: 24 }, (_, i) => {
|
||||
const d = new Date(currentYear, currentMonth - i, 1);
|
||||
const y = d.getFullYear();
|
||||
const m = d.getMonth() + 1;
|
||||
const label = new Intl.DateTimeFormat(i18n.language, { month: "long", year: "numeric" }).format(d);
|
||||
return { key: `${y}-${m}`, value: `${y}-${m}`, label: label.charAt(0).toUpperCase() + label.slice(1) };
|
||||
});
|
||||
}, [i18n.language]);
|
||||
|
||||
const hasCategories = ["byCategory", "overTime"].includes(state.tab) && filterCategories.length > 0;
|
||||
const showFilterPanel = hasCategories || (state.tab === "trends" && sources.length > 1);
|
||||
|
||||
|
|
@ -110,22 +123,11 @@ export default function ReportsPage() {
|
|||
}}
|
||||
className="text-2xl font-bold bg-[var(--card)] border border-[var(--border)] rounded-lg px-3 py-1 cursor-pointer hover:bg-[var(--muted)] transition-colors"
|
||||
>
|
||||
{(() => {
|
||||
const now = new Date();
|
||||
const currentMonth = now.getMonth(); // 0-based
|
||||
const currentYear = now.getFullYear();
|
||||
return Array.from({ length: 24 }, (_, i) => {
|
||||
const d = new Date(currentYear, currentMonth - i, 1);
|
||||
const y = d.getFullYear();
|
||||
const m = d.getMonth() + 1;
|
||||
const label = new Intl.DateTimeFormat(i18n.language, { month: "long", year: "numeric" }).format(d);
|
||||
return (
|
||||
<option key={`${y}-${m}`} value={`${y}-${m}`}>
|
||||
{label.charAt(0).toUpperCase() + label.slice(1)}
|
||||
{monthOptions.map((opt) => (
|
||||
<option key={opt.key} value={opt.value}>
|
||||
{opt.label}
|
||||
</option>
|
||||
);
|
||||
});
|
||||
})()}
|
||||
))}
|
||||
</select>
|
||||
</h1>
|
||||
) : (
|
||||
|
|
|
|||
Loading…
Reference in a new issue