From afddb3cfbc8f3576b943ad0c397a5faac7111b40 Mon Sep 17 00:00:00 2001 From: Vishesh 'ironeagle' Bangotra Date: Sat, 11 Jul 2026 17:10:26 +0530 Subject: [PATCH] removed dashboard and reports --- src/Dashboard.tsx | 339 ------------------ src/Header.tsx | 1 - src/components/Dashboard/Dashboard.models.ts | 61 ---- src/components/Dashboard/Dashboard.view.tsx | 105 ------ src/components/Dashboard/index.ts | 2 - .../HistoryChart/HistoryChart.adapter.ts | 73 ---- .../HistoryChart/HistoryChart.models.ts | 10 - .../HistoryChart/HistoryChart.props.ts | 21 -- src/components/HistoryChart/HistoryChart.tsx | 96 ----- .../HistoryChart/HistoryChart.utils.ts | 27 -- .../HistoryChart/HistoryChart.view.tsx | 205 ----------- src/components/HistoryChart/index.ts | 2 - .../LatestItems/LatestItems.adapter.ts | 31 -- .../LatestItems/LatestItems.models.ts | 7 - .../LatestItems/LatestItems.props.ts | 10 - src/components/LatestItems/LatestItems.tsx | 40 --- .../LatestItems/LatestItems.view.tsx | 93 ----- src/components/LatestItems/index.ts | 2 - .../ProgressCard/ProgressCard.props.ts | 14 - .../ProgressCard/ProgressCard.view.tsx | 129 ------- .../ProgressCard/TopPayees.adapter.ts | 31 -- src/components/ProgressCard/TopPayees.tsx | 83 ----- .../ProgressCard/TopTags.adapter.ts | 31 -- src/components/ProgressCard/TopTags.tsx | 83 ----- src/components/ProgressCard/index.ts | 2 - src/components/report.helpers.ts | 230 ------------ src/dashboard-config.ts | 40 --- src/features/report-snapshots/index.ts | 9 - .../report-snapshots.models.ts | 15 - .../report-snapshots/useReportSnapshots.ts | 28 -- src/features/report/index.ts | 15 - src/features/report/report.models.ts | 112 ------ src/features/report/report.utils.ts | 117 ------ src/features/report/useReport.ts | 22 -- src/main.jsx | 4 - 35 files changed, 2090 deletions(-) delete mode 100644 src/Dashboard.tsx delete mode 100644 src/components/Dashboard/Dashboard.models.ts delete mode 100644 src/components/Dashboard/Dashboard.view.tsx delete mode 100644 src/components/Dashboard/index.ts delete mode 100644 src/components/HistoryChart/HistoryChart.adapter.ts delete mode 100644 src/components/HistoryChart/HistoryChart.models.ts delete mode 100644 src/components/HistoryChart/HistoryChart.props.ts delete mode 100644 src/components/HistoryChart/HistoryChart.tsx delete mode 100644 src/components/HistoryChart/HistoryChart.utils.ts delete mode 100644 src/components/HistoryChart/HistoryChart.view.tsx delete mode 100644 src/components/HistoryChart/index.ts delete mode 100644 src/components/LatestItems/LatestItems.adapter.ts delete mode 100644 src/components/LatestItems/LatestItems.models.ts delete mode 100644 src/components/LatestItems/LatestItems.props.ts delete mode 100644 src/components/LatestItems/LatestItems.tsx delete mode 100644 src/components/LatestItems/LatestItems.view.tsx delete mode 100644 src/components/LatestItems/index.ts delete mode 100644 src/components/ProgressCard/ProgressCard.props.ts delete mode 100644 src/components/ProgressCard/ProgressCard.view.tsx delete mode 100644 src/components/ProgressCard/TopPayees.adapter.ts delete mode 100644 src/components/ProgressCard/TopPayees.tsx delete mode 100644 src/components/ProgressCard/TopTags.adapter.ts delete mode 100644 src/components/ProgressCard/TopTags.tsx delete mode 100644 src/components/ProgressCard/index.ts delete mode 100644 src/components/report.helpers.ts delete mode 100644 src/dashboard-config.ts delete mode 100644 src/features/report-snapshots/index.ts delete mode 100644 src/features/report-snapshots/report-snapshots.models.ts delete mode 100644 src/features/report-snapshots/useReportSnapshots.ts delete mode 100644 src/features/report/index.ts delete mode 100644 src/features/report/report.models.ts delete mode 100644 src/features/report/report.utils.ts delete mode 100644 src/features/report/useReport.ts diff --git a/src/Dashboard.tsx b/src/Dashboard.tsx deleted file mode 100644 index a4a7349..0000000 --- a/src/Dashboard.tsx +++ /dev/null @@ -1,339 +0,0 @@ -import * as React from "react"; -import { - Box, - Container, - CircularProgress, - Alert, - TextField, - Paper, - Autocomplete, - Button -} from "@mui/material"; - -import DashboardView from "./components/Dashboard"; - -import { - DashboardState, - DashboardStateSetters, - DashboardFlow, -} from "./components/Dashboard"; - -import { configuration } from "./dashboard-config"; -import { - useReport, - prepareReport, -} from "./features/report"; -import { useReportSnapshotsList } from "./features/report-snapshots"; - -function formatSnapshotDate(iso: string) { - const d = new Date(iso); - return d.toLocaleString(undefined, { - year: "numeric", - month: "short", - day: "numeric", - hour: "2-digit", - minute: "2-digit", - }); -} - -export default function Dashboard() { - const [state, setState] = React.useState({ - flow: "outflows", - periodType: "rolling", - selectedPeriodId: null, - selectedGroupKey: null, - comparison: false, - }); - - const [appliedPayees, setAppliedPayees] = React.useState([]); - const [appliedTags, setAppliedTags] = React.useState([]); - - const [payeeInput, setPayeeInput] = React.useState([]); - const [tagsInput, setTagsInput] = React.useState([]); - - const [loadedPayees, setLoadedPayees] = React.useState([]); - const [loadedTags, setLoadedTags] = React.useState([]); - - const [selectedSnapshotId, setSelectedSnapshotId] = React.useState(null); - - const { data: snapshotsData } = useReportSnapshotsList(); - const snapshotOptions = React.useMemo(() => { - const options: { label: string; value: string | null }[] = [ - { label: "Latest (auto)", value: null }, - ]; - if (snapshotsData?.items) { - for (const snap of snapshotsData.items) { - options.push({ - label: `Snapshot from ${formatSnapshotDate(snap.created_at)}`, - value: snap.snapshot_id, - }); - } - } - return options; - }, [snapshotsData]); - - const selectedSnapshotOption = snapshotOptions.find((o) => o.value === selectedSnapshotId) ?? snapshotOptions[0]; - - const report = useReport({ - snapshot_id: selectedSnapshotId ?? undefined, - periods: ["daily", "weekly", "monthly", "all"], - flow: state.flow, - payee: appliedPayees.length > 0 ? appliedPayees : undefined, - tags: appliedTags.length > 0 ? appliedTags : undefined, - }); - - React.useEffect(() => { - if (report.data) { - setLoadedPayees(prev => { - const pSet = new Set(prev); - report.data.buckets.forEach((b: any) => { - Object.values(b.periods).forEach((periodArray: any) => { - periodArray?.forEach((p: any) => { - p.metric?.transactions?.forEach((t: any) => { - if (t.payee?.name) pSet.add(t.payee.name); - }); - }); - }); - }); - return Array.from(pSet).sort(); - }); - - setLoadedTags(prev => { - const tSet = new Set(prev); - report.data.buckets.forEach((b: any) => { - Object.values(b.periods).forEach((periodArray: any) => { - periodArray?.forEach((p: any) => { - p.metric?.transactions?.forEach((t: any) => { - t.tags?.forEach((tag: any) => tSet.add(tag.name || tag)); - }); - }); - }); - }); - return Array.from(tSet).sort(); - }); - } - }, [report.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; - - if (isLoading && !report.data) { - return ( - - - - ); - } - - if (error) { - return ( - - {String(error)} - - ); - } - - if (!report.data) { - return null; - } - - const data = prepareReport(report.data); - return ( - - - - - - Filter by Payee - - setPayeeInput(val as string[])} - renderInput={(params) => } - sx={{ '& .MuiOutlinedInput-root': { height: 'auto', minHeight: '2.5rem', py: 0.5 } }} - /> - - - - Filter by Tags - - setTagsInput(val as string[])} - renderInput={(params) => } - sx={{ '& .MuiOutlinedInput-root': { height: 'auto', minHeight: '2.5rem', py: 0.5 } }} - /> - - - - Snapshot - - setSelectedSnapshotId(option?.value ?? null)} - getOptionLabel={(o) => o.label} - isOptionEqualToValue={(o, v) => o.value === v.value} - renderInput={(params) => } - sx={{ '& .MuiOutlinedInput-root': { height: 'auto', minHeight: '2.5rem', py: 0.5 } }} - /> - - - - - - - - ); -} diff --git a/src/Header.tsx b/src/Header.tsx index f25baf6..a35e5aa 100644 --- a/src/Header.tsx +++ b/src/Header.tsx @@ -101,7 +101,6 @@ export default function Header({ }} > {[ - { label: "Dashboard", path: "/dashboard" }, { label: "Fetch", path: "/fetch-requests" }, { label: "Reports", path: "/reports" }, ].map(({ label, path }) => ( diff --git a/src/components/Dashboard/Dashboard.models.ts b/src/components/Dashboard/Dashboard.models.ts deleted file mode 100644 index 9c7c625..0000000 --- a/src/components/Dashboard/Dashboard.models.ts +++ /dev/null @@ -1,61 +0,0 @@ -import * as React from "react"; -import { - ReportData, - GroupKey, -} from "../../features/report"; - -export type DashboardFlow = "outflows" | "inflows"; -export type DashboardPeriodType = "rolling" | "calendar"; -export type DashboardSelectedPeriodId = string | null; - -export interface DashboardState { - flow: DashboardFlow; - periodType: DashboardPeriodType; - selectedPeriodId: DashboardSelectedPeriodId; - selectedGroupKey: GroupKey | null; - comparison: boolean; -} - -export interface DashboardStateSetters { - setSelectedPeriodId: (id: DashboardSelectedPeriodId) => void; - setSelectedGroupKey: (groupKey: GroupKey | null) => void; - toggleFlow: () => void; - togglePeriodType: () => void; - toggleComparison: () => void; -} - -export interface DashboardSection { - id: string; - title: string; - component: React.ComponentType; - summary?: string; - settings?: Record; -} - -export interface DashboardConfig { - sections: DashboardSection[]; -} - -export interface DashboardViewProps { - config: DashboardConfig; - data: ReportData; - state: DashboardState; - stateSetters: DashboardStateSetters; - isFetching: boolean; -} - -export interface ColorScheme { - primary: string; - surface: string; - text: string; -} - -export interface ComponentProps extends DashboardSection { - reportData: ReportData; - - state: DashboardState; - stateSetters: DashboardStateSetters; - isFetching: boolean; - - colorScheme: ColorScheme; -} diff --git a/src/components/Dashboard/Dashboard.view.tsx b/src/components/Dashboard/Dashboard.view.tsx deleted file mode 100644 index 2ba5427..0000000 --- a/src/components/Dashboard/Dashboard.view.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import * as React from "react"; -import { - Box, - Container, - Grid, - ToggleButton, - ToggleButtonGroup, - Button -} from "@mui/material"; -import { useTheme, alpha } from "@mui/material/styles"; -import { DashboardViewProps } from "./Dashboard.models"; - -export default function DashboardView({ - config, - data, - state, - stateSetters, - isFetching, -}: DashboardViewProps) { - const theme = useTheme(); - - const { - flow, - selectedGroupKey, - } = state; - - const colorScheme = flow === "outflows" ? theme.palette.flows.outflows : theme.palette.flows.inflows; - - return ( - - - - Outflows - Inflows - - - {selectedGroupKey && Object.keys(selectedGroupKey).length > 0 && ( - - )} - - - - {config.sections.map((section) => { - const Component = section.component; - - return ( - - - - ); - })} - - - ); -} diff --git a/src/components/Dashboard/index.ts b/src/components/Dashboard/index.ts deleted file mode 100644 index 892b9c4..0000000 --- a/src/components/Dashboard/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./Dashboard.view"; -export * from "./Dashboard.models"; diff --git a/src/components/HistoryChart/HistoryChart.adapter.ts b/src/components/HistoryChart/HistoryChart.adapter.ts deleted file mode 100644 index bb660e1..0000000 --- a/src/components/HistoryChart/HistoryChart.adapter.ts +++ /dev/null @@ -1,73 +0,0 @@ -import { ReportData } from "../../features/report"; -import { - mergeBucketPeriods, - getAmount, - PeriodKey, -} from "../report.helpers"; -import { ChartDataPoint } from "./HistoryChart.models"; - -// ─── Tab → PeriodKey ───────────────────────────────────────── - -const TAB_TO_KEY: Record = { - Daily: "daily", - Weekly: "weekly", - Monthly: "monthly", - "All Time": "all", -}; - -export function tabToKey(tab: string): PeriodKey { - return TAB_TO_KEY[tab] ?? "all"; -} - -// ─── Comparison ────────────────────────────────────────────── - -function attachComparison( - points: ChartDataPoint[], - key: PeriodKey -): ChartDataPoint[] { - const getCompareIndex = (i: number) => { - if (key === "daily") return i - 7; - if (key === "weekly") return i - 4; - if (key === "monthly") return i - 12; - return -1; - }; - - return points.map((p, i) => { - const ci = getCompareIndex(i); - - return { - ...p, - compare: - ci >= 0 && points[ci] - ? { - id: points[ci].id, - label: points[ci].label, - amount: points[ci].amount, - } - : undefined, - }; - }); -} - -// ─── Main adapter ──────────────────────────────────────────── - -export function buildChartData( - reportData: ReportData, - key: PeriodKey, - flow: "outflows" | "inflows", - comparison: boolean -): ChartDataPoint[] { - const merged = mergeBucketPeriods(reportData.buckets, key); - - let points: ChartDataPoint[] = merged.map((p) => ({ - id: p.id, - label: p.label, - amount: getAmount(p), - })); - - if (comparison) { - points = attachComparison(points, key); - } - - return points; -} diff --git a/src/components/HistoryChart/HistoryChart.models.ts b/src/components/HistoryChart/HistoryChart.models.ts deleted file mode 100644 index 08f69f6..0000000 --- a/src/components/HistoryChart/HistoryChart.models.ts +++ /dev/null @@ -1,10 +0,0 @@ -export interface _ChartDataPoint { - id: string; - label: string; - amount: number; - highlighted?: boolean; -} - -export interface ChartDataPoint extends _ChartDataPoint { - compare?: _ChartDataPoint; -} diff --git a/src/components/HistoryChart/HistoryChart.props.ts b/src/components/HistoryChart/HistoryChart.props.ts deleted file mode 100644 index 0e1cc61..0000000 --- a/src/components/HistoryChart/HistoryChart.props.ts +++ /dev/null @@ -1,21 +0,0 @@ -import * as React from "react"; -import { ComponentProps } from "../Dashboard"; -import { ChartDataPoint } from "./HistoryChart.models"; - -export interface HistoryChartProps extends ComponentProps { - settings: { - tabs: string[]; - }; -} - -export interface HistoryChartViewProps extends HistoryChartProps { - activeTab: string; - setActiveTab: (v: string) => void; - currentData: ChartDataPoint[]; - visibleData: ChartDataPoint[]; - maxAmount: number; - visibleCount: number; - startIndex: number; - setStartIndex: React.Dispatch>; - activeDataKey: string; -} diff --git a/src/components/HistoryChart/HistoryChart.tsx b/src/components/HistoryChart/HistoryChart.tsx deleted file mode 100644 index 95291c6..0000000 --- a/src/components/HistoryChart/HistoryChart.tsx +++ /dev/null @@ -1,96 +0,0 @@ -import * as React from "react"; -import HistoryChartView from "./HistoryChart.view"; -import { buildChartData, tabToKey } from "./HistoryChart.adapter"; -import { HistoryChartProps } from "./HistoryChart.props"; - - -export default function HistoryChart(props: HistoryChartProps) { - const { - settings, - reportData, - state, - stateSetters, - - isFetching, - } = props; - - const { flow, comparison, selectedPeriodId } = state; - const { setSelectedPeriodId } = stateSetters; - const { tabs } = settings; - - const [activeTab, setActiveTab] = React.useState(tabs[0] || ""); - const [startIndex, setStartIndex] = React.useState(0); - - const activeDataKey = tabToKey(activeTab); - - const currentData = React.useMemo(() => { - return buildChartData(reportData, activeDataKey, flow, comparison); - }, [reportData, activeDataKey, flow, comparison]); - - const maxAmount = - currentData.length > 0 - ? Math.max( - ...currentData.flatMap((d) => - comparison - ? [d.amount, ...(d.compare ? [d.compare.amount] : [])] - : [d.amount] - ), - 1 - ) - : 1; - - const visibleCountMap = { - daily: 7, - weekly: 6, - monthly: 4, - all: 4, - }; - - const visibleCount = visibleCountMap[activeDataKey] ?? 4; - - const total = currentData.length; - - const clampedStartIndex = Math.min( - startIndex, - Math.max(total - visibleCount, 0) - ); - - React.useEffect(() => { - if (startIndex !== clampedStartIndex) { - setStartIndex(clampedStartIndex); - } - }, [startIndex, clampedStartIndex]); - - const visibleData = currentData.slice( - clampedStartIndex, - clampedStartIndex + visibleCount - ); - - React.useEffect(() => { - setSelectedPeriodId(null); - }, [activeTab]); - - React.useEffect(() => { - if ( - selectedPeriodId && - !visibleData.some((p) => p.id === selectedPeriodId) - ) { - setSelectedPeriodId(null); - } - }, [visibleData, selectedPeriodId]); - - return ( - - ); -} diff --git a/src/components/HistoryChart/HistoryChart.utils.ts b/src/components/HistoryChart/HistoryChart.utils.ts deleted file mode 100644 index a7bed97..0000000 --- a/src/components/HistoryChart/HistoryChart.utils.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { ChartDataPoint } from "./HistoryChart.models"; - -export const formatDisplay = ( - point: ChartDataPoint, - tab: string, - comparison: boolean -) => { - const base = point.amount; - const cmp = point.compare?.amount ?? 0; - - const formatShort = (val: number) => { - if (tab === "monthly" && val >= 100000) { - return `${(val / 100000).toFixed(2)}L`; - } - if (tab === "weekly" && val >= 1000) { - return `${(val / 1000).toFixed(1)}K`; - } - return val.toLocaleString("en-IN"); - }; - - if (!comparison) return `₹ ${formatShort(base)}`; - - const diff = base - cmp; - const sign = diff >= 0 ? "+" : "-"; - - return `₹ ${formatShort(base)} (${sign}${formatShort(Math.abs(diff))})`; -}; diff --git a/src/components/HistoryChart/HistoryChart.view.tsx b/src/components/HistoryChart/HistoryChart.view.tsx deleted file mode 100644 index 954ca69..0000000 --- a/src/components/HistoryChart/HistoryChart.view.tsx +++ /dev/null @@ -1,205 +0,0 @@ -import * as React from "react"; -import { - Box, - Typography, - ToggleButtonGroup, - ToggleButton, - Paper -} from "@mui/material"; -import { useTheme, alpha } from "@mui/material/styles"; -import IconButton from "@mui/material/IconButton"; -import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"; -import ChevronRightIcon from "@mui/icons-material/ChevronRight"; -import { - HistoryChartViewProps, -} from "./HistoryChart.props"; -import { formatDisplay } from "./HistoryChart.utils"; - -export default function HistoryChartView({ - title, - summary, - settings, - - state, - stateSetters, - isFetching, - - colorScheme, - - activeTab, - setActiveTab, - currentData, - visibleData, - maxAmount, - visibleCount, - startIndex, - setStartIndex, - activeDataKey, -}: HistoryChartViewProps) { - - const { flow, periodType, selectedPeriodId, comparison } = state; - const { togglePeriodType, setSelectedPeriodId, toggleComparison } = stateSetters; - - const theme = useTheme(); - const isDark = theme.palette.mode === "dark"; - - const total = currentData.length; - const maxStartIndex = Math.max(total - visibleCount, 0); - const clampedStartIndex = Math.min(startIndex, maxStartIndex); - - const handleTabChange = (_: React.MouseEvent, newTab: string | null) => { - if (newTab !== null) setActiveTab(newTab); - }; - - const canGoLeft = clampedStartIndex > 0; - const canGoRight = clampedStartIndex < maxStartIndex; - - const handlePrev = () => { - if (!canGoLeft) return; - setStartIndex((prev) => Math.max(prev - visibleCount, 0)); - }; - - const handleNext = () => { - if (!canGoRight) return; - setStartIndex((prev) => { - const next = prev + visibleCount; - return Math.min(next, maxStartIndex); - }); - }; - - return ( - - - {title} - - - {summary && ( - - {summary} - - )} - - - {settings.tabs.map((tab) => ( - - {tab} - - ))} - - - - - Rolling - Calendar - - - - Compare - - - - {currentData.length > 0 ? ( - - {canGoLeft && ( - - - - )} - - - {visibleData.map((point) => { - const currentHeight = (point.amount / maxAmount) * 100; - const compareHeight = comparison - ? ((point.compare?.amount ?? 0) / maxAmount) * 100 - : 0; - - const isSelected = selectedPeriodId === point.id; - const display = formatDisplay(point, activeDataKey, comparison); - - return ( - - setSelectedPeriodId(isSelected ? null : point.id) - } - sx={{ - flex: 1, - display: "flex", - flexDirection: "column", - alignItems: "center", - cursor: "pointer", - height: "100%" - }} - > - - {comparison && ( - - )} - - - - - - {point.label} - - - {comparison && point.compare && ( - - {point.compare.label} - - )} - - - {display} - - - ); - })} - - - {canGoRight && ( - - - - )} - - ) : ( - - No Data Available - - )} - - ); -} diff --git a/src/components/HistoryChart/index.ts b/src/components/HistoryChart/index.ts deleted file mode 100644 index 28b6303..0000000 --- a/src/components/HistoryChart/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./HistoryChart"; -export * from "./HistoryChart.models"; diff --git a/src/components/LatestItems/LatestItems.adapter.ts b/src/components/LatestItems/LatestItems.adapter.ts deleted file mode 100644 index 5720ab6..0000000 --- a/src/components/LatestItems/LatestItems.adapter.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ReportData, GroupKey } from "../../features/report"; -import { - formatCurrency, - extractFilteredTransactions, -} from "../report.helpers"; -import { LatestItem } from "./LatestItems.models"; - -// ─── Main adapter ──────────────────────────────────────────── - -export function buildLatestItems( - reportData: ReportData, - selectedPeriodId: string | null | undefined, - selectedGroupKey: GroupKey | null | undefined, - flow: "outflows" | "inflows" -): LatestItem[] { - const txns = extractFilteredTransactions(reportData, selectedPeriodId, selectedGroupKey); - - return txns - .sort( - (a, b) => - new Date(b.occurred_at).getTime() - - new Date(a.occurred_at).getTime() - ) - .map((t, index) => ({ - id: index + 1, - title: t.payee.name, - subtitle: t.tags.map((tag) => tag.name).join(", "), - amount: formatCurrency(t.amount), - timeAgo: new Date(t.occurred_at).toLocaleDateString("en-IN"), - })); -} diff --git a/src/components/LatestItems/LatestItems.models.ts b/src/components/LatestItems/LatestItems.models.ts deleted file mode 100644 index 336ea01..0000000 --- a/src/components/LatestItems/LatestItems.models.ts +++ /dev/null @@ -1,7 +0,0 @@ -export interface LatestItem { - id: string | number; - title: string; - subtitle: string; - amount: string; - timeAgo: string; -} diff --git a/src/components/LatestItems/LatestItems.props.ts b/src/components/LatestItems/LatestItems.props.ts deleted file mode 100644 index 49c3721..0000000 --- a/src/components/LatestItems/LatestItems.props.ts +++ /dev/null @@ -1,10 +0,0 @@ -import { ComponentProps } from "../Dashboard"; -import { LatestItem } from "./LatestItems.models"; - -export interface LatestItemsProps extends ComponentProps {} - -export interface LatestItemsViewProps extends LatestItemsProps { - items: LatestItem[]; - canExpand: boolean; - onExpand: () => void; -} diff --git a/src/components/LatestItems/LatestItems.tsx b/src/components/LatestItems/LatestItems.tsx deleted file mode 100644 index 0b182e3..0000000 --- a/src/components/LatestItems/LatestItems.tsx +++ /dev/null @@ -1,40 +0,0 @@ -import * as React from "react"; -import { buildLatestItems } from "./LatestItems.adapter"; -import LatestItemsView from "./LatestItems.view"; -import { LatestItemsProps } from "./LatestItems.props"; - -export default function LatestItems(props: LatestItemsProps) { - const { - reportData, - state, - stateSetters, - isFetching, - } = props; - - const { flow, selectedPeriodId, selectedGroupKey } = state; - const [visibleCount, setVisibleCount] = React.useState(5); - - // Reset count when flow changes to start clean - React.useEffect(() => { - setVisibleCount(5); - }, [flow]); - - const allItems = React.useMemo(() => { - return buildLatestItems(reportData, selectedPeriodId, selectedGroupKey, flow); - }, [reportData, selectedPeriodId, selectedGroupKey, flow]); - - const visibleItems = React.useMemo(() => { - return allItems.slice(0, visibleCount); - }, [allItems, visibleCount]); - - const canExpand = visibleCount < allItems.length; - - return ( - setVisibleCount((prev) => prev + 5)} - /> - ); -} diff --git a/src/components/LatestItems/LatestItems.view.tsx b/src/components/LatestItems/LatestItems.view.tsx deleted file mode 100644 index 50b3d00..0000000 --- a/src/components/LatestItems/LatestItems.view.tsx +++ /dev/null @@ -1,93 +0,0 @@ -import * as React from "react"; -import { - List, - ListItem, - ListItemAvatar, - ListItemText, - Avatar, - Typography, - Box, - IconButton, -} from "@mui/material"; -import { alpha } from "@mui/material/styles"; -import ExpandMoreIcon from "@mui/icons-material/ExpandMore"; -import { LatestItemsViewProps } from "./LatestItems.props"; - -export default function LatestItemsView({ - items, - title, - canExpand, - onExpand, - isFetching, - colorScheme, -}: LatestItemsViewProps) { - const accentColor = colorScheme?.primary || ""; - - return ( - - - - {title} - - - - - {items.map((item, index) => ( - - - - - - - {item.title} - - } - secondary={ - - {item.subtitle} - - } - /> - - - - {item.amount} - - - {item.timeAgo} - - - - ))} - - {canExpand && ( - - - - - - )} - - - ); -} diff --git a/src/components/LatestItems/index.ts b/src/components/LatestItems/index.ts deleted file mode 100644 index 2847eeb..0000000 --- a/src/components/LatestItems/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./LatestItems"; -export * from "./LatestItems.models"; diff --git a/src/components/ProgressCard/ProgressCard.props.ts b/src/components/ProgressCard/ProgressCard.props.ts deleted file mode 100644 index 5ff8517..0000000 --- a/src/components/ProgressCard/ProgressCard.props.ts +++ /dev/null @@ -1,14 +0,0 @@ -import { ComponentProps } from "../Dashboard"; - -export interface ProgressCardProps extends ComponentProps { - settings: { - compact: boolean; - }; -} - -export interface ProgressCardViewProps extends ProgressCardProps { - progressAmount: number; - totalAmount: number; - selected: boolean; - onClick: () => void; -} diff --git a/src/components/ProgressCard/ProgressCard.view.tsx b/src/components/ProgressCard/ProgressCard.view.tsx deleted file mode 100644 index e10ed97..0000000 --- a/src/components/ProgressCard/ProgressCard.view.tsx +++ /dev/null @@ -1,129 +0,0 @@ -import * as React from "react"; -import { - Box, - Typography, - Paper, - LinearProgress, - Divider, - linearProgressClasses -} from "@mui/material"; -import { useTheme, alpha } from "@mui/material/styles"; -import { getPercentage, formatCurrency } from "../report.helpers"; -import { ProgressCardViewProps } from "./ProgressCard.props"; - -export default function ProgressCardView({ - title, - settings, - - isFetching, - - colorScheme, - - progressAmount, - totalAmount, - selected, - onClick, -}: ProgressCardViewProps) { - const theme = useTheme(); - - const percentage = getPercentage(progressAmount, totalAmount); - const formattedProgress = formatCurrency(progressAmount); - const formattedTotal = formatCurrency(totalAmount); - - return ( - - - {title} - - - - - {formattedProgress} - - - - - - of {formattedTotal} - - - - - - - - ); -} diff --git a/src/components/ProgressCard/TopPayees.adapter.ts b/src/components/ProgressCard/TopPayees.adapter.ts deleted file mode 100644 index dffbdc9..0000000 --- a/src/components/ProgressCard/TopPayees.adapter.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { GroupKey, ReportData } from "../../features/report"; -import { - extractFilteredTransactions, - aggregateTransactions, -} from "../report.helpers"; - -export interface PayeeItem { - name: string; - amount: number; -} - -export function extractTopPayees( - reportData: ReportData, - flow: "outflows" | "inflows", - selectedPeriodId?: string | null, - selectedGroupKey?: GroupKey | null -): { items: PayeeItem[]; total: number } { - const txns = extractFilteredTransactions(reportData, selectedPeriodId, selectedGroupKey); - - const { items, total } = aggregateTransactions(txns, (txn) => { - if (txn.payee && txn.payee.name) { - return [txn.payee.name]; - } - return []; - }); - - return { - items, - total, - }; -} diff --git a/src/components/ProgressCard/TopPayees.tsx b/src/components/ProgressCard/TopPayees.tsx deleted file mode 100644 index 37786d5..0000000 --- a/src/components/ProgressCard/TopPayees.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import * as React from "react"; -import { Box, Paper, Typography } from "@mui/material"; -import ProgressCardView from "./ProgressCard.view"; -import { extractTopPayees } from "./TopPayees.adapter"; -import { ProgressCardProps } from "./ProgressCard.props"; - -export default function TopPayees(props: ProgressCardProps) { - const { - title, - - reportData, - state, - stateSetters, - - isFetching, - } = props - const { flow, selectedPeriodId, selectedGroupKey } = state; - const { setSelectedGroupKey } = stateSetters; - - const { items, total } = React.useMemo(() => { - return extractTopPayees(reportData, flow, selectedPeriodId, selectedGroupKey); - }, [reportData, flow, selectedPeriodId, selectedGroupKey]); - - return ( - - - {title} - - - - {items.map((item) => { - const isSelected = !!selectedGroupKey?.payee?.includes(item.name); - return ( - { - if (setSelectedGroupKey) { - let newKey = selectedGroupKey ? { ...selectedGroupKey } : {}; - - if (isSelected) { - delete newKey.payee; - } else { - newKey.payee = [item.name]; - } - - setSelectedGroupKey(Object.keys(newKey).length ? newKey : null); - } - }} - /> - ); - })} - - - ); -} diff --git a/src/components/ProgressCard/TopTags.adapter.ts b/src/components/ProgressCard/TopTags.adapter.ts deleted file mode 100644 index 871fb94..0000000 --- a/src/components/ProgressCard/TopTags.adapter.ts +++ /dev/null @@ -1,31 +0,0 @@ -import { ReportData, GroupKey } from "../../features/report"; -import { - extractFilteredTransactions, - aggregateTransactions, -} from "../report.helpers"; - -export interface TagItem { - tag: string; - amount: number; -} - -export function extractTopTags( - reportData: ReportData, - flow: "outflows" | "inflows", - selectedPeriodId?: string | null, - selectedGroupKey?: GroupKey | null -): { items: TagItem[]; total: number } { - const txns = extractFilteredTransactions(reportData, selectedPeriodId, selectedGroupKey); - - const { items, total } = aggregateTransactions(txns, (txn) => { - if (txn.tags && txn.tags.length > 0) { - return txn.tags.map((t) => (typeof t === "string" ? t : t.name)); - } - return ["Untagged"]; - }); - - return { - items: items.map((item) => ({ tag: item.name, amount: item.amount })), - total, - }; -} diff --git a/src/components/ProgressCard/TopTags.tsx b/src/components/ProgressCard/TopTags.tsx deleted file mode 100644 index 402ba7a..0000000 --- a/src/components/ProgressCard/TopTags.tsx +++ /dev/null @@ -1,83 +0,0 @@ -import * as React from "react"; -import { Box, Paper, Typography } from "@mui/material"; -import ProgressCardView from "./ProgressCard.view"; -import { extractTopTags } from "./TopTags.adapter"; -import { ProgressCardProps } from "./ProgressCard.props"; - -export default function TopTags(props: ProgressCardProps) { - const { - title, - - reportData, - state, - stateSetters, - - isFetching, - } = props - const { flow, selectedPeriodId, selectedGroupKey } = state; - const { setSelectedGroupKey } = stateSetters; - - const { items, total } = React.useMemo(() => { - return extractTopTags(reportData, flow, selectedPeriodId, selectedGroupKey); - }, [reportData, flow, selectedPeriodId, selectedGroupKey]); - - return ( - - - {title} - - - - {items.map((item) => { - const isSelected = !!selectedGroupKey?.tags?.includes(item.tag); - return ( - { - if (setSelectedGroupKey) { - let newKey = selectedGroupKey ? { ...selectedGroupKey } : {}; - - if (isSelected) { - delete newKey.tags; - } else { - newKey.tags = [item.tag]; - } - - setSelectedGroupKey(Object.keys(newKey).length ? newKey : null); - } - }} - /> - ); - })} - - - ); -} diff --git a/src/components/ProgressCard/index.ts b/src/components/ProgressCard/index.ts deleted file mode 100644 index c2d6d76..0000000 --- a/src/components/ProgressCard/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { default } from "./ProgressCard.view"; -export * from "./ProgressCard.props"; diff --git a/src/components/report.helpers.ts b/src/components/report.helpers.ts deleted file mode 100644 index df441a2..0000000 --- a/src/components/report.helpers.ts +++ /dev/null @@ -1,230 +0,0 @@ -import { - ReportPeriod, - ReportBucket, - GroupKey, - PeriodType, - ReportData, - Transaction, -} from "../features/report"; - -// ─── Types ──────────────────────────────────────────────────── - -export type PeriodKey = PeriodType; - -export type DecoratedPeriod = ReportPeriod & { - id: string; - label: string; -}; - -// ─── Period helpers ─────────────────────────────────────────── - -const PREFIX_TO_KEY: Record = { - D: "daily", - W: "weekly", - M: "monthly", - ALL: "all", -}; - -/** - * Derive the period key from a decorated-period id. - * E.g. `"W:2026-04-28_2026-05-04"` → `"weekly"` - */ -export function periodIdToKey(periodId: string): PeriodKey { - const prefix = periodId.split(":")[0]; - return PREFIX_TO_KEY[prefix] ?? "all"; -} - -// ─── Metric helpers ─────────────────────────────────────────── - -export function getAmount(period: ReportPeriod): number { - return period.metric.sum; -} - -function mergeMetric(a: ReportPeriod["metric"], b: ReportPeriod["metric"]) { - const sum = a.sum + b.sum; - const count = a.count + b.count; - - return { - ...a, - sum, - count, - average: count > 0 ? sum / count : 0, - transactions: - a.transactions || b.transactions - ? [...(a.transactions || []), ...(b.transactions || [])] - : undefined, - }; -} - -/** - * Merge periods with the same id across all buckets, summing - * their metrics and concatenating transactions. - * - * Returns sorted by start date ascending. - */ -export function mergeBucketPeriods( - buckets: ReportBucket[], - key: PeriodKey -): DecoratedPeriod[] { - const map = new Map(); - - for (const bucket of buckets) { - const periods = (bucket.periods[key] || []) as DecoratedPeriod[]; - - for (const p of periods) { - const existing = map.get(p.id); - - if (!existing) { - map.set(p.id, { - ...p, - metric: { ...p.metric }, - }); - } else { - map.set(p.id, { - ...existing, - metric: mergeMetric(existing.metric, p.metric), - }); - } - } - } - - return Array.from(map.values()).sort( - (a, b) => new Date(a.start).getTime() - new Date(b.start).getTime() - ); -} - -// ─── Formatting ─────────────────────────────────────────────── - -export const formatCurrency = (val: number) => { - const absVal = Math.abs(val); - if (absVal >= 100000) { - return `₹ ${(val / 100000).toFixed(2)}L`; - } - if (absVal >= 1000) { - return `₹ ${(val / 1000).toFixed(2)}k`; - } - return `₹ ${val.toFixed(2)}`; -}; - -export const getPercentage = (progressAmount: number, totalAmount: number) => { - if (!totalAmount) return 0; - return Math.min(100, Math.max(0, (progressAmount / totalAmount) * 100)); -}; - -// ─── Group filtering ────────────────────────────────────────── - -/** - * Check if a bucket's group_key matches the selected GroupKey. - * Every dimension present in `selected` must exist in the bucket - * and contain all the selected values. - */ -export function matchesGroupKey( - bucket: ReportBucket, - selected: GroupKey -): boolean { - for (const [dim, values] of Object.entries(selected)) { - const bucketValues = bucket.group_key[dim]; - if (!bucketValues) return false; - if (!(values as string[]).every((v) => bucketValues.includes(v))) - return false; - } - return true; -} - -/** - * Return only buckets matching the selected group key, - * or all buckets if no selection. - */ -export function filterBuckets( - buckets: ReportBucket[], - selectedGroupKey: GroupKey | null -): ReportBucket[] { - if (!selectedGroupKey) return buckets; - return buckets.filter((b) => matchesGroupKey(b, selectedGroupKey)); -} - -export function extractFilteredTransactions( - reportData: ReportData, - selectedPeriodId: string | null | undefined, - selectedGroupKey: GroupKey | null | undefined -): Transaction[] { - let txns: Transaction[] = []; - - if (selectedPeriodId) { - const key = periodIdToKey(selectedPeriodId); - const periods = mergeBucketPeriods(reportData.buckets, key); - const selected = periods.find((p) => p.id === selectedPeriodId); - txns = selected?.metric.transactions || []; - } else { - const periods = mergeBucketPeriods(reportData.buckets, "all"); - if (periods.length > 0) { - const period = periods.reduce((latest, p) => - new Date(p.start).getTime() > new Date(latest.start).getTime() - ? p - : latest - , periods[0]); - txns = period?.metric.transactions || []; - } - } - - if (selectedGroupKey) { - txns = txns.filter((txn) => { - let match = true; - if (selectedGroupKey.tags && selectedGroupKey.tags.length > 0) { - if (!txn.tags) { - match = false; - } else { - const txnTags = txn.tags.map((t: any) => - typeof t === "string" ? t : t.name - ); - if ( - !selectedGroupKey.tags.every((selectedTag) => - txnTags.includes(selectedTag) - ) - ) { - match = false; - } - } - } - if (match && selectedGroupKey.payee && selectedGroupKey.payee.length > 0) { - if (!txn.payee || !txn.payee.name) { - match = false; - } else { - if (!selectedGroupKey.payee.includes(txn.payee.name)) { - match = false; - } - } - } - return match; - }); - } - - return txns; -} - -export function aggregateTransactions( - transactions: Transaction[], - keyExtractor: (txn: Transaction) => string[], - limit = 4 -): { items: { name: string; amount: number }[]; total: number } { - const map = new Map(); - - for (const txn of transactions) { - const keys = keyExtractor(txn); - for (const key of keys) { - map.set(key, (map.get(key) || 0) + txn.amount); - } - } - - const items = Array.from(map.entries()).map(([name, amount]) => ({ - name, - amount, - })); - - items.sort((a, b) => b.amount - a.amount); - - const top = items.slice(0, limit); - const total = top.reduce((sum, item) => sum + item.amount, 0); - - return { items: top, total }; -} diff --git a/src/dashboard-config.ts b/src/dashboard-config.ts deleted file mode 100644 index af42bad..0000000 --- a/src/dashboard-config.ts +++ /dev/null @@ -1,40 +0,0 @@ -import HistoryChart from "./components/HistoryChart"; -import LatestItems from "./components/LatestItems"; -import { DashboardConfig } from "./components/Dashboard"; -import TopTags from "./components/ProgressCard/TopTags"; -import TopPayees from "./components/ProgressCard/TopPayees"; - -export const configuration: DashboardConfig = { - sections: [ - { - id: "breakdown", - title: "Breakdown", - summary: "Interactive chronological tracking", - component: HistoryChart, - settings: { - tabs: ["Weekly", "Monthly"], - }, - }, - { - id: "top-categories", - title: 'Top Categories', - component: TopTags, - settings: { - compact: true, - }, - }, - { - id: "top-payees", - title: 'Top Payees', - component: TopPayees, - settings: { - compact: true, - }, - }, - { - id: "items", - title: 'Recent Transactions', - component: LatestItems, - }, - ], -}; diff --git a/src/features/report-snapshots/index.ts b/src/features/report-snapshots/index.ts deleted file mode 100644 index 65c0011..0000000 --- a/src/features/report-snapshots/index.ts +++ /dev/null @@ -1,9 +0,0 @@ -export type { - ReportSnapshot, - ReportQuery, -} from "./report-snapshots.models"; -export { - useReportSnapshotsList, - useCreateSnapshot, - useDeleteSnapshot, -} from "./useReportSnapshots"; \ No newline at end of file diff --git a/src/features/report-snapshots/report-snapshots.models.ts b/src/features/report-snapshots/report-snapshots.models.ts deleted file mode 100644 index 4bf5698..0000000 --- a/src/features/report-snapshots/report-snapshots.models.ts +++ /dev/null @@ -1,15 +0,0 @@ -export interface ReportQuery { - accounts?: string[] | null; - ignore_self?: boolean | null; - start_date?: string | null; - end_date?: string | null; - min_amount?: number | null; - max_amount?: number | null; -} - -export interface ReportSnapshot { - id: string; - snapshot_id: string; - created_at: string; - query?: ReportQuery; -} diff --git a/src/features/report-snapshots/useReportSnapshots.ts b/src/features/report-snapshots/useReportSnapshots.ts deleted file mode 100644 index 668d1a1..0000000 --- a/src/features/report-snapshots/useReportSnapshots.ts +++ /dev/null @@ -1,28 +0,0 @@ -import { useResource } from "../../../react-openapi"; -import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; - -export function useReportSnapshotsList() { - const { list } = useResource("reports"); - return useQuery({ - queryKey: ["reports", "list"], - queryFn: () => list(), - }); -} - -export function useCreateSnapshot() { - const { create } = useResource("reports"); - return useMutation({ - mutationFn: (data: any) => create(data), - }); -} - -export function useDeleteSnapshot() { - const queryClient = useQueryClient(); - const { remove } = useResource("reports"); - return useMutation({ - mutationFn: (id: string) => remove(id), - onSuccess: () => { - queryClient.invalidateQueries({ queryKey: ["reports", "list"] }); - }, - }); -} diff --git a/src/features/report/index.ts b/src/features/report/index.ts deleted file mode 100644 index 851610e..0000000 --- a/src/features/report/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -export { - useReport -} from './useReport' -export type { - Transaction, - ReportData, - ReportBucket, - ReportPeriod, - ReportQuery, - GroupKey, - PeriodType, -} from './report.models' -export { - prepareReport -} from './report.utils' diff --git a/src/features/report/report.models.ts b/src/features/report/report.models.ts deleted file mode 100644 index 4f3ee20..0000000 --- a/src/features/report/report.models.ts +++ /dev/null @@ -1,112 +0,0 @@ -export interface Payor { - id?: string; - name: string; - username: string; - email: string; -} - -export interface Payee { - type: "merchant" | "person" | "transfer" | "other"; - name: string; -} - -export interface Account { - id: string; - name: string; - number: string; - type: "cash" | "bank" | "credit_card" | "wallet" | "other"; - currency: string; - is_active?: boolean; -} - -export interface Tag { - id: string; - name: string; - icon: string; - parent_id?: string | null; -} - -export interface Transaction { - id: string; - payor: Payor; - payee: Payee; - amount: number; - account: Account; - tags: Tag[]; - occurred_at: string; - created_at: string; -} - -// ----------------------------- -// Metrics -// ----------------------------- - -export interface ReportMetric { - sum: number; - count: number; - average: number; - transactions?: Transaction[]; -} - -// ----------------------------- -// Period -// ----------------------------- - -export type PeriodType = "daily" | "weekly" | "monthly" | "all"; - -export interface ReportPeriod { - start: string; - end: string; - metric: ReportMetric; -} - -// ----------------------------- -// Group (bucket) -// ----------------------------- - -export type GroupKey = { - [dimension: string]: string[]; -}; - -export interface ReportBucket { - group_key: GroupKey; - - periods: { - daily?: ReportPeriod[]; - weekly?: ReportPeriod[]; - monthly?: ReportPeriod[]; - all?: ReportPeriod[]; - }; -} - -// ----------------------------- -// Report Query -// ----------------------------- - -export interface ReportQuery { - accounts?: string[] | null; - ignore_self?: boolean | null; - start_date?: string | null; - end_date?: string | null; - min_amount?: number | null; - max_amount?: number | null; -} - -// ----------------------------- -// Final Report -// ----------------------------- - -export interface ReportData { - snapshot_id?: string | null; - - flow?: "inflows" | "outflows" | null; - - periods: PeriodType[]; - - tags?: string[] | null; - payee?: string[] | null; - - buckets: ReportBucket[]; - - query: ReportQuery; -} diff --git a/src/features/report/report.utils.ts b/src/features/report/report.utils.ts deleted file mode 100644 index 81dd2a5..0000000 --- a/src/features/report/report.utils.ts +++ /dev/null @@ -1,117 +0,0 @@ -import { - ReportData, - ReportPeriod, - PeriodType, -} from "./report.models"; - -/* ---------- ID BUILDING ---------- */ - -function formatDate(d: Date): string { - const y = d.getUTCFullYear(); - const m = String(d.getUTCMonth() + 1).padStart(2, "0"); - const day = String(d.getUTCDate()).padStart(2, "0"); - return `${y}-${m}-${day}`; -} - -function buildPeriodId( - type: PeriodType, - start: Date, - end: Date -): string { - const s = formatDate(start); - const e = formatDate(end); - - switch (type) { - case "daily": - return `D:${s}_${e}`; - case "weekly": - return `W:${s}_${e}`; - case "monthly": - return `M:${s}_${e}`; - case "all": - return `ALL:${s}_${e}`; - default: - return `${s}_${e}`; - } -} - -/* ---------- LABEL BUILDING ---------- */ - -const dayFmt = new Intl.DateTimeFormat("en-GB", { - day: "numeric", - month: "short", - timeZone: "UTC", -}); - -const monthDayFmt = new Intl.DateTimeFormat("en-GB", { - month: "short", - day: "numeric", - timeZone: "UTC", -}); - -const monthFmt = new Intl.DateTimeFormat("en-GB", { - month: "short", - timeZone: "UTC", -}); - -const yearFmt = new Intl.DateTimeFormat("en-GB", { - year: "numeric", - timeZone: "UTC", -}); - -function buildLabel( - type: PeriodType, - start: Date, - end: Date -): string { - switch (type) { - case "daily": - return dayFmt.format(start); - - case "weekly": { - const sDay = start.getUTCDate(); - const m = monthFmt.format(start); - return `${sDay} ${m}`; - } - - case "monthly": - return `${monthFmt.format(start)} ${yearFmt.format(start)}`; - - default: - return `${monthDayFmt.format(start)} - ${monthDayFmt.format(end)}`; - } -} - -/* ---------- MAIN ---------- */ - -function decoratePeriods( - type: PeriodType, - periods: ReportPeriod[] -): (ReportPeriod & { id: string; label: string })[] { - return periods.map((p) => ({ - ...p, - id: buildPeriodId(type, new Date(p.start + "Z"), new Date(p.end + "Z")), - label: buildLabel(type, new Date(p.start + "Z"), new Date(p.end + "Z")), - })); -} - -export function prepareReport(reportData: ReportData): ReportData { - return { - ...reportData, - buckets: reportData.buckets.map((bucket) => { - const newPeriods: typeof bucket.periods = {}; - - for (const type of reportData.periods) { - const arr = bucket.periods[type]; - if (arr) { - newPeriods[type] = decoratePeriods(type, arr); - } - } - - return { - ...bucket, - periods: newPeriods, - }; - }), - }; -} \ No newline at end of file diff --git a/src/features/report/useReport.ts b/src/features/report/useReport.ts deleted file mode 100644 index b7b24f5..0000000 --- a/src/features/report/useReport.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { useResource } from "../../../react-openapi"; -import { useQuery } from "@tanstack/react-query"; - -export interface ReportParams { - snapshot_id?: string; - periods?: ("daily" | "weekly" | "monthly" | "all")[]; - flow?: "inflows" | "outflows"; - payee?: string[]; - tags?: string[]; -} - -export function useReport(params: ReportParams) { - const { get } = useResource("reports"); - - const { snapshot_id, ...queryParams } = params; - - return useQuery({ - queryKey: ["reports", "read", params], - queryFn: () => - get(snapshot_id ?? "latest", queryParams), - }); -} diff --git a/src/main.jsx b/src/main.jsx index 3859673..bb1a06d 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -12,10 +12,8 @@ import { Toolbar } from "@mui/material"; import Home from './Home'; -import Dashboard from './Dashboard'; import FetchRequests from './FetchRequests'; import FetchRequestDetail from './FetchRequestDetail'; -import ReportSnapshots from './ReportSnapshots'; import { RequireAuth } from './RequireAuth'; import { AppProvider, Admin } from '../react-openapi'; import { Buffer } from 'buffer'; @@ -39,10 +37,8 @@ const AUTH_BASE = import.meta.env.VITE_AUTH_BASE_URL; const routerMapping = [ { path: "/", component: Home, headerTitle: "Home" }, { path: "/home", component: Home, headerTitle: "Home" }, - { path: "/dashboard", component: Dashboard, headerTitle: "Dashboard" }, { path: "/fetch-requests", component: FetchRequests, headerTitle: "Fetch Requests" }, { path: "/fetch-requests/:id", component: FetchRequestDetail, headerTitle: "Fetch Request" }, - { path: "/reports", component: ReportSnapshots, headerTitle: "Reports" }, { path: "/admin/*", component: Admin, headerTitle: "Admin" }, ];