♻️ custom reports - moving to url identifiers (#3744)

This commit is contained in:
Matiss Janis Aboltins
2024-11-04 17:56:14 +00:00
committed by GitHub
parent 29fc22a171
commit b08756cc39
9 changed files with 108 additions and 59 deletions

View File

@@ -6,8 +6,6 @@ import React, {
type CSSProperties,
} from 'react';
import { type CustomReportEntity } from 'loot-core/src/types/models';
import { useIsInViewport } from '../../hooks/useIsInViewport';
import { useNavigate } from '../../hooks/useNavigate';
import { useResponsive } from '../../ResponsiveProvider';
@@ -23,7 +21,6 @@ type ReportCardProps = {
isEditing?: boolean;
to?: string;
children: ReactNode;
report?: CustomReportEntity;
menuItems?: ComponentProps<typeof Menu>['items'];
onMenuSelect?: ComponentProps<typeof Menu>['onMenuSelect'];
size?: number;
@@ -33,7 +30,6 @@ type ReportCardProps = {
export function ReportCard({
isEditing,
to,
report,
menuItems,
onMenuSelect,
children,
@@ -99,9 +95,7 @@ export function ReportCard({
<Layout {...layoutProps}>
<View
role="button"
onClick={
isEditing ? undefined : () => navigate(to, { state: { report } })
}
onClick={isEditing ? undefined : () => navigate(to)}
style={{
height: '100%',
width: '100%',

View File

@@ -16,6 +16,7 @@ export function ReportRouter() {
<Route path="/cash-flow" element={<CashFlow />} />
<Route path="/cash-flow/:id" element={<CashFlow />} />
<Route path="/custom" element={<CustomReport />} />
<Route path="/custom/:id" element={<CustomReport />} />
<Route path="/spending" element={<Spending />} />
<Route path="/spending/:id" element={<Spending />} />
</Routes>

View File

@@ -51,13 +51,7 @@ type ReportSidebarProps = {
dateEnd: string,
mode: TimeFrame['mode'],
) => void;
onReportChange: ({
savedReport,
type,
}: {
savedReport?: CustomReportEntity;
type: string;
}) => void;
onReportChange: ({ type }: { type: 'modify' }) => void;
disabledItems: (type: string) => string[];
defaultItems: (item: string) => void;
defaultModeItems: (graph: string, item: string) => void;

View File

@@ -1,4 +1,4 @@
import React from 'react';
import React, { type ComponentProps } from 'react';
import { type CustomReportEntity } from 'loot-core/types/models/reports';
import { type RuleConditionEntity } from 'loot-core/types/models/rule';
@@ -32,13 +32,7 @@ type ReportTopbarProps = {
viewLabels: boolean;
onApplyFilter: (newFilter: RuleConditionEntity) => void;
onChangeViews: (viewType: string) => void;
onReportChange: ({
savedReport,
type,
}: {
savedReport?: CustomReportEntity;
type: string;
}) => void;
onReportChange: ComponentProps<typeof SaveReport>['onReportChange'];
isItemDisabled: (type: string) => boolean;
defaultItems: (item: string) => void;
};

View File

@@ -19,13 +19,30 @@ type SaveReportProps<T extends CustomReportEntity = CustomReportEntity> = {
customReportItems: T;
report: CustomReportEntity;
savedStatus: string;
onReportChange: ({
savedReport,
type,
}: {
savedReport?: T;
type: string;
}) => void;
onReportChange: (
params:
| {
type: 'add-update';
savedReport: CustomReportEntity;
}
| {
type: 'rename';
savedReport?: CustomReportEntity;
}
| {
type: 'modify';
}
| {
type: 'reload';
}
| {
type: 'reset';
}
| {
type: 'choose';
savedReport?: CustomReportEntity;
},
) => void;
};
export function SaveReport({

View File

@@ -1,9 +1,10 @@
import React, { useState, useEffect, useMemo } from 'react';
import { useTranslation } from 'react-i18next';
import { useLocation } from 'react-router-dom';
import { useLocation, useParams } from 'react-router-dom';
import * as d from 'date-fns';
import { useReport as useCustomReport } from 'loot-core/src/client/data-hooks/reports';
import { calculateHasWarning } from 'loot-core/src/client/reports';
import { send } from 'loot-core/src/platform/client/fetch';
import * as monthUtils from 'loot-core/src/shared/months';
@@ -103,6 +104,21 @@ function useSelectedCategories(
}
export function CustomReport() {
const params = useParams();
const { data: report, isLoading } = useCustomReport(params.id ?? '');
if (isLoading) {
return <LoadingIndicator />;
}
return <CustomReportInner key={report?.id} report={report} />;
}
type CustomReportInnerProps = {
report?: CustomReportEntity;
};
function CustomReportInner({ report: initialReport }: CustomReportInnerProps) {
const { t } = useTranslation();
const categories = useCategories();
const { isNarrowWidth } = useResponsive();
@@ -138,9 +154,7 @@ export function CustomReport() {
const session = reportFromSessionStorage
? JSON.parse(reportFromSessionStorage)
: {};
const combine = location.state
? (location.state.report ?? defaultReport)
: defaultReport;
const combine = initialReport ?? defaultReport;
const loadReport = { ...combine, ...session };
const [allIntervals, setAllIntervals] = useState<
@@ -243,17 +257,13 @@ export function CustomReport() {
const [earliestTransaction, setEarliestTransaction] = useState('');
const [report, setReport] = useState(loadReport);
const [savedStatus, setSavedStatus] = useState(
location.state
? location.state.report
? 'saved'
: (loadReport.savedStatus ?? 'new')
: (loadReport.savedStatus ?? 'new'),
session.savedStatus ?? (initialReport ? 'saved' : 'new'),
);
useEffect(() => {
async function run() {
onApplyFilter(null);
report.conditions.forEach((condition: RuleConditionEntity) =>
report.conditions?.forEach((condition: RuleConditionEntity) =>
onApplyFilter(condition),
);
const trans = await send('get-earliest-transaction');
@@ -473,11 +483,10 @@ export function CustomReport() {
const defaultModeItems = (graph: string, item: string) => {
const chooseGraph = graph || graphType;
const newGraph = (disabledList.modeGraphsMap.get(item) || []).includes(
chooseGraph,
)
? defaultsList.modeGraphsMap.get(item)
: chooseGraph;
const newGraph =
((disabledList.modeGraphsMap.get(item) || []).includes(chooseGraph)
? defaultsList.modeGraphsMap.get(item)
: chooseGraph) ?? chooseGraph;
if ((disabledList.modeGraphsMap.get(item) || []).includes(graphType)) {
setSessionReport('graphType', newGraph);
setGraphType(newGraph);
@@ -594,21 +603,43 @@ export function CustomReport() {
onConditionsOpChange(input.conditionsOp);
};
const onReportChange = ({
savedReport,
type,
}: {
savedReport?: CustomReportEntity;
type: string;
}) => {
switch (type) {
const onReportChange = (
params:
| {
type: 'add-update';
savedReport: CustomReportEntity;
}
| {
type: 'rename';
savedReport?: CustomReportEntity;
}
| {
type: 'modify';
}
| {
type: 'reload';
}
| {
type: 'reset';
}
| {
type: 'choose';
savedReport?: CustomReportEntity;
},
) => {
switch (params.type) {
case 'add-update':
sessionStorage.clear();
setSessionReport('savedStatus', 'saved');
setSavedStatus('saved');
setReport(savedReport);
setReport(params.savedReport);
if (params.savedReport.id !== initialReport?.id) {
navigate(`/reports/custom/${params.savedReport.id}`);
}
break;
case 'rename':
setReport({ ...report, name: savedReport?.name || '' });
setReport({ ...report, name: params.savedReport?.name || '' });
break;
case 'modify':
if (report.name) {
@@ -617,9 +648,10 @@ export function CustomReport() {
}
break;
case 'reload':
sessionStorage.clear();
setSessionReport('savedStatus', 'saved');
setSavedStatus('saved');
setReportData(report);
setReportData(initialReport ?? defaultReport);
break;
case 'reset':
sessionStorage.clear();
@@ -628,10 +660,13 @@ export function CustomReport() {
setReportData(defaultReport);
break;
case 'choose':
sessionStorage.clear();
const newReport = params.savedReport || report;
setSessionReport('savedStatus', 'saved');
setSavedStatus('saved');
setReport(savedReport);
setReportData(savedReport || report);
setReport(newReport);
setReportData(newReport);
navigate(`/reports/custom/${newReport.id}`);
break;
default:
}

View File

@@ -119,8 +119,7 @@ function CustomReportListCardsInner({
return (
<ReportCard
isEditing={isEditing}
to="/reports/custom"
report={report}
to={`/reports/custom/${report.id}`}
menuItems={[
{
name: 'rename',

View File

@@ -60,3 +60,12 @@ export function useReports() {
[queryData],
);
}
export function useReport(id: string) {
const { data, isLoading } = useReports();
return {
data: data.find(report => report.id === id),
isLoading,
};
}

View File

@@ -0,0 +1,6 @@
---
category: Maintenance
authors: [MatissJanis]
---
Custom reports: moving from session storage and local state for inner report pages to using unique URL identifiers for each custom report page.