Theme System Refactor (#6)
# Dashboard State Lift + Theme System Refactor ## Summary Refactored dashboard state ownership, centralized theme semantics, and simplified component styling across the application. ## Changes ### Dashboard State Refactor * Moved dashboard state management from `Dashboard.view` into `Dashboard.tsx` * Added centralized `DashboardState` initialization in parent container * Introduced memoized dashboard state setter callbacks: * `toggleFlow` * `setFlow` * `togglePeriodType` * `toggleComparison` * `setSelectedPeriodId` * `setSelectedGroupKey` * Added `DashboardStateSetters` memoized object for prop-driven state management * Removed `onFlowChange` callback pattern * Converted dashboard component into stateless view layer * Renamed component export flow: * `Dashboard.tsx` → removed * `Dashboard.view.tsx` → primary implementation ### Dashboard Models Cleanup * Removed legacy palette configuration interfaces: * `ColorDefinition` * `ThemeAwarePalette` * Removed config-level style palette support from `DashboardConfig` * Renamed `DashboardProps` → `DashboardViewProps` * Added reusable `ColorScheme` interface * Simplified component color contract: * `primary` * `surface` * `text` ### Theme Architecture Refactor * Moved `AppTheme.tsx` into `shared-theme` * Added centralized semantic theme system * Introduced `themeConfig.ts` with semantic tokens: * surface * border * text * Added `semantic` extension to MUI theme typing * Added `flows` palette extension: * outflows * inflows * Centralized flow colors inside theme primitives * Added CSS semantic variables: * `--bg-page` * `--bg-card` * `--bg-elevated` * `--border-default` * `--border-subtle` * `--text-primary` * `--text-secondary` * `--text-muted` ### Theme Mode Improvements * Added explicit `ColorMode` type * Expanded `ColorModeContext`: * `mode` * `setMode` * `toggleColorMode` * Added `CssBaseline` * Added configurable `defaultMode` * Simplified dark theme palette handling * Standardized dark surfaces and shadows * Reduced excessive dark-mode glow/shadow intensity ### Dashboard UI Styling Improvements * Replaced hardcoded dashboard palette config with theme palette usage * Updated dashboard background gradients to use alpha-based semantic colors * Replaced `colorScheme.light` usage with `colorScheme.surface` * Standardized border usage with theme divider tokens * Removed manual dark-mode conditional styling where redundant * Simplified card and progress styling logic ### Shared Theme Customization Cleanup Updated customization layers for improved consistency: * `inputs` * `navigation` * `feedback` * `surfaces` Key improvements: * Reduced dark-mode contrast harshness * Unified divider usage * Replaced hardcoded grayscale backgrounds with semantic surfaces * Simplified hover and active state styling * Reduced shadow intensity across components * Improved dark-mode readability and layering ### Home Page Styling Cleanup * Replaced manual RGBA handling with `alpha()` utility * Improved dark-mode glassmorphism consistency * Updated CTA hover shadow to use theme primary color ### Miscellaneous Cleanup * Updated imports to new theme structure * Removed unused legacy color mode components: * `ColorModeIconDropdown.tsx` * `ColorModeSelect.tsx` * Removed dashboard config style palette definitions * Simplified flow-based color derivation logic ## Result * Cleaner separation of stateful vs presentational dashboard logic * Centralized semantic theming system * Consistent dark/light mode behavior * Reduced styling duplication * Improved maintainability and extensibility of theme architecture * Simplified dashboard component contracts * Better UI consistency across surfaces and controls Reviewed-on: #6 Co-authored-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com> Co-committed-by: Vishesh 'ironeagle' Bangotra <aetoskia@gmail.com>
This commit is contained in:
@@ -10,8 +10,14 @@ import {
|
||||
Button
|
||||
} from "@mui/material";
|
||||
|
||||
import ConfigurableDashboard from "./components/Dashboard";
|
||||
import { DashboardState } from "./components/Dashboard";
|
||||
import DashboardView from "./components/Dashboard";
|
||||
|
||||
import {
|
||||
DashboardState,
|
||||
DashboardStateSetters,
|
||||
DashboardFlow,
|
||||
} from "./components/Dashboard";
|
||||
|
||||
import { configuration } from "./dashboard-config";
|
||||
import {
|
||||
useReport,
|
||||
@@ -19,7 +25,13 @@ import {
|
||||
} from "./features/report";
|
||||
|
||||
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 [appliedTags, setAppliedTags] = React.useState<string[]>([]);
|
||||
@@ -32,7 +44,7 @@ export default function Dashboard() {
|
||||
|
||||
const report = useReport({
|
||||
periods: ["daily", "weekly", "monthly", "all"],
|
||||
flow: flow,
|
||||
flow: state.flow,
|
||||
payee: appliedPayees.length > 0 ? appliedPayees : undefined,
|
||||
tags: appliedTags.length > 0 ? appliedTags : undefined,
|
||||
});
|
||||
@@ -69,14 +81,124 @@ export default function Dashboard() {
|
||||
}
|
||||
}, [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 error = report.error;
|
||||
|
||||
/** Callback for the ConfigurableDashboard's flow toggle */
|
||||
const handleFlowChange = React.useCallback((newState: DashboardState) => {
|
||||
setFlow(newState.flow);
|
||||
}, []);
|
||||
|
||||
if (isLoading && !report.data) {
|
||||
return (
|
||||
<Box sx={{ display: "flex", justifyContent: "center", alignItems: "center", height: "60vh" }}>
|
||||
@@ -143,8 +265,8 @@ export default function Dashboard() {
|
||||
sx={{ '& .MuiOutlinedInput-root': { height: 'auto', minHeight: '2.5rem', py: 0.5 } }}
|
||||
/>
|
||||
</Box>
|
||||
<Button
|
||||
variant="contained"
|
||||
<Button
|
||||
variant="contained"
|
||||
size="large"
|
||||
onClick={() => {
|
||||
setAppliedPayees(payeeInput);
|
||||
@@ -157,11 +279,12 @@ export default function Dashboard() {
|
||||
</Button>
|
||||
</Paper>
|
||||
</Container>
|
||||
<ConfigurableDashboard
|
||||
<DashboardView
|
||||
config={configuration}
|
||||
data={data}
|
||||
state={state}
|
||||
stateSetters={stateSetters}
|
||||
isFetching={report.isFetching}
|
||||
onFlowChange={handleFlowChange}
|
||||
/>
|
||||
</Box>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user