Compare commits
7 Commits
0.0.1
...
39f3d87d21
| Author | SHA1 | Date | |
|---|---|---|---|
| 39f3d87d21 | |||
| 17b5a107fe | |||
| f5322b8467 | |||
| 1423f889ba | |||
| 4c8552051c | |||
| f025a7d9bf | |||
| 052c5a3026 |
@@ -3,11 +3,7 @@ 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";
|
||||||
@@ -18,67 +14,18 @@ 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 && !report.data) {
|
if (isLoading) {
|
||||||
return (
|
return (
|
||||||
<Box sx={{ display: "flex", justifyContent: "center", alignItems: "center", height: "60vh" }}>
|
<Box sx={{ display: "flex", justifyContent: "center", alignItems: "center", height: "60vh" }}>
|
||||||
<CircularProgress />
|
<CircularProgress />
|
||||||
@@ -94,74 +41,15 @@ export default function Dashboard() {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!report.data) {
|
if (!report) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const data = prepareReport(report.data.data);
|
const data = prepareReport(report.data?.data);
|
||||||
return (
|
return (
|
||||||
<Box>
|
<ConfigurableDashboard
|
||||||
<Container>
|
config={configuration}
|
||||||
<Paper
|
data={data}
|
||||||
sx={{
|
/>
|
||||||
mt: 4,
|
|
||||||
p: 2,
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: { xs: "column", sm: "row" },
|
|
||||||
gap: 2,
|
|
||||||
alignItems: { xs: "stretch", sm: "flex-end" },
|
|
||||||
borderRadius: 4,
|
|
||||||
mb: -2 // pull up to be closer to the dashboard container below
|
|
||||||
}}
|
|
||||||
elevation={0}
|
|
||||||
variant="outlined"
|
|
||||||
>
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', flex: 1, minWidth: { sm: 250 } }}>
|
|
||||||
<Box sx={{ typography: 'caption', mb: 1, color: 'text.secondary' }}>
|
|
||||||
Filter by Payee
|
|
||||||
</Box>
|
|
||||||
<Autocomplete
|
|
||||||
multiple
|
|
||||||
freeSolo
|
|
||||||
options={loadedPayees}
|
|
||||||
value={payeeInput}
|
|
||||||
onChange={(_, val) => setPayeeInput(val as string[])}
|
|
||||||
renderInput={(params) => <TextField {...params} placeholder="Add payees..." />}
|
|
||||||
sx={{ '& .MuiOutlinedInput-root': { height: 'auto', minHeight: '2.5rem', py: 0.5 } }}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', flex: 1, minWidth: { sm: 250 } }}>
|
|
||||||
<Box sx={{ typography: 'caption', mb: 1, color: 'text.secondary' }}>
|
|
||||||
Filter by Tags
|
|
||||||
</Box>
|
|
||||||
<Autocomplete
|
|
||||||
multiple
|
|
||||||
freeSolo
|
|
||||||
options={loadedTags}
|
|
||||||
value={tagsInput}
|
|
||||||
onChange={(_, val) => setTagsInput(val as string[])}
|
|
||||||
renderInput={(params) => <TextField {...params} placeholder="Add tags..." />}
|
|
||||||
sx={{ '& .MuiOutlinedInput-root': { height: 'auto', minHeight: '2.5rem', py: 0.5 } }}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
<Button
|
|
||||||
variant="contained"
|
|
||||||
size="large"
|
|
||||||
onClick={() => {
|
|
||||||
setAppliedPayees(payeeInput);
|
|
||||||
setAppliedTags(tagsInput);
|
|
||||||
}}
|
|
||||||
disabled={isLoading}
|
|
||||||
sx={{ height: 40, borderRadius: 2 }} // Changed from 56 to 40 to match minHeight of inputs
|
|
||||||
>
|
|
||||||
Apply
|
|
||||||
</Button>
|
|
||||||
</Paper>
|
|
||||||
</Container>
|
|
||||||
<ConfigurableDashboard
|
|
||||||
config={configuration}
|
|
||||||
data={data}
|
|
||||||
/>
|
|
||||||
</Box>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,14 +86,5 @@ 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[];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,14 +7,6 @@ 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) {
|
||||||
|
|||||||
Reference in New Issue
Block a user