Skip to content

Commit

Permalink
Customizeable Reports (actualbudget#1791)
Browse files Browse the repository at this point in the history
* Reorganize and add graphs

* Create Customizable Chart

* Notes

* Hide Menu update Donut

* lint fixes

* Organize Menus

* Change Title

* UI changes

* UI updates

* Add Data Table

* Functionality additions and Privacy Filters

* Date filters working and formatting changes

* Fix default spreadsheet and add tableGraph

* Integrate Summary Data and Split Legend

* started adding functionality on charts

* list fix

* Enabling more graphs, fixing errors

* Legend, interactions, Empty Rows Filter

* fixes for EmptyRows/interactions/legends

* formatting UI and filtering data

* format date

* fix errors

* Fix Legend Order

* lint fixes

* Add tooltips

* Feature Flag

* fix overview card, fix offbudget checkbox

* Revamped dataType, added scrollBars

* data display adjustments

* data spreadsheet updates/groups added to matrix

* Add Category Selector

* Add Labels Button

* formatting fixes

* Add Averages to dataTable

* data bug fix

* Added all type back in with exceptions

* formatting

* split assets/debts, add Uncategorized

* bug fixes and UI updates

* add scrollbars to table

* formatting dataTable

* tooltips, navigation and graph labels

* Code clean-up and re-org

* revert color change

* Change labels name

* organize files

* code cleanup

* Tooltip Colors

* Descoping legend for future PR

* descope legend & rename split

* rename type variable to be more descriptive

* adjustments for sankey and eslint merges

* notes update

* code review fixes

* code fixes

* fix date selections
  • Loading branch information
carkom authored Nov 16, 2023
1 parent 14ebff7 commit 23fd3e2
Show file tree
Hide file tree
Showing 46 changed files with 4,069 additions and 498 deletions.
2 changes: 1 addition & 1 deletion packages/desktop-client/src/components/accounts/Header.js
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,7 @@ export function AccountHeader({
</Button>
)}
<View style={{ flexShrink: 0 }}>
<FilterButton onApply={onApplyFilter} />
<FilterButton onApply={onApplyFilter} type="accounts" />
</View>
<View style={{ flex: 1 }} />
<Search
Expand Down
31 changes: 24 additions & 7 deletions packages/desktop-client/src/components/filters/FiltersMenu.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ 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';
Expand Down Expand Up @@ -335,7 +336,28 @@ function ConfigureField({
);
}

export function FilterButton({ onApply }) {
function ButtonType({ type, dispatch }) {
return (
<Button
type="bare"
onClick={() => dispatch({ type: 'select-field' })}
title={type && 'Filters'}
>
{type === 'reports' ? (
<Filter width={15} height={15} />
) : (
<>
<SettingsSliderAlternate
style={{ width: 16, height: 16, marginRight: 5 }}
/>{' '}
Filter
</>
)}
</Button>
);
}

export function FilterButton({ onApply, type }) {
let filters = useFilters();

let { dateFormat } = useSelector(state => {
Expand Down Expand Up @@ -418,12 +440,7 @@ export function FilterButton({ onApply }) {

return (
<View>
<Button type="bare" onClick={() => dispatch({ type: 'select-field' })}>
<SettingsSliderAlternate
style={{ width: 16, height: 16, marginRight: 5 }}
/>{' '}
Filter
</Button>
<ButtonType type={type} dispatch={dispatch} />
{state.fieldsOpen && (
<Tooltip
position="bottom-left"
Expand Down
138 changes: 138 additions & 0 deletions packages/desktop-client/src/components/reports/ChooseGraph.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
import React from 'react';

import View from '../common/View';

import AreaGraph from './graphs/AreaGraph';
import BarGraph from './graphs/BarGraph';
import BarLineGraph from './graphs/BarLineGraph';
import DonutGraph from './graphs/DonutGraph';
import LineGraph from './graphs/LineGraph';
import StackedBarGraph from './graphs/StackedBarGraph';
import { ReportOptions } from './ReportOptions';
import ReportTable from './ReportTable';
import ReportTableHeader from './ReportTableHeader';
import ReportTableList from './ReportTableList';
import ReportTableTotals from './ReportTableTotals';

export function ChooseGraph({
start,
end,
data,
mode,
graphType,
balanceType,
groupBy,
empty,
scrollWidth,
setScrollWidth,
months,
}) {
function saveScrollWidth(parent, child) {
let width = parent > 0 && child > 0 && parent - child;

setScrollWidth(!width ? 0 : width);
}

if (graphType === 'AreaGraph') {
return (
<AreaGraph
style={{ flexGrow: 1 }}
start={start}
end={end}
data={data}
balanceTypeOp={ReportOptions.balanceTypeMap.get(balanceType)}
/>
);
}
if (graphType === 'BarGraph') {
return (
<BarGraph
style={{ flexGrow: 1 }}
start={start}
end={end}
data={data}
groupBy={groupBy}
empty={empty}
balanceTypeOp={ReportOptions.balanceTypeMap.get(balanceType)}
/>
);
}
if (graphType === 'BarLineGraph') {
return (
<BarLineGraph
style={{ flexGrow: 1 }}
start={start}
end={end}
graphData={data.graphData}
/>
);
}
if (graphType === 'DonutGraph') {
return (
<DonutGraph
style={{ flexGrow: 1 }}
start={start}
end={end}
data={data}
groupBy={groupBy}
empty={empty}
balanceTypeOp={ReportOptions.balanceTypeMap.get(balanceType)}
/>
);
}
if (graphType === 'LineGraph') {
return (
<LineGraph
style={{ flexGrow: 1 }}
start={start}
end={end}
graphData={data.graphData}
/>
);
}
if (graphType === 'StackedBarGraph') {
return (
<StackedBarGraph
style={{ flexGrow: 1 }}
start={start}
end={end}
data={data}
balanceTypeOp={ReportOptions.balanceTypeMap.get(balanceType)}
/>
);
}
if (graphType === 'TableGraph') {
return (
<View
style={{
overflow: 'auto',
}}
>
<ReportTableHeader
interval={mode === 'time' && months}
scrollWidth={scrollWidth}
groupBy={groupBy}
balanceType={balanceType}
/>
<ReportTable saveScrollWidth={saveScrollWidth}>
<ReportTableList
data={data}
empty={empty}
monthsCount={months.length}
balanceTypeOp={ReportOptions.balanceTypeMap.get(balanceType)}
mode={mode}
groupBy={groupBy}
/>
<ReportTableTotals
scrollWidth={scrollWidth}
data={data}
mode={mode}
balanceTypeOp={ReportOptions.balanceTypeMap.get(balanceType)}
monthsCount={months.length}
balanceType={balanceType}
/>
</ReportTable>
</View>
);
}
}
127 changes: 71 additions & 56 deletions packages/desktop-client/src/components/reports/Header.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import { useLocation } from 'react-router-dom';

import * as monthUtils from 'loot-core/src/shared/months';

import ArrowLeft from '../../icons/v1/ArrowLeft';
Expand All @@ -8,15 +10,15 @@ import Select from '../common/Select';
import View from '../common/View';
import { FilterButton, AppliedFilters } from '../filters/FiltersMenu';

function validateStart(allMonths, start, end) {
export function validateStart(allMonths, start, end) {
const earliest = allMonths[allMonths.length - 1].name;
if (end < start) {
end = monthUtils.addMonths(start, 6);
}
return boundedRange(earliest, start, end);
}

function validateEnd(allMonths, start, end) {
export function validateEnd(allMonths, start, end) {
const earliest = allMonths[allMonths.length - 1].name;
if (start > end) {
start = monthUtils.subMonths(end, 6);
Expand All @@ -35,13 +37,13 @@ function boundedRange(earliest, start, end) {
return [start, end];
}

function getLatestRange(offset) {
export function getLatestRange(offset) {
const end = monthUtils.currentMonth();
const start = monthUtils.subMonths(end, offset);
return [start, end];
}

function getFullRange(allMonths) {
export function getFullRange(allMonths) {
const start = allMonths[allMonths.length - 1].name;
const end = monthUtils.currentMonth();
return [start, end];
Expand All @@ -61,7 +63,11 @@ function Header({
onDeleteFilter,
onCondOpChange,
headerPrefixItems,
selectGraph,
}) {
let location = useLocation();
let path = location.pathname;

return (
<View
style={{
Expand All @@ -79,70 +85,79 @@ function Header({
</ButtonLink>
<View style={styles.veryLargeText}>{title}</View>

<View
style={{
flexDirection: 'row',
alignItems: 'center',
marginTop: 15,
gap: 15,
}}
>
{headerPrefixItems}

{path !== '/reports/custom' && (
<View
style={{
flexDirection: 'row',
alignItems: 'center',
gap: 5,
marginTop: 15,
gap: 15,
}}
>
<Select
onChange={newValue =>
onChangeDates(...validateStart(allMonths, newValue, end))
}
value={start}
defaultLabel={monthUtils.format(start, 'MMMM, yyyy')}
options={allMonths.map(({ name, pretty }) => [name, pretty])}
/>
<View>to</View>
<Select
onChange={newValue =>
onChangeDates(...validateEnd(allMonths, start, newValue))
}
value={end}
options={allMonths.map(({ name, pretty }) => [name, pretty])}
/>
</View>
{headerPrefixItems}

{filters && <FilterButton onApply={onApply} />}
<View
style={{
flexDirection: 'row',
alignItems: 'center',
gap: 5,
}}
>
<Select
onChange={newValue =>
onChangeDates(...validateStart(allMonths, newValue, end))
}
value={start}
defaultLabel={monthUtils.format(start, 'MMMM, yyyy')}
options={allMonths.map(({ name, pretty }) => [name, pretty])}
/>
<View>to</View>
<Select
onChange={newValue =>
onChangeDates(...validateEnd(allMonths, start, newValue))
}
value={end}
options={allMonths.map(({ name, pretty }) => [name, pretty])}
/>
</View>

{filters && <FilterButton onApply={onApply} type="accounts" />}

{show1Month && (
{show1Month && (
<Button
type="bare"
onClick={() => onChangeDates(...getLatestRange(1))}
>
1 month
</Button>
)}
<Button
type="bare"
onClick={() => onChangeDates(...getLatestRange(1))}
onClick={() => onChangeDates(...getLatestRange(2))}
>
1 month
3 months
</Button>
)}
<Button type="bare" onClick={() => onChangeDates(...getLatestRange(2))}>
3 months
</Button>
<Button type="bare" onClick={() => onChangeDates(...getLatestRange(5))}>
6 months
</Button>
<Button
type="bare"
onClick={() => onChangeDates(...getLatestRange(11))}
>
1 Year
</Button>
<Button
type="bare"
onClick={() => onChangeDates(...getFullRange(allMonths))}
>
All Time
</Button>
</View>
<Button
type="bare"
onClick={() => onChangeDates(...getLatestRange(5))}
>
6 months
</Button>
<Button
type="bare"
onClick={() => onChangeDates(...getLatestRange(11))}
>
1 Year
</Button>
<Button
type="bare"
onClick={() => onChangeDates(...getFullRange(allMonths))}
>
All Time
</Button>
<View style={{ flex: 1 }} />
</View>
)}
{filters && filters.length > 0 && (
<View
style={{ marginTop: 5 }}
Expand Down
Loading

0 comments on commit 23fd3e2

Please sign in to comment.