diff --git a/packages/desktop-client/src/components/filters/CompactFiltersButton.tsx b/packages/desktop-client/src/components/filters/CompactFiltersButton.tsx new file mode 100644 index 00000000000..28f73ef9a1b --- /dev/null +++ b/packages/desktop-client/src/components/filters/CompactFiltersButton.tsx @@ -0,0 +1,18 @@ +import React from 'react'; + +import { Filter } from '../../icons/v1'; +import Button from '../common/Button'; + +type CompactFiltersButtonProps = { + onClick: (newValue) => void; +}; + +function CompactFiltersButton({ onClick }: CompactFiltersButtonProps) { + return ( + + ); +} + +export default CompactFiltersButton; diff --git a/packages/desktop-client/src/components/filters/FiltersButton.tsx b/packages/desktop-client/src/components/filters/FiltersButton.tsx new file mode 100644 index 00000000000..1b3ef60aafc --- /dev/null +++ b/packages/desktop-client/src/components/filters/FiltersButton.tsx @@ -0,0 +1,21 @@ +import React from 'react'; + +import { SettingsSliderAlternate } from '../../icons/v2'; +import Button from '../common/Button'; + +type FiltersButtonProps = { + onClick: (newValue) => void; +}; + +function FiltersButton({ onClick }: FiltersButtonProps) { + return ( + + ); +} + +export default FiltersButton; diff --git a/packages/desktop-client/src/components/filters/FiltersMenu.js b/packages/desktop-client/src/components/filters/FiltersMenu.js index dd5411203d9..544c3cedda1 100644 --- a/packages/desktop-client/src/components/filters/FiltersMenu.js +++ b/packages/desktop-client/src/components/filters/FiltersMenu.js @@ -24,10 +24,9 @@ import { import { titleFirst } from 'loot-core/src/shared/util'; import DeleteIcon from '../../icons/v0/Delete'; -import Filter from '../../icons/v1/Filter'; -import SettingsSliderAlternate from '../../icons/v2/SettingsSliderAlternate'; import { theme } from '../../style'; import Button from '../common/Button'; +import HoverTarget from '../common/HoverTarget'; import Menu from '../common/Menu'; import Select from '../common/Select'; import Stack from '../common/Stack'; @@ -37,6 +36,8 @@ import Value from '../rules/Value'; import { Tooltip } from '../tooltips'; import GenericInput from '../util/GenericInput'; +import CompactFiltersButton from './CompactFiltersButton'; +import FiltersButton from './FiltersButton'; import { CondOpMenu } from './SavedFilters'; let filterFields = [ @@ -336,28 +337,7 @@ function ConfigureField({ ); } -function ButtonType({ type, dispatch }) { - return ( - - ); -} - -export function FilterButton({ onApply, type }) { +export function FilterButton({ onApply, compact, hover }) { let filters = useFilters(); let { dateFormat } = useSelector(state => { @@ -440,7 +420,32 @@ export function FilterButton({ onApply, type }) { return ( - + + hover && ( + + Filters + + ) + } + > + {compact ? ( + dispatch({ type: 'select-field' })} + /> + ) : ( + dispatch({ type: 'select-field' })} /> + )} + {state.fieldsOpen && ( ; + categories: Array; selectedCategories: CategoryListProps['items']; setSelectedCategories: (selectedCategories: CategoryEntity[]) => null; }; export default function CategorySelector({ categoryGroups, + categories, selectedCategories, setSelectedCategories, }: CategorySelectorProps) { const [uncheckedHidden, setUncheckedHidden] = useState(false); + const selectedCategoryMap = useMemo( + () => selectedCategories.map(selected => selected.id), + [selectedCategories], + ); + const allCategoriesSelected = categories.every(category => + selectedCategoryMap.includes(category.id), + ); + + const allCategoriesUnselected = !categories.some(category => + selectedCategoryMap.includes(category.id), + ); + + let selectAll: CategoryEntity[] = []; + categoryGroups.map(categoryGroup => + categoryGroup.categories.map(category => selectAll.push(category)), + ); + return ( - <> -
- -
+ + + { + setSelectedCategories(selectAll); + }} + style={{ marginRight: 5, padding: 8 }} + > + + + { + setSelectedCategories([]); + }} + style={{ padding: 8 }} + > + + + +
    {categoryGroups && @@ -166,6 +215,6 @@ export default function CategorySelector({ ); })}
- +
); } diff --git a/packages/desktop-client/src/components/reports/GraphButton.tsx b/packages/desktop-client/src/components/reports/GraphButton.tsx new file mode 100644 index 00000000000..d3857def566 --- /dev/null +++ b/packages/desktop-client/src/components/reports/GraphButton.tsx @@ -0,0 +1,59 @@ +import React, { type HTMLProps } from 'react'; + +import { type CSSProperties, theme } from '../../style'; +import Button from '../common/Button'; +import HoverTarget from '../common/HoverTarget'; +import Text from '../common/Text'; +import { Tooltip } from '../tooltips'; + +type GraphButtonProps = HTMLProps & { + selected?: boolean; + style?: CSSProperties; + onSelect?: (newValue) => void; + title?: string; + disabled?: boolean; +}; + +const GraphButton = ({ + selected, + children, + onSelect, + title, + style, + disabled, +}: GraphButtonProps) => { + return ( + ( + + {title} + + )} + > + + + ); +}; + +export default GraphButton; diff --git a/packages/desktop-client/src/components/reports/Header.js b/packages/desktop-client/src/components/reports/Header.js index 1e8fc518501..e1b6408bd1b 100644 --- a/packages/desktop-client/src/components/reports/Header.js +++ b/packages/desktop-client/src/components/reports/Header.js @@ -26,6 +26,18 @@ export function validateEnd(allMonths, start, end) { return boundedRange(earliest, start, end); } +export function validateRange(allMonths, start, end) { + const latest = monthUtils.currentMonth(); + const earliest = allMonths[allMonths.length - 1].name; + if (end > latest) { + end = latest; + } + if (start < earliest) { + start = earliest; + } + return [start, end]; +} + function boundedRange(earliest, start, end) { const latest = monthUtils.currentMonth(); if (end > latest) { diff --git a/packages/desktop-client/src/components/reports/LoadingIndicator.tsx b/packages/desktop-client/src/components/reports/LoadingIndicator.tsx new file mode 100644 index 00000000000..8e71593df3c --- /dev/null +++ b/packages/desktop-client/src/components/reports/LoadingIndicator.tsx @@ -0,0 +1,33 @@ +import React from 'react'; + +import AnimatedLoading from '../../icons/AnimatedLoading'; +import { theme, styles } from '../../style'; +import Block from '../common/Block'; +import View from '../common/View'; + +type LoadingIndicatorProps = { + message?: string; +}; + +const LoadingIndicator = ({ message }: LoadingIndicatorProps) => { + return ( + + {message && ( + {message} + )} + + + ); +}; + +export default LoadingIndicator; diff --git a/packages/desktop-client/src/components/reports/Overview.js b/packages/desktop-client/src/components/reports/Overview.js index 05082df5a88..5c2784bd001 100644 --- a/packages/desktop-client/src/components/reports/Overview.js +++ b/packages/desktop-client/src/components/reports/Overview.js @@ -2,7 +2,6 @@ import React from 'react'; import { useSelector } from 'react-redux'; import useFeatureFlag from '../../hooks/useFeatureFlag'; -import AnimatedLoading from '../../icons/AnimatedLoading'; import { styles } from '../../style'; import View from '../common/View'; @@ -12,20 +11,6 @@ import CustomReportCard from './reports/CustomReportCard'; import NetWorthCard from './reports/NetWorthCard'; import SankeyCard from './reports/SankeyCard'; -export function LoadingIndicator() { - return ( - - - - ); -} - export default function Overview() { let categorySpendingReportFeatureFlag = useFeatureFlag( 'categorySpendingReport', diff --git a/packages/desktop-client/src/components/reports/ReportOptions.tsx b/packages/desktop-client/src/components/reports/ReportOptions.tsx index a310a5f026d..73e2fff5b51 100644 --- a/packages/desktop-client/src/components/reports/ReportOptions.tsx +++ b/packages/desktop-client/src/components/reports/ReportOptions.tsx @@ -18,6 +18,8 @@ const dateRangeOptions = [ { description: '3 months', name: 2 }, { description: '6 months', name: 5 }, { description: '1 year', name: 11 }, + { description: 'Year to date', name: 'yearToDate' }, + { description: 'Last year', name: 'lastYear' }, { description: 'All time', name: 'allMonths' }, ]; diff --git a/packages/desktop-client/src/components/reports/ReportSidebar.js b/packages/desktop-client/src/components/reports/ReportSidebar.js index 8a43f59d648..5f22d0a13c7 100644 --- a/packages/desktop-client/src/components/reports/ReportSidebar.js +++ b/packages/desktop-client/src/components/reports/ReportSidebar.js @@ -15,6 +15,7 @@ import { validateEnd, getLatestRange, getFullRange, + validateRange, } from './Header'; import { ReportOptions } from './ReportOptions'; @@ -73,7 +74,39 @@ export function ReportSidebar({ selectedCategories, setSelectedCategories, }) { - function onChangeMode(cond) { + const onSelectRange = cond => { + switch (cond) { + case 'All time': + onChangeDates(...getFullRange(allMonths)); + break; + case 'Year to date': + onChangeDates( + ...validateRange( + allMonths, + monthUtils.getYearStart(monthUtils.currentMonth()), + monthUtils.currentMonth(), + ), + ); + break; + case 'Last year': + onChangeDates( + ...validateRange( + allMonths, + monthUtils.getYearStart( + monthUtils.prevYear(monthUtils.currentMonth()), + ), + monthUtils.getYearEnd( + monthUtils.prevYear(monthUtils.currentDate()), + ), + ), + ); + break; + default: + onChangeDates(...getLatestRange(ReportOptions.dateRangeMap.get(cond))); + } + }; + + const onChangeMode = cond => { setMode(cond); if (cond === 'time') { if (graphType === 'TableGraph') { @@ -101,9 +134,9 @@ export function ReportSidebar({ setTypeDisabled([]); } } - } + }; - function onChangeSplit(cond) { + const onChangeSplit = cond => { setGroupBy(cond); if (mode === 'total') { if (graphType !== 'TableGraph') { @@ -113,15 +146,16 @@ export function ReportSidebar({ if (['Net'].includes(balanceType) && graphType !== 'TableGraph') { setBalanceType('Expense'); } - } + }; return ( @@ -301,7 +335,7 @@ export function ReportSidebar({ { setDateRange(e); - if (e === 'allMonths') { - onChangeDates(...getFullRange(allMonths)); - } else { - onChangeDates( - ...getLatestRange(ReportOptions.dateRangeMap.get(e)), - ); - } + onSelectRange(e); }} options={ReportOptions.dateRange.map(option => [ option.description, @@ -387,7 +415,7 @@ export function ReportSidebar({ diff --git a/packages/desktop-client/src/components/reports/ReportTableHeader.tsx b/packages/desktop-client/src/components/reports/ReportTableHeader.tsx index 932d7e4a341..48ce3585072 100644 --- a/packages/desktop-client/src/components/reports/ReportTableHeader.tsx +++ b/packages/desktop-client/src/components/reports/ReportTableHeader.tsx @@ -2,7 +2,7 @@ import React from 'react'; import * as d from 'date-fns'; -import { theme } from '../../style'; +import { styles, theme } from '../../style'; import { Row, Cell } from '../table'; export default function ReportTableHeader({ @@ -23,6 +23,7 @@ export default function ReportTableHeader({ 12 && item[groupByItem]} style={{ minWidth: 125, + ...styles.tnum, }} /> {item.monthData && mode === 'time' @@ -59,6 +60,7 @@ const TableRow = memo( @@ -106,6 +110,7 @@ const TableRow = memo( style={{ fontWeight: 600, minWidth: 85, + ...styles.tnum, }} width="flex" privacyFilter @@ -119,6 +124,7 @@ const TableRow = memo( style={{ fontWeight: 600, minWidth: 85, + ...styles.tnum, }} width="flex" privacyFilter diff --git a/packages/desktop-client/src/components/reports/ReportTableTotals.tsx b/packages/desktop-client/src/components/reports/ReportTableTotals.tsx index 5f37740a9c5..1286a7f87dd 100644 --- a/packages/desktop-client/src/components/reports/ReportTableTotals.tsx +++ b/packages/desktop-client/src/components/reports/ReportTableTotals.tsx @@ -6,7 +6,7 @@ import { integerToCurrency, } from 'loot-core/src/shared/util'; -import { theme } from '../../style'; +import { styles, theme } from '../../style'; import { Row, Cell } from '../table'; export default function ReportTableTotals({ @@ -29,6 +29,7 @@ export default function ReportTableTotals({ - {children} - - ); -} - export function ReportTopbar({ graphType, setGraphType, @@ -67,6 +48,7 @@ export function ReportTopbar({ //setViewLegend(false); setTypeDisabled([]); }} + style={{ marginRight: 15 }} > @@ -86,7 +68,7 @@ export function ReportTopbar({ setBalanceType('Expense'); } }} - style={{ marginLeft: 15 }} + style={{ marginRight: 15 }} > @@ -99,7 +81,7 @@ export function ReportTopbar({ //setViewLegend(false); setTypeDisabled([]); }} - style={{ marginLeft: 15 }} + style={{ marginRight: 15 }} disabled={mode === 'total' ? false : true} > @@ -112,7 +94,7 @@ export function ReportTopbar({ setTypeDisabled(['Net']); setBalanceType('Expense'); }} - style={{ marginLeft: 15 }} + style={{ marginRight: 15 }} disabled={mode === 'total' ? false : true} > @@ -121,8 +103,8 @@ export function ReportTopbar({ style={{ width: 1, height: 30, - backgroundColor: theme.altPillBorder, - marginLeft: 15, + backgroundColor: theme.pillBorderDark, + marginRight: 15, flexShrink: 0, }} /> @@ -131,7 +113,7 @@ export function ReportTopbar({ onSelect={() => { setViewLegend(!viewLegend); }} - style={{ marginLeft: 15 }} + style={{ marginRight: 15 }} title="Show Legend" disabled={ true //descoping for future PR @@ -145,7 +127,7 @@ export function ReportTopbar({ onSelect={() => { setViewSummary(!viewSummary); }} - style={{ marginLeft: 15 }} + style={{ marginRight: 15 }} title="Show Summary" > @@ -155,7 +137,7 @@ export function ReportTopbar({ onSelect={() => { setViewLabels(!viewLabels); }} - style={{ marginLeft: 15 }} + style={{ marginRight: 15 }} title="Show labels" disabled={true} > @@ -165,13 +147,12 @@ export function ReportTopbar({ style={{ width: 1, height: 30, - backgroundColor: theme.altPillBorder, + backgroundColor: theme.pillBorderDark, marginRight: 15, - marginLeft: 15, flexShrink: 0, }} /> - + diff --git a/packages/desktop-client/src/components/reports/getCustomTick.ts b/packages/desktop-client/src/components/reports/getCustomTick.ts new file mode 100644 index 00000000000..07ab8f777b6 --- /dev/null +++ b/packages/desktop-client/src/components/reports/getCustomTick.ts @@ -0,0 +1,9 @@ +const getCustomTick = (value: string, isPrivacyModeEnabled: boolean) => { + if (isPrivacyModeEnabled) { + return '...'; + } else { + return value; + } +}; + +export default getCustomTick; diff --git a/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx b/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx index 174e649f1c1..2370d57d0fe 100644 --- a/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/AreaGraph.tsx @@ -11,6 +11,7 @@ import { ResponsiveContainer, } from 'recharts'; +import usePrivacyMode from 'loot-core/src/client/privacy'; import { amountToCurrency } from 'loot-core/src/shared/util'; import { theme } from '../../../style'; @@ -94,14 +95,14 @@ type AreaGraphProps = { data; balanceTypeOp; compact: boolean; - domain?: { - totalTotals?: [number, number]; - }; }; function AreaGraph({ style, data, balanceTypeOp, compact }: AreaGraphProps) { + let privacyMode = usePrivacyMode(); + const tickFormatter = tick => { - return `${Math.round(tick).toLocaleString()}`; // Formats the tick values as strings with commas + if (!privacyMode) return `${Math.round(tick).toLocaleString()}`; // Formats the tick values as strings with commas + return '...'; }; const gradientOffset = () => { diff --git a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx index 79f169864a5..6fdaf45a21e 100644 --- a/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/BarGraph.tsx @@ -14,6 +14,7 @@ import { ResponsiveContainer, } from 'recharts'; +import usePrivacyMode from 'loot-core/src/client/privacy'; import { amountToCurrency } from 'loot-core/src/shared/util'; import { theme } from '../../../style'; @@ -22,6 +23,7 @@ import AlignedText from '../../common/AlignedText'; import PrivacyFilter from '../../PrivacyFilter'; import { getColorScale } from '../chart-theme'; import Container from '../Container'; +import getCustomTick from '../getCustomTick'; import numberFormatterTooltip from '../numberFormatter'; type PayloadChild = { @@ -32,7 +34,6 @@ type PayloadChild = { }; type PayloadItem = { - value: string; payload: { name: string; totalAssets: number | string; @@ -105,7 +106,6 @@ const CustomTooltip = ({ ); } }; - /* Descoped for future PR type CustomLegendProps = { active?: boolean; @@ -148,6 +148,8 @@ function BarGraph({ compact, domain, }: BarGraphProps) { + let privacyMode = usePrivacyMode(); + const colorScale = getColorScale('qualitative'); const yAxis = ['Month', 'Year'].includes(groupBy) ? 'date' : 'name'; const splitData = ['Month', 'Year'].includes(groupBy) ? 'monthData' : 'data'; @@ -211,6 +213,7 @@ function BarGraph({ )} {!compact && ( getCustomTick(value, privacyMode)} tick={{ fill: theme.pageText }} tickLine={{ stroke: theme.pageText }} /> diff --git a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx index 228798f7081..f0925ab9a1d 100644 --- a/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx +++ b/packages/desktop-client/src/components/reports/graphs/StackedBarGraph.tsx @@ -12,6 +12,7 @@ import { ResponsiveContainer, } from 'recharts'; +import usePrivacyMode from 'loot-core/src/client/privacy'; import { amountToCurrency } from 'loot-core/src/shared/util'; import { theme } from '../../../style'; @@ -20,6 +21,7 @@ import AlignedText from '../../common/AlignedText'; import PrivacyFilter from '../../PrivacyFilter'; import { getColorScale } from '../chart-theme'; import Container from '../Container'; +import getCustomTick from '../getCustomTick'; import numberFormatterTooltip from '../numberFormatter'; type PayloadItem = { @@ -116,28 +118,12 @@ type StackedBarGraphProps = { data; balanceTypeOp; compact: boolean; - domain?: { - y?: [number, number]; - }; }; -function StackedBarGraph({ - style, - data, - balanceTypeOp, - compact, - domain, -}: StackedBarGraphProps) { +function StackedBarGraph({ style, data, compact }: StackedBarGraphProps) { + let privacyMode = usePrivacyMode(); const colorScale = getColorScale('qualitative'); - const getVal = (obj, key) => { - if (balanceTypeOp === 'totalDebts') { - return -1 * obj[key].amount; - } else { - return obj[key].amount; - } - }; - return ( {!compact && ( getCustomTick(value, privacyMode)} tick={{ fill: theme.pageText }} tickLine={{ stroke: theme.pageText }} /> )} - {data.groupBy - .slice(0) - .reverse() - .map((c, index) => ( - getVal(val, c.name)} - name={c.name} - stackId="a" - fill={colorScale[index % colorScale.length]} - /> - ))} + {data.groupBy.reverse().map((c, index) => ( + + ))} diff --git a/packages/desktop-client/src/components/reports/reports/CashFlowCard.js b/packages/desktop-client/src/components/reports/reports/CashFlowCard.js index 1ccf6dfc650..bc752f4bbf4 100644 --- a/packages/desktop-client/src/components/reports/reports/CashFlowCard.js +++ b/packages/desktop-client/src/components/reports/reports/CashFlowCard.js @@ -13,7 +13,7 @@ import Change from '../Change'; import { chartTheme } from '../chart-theme'; import Container from '../Container'; import DateRange from '../DateRange'; -import { LoadingIndicator } from '../Overview'; +import LoadingIndicator from '../LoadingIndicator'; import ReportCard from '../ReportCard'; import { simpleCashFlow } from '../spreadsheets/cash-flow-spreadsheet'; import Tooltip from '../Tooltip'; @@ -56,7 +56,7 @@ function CashFlowCard() { diff --git a/packages/desktop-client/src/components/reports/reports/CategorySpendingCard.js b/packages/desktop-client/src/components/reports/reports/CategorySpendingCard.js index 1b4742c1820..2a705305d9f 100644 --- a/packages/desktop-client/src/components/reports/reports/CategorySpendingCard.js +++ b/packages/desktop-client/src/components/reports/reports/CategorySpendingCard.js @@ -8,7 +8,7 @@ import Block from '../../common/Block'; import View from '../../common/View'; import DateRange from '../DateRange'; import CategorySpendingGraph from '../graphs/CategorySpendingGraph'; -import { LoadingIndicator } from '../Overview'; +import LoadingIndicator from '../LoadingIndicator'; import ReportCard from '../ReportCard'; import categorySpendingSpreadsheet from '../spreadsheets/category-spending-spreadsheet'; import useReport from '../useReport'; diff --git a/packages/desktop-client/src/components/reports/reports/CustomReport.js b/packages/desktop-client/src/components/reports/reports/CustomReport.js index 8aeb682c95d..a2b43f8184f 100644 --- a/packages/desktop-client/src/components/reports/reports/CustomReport.js +++ b/packages/desktop-client/src/components/reports/reports/CustomReport.js @@ -1,8 +1,9 @@ import React, { useState, useEffect, useMemo } from 'react'; -import { useSelector } from 'react-redux'; import * as d from 'date-fns'; +import { useCachedAccounts } from 'loot-core/src/client/data-hooks/accounts'; +import { useCachedPayees } from 'loot-core/src/client/data-hooks/payees'; import { send } from 'loot-core/src/platform/client/fetch'; import * as monthUtils from 'loot-core/src/shared/months'; import { amountToCurrency } from 'loot-core/src/shared/util'; @@ -18,6 +19,7 @@ import { AppliedFilters } from '../../filters/FiltersMenu'; import PrivacyFilter from '../../PrivacyFilter'; import { ChooseGraph } from '../ChooseGraph'; import Header from '../Header'; +import LoadingIndicator from '../LoadingIndicator'; import { ReportOptions } from '../ReportOptions'; import { ReportSidebar } from '../ReportSidebar'; import { ReportLegend, ReportSummary } from '../ReportSummary'; @@ -29,13 +31,6 @@ import { fromDateRepr } from '../util'; export default function CustomReport() { const categories = useCategories(); - let { payees, accounts } = useSelector(state => { - return { - payees: state.queries.payees, - accounts: state.queries.accounts, - }; - }); - const { filters, conditionsOp, @@ -60,6 +55,7 @@ export default function CustomReport() { const [hidden, setHidden] = useState(false); const [uncat, setUncat] = useState(false); const [dateRange, setDateRange] = useState('6 months'); + const [dataCheck, setDataCheck] = useState(false); const [graphType, setGraphType] = useState('BarGraph'); const [viewLegend, setViewLegend] = useState(false); @@ -67,39 +63,8 @@ export default function CustomReport() { const [viewLabels, setViewLabels] = useState(false); //const [legend, setLegend] = useState([]); let legend = []; - const dateRangeLine = ReportOptions.dateRange.length - 1; - + const dateRangeLine = ReportOptions.dateRange.length - 3; const months = monthUtils.rangeInclusive(start, end); - const getGraphData = useMemo(() => { - return defaultSpreadsheet( - start, - end, - groupBy, - ReportOptions.balanceTypeMap.get(balanceType), - categories, - selectedCategories, - payees, - accounts, - filters, - conditionsOp, - hidden, - uncat, - ); - }, [ - start, - end, - groupBy, - balanceType, - categories, - selectedCategories, - payees, - accounts, - filters, - conditionsOp, - hidden, - uncat, - ]); - const data = useReport('default', getGraphData); useEffect(() => { if (selectedCategories === null && categories.list.length !== 0) { @@ -136,6 +101,42 @@ export default function CustomReport() { run(); }, []); + let payees = useCachedPayees(); + let accounts = useCachedAccounts(); + + const getGraphData = useMemo(() => { + setDataCheck(false); + return defaultSpreadsheet( + start, + end, + groupBy, + ReportOptions.balanceTypeMap.get(balanceType), + categories, + selectedCategories, + payees, + accounts, + filters, + conditionsOp, + hidden, + uncat, + setDataCheck, + ); + }, [ + start, + end, + groupBy, + balanceType, + categories, + selectedCategories, + payees, + accounts, + filters, + conditionsOp, + hidden, + uncat, + ]); + const data = useReport('default', getGraphData); + let [scrollWidth, setScrollWidth] = useState(0); if (!allMonths || !data) { @@ -149,12 +150,7 @@ export default function CustomReport() { return ( -
+
)} - + + {dataCheck ? ( + + ) : ( + + )} {(viewLegend || viewSummary) && ( diff --git a/packages/desktop-client/src/components/reports/reports/SankeyCard.js b/packages/desktop-client/src/components/reports/reports/SankeyCard.js index 1732311dbfa..f79ec227260 100644 --- a/packages/desktop-client/src/components/reports/reports/SankeyCard.js +++ b/packages/desktop-client/src/components/reports/reports/SankeyCard.js @@ -8,7 +8,7 @@ import Block from '../../common/Block'; import View from '../../common/View'; import DateRange from '../DateRange'; import SankeyGraph from '../graphs/SankeyGraph'; -import { LoadingIndicator } from '../Overview'; +import LoadingIndicator from '../LoadingIndicator'; import ReportCard from '../ReportCard'; import sankeySpreadsheet from '../spreadsheets/sankey-spreadsheet'; import useReport from '../useReport'; diff --git a/packages/desktop-client/src/components/reports/spreadsheets/default-spreadsheet.tsx b/packages/desktop-client/src/components/reports/spreadsheets/default-spreadsheet.tsx index 5e20982ce58..b55a7b71bd8 100644 --- a/packages/desktop-client/src/components/reports/spreadsheets/default-spreadsheet.tsx +++ b/packages/desktop-client/src/components/reports/spreadsheets/default-spreadsheet.tsx @@ -5,13 +5,13 @@ import { send } from 'loot-core/src/platform/client/fetch'; import * as monthUtils from 'loot-core/src/shared/months'; import { integerToAmount, amountToInteger } from 'loot-core/src/shared/util'; -import { index } from '../util'; +import { index, indexStack } from '../util'; export default function createSpreadsheet( start, end, groupBy, - typeItem, + balanceTypeOp, categories, selectedCategories, payees, @@ -20,6 +20,7 @@ export default function createSpreadsheet( conditionsOp, hidden, uncat, + setDataCheck, ) { let uncatCat = { name: 'Uncategorized', @@ -282,8 +283,9 @@ export default function createSpreadsheet( }; }); - const categoryGroupCalcData = catGroup.map(group => { - if (hidden || group.hidden === 0) { + const categoryGroupCalcData = catGroup + .filter(f => hidden || f.hidden === 0) + .map(group => { let groupedStarting = 0; const mon = months.map(month => { let groupedAssets = 0; @@ -317,10 +319,7 @@ export default function createSpreadsheet( hidden: group.hidden, balances: index(mon, 'date'), }; - } else { - return null; - } - }); + }); const groupByData = groupBy === 'Group' ? categoryGroupCalcData : calcData; @@ -329,35 +328,37 @@ export default function createSpreadsheet( return { ...calc }; }); - const categoryGroupData = catGroup.map(group => { - const catData = group.categories.map(graph => { - let catMatch = null; - calcData.map(cat => { - if ( - cat.id === null - ? cat.uncat_id === graph.uncat_id - : cat.id === graph.id - ) { - catMatch = cat; + const categoryGroupData = catGroup + .filter(f => hidden || f.hidden === 0) + .map(group => { + const catData = group.categories.map(graph => { + let catMatch = null; + calcData.map(cat => { + if ( + cat.id === null + ? cat.uncat_id === graph.uncat_id + : cat.id === graph.id + ) { + catMatch = cat; + } + return null; + }); + const calcCat = catMatch && recalculate(catMatch, start, end); + return { ...calcCat }; + }); + let groupMatch = null; + categoryGroupCalcData.map(split => { + if (split.id === group.id) { + groupMatch = split; } return null; }); - const calcCat = catMatch && recalculate(catMatch, start, end); - return { ...calcCat }; - }); - let groupMatch = null; - categoryGroupCalcData.map(split => { - if (split.id === group.id) { - groupMatch = split; - } - return null; + const calcGroup = groupMatch && recalculate(groupMatch, start, end); + return { + ...calcGroup, + categories: catData, + }; }); - const calcGroup = groupMatch && recalculate(groupMatch, start, end); - return { - ...calcGroup, - categories: catData, - }; - }); let totalAssets = 0; let totalDebts = 0; @@ -393,26 +394,27 @@ export default function createSpreadsheet( }); const stackedData = months.map(month => { - let perMonthAmounts = 0; const stacked = data.map(graph => { let stackAmounts = 0; if (graph.indexedMonthData[month]) { - perMonthAmounts += graph.indexedMonthData[month][typeItem]; - stackAmounts += graph.indexedMonthData[month][typeItem]; + stackAmounts += graph.indexedMonthData[month][balanceTypeOp]; } return { name: graph.name, id: graph.id, - amount: stackAmounts, + amount: Math.abs(stackAmounts), }; }); - const indexedStack = index(stacked, 'name'); + const indexedStack = indexStack( + stacked.filter(i => i[balanceTypeOp] !== 0), + 'name', + 'amount', + ); return { // eslint-disable-next-line rulesdir/typography date: d.format(d.parseISO(`${month}-01`), "MMM ''yy"), ...indexedStack, - totalTotals: perMonthAmounts, }; }); @@ -428,6 +430,7 @@ export default function createSpreadsheet( totalAssets: integerToAmount(totalAssets), totalTotals: integerToAmount(totalTotals), }); + setDataCheck?.(true); }; } diff --git a/packages/desktop-client/src/components/reports/util.js b/packages/desktop-client/src/components/reports/util.js index 1cae0b43d3f..b3cb72c85ad 100644 --- a/packages/desktop-client/src/components/reports/util.js +++ b/packages/desktop-client/src/components/reports/util.js @@ -21,6 +21,14 @@ export function index(data, field, mapper) { return result; } +export function indexStack(data, fieldName, field) { + const result = {}; + data.forEach(item => { + result[item[fieldName]] = item[field]; + }); + return result; +} + export function indexCashFlow(data, date, isTransfer) { const results = {}; data.forEach(item => { diff --git a/packages/desktop-client/src/icons/v2/CheckAll.tsx b/packages/desktop-client/src/icons/v2/CheckAll.tsx new file mode 100644 index 00000000000..69d0dc07965 --- /dev/null +++ b/packages/desktop-client/src/icons/v2/CheckAll.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const SvgCheckAll = (props: SVGProps) => ( + + + +); +export default SvgCheckAll; diff --git a/packages/desktop-client/src/icons/v2/UncheckAll.tsx b/packages/desktop-client/src/icons/v2/UncheckAll.tsx new file mode 100644 index 00000000000..13698bbea4d --- /dev/null +++ b/packages/desktop-client/src/icons/v2/UncheckAll.tsx @@ -0,0 +1,19 @@ +import * as React from 'react'; +import type { SVGProps } from 'react'; +const SvgUncheckAll = (props: SVGProps) => ( + + + +); +export default SvgUncheckAll; diff --git a/packages/desktop-client/src/icons/v2/check-all.svg b/packages/desktop-client/src/icons/v2/check-all.svg new file mode 100644 index 00000000000..25f62f457bf --- /dev/null +++ b/packages/desktop-client/src/icons/v2/check-all.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/packages/desktop-client/src/icons/v2/index.ts b/packages/desktop-client/src/icons/v2/index.ts index c1455d4cdcb..a27e73a12f2 100644 --- a/packages/desktop-client/src/icons/v2/index.ts +++ b/packages/desktop-client/src/icons/v2/index.ts @@ -8,6 +8,7 @@ export { default as ArrowsShrink3 } from './ArrowsShrink3'; export { default as ArrowsSynchronize } from './ArrowsSynchronize'; export { default as Calendar3 } from './Calendar3'; export { default as Calendar } from './Calendar'; +export { default as CheckAll } from './CheckAll'; export { default as CheckCircle1 } from './CheckCircle1'; export { default as CheckCircleHollow } from './CheckCircleHollow'; export { default as Check } from './Check'; @@ -37,6 +38,7 @@ export { default as SearchAlternate } from './SearchAlternate'; export { default as SettingsSliderAlternate } from './SettingsSliderAlternate'; export { default as Subtract } from './Subtract'; export { default as Sun } from './Sun'; +export { default as UncheckAll } from './UncheckAll'; export { default as UploadThickBottom } from './UploadThickBottom'; export { default as ValidationCheck } from './ValidationCheck'; export { default as ViewHide } from './ViewHide'; diff --git a/packages/desktop-client/src/icons/v2/uncheck-all.svg b/packages/desktop-client/src/icons/v2/uncheck-all.svg new file mode 100644 index 00000000000..b11cab4cc73 --- /dev/null +++ b/packages/desktop-client/src/icons/v2/uncheck-all.svg @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/packages/desktop-client/src/style/themes/dark.ts b/packages/desktop-client/src/style/themes/dark.ts index c1808b69673..071f0a28d28 100644 --- a/packages/desktop-client/src/style/themes/dark.ts +++ b/packages/desktop-client/src/style/themes/dark.ts @@ -10,7 +10,7 @@ export const pageBackgroundLineBottom = colorPalette.navy150; export const pageText = colorPalette.navy150; export const pageTextLight = colorPalette.navy300; export const pageTextSubdued = colorPalette.navy500; -export const pageTextDark = colorPalette.navy800; +export const pageTextDark = colorPalette.navy100; export const pageTextPositive = colorPalette.purple200; export const pageTextLink = colorPalette.purple400; export const pageTextLinkLight = colorPalette.purple200; diff --git a/packages/loot-core/src/shared/months.ts b/packages/loot-core/src/shared/months.ts index c87d8694d65..d314391a825 100644 --- a/packages/loot-core/src/shared/months.ts +++ b/packages/loot-core/src/shared/months.ts @@ -118,6 +118,10 @@ export function nextMonth(month: DateLike): string { return d.format(d.addMonths(_parse(month), 1), 'yyyy-MM'); } +export function prevYear(month: DateLike): string { + return d.format(d.subMonths(_parse(month), 12), 'yyyy-MM'); +} + export function prevMonth(month: DateLike): string { return d.format(d.subMonths(_parse(month), 1), 'yyyy-MM'); } diff --git a/upcoming-release-notes/1930.md b/upcoming-release-notes/1930.md new file mode 100644 index 00000000000..5ecd2fc4726 --- /dev/null +++ b/upcoming-release-notes/1930.md @@ -0,0 +1,6 @@ +--- +category: Bugfix +authors: [carkom] +--- + +Bug fixes for custom reports.