filter-by-payee-and-tags #3
@@ -3,7 +3,11 @@ import {
|
||||
Box,
|
||||
Container,
|
||||
CircularProgress,
|
||||
Alert
|
||||
Alert,
|
||||
TextField,
|
||||
Paper,
|
||||
Autocomplete,
|
||||
Button
|
||||
} from "@mui/material";
|
||||
|
||||
import ConfigurableDashboard from "./components/Dashboard";
|
||||
@@ -14,18 +18,67 @@ import {
|
||||
} from "./features/report";
|
||||
|
||||
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({
|
||||
periods: ["weekly", "monthly", "full"],
|
||||
rolling: true,
|
||||
include_transactions: true,
|
||||
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 error = report.error;
|
||||
|
||||
|
||||
if (isLoading) {
|
||||
if (isLoading && !report.data) {
|
||||
return (
|
||||
<Box sx={{ display: "flex", justifyContent: "center", alignItems: "center", height: "60vh" }}>
|
||||
<CircularProgress />
|
||||
@@ -41,15 +94,47 @@ export default function Dashboard() {
|
||||
);
|
||||
}
|
||||
|
||||
if (!report) {
|
||||
if (!report.data) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const data = prepareReport(report.data?.data);
|
||||
const data = prepareReport(report.data.data);
|
||||
return (
|
||||
<ConfigurableDashboard
|
||||
config={configuration}
|
||||
data={data}
|
||||
/>
|
||||
<Box>
|
||||
<Paper sx={{ m: 2, p: 2, display: "flex", gap: 2, alignItems: "center" }} elevation={0} variant="outlined">
|
||||
<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>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -86,5 +86,14 @@ export interface ReportData {
|
||||
ignore_self: 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[];
|
||||
}
|
||||
|
||||
@@ -7,6 +7,14 @@ export interface ReportParams {
|
||||
group_by?: ("payee" | "tags")[];
|
||||
ignore_self?: 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) {
|
||||
|
||||
Reference in New Issue
Block a user