feat: add period quick-select filter to transactions page
Add period buttons (This month, 3 months, 6 months, This year, All) above the transaction filters. "This year" is selected by default so the page no longer shows all transactions since the beginning of time. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
13989dc0b8
commit
f9c6fabc13
4 changed files with 67 additions and 2 deletions
|
|
@ -1,9 +1,44 @@
|
|||
import { useMemo } from "react";
|
||||
import { useMemo, useCallback } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Search } from "lucide-react";
|
||||
import type { TransactionFilters, Category, ImportSource } from "../../shared/types";
|
||||
import CategoryCombobox from "../shared/CategoryCombobox";
|
||||
|
||||
type QuickPeriod = "month" | "3months" | "6months" | "year" | "all";
|
||||
const PERIODS: QuickPeriod[] = ["month", "3months", "6months", "year", "all"];
|
||||
|
||||
function computePeriodDates(period: QuickPeriod): { dateFrom: string | null; dateTo: string | null } {
|
||||
if (period === "all") return { dateFrom: null, dateTo: null };
|
||||
const now = new Date();
|
||||
const year = now.getFullYear();
|
||||
const month = now.getMonth();
|
||||
if (period === "year") return { dateFrom: `${year}-01-01`, dateTo: null };
|
||||
let from: Date;
|
||||
switch (period) {
|
||||
case "month":
|
||||
from = new Date(year, month, 1);
|
||||
break;
|
||||
case "3months":
|
||||
from = new Date(year, month - 2, 1);
|
||||
break;
|
||||
case "6months":
|
||||
from = new Date(year, month - 5, 1);
|
||||
break;
|
||||
}
|
||||
const dateFrom = `${from.getFullYear()}-${String(from.getMonth() + 1).padStart(2, "0")}-${String(from.getDate()).padStart(2, "0")}`;
|
||||
return { dateFrom, dateTo: null };
|
||||
}
|
||||
|
||||
function detectActivePeriod(filters: TransactionFilters): QuickPeriod | null {
|
||||
if (filters.dateTo) return null;
|
||||
if (!filters.dateFrom) return "all";
|
||||
for (const p of PERIODS) {
|
||||
const { dateFrom } = computePeriodDates(p);
|
||||
if (dateFrom === filters.dateFrom) return p;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
interface TransactionFilterBarProps {
|
||||
filters: TransactionFilters;
|
||||
categories: Category[];
|
||||
|
|
@ -19,6 +54,17 @@ export default function TransactionFilterBar({
|
|||
}: TransactionFilterBarProps) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const activePeriod = detectActivePeriod(filters);
|
||||
|
||||
const handlePeriodChange = useCallback(
|
||||
(period: QuickPeriod) => {
|
||||
const { dateFrom, dateTo } = computePeriodDates(period);
|
||||
onFilterChange("dateFrom", dateFrom);
|
||||
onFilterChange("dateTo", dateTo);
|
||||
},
|
||||
[onFilterChange]
|
||||
);
|
||||
|
||||
const categoryExtras = useMemo(
|
||||
() => [
|
||||
{ value: "", label: t("transactions.filters.allCategories") },
|
||||
|
|
@ -37,6 +83,23 @@ export default function TransactionFilterBar({
|
|||
|
||||
return (
|
||||
<div className="bg-[var(--card)] rounded-xl p-4 border border-[var(--border)] mb-4">
|
||||
{/* Period quick-select */}
|
||||
<div className="flex flex-wrap gap-2 mb-3">
|
||||
{PERIODS.map((p) => (
|
||||
<button
|
||||
key={p}
|
||||
onClick={() => handlePeriodChange(p)}
|
||||
className={`px-3 py-1.5 rounded-lg text-sm font-medium transition-colors ${
|
||||
p === activePeriod
|
||||
? "bg-[var(--primary)] text-white"
|
||||
: "bg-[var(--background)] border border-[var(--border)] text-[var(--foreground)] hover:bg-[var(--muted)]"
|
||||
}`}
|
||||
>
|
||||
{t(`dashboard.period.${p}`)}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
|
||||
<div className="flex flex-wrap items-center gap-3">
|
||||
{/* Search */}
|
||||
<div className="relative flex-1 min-w-[200px]">
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ const initialFilters: TransactionFilters = {
|
|||
search: "",
|
||||
categoryId: null,
|
||||
sourceId: null,
|
||||
dateFrom: null,
|
||||
dateFrom: `${new Date().getFullYear()}-01-01`,
|
||||
dateTo: null,
|
||||
uncategorizedOnly: false,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
"3months": "3 months",
|
||||
"6months": "6 months",
|
||||
"12months": "12 months",
|
||||
"year": "This year",
|
||||
"all": "All"
|
||||
},
|
||||
"help": {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@
|
|||
"3months": "3 mois",
|
||||
"6months": "6 mois",
|
||||
"12months": "12 mois",
|
||||
"year": "Cette année",
|
||||
"all": "Tout"
|
||||
},
|
||||
"help": {
|
||||
|
|
|
|||
Loading…
Reference in a new issue