mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-11 12:43:09 -05:00
♻️ custom reports - moving to url identifiers (#3744)
This commit is contained in:
committed by
GitHub
parent
29fc22a171
commit
b08756cc39
@@ -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%',
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -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:
|
||||
}
|
||||
|
||||
@@ -119,8 +119,7 @@ function CustomReportListCardsInner({
|
||||
return (
|
||||
<ReportCard
|
||||
isEditing={isEditing}
|
||||
to="/reports/custom"
|
||||
report={report}
|
||||
to={`/reports/custom/${report.id}`}
|
||||
menuItems={[
|
||||
{
|
||||
name: 'rename',
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
}
|
||||
|
||||
6
upcoming-release-notes/3744.md
Normal file
6
upcoming-release-notes/3744.md
Normal 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.
|
||||
Reference in New Issue
Block a user