Skip to content

Commit

Permalink
Custom Reports AutoComplete (actualbudget#2350)
Browse files Browse the repository at this point in the history
* updated saved work

* merge fixes

* Disable CREATE TABLE

* notes

* turn on db table

* Fix TableGraph recall crash

* table format changes

* type fixes

* fixing some card displays

* merge fixes

* revert table change

* cardMenu width

* Add Saved Reports Autocomplete

* notes

* fix invalid values crash

* Title and auto-focus and esc

* notes

* fix filtering logic

* reload saved filters

* lint fix

* visual graph changes

* merge fixes

* fix

* review updates
  • Loading branch information
carkom authored Feb 28, 2024
1 parent 3600626 commit 38b9869
Show file tree
Hide file tree
Showing 13 changed files with 319 additions and 107 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import React, { type ComponentProps } from 'react';

import { useReports } from 'loot-core/client/data-hooks/reports';
import { type CustomReportEntity } from 'loot-core/src/types/models/reports';

import { Autocomplete } from './Autocomplete';
import { ReportList } from './ReportList';

export function ReportAutocomplete({
embedded,
...props
}: {
embedded?: boolean;
} & ComponentProps<typeof Autocomplete<CustomReportEntity>>) {
const reports = useReports() || [];

return (
<Autocomplete
strict={true}
highlightFirst={true}
embedded={embedded}
suggestions={reports}
renderItems={(items, getItemProps, highlightedIndex) => (
<ReportList
items={items}
getItemProps={getItemProps}
highlightedIndex={highlightedIndex}
embedded={embedded}
/>
)}
{...props}
/>
);
}
52 changes: 52 additions & 0 deletions packages/desktop-client/src/components/autocomplete/ReportList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import React, { Fragment, type ComponentProps } from 'react';

import { theme } from '../../style/theme';
import { View } from '../common/View';

import { ItemHeader } from './ItemHeader';

export function ReportList<T extends { id: string; name: string }>({
items,
getItemProps,
highlightedIndex,
embedded,
}: {
items: T[];
getItemProps: (arg: { item: T }) => ComponentProps<typeof View>;
highlightedIndex: number;
embedded?: boolean;
}) {
return (
<View>
<View
style={{
overflow: 'auto',
padding: '5px 0',
...(!embedded && { maxHeight: 175 }),
}}
>
<Fragment>{ItemHeader({ title: 'Saved Reports' })}</Fragment>
{items.map((item, idx) => {
return [
<div
{...(getItemProps ? getItemProps({ item }) : null)}
key={item.id}
style={{
backgroundColor:
highlightedIndex === idx
? theme.menuAutoCompleteBackgroundHover
: 'transparent',
padding: 4,
paddingLeft: 20,
borderRadius: embedded ? 4 : 0,
}}
data-highlighted={highlightedIndex === idx || undefined}
>
{item.name}
</div>,
];
})}
</View>
</View>
);
}
4 changes: 2 additions & 2 deletions packages/desktop-client/src/components/common/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,11 @@ type MenuItem = {
tooltip?: string;
};

type MenuProps<T extends MenuItem = MenuItem> = {
export type MenuProps<T extends MenuItem = MenuItem> = {
header?: ReactNode;
footer?: ReactNode;
items: Array<T | typeof Menu.line>;
onMenuSelect: (itemName: T['name']) => void;
onMenuSelect?: (itemName: T['name']) => void;
style?: CSSProperties;
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ const startDate = monthUtils.subMonths(monthUtils.currentMonth(), 5);
const endDate = monthUtils.currentMonth();

export const defaultReport: CustomReportEntity = {
id: '',
name: '',
startDate,
endDate,
isDateStatic: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ export function ReportTopbar({
onApplyFilter,
onChangeViews,
onReportChange,
onResetReports,
}) {
return (
<View
Expand Down Expand Up @@ -179,7 +178,6 @@ export function ReportTopbar({
report={report}
savedStatus={savedStatus}
onReportChange={onReportChange}
onResetReports={onResetReports}
/>
</View>
);
Expand Down
31 changes: 25 additions & 6 deletions packages/desktop-client/src/components/reports/SaveReport.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import React, { createRef, useState } from 'react';

import { useReports } from 'loot-core/client/data-hooks/reports';
import { send, sendCatch } from 'loot-core/src/platform/client/fetch';
import { type CustomReportEntity } from 'loot-core/src/types/models';

Expand All @@ -8,6 +9,7 @@ import { Button } from '../common/Button';
import { Text } from '../common/Text';
import { View } from '../common/View';

import { SaveReportChoose } from './SaveReportChoose';
import { SaveReportMenu } from './SaveReportMenu';
import { SaveReportName } from './SaveReportName';

Expand All @@ -22,23 +24,30 @@ type SaveReportProps<T extends CustomReportEntity = CustomReportEntity> = {
savedReport?: T;
type: string;
}) => void;
onResetReports: () => void;
};

export function SaveReport({
customReportItems,
report,
savedStatus,
onReportChange,
onResetReports,
}: SaveReportProps) {
const listReports = useReports();
const [nameMenuOpen, setNameMenuOpen] = useState(false);
const [menuOpen, setMenuOpen] = useState(false);
const [chooseMenuOpen, setChooseMenuOpen] = useState(false);
const [menuItem, setMenuItem] = useState('');
const [err, setErr] = useState('');
const [name, setName] = useState(report.name ?? '');
const inputRef = createRef<HTMLInputElement>();

async function onApply(cond: string) {
const chooseSavedReport = listReports.find(r => cond === r.id);
onReportChange({ savedReport: chooseSavedReport, type: 'choose' });
setChooseMenuOpen(false);
setName(chooseSavedReport === undefined ? '' : chooseSavedReport.name);
}

const onAddUpdate = async (menuChoice: string) => {
if (menuChoice === 'save-report') {
const newSavedReport = {
Expand Down Expand Up @@ -78,7 +87,6 @@ export function SaveReport({
setNameMenuOpen(true);
return;
}

setNameMenuOpen(false);
onReportChange({
savedReport: updatedReport,
Expand All @@ -98,7 +106,7 @@ export function SaveReport({
setMenuOpen(false);
setName('');
await send('report/delete', report.id);
onResetReports();
onReportChange({ type: 'reset' });
break;
case 'update-report':
setErr('');
Expand All @@ -117,7 +125,12 @@ export function SaveReport({
case 'reset-report':
setMenuOpen(false);
setName('');
onResetReports();
onReportChange({ type: 'reset' });
break;
case 'choose-report':
setErr('');
setMenuOpen(false);
setChooseMenuOpen(true);
break;
default:
}
Expand Down Expand Up @@ -153,9 +166,9 @@ export function SaveReport({
{menuOpen && (
<SaveReportMenu
onClose={() => setMenuOpen(false)}
report={report}
onMenuSelect={onMenuSelect}
savedStatus={savedStatus}
listReports={listReports && listReports.length}
/>
)}
{nameMenuOpen && (
Expand All @@ -169,6 +182,12 @@ export function SaveReport({
err={err}
/>
)}
{chooseMenuOpen && (
<SaveReportChoose
onApply={onApply}
onClose={() => setChooseMenuOpen(false)}
/>
)}
</View>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import React, { createRef, useEffect, useState } from 'react';

import { theme } from '../../style/theme';
import { Button } from '../common/Button';
import { Stack } from '../common/Stack';
import { Text } from '../common/Text';
import { View } from '../common/View';
import { Tooltip } from '../tooltips';
import { GenericInput } from '../util/GenericInput';

type SaveReportChooseProps = {
onApply: (cond: string) => void;
onClose: () => void;
};

export function SaveReportChoose({ onApply, onClose }: SaveReportChooseProps) {
const inputRef = createRef<HTMLInputElement>();
const [err, setErr] = useState('');
const [value, setValue] = useState('');

useEffect(() => {
if (inputRef.current) {
inputRef.current.focus();
}
});

return (
<Tooltip
position="bottom-right"
style={{ padding: 15, color: theme.menuItemText }}
width={275}
onClose={onClose}
>
<form>
<View style={{ flexDirection: 'row', align: 'center' }}>
<Text style={{ userSelect: 'none', flex: 1 }}>Choose Report</Text>
<View style={{ flex: 1 }} />
</View>
<GenericInput
inputRef={inputRef}
field="report"
subfield={null}
type="saved"
value={value}
multi={false}
style={{ marginTop: 10 }}
onChange={(v: string) => setValue(v)}
/>

<Stack
direction="row"
justify="flex-end"
align="center"
style={{ marginTop: 15 }}
>
<View style={{ flex: 1 }} />
<Button
type="primary"
onClick={e => {
e.preventDefault();
if (!value) {
setErr('Invalid report entered');
return;
}

onApply(value);
}}
>
Apply
</Button>
</Stack>
</form>
{err !== '' ? (
<Stack direction="row" align="center" style={{ padding: 10 }}>
<Text style={{ color: theme.errorText }}>{err}</Text>
</Stack>
) : (
<View />
)}
</Tooltip>
);
}
Loading

0 comments on commit 38b9869

Please sign in to comment.