added basic filtering

This commit is contained in:
2026-05-10 19:46:32 +05:30
parent 77b60ba073
commit 3ef71275a8
3 changed files with 111 additions and 9 deletions

View File

@@ -3,7 +3,11 @@ import {
Box, Box,
Container, Container,
CircularProgress, CircularProgress,
Alert Alert,
TextField,
Paper,
Autocomplete,
Button
} from "@mui/material"; } from "@mui/material";
import ConfigurableDashboard from "./components/Dashboard"; import ConfigurableDashboard from "./components/Dashboard";
@@ -14,18 +18,67 @@ import {
} from "./features/report"; } from "./features/report";
export default function Dashboard() { export default function Dashboard() {
const [appliedPayees, setAppliedPayees] = React.useState<string[]>([]);
const [appliedTags, setAppliedTags] = React.useState<string[]>([]);
const [payeeInput, setPayeeInput] = React.useState<string[]>([]);
const [tagsInput, setTagsInput] = React.useState<string[]>([]);
const [loadedPayees, setLoadedPayees] = React.useState<string[]>([]);
const [loadedTags, setLoadedTags] = React.useState<string[]>([]);
const report = useReport({ const report = useReport({
periods: ["weekly", "monthly", "full"], periods: ["weekly", "monthly", "full"],
rolling: true, rolling: true,
include_transactions: true, include_transactions: true,
group_by: ["tags"], group_by: ["tags"],
}) payee: appliedPayees.length > 0 ? appliedPayees : undefined,
tags: appliedTags.length > 0 ? appliedTags : undefined,
});
React.useEffect(() => {
if (report.data?.data) {
setLoadedPayees(prev => {
const pSet = new Set<string>(prev);
report.data.data.buckets.forEach((b: any) => {
Object.values(b.periods).forEach((periodArray: any) => {
periodArray?.forEach((p: any) => {
p.expenses?.transactions?.forEach((t: any) => {
if (t.payee?.name) pSet.add(t.payee.name);
});
p.incomes?.transactions?.forEach((t: any) => {
if (t.payee?.name) pSet.add(t.payee.name);
});
});
});
});
return Array.from(pSet).sort();
});
setLoadedTags(prev => {
const tSet = new Set<string>(prev);
report.data.data.buckets.forEach((b: any) => {
Object.values(b.periods).forEach((periodArray: any) => {
periodArray?.forEach((p: any) => {
p.expenses?.transactions?.forEach((t: any) => {
t.tags?.forEach((tag: any) => tSet.add(tag.name || tag));
});
p.incomes?.transactions?.forEach((t: any) => {
t.tags?.forEach((tag: any) => tSet.add(tag.name || tag));
});
});
});
});
return Array.from(tSet).sort();
});
}
}, [report.data?.data]);
const isLoading = report.isLoading; const isLoading = report.isLoading;
const error = report.error; const error = report.error;
if (isLoading) { if (isLoading && !report.data) {
return ( return (
<Box sx={{ display: "flex", justifyContent: "center", alignItems: "center", height: "60vh" }}> <Box sx={{ display: "flex", justifyContent: "center", alignItems: "center", height: "60vh" }}>
<CircularProgress /> <CircularProgress />
@@ -41,15 +94,47 @@ export default function Dashboard() {
); );
} }
if (!report) { if (!report.data) {
return null; return null;
} }
const data = prepareReport(report.data?.data); const data = prepareReport(report.data.data);
return ( return (
<ConfigurableDashboard <Box>
config={configuration} <Paper sx={{ m: 2, p: 2, display: "flex", gap: 2, alignItems: "center" }} elevation={0} variant="outlined">
data={data} <Autocomplete
/> multiple
freeSolo
options={loadedPayees}
value={payeeInput}
onChange={(_, val) => setPayeeInput(val as string[])}
renderInput={(params) => <TextField {...params} label="Filter by Payee" size="small" />}
sx={{ minWidth: 250, flex: 1 }}
/>
<Autocomplete
multiple
freeSolo
options={loadedTags}
value={tagsInput}
onChange={(_, val) => setTagsInput(val as string[])}
renderInput={(params) => <TextField {...params} label="Filter by Tags" size="small" />}
sx={{ minWidth: 250, flex: 1 }}
/>
<Button
variant="contained"
onClick={() => {
setAppliedPayees(payeeInput);
setAppliedTags(tagsInput);
}}
disabled={isLoading}
>
Apply
</Button>
</Paper>
<ConfigurableDashboard
config={configuration}
data={data}
/>
</Box>
); );
} }

View File

@@ -86,5 +86,14 @@ export interface ReportData {
ignore_self: boolean; ignore_self: boolean;
include_transactions: boolean; include_transactions: boolean;
start_date?: string | null;
end_date?: string | null;
flow?: "expense" | "income" | null;
payee?: string[] | null;
account?: string[] | null;
tags?: string[] | null;
min_amount?: number | null;
max_amount?: number | null;
buckets: ReportBucket[]; buckets: ReportBucket[];
} }

View File

@@ -7,6 +7,14 @@ export interface ReportParams {
group_by?: ("payee" | "tags")[]; group_by?: ("payee" | "tags")[];
ignore_self?: boolean; ignore_self?: boolean;
include_transactions?: boolean; include_transactions?: boolean;
start_date?: string;
end_date?: string;
flow?: "expense" | "income";
payee?: string[];
account?: string[];
tags?: string[];
min_amount?: number;
max_amount?: number;
} }
export function useReport(params: ReportParams) { export function useReport(params: ReportParams) {