removed expense and income vs outflow and inflow
This commit is contained in:
@@ -18,13 +18,8 @@ import {
|
|||||||
prepareReport,
|
prepareReport,
|
||||||
} from "./features/report";
|
} from "./features/report";
|
||||||
|
|
||||||
/** Map the internal UI mode to the API flow param */
|
|
||||||
function modeToFlow(mode: "expense" | "income"): "outflows" | "inflows" {
|
|
||||||
return mode === "expense" ? "outflows" : "inflows";
|
|
||||||
}
|
|
||||||
|
|
||||||
export default function Dashboard() {
|
export default function Dashboard() {
|
||||||
const [mode, setMode] = React.useState<"expense" | "income">("expense");
|
const [flow, setFlow] = React.useState<"outflows" | "inflows">("outflows");
|
||||||
|
|
||||||
const [appliedPayees, setAppliedPayees] = React.useState<string[]>([]);
|
const [appliedPayees, setAppliedPayees] = React.useState<string[]>([]);
|
||||||
const [appliedTags, setAppliedTags] = React.useState<string[]>([]);
|
const [appliedTags, setAppliedTags] = React.useState<string[]>([]);
|
||||||
@@ -37,7 +32,7 @@ export default function Dashboard() {
|
|||||||
|
|
||||||
const report = useReport({
|
const report = useReport({
|
||||||
periods: ["daily", "weekly", "monthly", "all"],
|
periods: ["daily", "weekly", "monthly", "all"],
|
||||||
flow: modeToFlow(mode),
|
flow: 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,
|
||||||
});
|
});
|
||||||
@@ -77,9 +72,9 @@ export default function Dashboard() {
|
|||||||
const isLoading = report.isLoading;
|
const isLoading = report.isLoading;
|
||||||
const error = report.error;
|
const error = report.error;
|
||||||
|
|
||||||
/** Callback for the ConfigurableDashboard's mode toggle */
|
/** Callback for the ConfigurableDashboard's flow toggle */
|
||||||
const handleModeChange = React.useCallback((newState: DashboardState) => {
|
const handleFlowChange = React.useCallback((newState: DashboardState) => {
|
||||||
setMode(newState.mode);
|
setFlow(newState.flow);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (isLoading && !report.data) {
|
if (isLoading && !report.data) {
|
||||||
@@ -166,7 +161,7 @@ export default function Dashboard() {
|
|||||||
config={configuration}
|
config={configuration}
|
||||||
data={data}
|
data={data}
|
||||||
isFetching={report.isFetching}
|
isFetching={report.isFetching}
|
||||||
onModeChange={handleModeChange}
|
onFlowChange={handleFlowChange}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ import {
|
|||||||
GroupKey,
|
GroupKey,
|
||||||
} from "../../features/report";
|
} from "../../features/report";
|
||||||
|
|
||||||
export type DashboardMode = "expense" | "income";
|
export type DashboardFlow = "outflows" | "inflows";
|
||||||
export type DashboardPeriodType = "rolling" | "calendar";
|
export type DashboardPeriodType = "rolling" | "calendar";
|
||||||
export type DashboardSelectedPeriodId = string | null;
|
export type DashboardSelectedPeriodId = string | null;
|
||||||
|
|
||||||
export interface DashboardState {
|
export interface DashboardState {
|
||||||
mode: DashboardMode;
|
flow: DashboardFlow;
|
||||||
periodType: DashboardPeriodType;
|
periodType: DashboardPeriodType;
|
||||||
selectedPeriodId: DashboardSelectedPeriodId;
|
selectedPeriodId: DashboardSelectedPeriodId;
|
||||||
selectedGroupKey: GroupKey | null;
|
selectedGroupKey: GroupKey | null;
|
||||||
@@ -43,7 +43,7 @@ export interface ThemeAwarePalette {
|
|||||||
export interface DashboardConfig {
|
export interface DashboardConfig {
|
||||||
sections: DashboardSection[];
|
sections: DashboardSection[];
|
||||||
style?: {
|
style?: {
|
||||||
palette?: Record<DashboardMode, ThemeAwarePalette>;
|
palette?: Record<DashboardFlow, ThemeAwarePalette>;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -51,5 +51,5 @@ export interface DashboardProps {
|
|||||||
config: DashboardConfig;
|
config: DashboardConfig;
|
||||||
data: ReportData;
|
data: ReportData;
|
||||||
isFetching?: boolean;
|
isFetching?: boolean;
|
||||||
onModeChange?: (state: DashboardState) => void;
|
onFlowChange?: (state: DashboardState) => void;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,24 +4,24 @@ import { DashboardProps, DashboardState } from "./Dashboard.models";
|
|||||||
|
|
||||||
export default function Dashboard(props: DashboardProps) {
|
export default function Dashboard(props: DashboardProps) {
|
||||||
const [state, setState] = React.useState<DashboardState>({
|
const [state, setState] = React.useState<DashboardState>({
|
||||||
mode: "expense",
|
flow: "outflows",
|
||||||
periodType: "rolling",
|
periodType: "rolling",
|
||||||
selectedPeriodId: null,
|
selectedPeriodId: null,
|
||||||
selectedGroupKey: null,
|
selectedGroupKey: null,
|
||||||
comparison: false,
|
comparison: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
const toggleMode = (
|
const toggleFlow = (
|
||||||
event: React.MouseEvent<HTMLElement>,
|
event: React.MouseEvent<HTMLElement>,
|
||||||
newMode: "expense" | "income" | null
|
newFlow: "outflows" | "inflows" | null
|
||||||
) => {
|
) => {
|
||||||
if (newMode !== null && newMode !== state.mode) {
|
if (newFlow !== null && newFlow !== state.flow) {
|
||||||
setState(prev => {
|
setState(prev => {
|
||||||
const next = {
|
const next = {
|
||||||
...prev,
|
...prev,
|
||||||
mode: newMode,
|
flow: newFlow,
|
||||||
};
|
};
|
||||||
props.onModeChange?.(next);
|
props.onFlowChange?.(next);
|
||||||
return next;
|
return next;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ export default function Dashboard(props: DashboardProps) {
|
|||||||
{...props}
|
{...props}
|
||||||
state={state}
|
state={state}
|
||||||
setState={setState}
|
setState={setState}
|
||||||
toggleMode={toggleMode}
|
toggleFlow={toggleFlow}
|
||||||
togglePeriodType={togglePeriodType}
|
togglePeriodType={togglePeriodType}
|
||||||
toggleComparison={toggleComparison}
|
toggleComparison={toggleComparison}
|
||||||
setSelectedPeriodId={setSelectedPeriodId}
|
setSelectedPeriodId={setSelectedPeriodId}
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import { DashboardProps, DashboardState } from "./Dashboard.models";
|
|||||||
interface ViewProps extends DashboardProps {
|
interface ViewProps extends DashboardProps {
|
||||||
state: DashboardState;
|
state: DashboardState;
|
||||||
setState: React.Dispatch<React.SetStateAction<DashboardState>>;
|
setState: React.Dispatch<React.SetStateAction<DashboardState>>;
|
||||||
toggleMode: (event: React.MouseEvent<HTMLElement>, newMode: "expense" | "income" | null) => void;
|
toggleFlow: (event: React.MouseEvent<HTMLElement>, newFlow: "outflows" | "inflows" | null) => void;
|
||||||
togglePeriodType: () => void;
|
togglePeriodType: () => void;
|
||||||
setSelectedPeriodId: (id: string | null) => void;
|
setSelectedPeriodId: (id: string | null) => void;
|
||||||
setSelectedGroupKey: (groupKey: GroupKey | null) => void;
|
setSelectedGroupKey: (groupKey: GroupKey | null) => void;
|
||||||
@@ -26,7 +26,7 @@ export default function DashboardView({
|
|||||||
data,
|
data,
|
||||||
state,
|
state,
|
||||||
setState,
|
setState,
|
||||||
toggleMode,
|
toggleFlow,
|
||||||
togglePeriodType,
|
togglePeriodType,
|
||||||
toggleComparison,
|
toggleComparison,
|
||||||
setSelectedPeriodId,
|
setSelectedPeriodId,
|
||||||
@@ -34,11 +34,11 @@ export default function DashboardView({
|
|||||||
}: ViewProps) {
|
}: ViewProps) {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
const themeMode = theme.palette.mode;
|
const themeMode = theme.palette.mode;
|
||||||
const { mode, periodType, comparison, selectedPeriodId, selectedGroupKey } = state;
|
const { flow, periodType, comparison, selectedPeriodId, selectedGroupKey } = state;
|
||||||
|
|
||||||
// Resolve colors with fallbacks
|
// Resolve colors with fallbacks
|
||||||
const colors = React.useMemo(() => {
|
const colors = React.useMemo(() => {
|
||||||
const palette = config.style?.palette?.[mode];
|
const palette = config.style?.palette?.[flow];
|
||||||
const modeColors = palette ? palette[themeMode] : null;
|
const modeColors = palette ? palette[themeMode] : null;
|
||||||
|
|
||||||
if (modeColors) {
|
if (modeColors) {
|
||||||
@@ -50,13 +50,13 @@ export default function DashboardView({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Fallback to standard theme colors
|
// Fallback to standard theme colors
|
||||||
const themeColor = mode === 'expense' ? theme.palette.error : theme.palette.success;
|
const themeColor = flow === 'outflows' ? theme.palette.error : theme.palette.success;
|
||||||
return {
|
return {
|
||||||
primary: themeColor.main,
|
primary: themeColor.main,
|
||||||
light: alpha(themeColor.main, themeMode === 'light' ? 0.08 : 0.15),
|
light: alpha(themeColor.main, themeMode === 'light' ? 0.08 : 0.15),
|
||||||
text: themeColor.main
|
text: themeColor.main
|
||||||
};
|
};
|
||||||
}, [config.style?.palette, mode, themeMode, theme.palette]);
|
}, [config.style?.palette, flow, themeMode, theme.palette]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container
|
<Container
|
||||||
@@ -71,9 +71,9 @@ export default function DashboardView({
|
|||||||
>
|
>
|
||||||
<Box sx={{ display: "flex", justifyContent: "center", mb: 3 }}>
|
<Box sx={{ display: "flex", justifyContent: "center", mb: 3 }}>
|
||||||
<ToggleButtonGroup
|
<ToggleButtonGroup
|
||||||
value={mode}
|
value={flow}
|
||||||
exclusive
|
exclusive
|
||||||
onChange={toggleMode}
|
onChange={toggleFlow}
|
||||||
sx={{
|
sx={{
|
||||||
borderRadius: 3,
|
borderRadius: 3,
|
||||||
overflow: "hidden",
|
overflow: "hidden",
|
||||||
@@ -89,8 +89,8 @@ export default function DashboardView({
|
|||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ToggleButton value="expense">Expenses</ToggleButton>
|
<ToggleButton value="outflows">Outflows</ToggleButton>
|
||||||
<ToggleButton value="income">Income</ToggleButton>
|
<ToggleButton value="inflows">Inflows</ToggleButton>
|
||||||
</ToggleButtonGroup>
|
</ToggleButtonGroup>
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
@@ -118,7 +118,7 @@ export default function DashboardView({
|
|||||||
colorScheme={colors}
|
colorScheme={colors}
|
||||||
|
|
||||||
// State management
|
// State management
|
||||||
mode={mode}
|
flow={flow}
|
||||||
|
|
||||||
periodType={periodType}
|
periodType={periodType}
|
||||||
comparison={comparison}
|
comparison={comparison}
|
||||||
|
|||||||
@@ -54,7 +54,7 @@ function attachComparison(
|
|||||||
export function buildChartData(
|
export function buildChartData(
|
||||||
reportData: ReportData,
|
reportData: ReportData,
|
||||||
key: PeriodKey,
|
key: PeriodKey,
|
||||||
mode: "expense" | "income",
|
flow: "outflows" | "inflows",
|
||||||
comparison: boolean
|
comparison: boolean
|
||||||
): ChartDataPoint[] {
|
): ChartDataPoint[] {
|
||||||
const merged = mergeBucketPeriods(reportData.buckets, key);
|
const merged = mergeBucketPeriods(reportData.buckets, key);
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import {
|
import {
|
||||||
DashboardMode,
|
DashboardFlow,
|
||||||
DashboardPeriodType,
|
DashboardPeriodType,
|
||||||
DashboardSelectedPeriodId
|
DashboardSelectedPeriodId
|
||||||
} from "../Dashboard";
|
} from "../Dashboard";
|
||||||
@@ -29,7 +29,7 @@ export interface HistoryChartProps {
|
|||||||
text: string;
|
text: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
mode: DashboardMode;
|
flow: DashboardFlow;
|
||||||
periodType: DashboardPeriodType;
|
periodType: DashboardPeriodType;
|
||||||
selectedPeriodId: DashboardSelectedPeriodId;
|
selectedPeriodId: DashboardSelectedPeriodId;
|
||||||
comparison: boolean;
|
comparison: boolean;
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ export default function HistoryChart(props: HistoryChartProps) {
|
|||||||
const {
|
const {
|
||||||
tabs,
|
tabs,
|
||||||
reportData,
|
reportData,
|
||||||
mode,
|
flow,
|
||||||
comparison,
|
comparison,
|
||||||
selectedPeriodId,
|
selectedPeriodId,
|
||||||
setSelectedPeriodId
|
setSelectedPeriodId
|
||||||
@@ -19,8 +19,8 @@ export default function HistoryChart(props: HistoryChartProps) {
|
|||||||
const activeDataKey = tabToKey(activeTab);
|
const activeDataKey = tabToKey(activeTab);
|
||||||
|
|
||||||
const currentData = React.useMemo(() => {
|
const currentData = React.useMemo(() => {
|
||||||
return buildChartData(reportData, activeDataKey, mode, comparison);
|
return buildChartData(reportData, activeDataKey, flow, comparison);
|
||||||
}, [reportData, activeDataKey, mode, comparison]);
|
}, [reportData, activeDataKey, flow, comparison]);
|
||||||
|
|
||||||
const maxAmount =
|
const maxAmount =
|
||||||
currentData.length > 0
|
currentData.length > 0
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ export default function HistoryChartView(props: ViewProps) {
|
|||||||
tabs,
|
tabs,
|
||||||
colorScheme,
|
colorScheme,
|
||||||
|
|
||||||
mode,
|
flow,
|
||||||
periodType,
|
periodType,
|
||||||
selectedPeriodId,
|
selectedPeriodId,
|
||||||
comparison,
|
comparison,
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function buildLatestItems(
|
|||||||
reportData: ReportData,
|
reportData: ReportData,
|
||||||
selectedPeriodId: string | null,
|
selectedPeriodId: string | null,
|
||||||
selectedGroupKey: GroupKey | null,
|
selectedGroupKey: GroupKey | null,
|
||||||
mode: "expense" | "income"
|
flow: "outflows" | "inflows"
|
||||||
): LatestItem[] {
|
): LatestItem[] {
|
||||||
const txns = extractTransactions(reportData, selectedPeriodId, selectedGroupKey);
|
const txns = extractTransactions(reportData, selectedPeriodId, selectedGroupKey);
|
||||||
|
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import LatestItemsView from "./LatestItems.view";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
reportData: ReportData;
|
reportData: ReportData;
|
||||||
mode: "expense" | "income";
|
flow: "outflows" | "inflows";
|
||||||
selectedPeriodId: string | null;
|
selectedPeriodId: string | null;
|
||||||
selectedGroupKey?: GroupKey | null;
|
selectedGroupKey?: GroupKey | null;
|
||||||
accentColor: string;
|
accentColor: string;
|
||||||
@@ -14,7 +14,7 @@ type Props = {
|
|||||||
|
|
||||||
export default function LatestItems({
|
export default function LatestItems({
|
||||||
reportData,
|
reportData,
|
||||||
mode,
|
flow,
|
||||||
selectedPeriodId,
|
selectedPeriodId,
|
||||||
selectedGroupKey = null,
|
selectedGroupKey = null,
|
||||||
accentColor,
|
accentColor,
|
||||||
@@ -23,8 +23,8 @@ export default function LatestItems({
|
|||||||
const [visibleCount, setVisibleCount] = React.useState(5);
|
const [visibleCount, setVisibleCount] = React.useState(5);
|
||||||
|
|
||||||
const allItems = React.useMemo(() => {
|
const allItems = React.useMemo(() => {
|
||||||
return buildLatestItems(reportData, selectedPeriodId, selectedGroupKey, mode);
|
return buildLatestItems(reportData, selectedPeriodId, selectedGroupKey, flow);
|
||||||
}, [reportData, selectedPeriodId, selectedGroupKey, mode]);
|
}, [reportData, selectedPeriodId, selectedGroupKey, flow]);
|
||||||
|
|
||||||
const visibleItems = React.useMemo(() => {
|
const visibleItems = React.useMemo(() => {
|
||||||
return allItems.slice(0, visibleCount);
|
return allItems.slice(0, visibleCount);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export interface TagItem {
|
|||||||
|
|
||||||
export function extractTopTags(
|
export function extractTopTags(
|
||||||
reportData: ReportData,
|
reportData: ReportData,
|
||||||
mode: "expense" | "income",
|
flow: "outflows" | "inflows",
|
||||||
selectedPeriodId?: string | null
|
selectedPeriodId?: string | null
|
||||||
): { items: TagItem[]; total: number } {
|
): { items: TagItem[]; total: number } {
|
||||||
const tagMap = new Map<string, number>();
|
const tagMap = new Map<string, number>();
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import { extractTopTags } from "./TopTags.adapter";
|
|||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
reportData: ReportData;
|
reportData: ReportData;
|
||||||
mode: "expense" | "income";
|
flow: "outflows" | "inflows";
|
||||||
selectedPeriodId?: string | null;
|
selectedPeriodId?: string | null;
|
||||||
selectedGroupKey?: GroupKey | null;
|
selectedGroupKey?: GroupKey | null;
|
||||||
setSelectedGroupKey?: (key: GroupKey | null) => void;
|
setSelectedGroupKey?: (key: GroupKey | null) => void;
|
||||||
@@ -16,7 +16,7 @@ type Props = {
|
|||||||
|
|
||||||
export default function TopTags({
|
export default function TopTags({
|
||||||
reportData,
|
reportData,
|
||||||
mode,
|
flow,
|
||||||
selectedPeriodId,
|
selectedPeriodId,
|
||||||
selectedGroupKey,
|
selectedGroupKey,
|
||||||
setSelectedGroupKey,
|
setSelectedGroupKey,
|
||||||
@@ -24,8 +24,8 @@ export default function TopTags({
|
|||||||
isFetching,
|
isFetching,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const { items, total } = React.useMemo(() => {
|
const { items, total } = React.useMemo(() => {
|
||||||
return extractTopTags(reportData, mode, selectedPeriodId);
|
return extractTopTags(reportData, flow, selectedPeriodId);
|
||||||
}, [reportData, mode, selectedPeriodId]);
|
}, [reportData, flow, selectedPeriodId]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Box
|
<Box
|
||||||
@@ -48,7 +48,7 @@ export default function TopTags({
|
|||||||
progressAmount={item.amount}
|
progressAmount={item.amount}
|
||||||
totalAmount={total}
|
totalAmount={total}
|
||||||
compact={compact}
|
compact={compact}
|
||||||
colorTheme={mode === "expense" ? "error" : "success"}
|
colorTheme={flow === "outflows" ? "error" : "success"}
|
||||||
selected={isSelected}
|
selected={isSelected}
|
||||||
isFetching={isFetching}
|
isFetching={isFetching}
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ export const configuration: DashboardConfig = {
|
|||||||
],
|
],
|
||||||
style: {
|
style: {
|
||||||
palette: {
|
palette: {
|
||||||
expense: {
|
outflows: {
|
||||||
light: {
|
light: {
|
||||||
primary: "#d32f2f",
|
primary: "#d32f2f",
|
||||||
background: "#fdecea",
|
background: "#fdecea",
|
||||||
@@ -51,7 +51,7 @@ export const configuration: DashboardConfig = {
|
|||||||
text: "#ffcdd2"
|
text: "#ffcdd2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
income: {
|
inflows: {
|
||||||
light: {
|
light: {
|
||||||
primary: "#2e7d32",
|
primary: "#2e7d32",
|
||||||
background: "#e8f5e9",
|
background: "#e8f5e9",
|
||||||
|
|||||||
Reference in New Issue
Block a user