From afa1c4b81d4f773d703fb9ed0f89a45299c5154f Mon Sep 17 00:00:00 2001 From: carkom Date: Thu, 18 Jan 2024 13:25:55 +0000 Subject: [PATCH 01/17] Add schema work --- .../src/components/common/AnchorLink.tsx | 5 + .../src/components/reports/Overview.jsx | 5 +- .../src/components/reports/ReportCard.tsx | 26 +++- .../src/components/reports/ReportOptions.ts | 22 ++++ .../reports/reports/CustomReport.jsx | 33 +++--- .../reports/reports/CustomReportCard.jsx | 4 +- .../src/client/data-hooks/reports.ts | 44 +++++++ .../loot-core/src/server/aql/schema/index.ts | 20 ++++ packages/loot-core/src/server/main.ts | 11 +- packages/loot-core/src/server/reports/app.ts | 111 ++++++++++++++++++ .../src/server/reports/types/handlers.ts | 9 ++ .../loot-core/src/types/models/index.d.ts | 1 + .../loot-core/src/types/models/reports.d.ts | 28 +++++ 13 files changed, 296 insertions(+), 23 deletions(-) create mode 100644 packages/loot-core/src/client/data-hooks/reports.ts create mode 100644 packages/loot-core/src/server/reports/app.ts create mode 100644 packages/loot-core/src/server/reports/types/handlers.ts create mode 100644 packages/loot-core/src/types/models/reports.d.ts diff --git a/packages/desktop-client/src/components/common/AnchorLink.tsx b/packages/desktop-client/src/components/common/AnchorLink.tsx index 5defecba686..ed596b00d28 100644 --- a/packages/desktop-client/src/components/common/AnchorLink.tsx +++ b/packages/desktop-client/src/components/common/AnchorLink.tsx @@ -3,6 +3,8 @@ import { NavLink, useMatch } from 'react-router-dom'; import { css } from 'glamor'; +import { type CustomReportEntity } from 'loot-core/src/types/models'; + import { type CSSProperties, styles } from '../../style'; type AnchorLinkProps = { @@ -10,6 +12,7 @@ type AnchorLinkProps = { style?: CSSProperties; activeStyle?: CSSProperties; children?: ReactNode; + report?: CustomReportEntity; }; export function AnchorLink({ @@ -17,12 +20,14 @@ export function AnchorLink({ style, activeStyle, children, + report, }: AnchorLinkProps) { const match = useMatch({ path: to }); return ( } {sankeyFeatureFlag && } {customReportsFeatureFlag ? ( - + ) : (
)} diff --git a/packages/desktop-client/src/components/reports/ReportCard.tsx b/packages/desktop-client/src/components/reports/ReportCard.tsx index db8b6b2429e..ad9fe83d189 100644 --- a/packages/desktop-client/src/components/reports/ReportCard.tsx +++ b/packages/desktop-client/src/components/reports/ReportCard.tsx @@ -1,11 +1,26 @@ -// @ts-strict-ignore -import React from 'react'; +import React, { type ReactNode } from 'react'; -import { theme } from '../../style'; +import { type CustomReportEntity } from 'loot-core/src/types/models'; + +import { type CSSProperties, theme } from '../../style'; import { AnchorLink } from '../common/AnchorLink'; import { View } from '../common/View'; -export function ReportCard({ flex, to, style, children }) { +type ReportCardProps = { + to: string; + report: CustomReportEntity; + children: ReactNode; + flex?: string; + style?: CSSProperties; +}; + +export function ReportCard({ + to, + report, + children, + flex, + style, +}: ReportCardProps) { const containerProps = { flex, margin: 15 }; const content = ( @@ -34,7 +49,8 @@ export function ReportCard({ flex, to, style, children }) { return ( {content} diff --git a/packages/desktop-client/src/components/reports/ReportOptions.ts b/packages/desktop-client/src/components/reports/ReportOptions.ts index f32dbe5e1fe..097d038fcf6 100644 --- a/packages/desktop-client/src/components/reports/ReportOptions.ts +++ b/packages/desktop-client/src/components/reports/ReportOptions.ts @@ -1,11 +1,33 @@ // @ts-strict-ignore +import * as monthUtils from 'loot-core/src/shared/months'; import { + type CustomReportEntity, type AccountEntity, type CategoryEntity, type CategoryGroupEntity, type PayeeEntity, } from 'loot-core/src/types/models'; +const startDate = monthUtils.subMonths(monthUtils.currentMonth(), 5); +const endDate = monthUtils.currentMonth(); + +export const defaultState: CustomReportEntity = { + id: null, + mode: 'total', + groupBy: 'Category', + balanceType: 'Payment', + showEmpty: false, + showOffBudgetHidden: false, + showUncategorized: false, + graphType: 'BarGraph', + startDate, + endDate, + selectedCategories: null, + isDateStatic: false, + conditionsOp: 'and', + name: 'Default', +}; + const balanceTypeOptions = [ { description: 'Payment', format: 'totalDebts' as const }, { description: 'Deposit', format: 'totalAssets' as const }, diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx index a5a149c93fa..7609c580e07 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx @@ -23,7 +23,7 @@ import { ChooseGraph } from '../ChooseGraph'; import { Header } from '../Header'; import { LoadingIndicator } from '../LoadingIndicator'; import { ReportLegend } from '../ReportLegend'; -import { ReportOptions } from '../ReportOptions'; +import { ReportOptions, defaultState } from '../ReportOptions'; import { ReportSidebar } from '../ReportSidebar'; import { ReportSummary } from '../ReportSummary'; import { ReportTopbar } from '../ReportTopbar'; @@ -31,6 +31,7 @@ import { createCustomSpreadsheet } from '../spreadsheets/custom-spreadsheet'; import { createGroupedSpreadsheet } from '../spreadsheets/grouped-spreadsheet'; import { useReport } from '../useReport'; import { fromDateRepr } from '../util'; +import { useLocation } from 'react-router-dom'; export function CustomReport() { const categories = useCategories(); @@ -52,26 +53,30 @@ export function CustomReport() { onCondOpChange, } = useFilters(); - const [selectedCategories, setSelectedCategories] = useState(null); + const location = useLocation(); + const loadReport = location.state.report ?? defaultState; + const [allMonths, setAllMonths] = useState(null); const [typeDisabled, setTypeDisabled] = useState(['Net']); - const [startDate, setStartDate] = useState( - monthUtils.subMonths(monthUtils.currentMonth(), 5), + + const [selectedCategories, setSelectedCategories] = useState( + loadReport.selectedCategories, ); - const [endDate, setEndDate] = useState(monthUtils.currentMonth()); + const [startDate, setStartDate] = useState(loadReport.startDate); + const [endDate, setEndDate] = useState(loadReport.endDate); + const [mode, setMode] = useState(loadReport.mode); + const [isDateStatic, setIsDateStatic] = useState(loadReport.isDateStatic); + const [groupBy, setGroupBy] = useState(loadReport.groupBy); + const [balanceType, setBalanceType] = useState(loadReport.balanceType); + const [showEmpty, setShowEmpty] = useState(loadReport.showEmpty); + const [showOffBudgetHidden, setShowOffBudgetHidden] = useState(loadReport.showOffBudgetHidden); + const [showUncategorized, setShowUncategorized] = useState(loadReport.showUncategorized); + const [graphType, setGraphType] = useState(loadReport.graphType); - const [mode, setMode] = useState('total'); - const [isDateStatic, setIsDateStatic] = useState(false); - const [groupBy, setGroupBy] = useState('Category'); - const [balanceType, setBalanceType] = useState('Payment'); - const [showEmpty, setShowEmpty] = useState(false); - const [showOffBudgetHidden, setShowOffBudgetHidden] = useState(false); - const [showUncategorized, setShowUncategorized] = useState(false); const [dateRange, setDateRange] = useState('Last 6 months'); const [dataCheck, setDataCheck] = useState(false); - - const [graphType, setGraphType] = useState('BarGraph'); const dateRangeLine = ReportOptions.dateRange.length - 3; + const months = monthUtils.rangeInclusive(startDate, endDate); useEffect(() => { diff --git a/packages/desktop-client/src/components/reports/reports/CustomReportCard.jsx b/packages/desktop-client/src/components/reports/reports/CustomReportCard.jsx index 7269529b4e2..6a1e6457262 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReportCard.jsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReportCard.jsx @@ -13,7 +13,7 @@ import { ReportCard } from '../ReportCard'; import { createCustomSpreadsheet } from '../spreadsheets/custom-spreadsheet'; import { useReport } from '../useReport'; -export function CustomReportCard() { +export function CustomReportCard(reports) { const categories = useCategories(); const endDate = monthUtils.currentMonth(); @@ -32,7 +32,7 @@ export function CustomReportCard() { const data = useReport('default', getGraphData); return ( - + diff --git a/packages/loot-core/src/client/data-hooks/reports.ts b/packages/loot-core/src/client/data-hooks/reports.ts new file mode 100644 index 00000000000..b12b892fd14 --- /dev/null +++ b/packages/loot-core/src/client/data-hooks/reports.ts @@ -0,0 +1,44 @@ +import { useMemo } from 'react'; + +import { q } from '../../shared/query'; +import { + type CustomReportData, + type CustomReportEntity, +} from '../../types/models'; +import { useLiveQuery } from '../query-hooks'; + +function toJS(rows: CustomReportData[]) { + const reports: CustomReportEntity[] = rows.map(row => { + const test: CustomReportEntity = { + ...row, + conditionsOp: row.conditions_op ?? 'and', + filters: row.conditions, + }; + return test; + }); + return reports; +} + +/* +leaving as a placeholder for saved reports implementation return an empty array because "reports" db table doesn't exist yet +*/ +export function useReports(): CustomReportEntity[] { + const reports: CustomReportEntity[] = toJS( + //useLiveQuery(() => q('reports').select('*'), []) || [], + useLiveQuery(() => q('transaction_filters').select('*'), []) || [], + ); + + /** Sort reports by alphabetical order */ + function sort(reports: CustomReportEntity[]) { + return reports.sort((a, b) => + a.name + .trim() + .localeCompare(b.name.trim(), undefined, { ignorePunctuation: true }), + ); + } + + const flag = true; + const order: CustomReportEntity[] = useMemo(() => sort(reports), [reports]); + const emptyReports: CustomReportEntity[] = flag ? [] : order; + return emptyReports; +} diff --git a/packages/loot-core/src/server/aql/schema/index.ts b/packages/loot-core/src/server/aql/schema/index.ts index 005eada6860..9d893e6b3da 100644 --- a/packages/loot-core/src/server/aql/schema/index.ts +++ b/packages/loot-core/src/server/aql/schema/index.ts @@ -125,6 +125,26 @@ export const schema = { conditions: f('json'), tombstone: f('boolean'), }, + custom_reports: { + id: f('id'), + name: f('string'), + start_date: f('string', { default: '2023-06' }), + end_date: f('string', { default: '2023-09' }), + mode: f('string', { default: 'total' }), + group_by: f('string', { default: 'Category' }), + balance_type: f('string', { default: 'Expense' }), + interval: f('string', { default: 'Monthly' }), + show_empty: f('integer', { default: 0 }), + show_offbudgethidden: f('integer', { default: 0 }), + show_uncategorized: f('integer', { default: 0 }), + selected_categories: f('json'), + graph_type: f('string', { default: 'BarGraph' }), + conditions: f('json'), + conditions_op: f('string'), + metadata: f('json'), + color_scheme: f('json'), + tombstone: f('boolean'), + }, reflect_budgets: { id: f('id'), month: f('integer'), diff --git a/packages/loot-core/src/server/main.ts b/packages/loot-core/src/server/main.ts index 3a80926cdc8..7911ab2a3b2 100644 --- a/packages/loot-core/src/server/main.ts +++ b/packages/loot-core/src/server/main.ts @@ -48,6 +48,7 @@ import { app as notesApp } from './notes/app'; import * as Platform from './platform'; import { get, post } from './post'; import * as prefs from './prefs'; +import { app as reportsApp } from './reports/app'; import { app as rulesApp } from './rules/app'; import { app as schedulesApp } from './schedules/app'; import { getServer, setServer } from './server-config'; @@ -2147,7 +2148,15 @@ injectAPI.override((name, args) => runHandler(app.handlers[name], args)); // A hack for now until we clean up everything app.handlers = handlers; -app.combine(schedulesApp, budgetApp, notesApp, toolsApp, filtersApp, rulesApp); +app.combine( + schedulesApp, + budgetApp, + notesApp, + toolsApp, + filtersApp, + reportsApp, + rulesApp, +); function getDefaultDocumentDir() { if (Platform.isMobile) { diff --git a/packages/loot-core/src/server/reports/app.ts b/packages/loot-core/src/server/reports/app.ts new file mode 100644 index 00000000000..0722b98385d --- /dev/null +++ b/packages/loot-core/src/server/reports/app.ts @@ -0,0 +1,111 @@ +import { v4 as uuidv4 } from 'uuid'; + +import { + type CustomReportData, + type CustomReportEntity, +} from '../../types/models'; +import { parseConditionsOrActions } from '../accounts/transaction-rules'; +import { createApp } from '../app'; +import * as db from '../db'; +import { requiredFields } from '../models'; +import { mutator } from '../mutators'; +import { undoable } from '../undo'; + +import { ReportsHandlers } from './types/handlers'; + +const reportModel = { + validate(report: CustomReportEntity, { update }: { update?: boolean } = {}) { + requiredFields('reports', report, ['conditions'], update); + + if (!update || 'conditionsOp' in report) { + if (!['and', 'or'].includes(report.conditionsOp)) { + throw new Error('Invalid filter conditionsOp: ' + report.conditionsOp); + } + } + + return report; + }, + + toJS(row: CustomReportData) { + return { + ...row, + conditionsOp: row.conditions_op, + filters: parseConditionsOrActions(row.conditions), + }; + }, + + fromJS(report: CustomReportEntity) { + const { filters, conditionsOp, ...row }: CustomReportData = report; + if (conditionsOp) { + row.conditions_op = conditionsOp; + row.conditions = filters; + } + return row; + }, +}; + +async function reportNameExists( + name: string, + reportId: string, + newItem: boolean, +) { + const idForName: { id: string } = await db.first( + 'SELECT id from reports WHERE tombstone = 0 AND name = ?', + [name], + ); + + if (idForName === null) { + return false; + } + if (!newItem) { + return idForName.id !== reportId; + } + return true; +} + +async function createReport(report: CustomReportEntity) { + const reportId = uuidv4(); + const item: CustomReportData = { + ...report, + id: reportId, + }; + + if (item.name !== undefined) { + if (await reportNameExists(item.name, item.id, true)) { + throw new Error('There is already a report named ' + item.name); + } + } else { + throw new Error('Report name is required'); + } + + // Create the report here based on the info + await db.insertWithSchema('reports', reportModel.fromJS(item)); + + return reportId; +} + +async function updateReport(report: CustomReportEntity) { + const item: CustomReportData = { + ...report, + }; + if (item.name !== undefined) { + if (await reportNameExists(item.name, item.id, false)) { + throw new Error('There is already a report named ' + item.name); + } + } else { + throw new Error('Report name is required'); + } + + await db.insertWithSchema('reports', reportModel.fromJS(item)); +} + +async function deleteReport(id: string) { + await db.delete_('reports', id); +} + +// Expose functions to the client +export const app = createApp(); + +app.method('report/create', mutator(createReport)); +app.method('report/update', mutator(updateReport)); +app.method('report/delete', mutator(undoable(deleteReport))); diff --git a/packages/loot-core/src/server/reports/types/handlers.ts b/packages/loot-core/src/server/reports/types/handlers.ts new file mode 100644 index 00000000000..4c8d3fbc6a0 --- /dev/null +++ b/packages/loot-core/src/server/reports/types/handlers.ts @@ -0,0 +1,9 @@ +import { type CustomReportEntity } from '../../../types/models'; + +export interface ReportsHandlers { + 'report/create': (report: CustomReportEntity) => Promise; + + 'report/update': (report: CustomReportEntity) => Promise; + + 'report/delete': (id: string) => Promise; +} diff --git a/packages/loot-core/src/types/models/index.d.ts b/packages/loot-core/src/types/models/index.d.ts index cceded549bb..307dac6b669 100644 --- a/packages/loot-core/src/types/models/index.d.ts +++ b/packages/loot-core/src/types/models/index.d.ts @@ -3,6 +3,7 @@ export type * from './category'; export type * from './category-group'; export type * from './gocardless'; export type * from './payee'; +export type * from './reports'; export type * from './rule'; export type * from './schedule'; export type * from './transaction'; diff --git a/packages/loot-core/src/types/models/reports.d.ts b/packages/loot-core/src/types/models/reports.d.ts new file mode 100644 index 00000000000..c84f5b9a1f9 --- /dev/null +++ b/packages/loot-core/src/types/models/reports.d.ts @@ -0,0 +1,28 @@ +import { DataEntity } from '../../../../desktop-client/src/components/reports/entities'; + +import { RuleConditionEntity } from './rule'; + +export interface CustomReportEntity { + id: string; + mode: string; + groupBy: string; + balanceType: string; + showEmpty: boolean; + showOffBudgetHidden: boolean; + showUncategorized: boolean; + graphType: string; + selectedCategories; + filters?: RuleConditionEntity[]; + conditionsOp: string; + name: string; + startDate: string; + endDate: string; + isDateStatic: boolean; + data?: DataEntity; + tombstone?: boolean; +} + +export interface CustomReportData extends CustomReportEntity { + conditions_op?: string; + conditions?: RuleConditionEntity[]; +} From 27f91383c099a71bb44563d989b929ac37223c53 Mon Sep 17 00:00:00 2001 From: carkom Date: Thu, 18 Jan 2024 13:33:22 +0000 Subject: [PATCH 02/17] notes --- upcoming-release-notes/2246.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 upcoming-release-notes/2246.md diff --git a/upcoming-release-notes/2246.md b/upcoming-release-notes/2246.md new file mode 100644 index 00000000000..39169c61456 --- /dev/null +++ b/upcoming-release-notes/2246.md @@ -0,0 +1,6 @@ +--- +category: Maintenance +authors: [carkom] +--- + +Add schema and backend functionality for custom reports. This is to enable saving reports in a future PR. From 41dcc69d6a96d2a4420d10cfdf4d3b992cf797e6 Mon Sep 17 00:00:00 2001 From: carkom Date: Fri, 19 Jan 2024 10:58:40 +0000 Subject: [PATCH 03/17] merge fixes --- packages/loot-core/src/types/models/reports.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/loot-core/src/types/models/reports.d.ts b/packages/loot-core/src/types/models/reports.d.ts index 60439fb056e..14bc072b2e5 100644 --- a/packages/loot-core/src/types/models/reports.d.ts +++ b/packages/loot-core/src/types/models/reports.d.ts @@ -16,7 +16,7 @@ export interface CustomReportEntity { startDate: string; endDate: string; isDateStatic: boolean; - data: GroupedEntity; + data?: GroupedEntity; tombstone?: boolean; } From cf364e137b9ddb5be3124554e5e966c47f139349 Mon Sep 17 00:00:00 2001 From: carkom Date: Fri, 19 Jan 2024 13:15:47 +0000 Subject: [PATCH 04/17] Add Reports Save Menu --- .../src/components/reports/ReportSidebar.jsx | 41 +++-- .../src/components/reports/ReportTopbar.jsx | 49 ++++-- .../src/components/reports/SaveReport.tsx | 165 ++++++++++++++---- .../src/components/reports/SaveReportMenu.tsx | 85 +++++++++ .../src/components/reports/SaveReportName.tsx | 73 ++++++++ .../reports/reports/CustomReport.jsx | 72 +++++++- packages/loot-core/src/types/handlers.d.ts | 2 + 7 files changed, 419 insertions(+), 68 deletions(-) create mode 100644 packages/desktop-client/src/components/reports/SaveReportMenu.tsx create mode 100644 packages/desktop-client/src/components/reports/SaveReportName.tsx diff --git a/packages/desktop-client/src/components/reports/ReportSidebar.jsx b/packages/desktop-client/src/components/reports/ReportSidebar.jsx index 843ab3697de..782c09c2703 100644 --- a/packages/desktop-client/src/components/reports/ReportSidebar.jsx +++ b/packages/desktop-client/src/components/reports/ReportSidebar.jsx @@ -49,8 +49,10 @@ export function ReportSidebar({ selectedCategories, setSelectedCategories, onChangeViews, + setSavedStatus, }) { const onSelectRange = cond => { + setSavedStatus('changed'); setDateRange(cond); switch (cond) { case 'All time': @@ -84,6 +86,7 @@ export function ReportSidebar({ }; const onChangeMode = cond => { + setSavedStatus('changed'); setMode(cond); if (cond === 'time') { if (graphType === 'TableGraph') { @@ -114,6 +117,7 @@ export function ReportSidebar({ }; const onChangeSplit = cond => { + setSavedStatus('changed'); setGroupBy(cond); if (mode === 'total') { if (graphType !== 'TableGraph') { @@ -125,6 +129,11 @@ export function ReportSidebar({ } }; + const onChangeBalanceType = cond => { + setSavedStatus('changed'); + setBalanceType(cond); + }; + return ( - onChangeDates(...validateStart(allMonths, newValue, endDate)) - } + onChange={newValue => { + onChangeDates(...validateStart(allMonths, newValue, endDate)); + setSavedStatus('changed'); + }} value={startDate} defaultLabel={monthUtils.format(startDate, 'MMMM, yyyy')} options={allMonths.map(({ name, pretty }) => [name, pretty])} @@ -403,9 +423,10 @@ export function ReportSidebar({ To: onNameChange(e)} /> + + + + + )} + {err && ( + + {err} + + )} + + ); +} diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx index 281b1f17a7f..93ee87e9257 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReport.jsx +++ b/packages/desktop-client/src/components/reports/reports/CustomReport.jsx @@ -77,6 +77,8 @@ export function CustomReport() { const [dataCheck, setDataCheck] = useState(false); const dateRangeLine = ReportOptions.dateRange.length - 3; + const [report, setReport] = useState(location.state.report ?? []); + const [savedStatus, setSavedStatus] = useState(location.state.report ? 'saved' : 'new'); const months = monthUtils.rangeInclusive(startDate, endDate); useEffect(() => { @@ -183,6 +185,23 @@ export function CustomReport() { const data = { ...graphData, groupedData }; + const items = { + id: null, + startDate: startDate, + endDate: endDate, + mode: mode, + groupBy: groupBy, + balanceType: balanceType, + showEmpty: showEmpty, + showOffBudgetHidden: showOffBudgetHidden, + showUncategorized: showUncategorized, + graphType: graphType, + filters: filters, + conditionsOp: conditionsOp, + selectedCategories: selectedCategories, + data: data, + }; + const [scrollWidth, setScrollWidth] = useState(0); if (!allMonths || !data) { @@ -192,6 +211,7 @@ export function CustomReport() { const onChangeDates = (startDate, endDate) => { setStartDate(startDate); setEndDate(endDate); + setSavedStatus('changed'); }; const onChangeViews = (viewType, status) => { @@ -206,6 +226,45 @@ export function CustomReport() { } }; + const onResetReports = () => { + setMode(stateDefault.mode); + setGroupBy(stateDefault.groupBy); + setBalanceType(stateDefault.balanceType); + setShowEmpty(stateDefault.empty); + setShowOffBudgetHidden(stateDefault.hidden); + setShowUncategorized(stateDefault.uncat); + setGraphType(stateDefault.graphType); + onApplyFilter(null); + onCondOpChange(stateDefault.conditionsOp); + setReport([]); + setStartDate(stateDefault.start); + setEndDate(stateDefault.end); + setSavedStatus('new'); + }; + + const onReportChange = (savedReport, type) => { + if (type === 'add-update') { //status = saved + setReport(savedReport); + } + + if (type === 'reload') { + setMode(report.mode); + setGroupBy(report.groupBy); + setBalanceType(report.balanceType); + setShowEmpty(report.empty); + setShowOffBudgetHidden(report.hidden); + setShowUncategorized(report.uncat); + setGraphType(report.graphType); + onApplyFilter(report.filters); + onCondOpChange(report.conditionsOp); + setStartDate(report.start); + setEndDate(report.end); + } else { + if (savedStatus === 'saved') { + } + } + }; + return (
@@ -248,6 +307,7 @@ export function CustomReport() { selectedCategories={selectedCategories} setSelectedCategories={setSelectedCategories} onChangeViews={onChangeViews} + setSavedStatus={setSavedStatus} /> {filters && filters.length > 0 && ( Date: Sat, 20 Jan 2024 12:00:30 +0000 Subject: [PATCH 05/17] merge fixes --- .../src/components/reports/ReportSidebar.jsx | 45 +++++++++++++------ .../src/components/reports/ReportTopbar.jsx | 19 +++++++- .../src/components/reports/SaveReport.tsx | 6 +-- 3 files changed, 51 insertions(+), 19 deletions(-) diff --git a/packages/desktop-client/src/components/reports/ReportSidebar.jsx b/packages/desktop-client/src/components/reports/ReportSidebar.jsx index dc2fdd24768..a0a3b06797a 100644 --- a/packages/desktop-client/src/components/reports/ReportSidebar.jsx +++ b/packages/desktop-client/src/components/reports/ReportSidebar.jsx @@ -38,8 +38,10 @@ export function ReportSidebar({ setSelectedCategories, onChangeDates, onChangeViews, + setSavedStatus, }) { const onSelectRange = cond => { + setSavedStatus('changed'); setDateRange(cond); switch (cond) { case 'All time': @@ -73,6 +75,7 @@ export function ReportSidebar({ }; const onChangeMode = cond => { + setSavedStatus('changed'); setMode(cond); if (cond === 'time') { if (customReportItems.graphType === 'TableGraph') { @@ -103,6 +106,7 @@ export function ReportSidebar({ }; const onChangeSplit = cond => { + setSavedStatus('changed'); setGroupBy(cond); if (customReportItems.mode === 'total') { if (customReportItems.graphType !== 'TableGraph') { @@ -119,6 +123,11 @@ export function ReportSidebar({ } }; + const onChangeBalanceType = cond => { + setSavedStatus('changed'); + setBalanceType(cond); + }; + return ( + onChange={newValue => { onChangeDates( ...validateStart( allMonths, newValue, customReportItems.endDate, ), - ) - } + ); + setSavedStatus('changed'); + }} value={customReportItems.startDate} defaultLabel={monthUtils.format( customReportItems.startDate, @@ -413,15 +429,16 @@ export function ReportSidebar({ To: { + onChange={newValue => onChangeDates( ...validateStart( allMonths, newValue, customReportItems.endDate, ), - ); - }} + ) + } value={customReportItems.startDate} defaultLabel={monthUtils.format( customReportItems.startDate, @@ -430,15 +428,15 @@ export function ReportSidebar({ To: onNameChange(e)} /> +