Dashboard.view.tsx to handle rendering and Dashboard.tsx to handle state

This commit is contained in:
2026-05-19 18:51:59 +05:30
parent 7dd685ae49
commit 2a12e33e22
4 changed files with 151 additions and 93 deletions

View File

@@ -10,8 +10,14 @@ import {
Button Button
} from "@mui/material"; } from "@mui/material";
import ConfigurableDashboard from "./components/Dashboard"; import DashboardView from "./components/Dashboard";
import { DashboardState } from "./components/Dashboard";
import {
DashboardState,
DashboardStateSetters,
DashboardFlow,
} from "./components/Dashboard";
import { configuration } from "./dashboard-config"; import { configuration } from "./dashboard-config";
import { import {
useReport, useReport,
@@ -19,7 +25,13 @@ import {
} from "./features/report"; } from "./features/report";
export default function Dashboard() { export default function Dashboard() {
const [flow, setFlow] = React.useState<"outflows" | "inflows">("outflows"); const [state, setState] = React.useState<DashboardState>({
flow: "outflows",
periodType: "rolling",
selectedPeriodId: null,
selectedGroupKey: null,
comparison: false,
});
const [appliedPayees, setAppliedPayees] = React.useState<string[]>([]); const [appliedPayees, setAppliedPayees] = React.useState<string[]>([]);
const [appliedTags, setAppliedTags] = React.useState<string[]>([]); const [appliedTags, setAppliedTags] = React.useState<string[]>([]);
@@ -32,7 +44,7 @@ export default function Dashboard() {
const report = useReport({ const report = useReport({
periods: ["daily", "weekly", "monthly", "all"], periods: ["daily", "weekly", "monthly", "all"],
flow: flow, flow: state.flow,
payee: appliedPayees.length > 0 ? appliedPayees : undefined, payee: appliedPayees.length > 0 ? appliedPayees : undefined,
tags: appliedTags.length > 0 ? appliedTags : undefined, tags: appliedTags.length > 0 ? appliedTags : undefined,
}); });
@@ -69,14 +81,124 @@ export default function Dashboard() {
} }
}, [report.data?.data]); }, [report.data?.data]);
const toggleFlow =
React.useCallback(() => {
setState((prev) => ({
...prev,
flow:
prev.flow ===
"outflows"
? "inflows"
: "outflows",
selectedGroupKey:
null,
selectedPeriodId:
null,
}));
}, []);
const setFlow =
React.useCallback(
(
flow: DashboardFlow
) => {
setState((prev) => ({
...prev,
flow,
selectedGroupKey:
null,
selectedPeriodId:
null,
}));
},
[]
);
const togglePeriodType =
React.useCallback(() => {
setState((prev) => ({
...prev,
periodType:
prev.periodType ===
"rolling"
? "calendar"
: "rolling",
}));
}, []);
const toggleComparison =
React.useCallback(() => {
setState((prev) => ({
...prev,
comparison:
!prev.comparison,
}));
}, []);
const setSelectedPeriodId =
React.useCallback(
(
selectedPeriodId: DashboardState["selectedPeriodId"]
) => {
setState((prev) => ({
...prev,
selectedPeriodId,
}));
},
[]
);
const setSelectedGroupKey =
React.useCallback(
(
selectedGroupKey: DashboardState["selectedGroupKey"]
) => {
setState((prev) => ({
...prev,
selectedGroupKey,
}));
},
[]
);
const stateSetters: DashboardStateSetters =
React.useMemo(
() => ({
toggleFlow,
setFlow,
togglePeriodType,
toggleComparison,
setSelectedPeriodId,
setSelectedGroupKey,
}),
[
toggleFlow,
setFlow,
togglePeriodType,
toggleComparison,
setSelectedPeriodId,
setSelectedGroupKey,
]
);
const isLoading = report.isLoading; const isLoading = report.isLoading;
const error = report.error; const error = report.error;
/** Callback for the ConfigurableDashboard's flow toggle */
const handleFlowChange = React.useCallback((newState: DashboardState) => {
setFlow(newState.flow);
}, []);
if (isLoading && !report.data) { 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" }}>
@@ -157,11 +279,12 @@ export default function Dashboard() {
</Button> </Button>
</Paper> </Paper>
</Container> </Container>
<ConfigurableDashboard <DashboardView
config={configuration} config={configuration}
data={data} data={data}
state={state}
stateSetters={stateSetters}
isFetching={report.isFetching} isFetching={report.isFetching}
onFlowChange={handleFlowChange}
/> />
</Box> </Box>
); );

View File

@@ -50,11 +50,12 @@ export interface DashboardConfig {
}; };
} }
export interface DashboardProps { export interface DashboardViewProps {
config: DashboardConfig; config: DashboardConfig;
data: ReportData; data: ReportData;
state: DashboardState;
stateSetters: DashboardStateSetters;
isFetching: boolean; isFetching: boolean;
onFlowChange?: (state: DashboardState) => void;
} }

View File

@@ -8,88 +8,22 @@ import {
Button Button
} from "@mui/material"; } from "@mui/material";
import { useTheme, alpha } from "@mui/material/styles"; import { useTheme, alpha } from "@mui/material/styles";
import { DashboardProps, DashboardState, DashboardStateSetters, DashboardFlow } from "./Dashboard.models"; import { DashboardViewProps } from "./Dashboard.models";
export default function Dashboard({ export default function DashboardView({
config, config,
data, data,
state,
stateSetters,
isFetching, isFetching,
onFlowChange, }: DashboardViewProps) {
}: DashboardProps) {
const theme = useTheme(); const theme = useTheme();
const themeMode = theme.palette.mode; const themeMode = theme.palette.mode;
const [state, setState] = React.useState<DashboardState>({ const {
flow: "outflows", flow,
periodType: "rolling", selectedGroupKey,
selectedPeriodId: null, } = state;
selectedGroupKey: null,
comparison: false,
});
const toggleFlow = () => {
setState(prev => {
const nextFlow: DashboardFlow = prev.flow === "outflows" ? "inflows" : "outflows";
const nextState: DashboardState = {
...prev,
flow: nextFlow,
selectedGroupKey: null,
selectedPeriodId: null,
};
onFlowChange?.(nextState);
return nextState;
});
};
const handleFlowChange = (
_event: React.MouseEvent<HTMLElement>,
newFlow: DashboardFlow | null
) => {
if (newFlow !== null && newFlow !== state.flow) {
setState(prev => {
const nextState: DashboardState = {
...prev,
flow: newFlow,
selectedGroupKey: null,
selectedPeriodId: null,
};
onFlowChange?.(nextState);
return nextState;
});
}
};
const togglePeriodType = () => {
setState(prev => ({
...prev,
periodType: prev.periodType === "rolling" ? "calendar" : "rolling",
}));
};
const toggleComparison = () => {
setState(prev => ({
...prev,
comparison: !prev.comparison,
}));
};
const setSelectedPeriodId = (selectedPeriodId: typeof state.selectedPeriodId) => {
setState(prev => ({ ...prev, selectedPeriodId }));
};
const setSelectedGroupKey = (groupKey: typeof state.selectedGroupKey) => {
setState(prev => ({ ...prev, selectedGroupKey: groupKey }));
};
const stateSetters: DashboardStateSetters = {
togglePeriodType,
toggleComparison,
toggleFlow,
setSelectedPeriodId,
setSelectedGroupKey,
};
const { flow, selectedGroupKey } = state;
const colors = React.useMemo(() => { const colors = React.useMemo(() => {
const palette = config.style.palette[flow]; const palette = config.style.palette[flow];
@@ -145,7 +79,7 @@ export default function Dashboard({
<ToggleButtonGroup <ToggleButtonGroup
value={flow} value={flow}
exclusive exclusive
onChange={handleFlowChange} onChange={stateSetters.toggleFlow}
sx={{ sx={{
borderRadius: 3, borderRadius: 3,
overflow: "hidden", overflow: "hidden",
@@ -169,7 +103,7 @@ export default function Dashboard({
<Button <Button
size="small" size="small"
sx={{ mt: 1, textTransform: "none" }} sx={{ mt: 1, textTransform: "none" }}
onClick={() => setSelectedGroupKey(null)} onClick={() => stateSetters.setSelectedGroupKey(null)}
> >
Clear Drill-down Clear Drill-down
</Button> </Button>

View File

@@ -1,2 +1,2 @@
export { default } from "./Dashboard"; export { default } from "./Dashboard.view";
export * from "./Dashboard.models"; export * from "./Dashboard.models";