Skip to content

Commit

Permalink
Merge pull request #43 from dsaltares/feat/26
Browse files Browse the repository at this point in the history
feat: 26 - use autocomplete for categories and accounts in report settings dialog
  • Loading branch information
dsaltares authored Jan 4, 2025
2 parents cc67cab + 4e5d4f5 commit 4c644b6
Show file tree
Hide file tree
Showing 7 changed files with 309 additions and 140 deletions.
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,9 +64,9 @@
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@fontsource/roboto-mono": "^5.1.0",
"@mui/icons-material": "^6.1.0",
"@mui/icons-material": "^6.3.1",
"@mui/lab": "^6.0.0-beta.9",
"@mui/material": "^6.1.0",
"@mui/material": "^6.3.1",
"@mui/x-date-pickers": "^7.16.0",
"@tanstack/react-query": "^5.56.2",
"@tanstack/react-query-devtools": "^5.56.2",
Expand Down
90 changes: 90 additions & 0 deletions src/components/AccountAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { useCallback, useMemo } from 'react';
import Checkbox from '@mui/material/Checkbox';
import FormControl from '@mui/material/FormControl';
import Stack from '@mui/material/Stack';
import Autocomplete from '@mui/material/Autocomplete';
import SelectAllIcon from '@mui/icons-material/SelectAll';
import DeselectIcon from '@mui/icons-material/Deselect';
import TextField from '@mui/material/TextField';
import IconButton from '@mui/material/IconButton';
import type { Account } from '@server/accounts/types';
import { isOptionEqualToValue } from '@lib/autoCompleteOptions';

type Props = {
accounts: Account[];
selected: number[];
onChange: (selected: number[]) => void;
};

const id = 'account-autocomplete';

export default function AccountAutocomplete({
accounts,
selected,
onChange,
}: Props) {
const accountsById = useMemo(
() =>
accounts.reduce<Record<string, Account>>(
(acc, account) => ({ ...acc, [account.id]: account }),
{},
),
[accounts],
);
const value = useMemo(
() =>
selected.map((id) => {
const category = accountsById[id];
return {
id: category.id.toString(),
label: category.name,
};
}),
[selected, accountsById],
);
const accountOptions = useMemo(
() => accounts.map(({ id, name }) => ({ id: id.toString(), label: name })),
[accounts],
);
const allSelected = selected.length === accounts.length;
const selectAll = useCallback(
() => onChange(accounts.map((account) => account.id)),
[accounts, onChange],
);
const deselectAll = useCallback(() => onChange([]), [onChange]);

return (
<FormControl fullWidth>
<Stack direction="row" spacing={1} alignItems="center">
<Autocomplete
multiple
fullWidth
disableCloseOnSelect
id={id}
value={value}
options={accountOptions}
isOptionEqualToValue={isOptionEqualToValue}
getOptionLabel={(option) => option.label}
onChange={(_event, newValue) =>
onChange(newValue.map((option) => Number(option.id)))
}
renderOption={(props, option, { selected }) => {
const { key, ...optionProps } = props;
return (
<li key={key} {...optionProps}>
<Checkbox style={{ marginRight: 8 }} checked={selected} />
{option.label}
</li>
);
}}
renderInput={(params) => <TextField {...params} label="Accounts" />}
/>
<div>
<IconButton onClick={allSelected ? deselectAll : selectAll}>
{allSelected ? <DeselectIcon /> : <SelectAllIcon />}
</IconButton>
</div>
</Stack>
</FormControl>
);
}
54 changes: 0 additions & 54 deletions src/components/AccountSelect.tsx

This file was deleted.

90 changes: 90 additions & 0 deletions src/components/CategoryAutocomplete.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import { useCallback, useMemo } from 'react';
import Checkbox from '@mui/material/Checkbox';
import FormControl from '@mui/material/FormControl';
import Autocomplete from '@mui/material/Autocomplete';
import TextField from '@mui/material/TextField';
import IconButton from '@mui/material/IconButton';
import SelectAllIcon from '@mui/icons-material/SelectAll';
import DeselectIcon from '@mui/icons-material/Deselect';
import Stack from '@mui/material/Stack';
import { isOptionEqualToValue } from '@lib/autoCompleteOptions';
import type { Category } from '@server/category/types';

type Props = {
categories: Category[];
selected: number[];
onChange: (selected: number[]) => void;
};

const id = 'category-autocomplete';

export default function CategoryAutocomplete({
categories,
selected,
onChange,
}: Props) {
const categoriesById = useMemo(
() =>
categories.reduce<Record<string, Category>>(
(acc, category) => ({ ...acc, [category.id]: category }),
{},
),
[categories],
);
const value = useMemo(
() =>
selected.map((id) => {
const category = categoriesById[id];
return {
id: category.id.toString(),
label: category.name,
};
}),
[selected, categoriesById],
);
const categoryOptions = useMemo(
() =>
categories.map(({ id, name }) => ({ id: id.toString(), label: name })),
[categories],
);
const allSelected = selected.length === categories.length;
const selectAll = useCallback(
() => onChange(categories.map((category) => category.id)),
[categories, onChange],
);
const deselectAll = useCallback(() => onChange([]), [onChange]);
return (
<FormControl fullWidth>
<Stack direction="row" spacing={1} alignItems="center">
<Autocomplete
multiple
fullWidth
disableCloseOnSelect
id={id}
value={value}
options={categoryOptions}
isOptionEqualToValue={isOptionEqualToValue}
getOptionLabel={(option) => option.label}
onChange={(_event, newValue) =>
onChange(newValue.map((option) => Number(option.id)))
}
renderOption={(props, option, { selected }) => {
const { key, ...optionProps } = props;
return (
<li key={key} {...optionProps}>
<Checkbox style={{ marginRight: 8 }} checked={selected} />
{option.label}
</li>
);
}}
renderInput={(params) => <TextField {...params} label="Categories" />}
/>
<div>
<IconButton onClick={allSelected ? deselectAll : selectAll}>
{allSelected ? <DeselectIcon /> : <SelectAllIcon />}
</IconButton>
</div>
</Stack>
</FormControl>
);
}
58 changes: 0 additions & 58 deletions src/components/CategorySelect.tsx

This file was deleted.

8 changes: 4 additions & 4 deletions src/components/Reports/ReportSettingsDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,8 @@ import { isPeriod, type Period } from '@server/types';
import type { Category } from '@server/category/types';
import type { Account } from '@server/accounts/types';
import PeriodSelect from '@components/PeriodSelect';
import AccountSelect from '@components/AccountSelect';
import CategorySelect from '@components/CategorySelect';
import AccountAutocomplete from '@components/AccountAutocomplete';
import CategoryAutocomplete from '@components/CategoryAutocomplete';
import TimeGranularitySelect from '@components/TimeGranularitySelect';
import CurrencyAutocomplete from '@components/CurrencyAutocomplete';

Expand Down Expand Up @@ -157,12 +157,12 @@ export default function ReportSettingsDialog({
/>
</Stack>
</Stack>
<AccountSelect
<AccountAutocomplete
accounts={accounts}
selected={selectedAccounts}
onChange={setSelectedAccounts}
/>
<CategorySelect
<CategoryAutocomplete
categories={categories}
selected={selectedCategories}
onChange={setSelectedCategories}
Expand Down
Loading

0 comments on commit 4c644b6

Please sign in to comment.