Skip to content

Commit

Permalink
Merge pull request #244 from zhang-accounting/feat/search-functionality
Browse files Browse the repository at this point in the history
Feat/search functionality
  • Loading branch information
Kilerd committed Feb 26, 2024
2 parents 436a921 + 0e1b03b commit d614313
Show file tree
Hide file tree
Showing 18 changed files with 281 additions and 119 deletions.
2 changes: 1 addition & 1 deletion bindings/python/src/domain.rs
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ impl CommodityDomain {
}

#[pyclass]
pub struct TransactionHeaderDomain(pub zhang_core::store::TransactionHeaderDomain);
pub struct TransactionHeaderDomain(pub zhang_core::store::TransactionDomain);

#[pymethods]
impl TransactionHeaderDomain {
Expand Down
5 changes: 4 additions & 1 deletion frontend/public/locales/en/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,5 +27,8 @@
"TransactionDoesNotBalance": "Transaction does not balance",
"CommodityDoesNotDefine": "Try to use a undefined commodity",
"TransactionHasMultipleImplicitPosting": "Transaction has more than one implicit posting unit",
"CloseNonZeroAccount": "Trying to close an account with non zero balance"
"CloseNonZeroAccount": "Trying to close an account with non zero balance",

"ACCOUNT_FILTER_PLACEHOLDER": "filter by keyword...",
"ACCOUNT_FILTER_CLOSE_BUTTON_ARIA": "clean account filter keyword"
}
4 changes: 3 additions & 1 deletion frontend/public/locales/zh/translation.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,5 +19,7 @@
"NAV_RAW_EDITING": "编辑",
"NAV_TOOLS": "工具",
"NAV_SETTING": "设置",
"NAV_BUDGETS": "预算"
"NAV_BUDGETS": "预算",
"ACCOUNT_FILTER_PLACEHOLDER": "按关键字过滤...",
"ACCOUNT_FILTER_CLOSE_BUTTON_ARIA": "清空过滤关键字"
}
28 changes: 23 additions & 5 deletions frontend/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import Report from './pages/Report';
import Settings from './pages/Settings';
import SingleAccount from './pages/SingleAccount';
import SingleCommodity from './pages/SingleCommodity';
import { matchPath, useLocation } from 'react-router';

import { ActionIcon, Badge, Box, createStyles, Group, MediaQuery, Navbar, px, Text, TextInput, UnstyledButton, Anchor } from '@mantine/core';
import {
Expand Down Expand Up @@ -93,17 +94,23 @@ const useStyles = createStyles((theme) => ({
alignItems: 'center',
width: '100%',
fontSize: theme.fontSizes.sm,
padding: `${px(theme.spacing.sm)}px ${px(theme.spacing.xs)}px`,
margin: `${px(theme.spacing.sm) * 0.25}px 0`,
padding: `${px(theme.spacing.sm) * 0.75}px ${px(theme.spacing.xs)}px`,
borderRadius: theme.radius.sm,
fontWeight: 500,
color: theme.colorScheme === 'dark' ? theme.colors.dark[0] : theme.colors.gray[7],

border: `2px solid transparent`,
'&:hover': {
backgroundColor: theme.colorScheme === 'dark' ? theme.colors.dark[6] : theme.colors.gray[0],
borderColor: theme.colors[theme.primaryColor][6],
color: theme.colorScheme === 'dark' ? theme.white : theme.black,
},
},

activeMainLink: {
borderColor: theme.colors[theme.primaryColor][6],
},

mainLinkInner: {
display: 'flex',
alignItems: 'center',
Expand Down Expand Up @@ -177,6 +184,7 @@ export default function App() {
const { t } = useTranslation();
const dispatch = useAppDispatch();
const basicInfo = useAppSelector((state) => state.basic);
const location = useLocation();

useEffect(() => {
dispatch(fetchError(1));
Expand Down Expand Up @@ -240,7 +248,12 @@ export default function App() {
const { total_number } = useAppSelector((state) => state.errors);

const mainLinks = links.map((link) => (
<UnstyledButton component={RouteLink} to={link.uri} key={link.label} className={classes.mainLink}>
<UnstyledButton
component={RouteLink}
to={link.uri}
key={link.label}
className={`${classes.mainLink} ${matchPath(location.pathname, link.uri) ? classes.activeMainLink : ''}`}
>
<div className={classes.mainLinkInner}>
<link.icon size={20} className={classes.mainLinkIcon} stroke={1.5} />
<span>{t(link.label)}</span>
Expand Down Expand Up @@ -288,7 +301,12 @@ export default function App() {

<Navbar.Section grow className={classes.section} mx="sm">
<div className={classes.mainLinks}>
<UnstyledButton component={RouteLink} to={'/'} key={'NAV_HOME'} className={classes.mainLink}>
<UnstyledButton
component={RouteLink}
to={'/'}
key={'NAV_HOME'}
className={`${classes.mainLink} ${matchPath(location.pathname, '/') ? classes.activeMainLink : ''}`}
>
<div className={classes.mainLinkInner}>
<IconSmartHome size={20} className={classes.mainLinkIcon} stroke={1.5} />
<span>{t('NAV_HOME')}</span>
Expand All @@ -305,7 +323,7 @@ export default function App() {

{basicInfo.updatableVersion && (
<Navbar.Section className={classes.section}>
<Group position="center" spacing={"sm"}>
<Group position="center" spacing={'sm'}>
<Anchor href="https://github.com/zhang-accounting/zhang/wiki/Guide-of-Updating" target="_blank">
🎉 New Version is available!
</Anchor>
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/AccountDocumentUpload.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ export default function AccountDocumentUpload(props: Props) {
const [files, setFiles] = useState<FileWithPath[]>([]);

useEffect(() => {
console.log('files', files);
if (files.length > 0) {
const formData = new FormData();
files.forEach((file) => formData.append('file', file));
Expand Down
1 change: 0 additions & 1 deletion frontend/src/components/Amount.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ export default function Amount({ amount, currency, negetive, mask, className }:
const shouldMask = mask || false;
const shouldDisplayCurrencyName = !commodity?.prefix && !commodity?.suffix;

console.log('shouldDisplayCurrencyName', shouldDisplayCurrencyName, commodity, currency);
let parsedValue: BigNumber;
if (typeof amount === 'string') {
parsedValue = new BigNumber(amount);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export default function TransactionLine({ data, onClick }: Props) {
// const date = format(new Date(data.datetime), 'yyyy-MM-dd');
const time = format(new Date(data.datetime), 'hh:mm');
const trClick = () => {
console.log('clock');
if (onClick) {
onClick(data);
}
Expand Down
24 changes: 17 additions & 7 deletions frontend/src/pages/Accounts.tsx
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
import { Button, Chip, Container, Group, Table } from '@mantine/core';
import { useLocalStorage } from '@mantine/hooks';
import { Button, Checkbox, CloseButton, Container, Group, Input, Table } from '@mantine/core';
import { useInputState, useLocalStorage } from '@mantine/hooks';
import { useEffect } from 'react';
import AccountLine from '../components/AccountLine';
import { LoadingState } from '../rest-model';
import { useAppDispatch, useAppSelector } from '../states';
import { fetchAccounts, getAccountsTrie } from '../states/account';
import { Heading } from '../components/basic/Heading';
import { useTranslation } from 'react-i18next';
import { IconFilter } from '@tabler/icons';

export default function Accounts() {
const { t } = useTranslation();
const [filterKeyword, setFilterKeyword] = useInputState('');
const [hideClosedAccount, setHideClosedAccount] = useLocalStorage({ key: 'hideClosedAccount', defaultValue: false });
const dispatch = useAppDispatch();
const accountStatus = useAppSelector((state) => state.accounts.status);
const accountTrie = useAppSelector(getAccountsTrie(hideClosedAccount));
const accountTrie = useAppSelector(getAccountsTrie(hideClosedAccount, filterKeyword));

useEffect(() => {
if (accountStatus === LoadingState.NotReady) {
Expand All @@ -24,15 +26,23 @@ export default function Accounts() {
return (
<Container fluid>
<Heading title={`Accounts`}></Heading>
<Group my="lg">
<Input
icon={<IconFilter size="1rem" />}
placeholder={t('ACCOUNT_FILTER_PLACEHOLDER')}
value={filterKeyword}
onChange={setFilterKeyword}
rightSection={<CloseButton aria-label={t('ACCOUNT_FILTER_CLOSE_BUTTON_ARIA')} onClick={() => setFilterKeyword('')} />}
/>
</Group>
<Group my="lg">
<Button variant="outline" color="gray" radius="xl" size="xs" onClick={() => dispatch(fetchAccounts())}>
{t('REFRESH')}
</Button>
<Chip checked={hideClosedAccount} onChange={() => setHideClosedAccount(!hideClosedAccount)}>
Hide closed accounts
</Chip>
<Checkbox checked={hideClosedAccount} onChange={() => setHideClosedAccount(!hideClosedAccount)} label={'Hide closed accounts'} />
</Group>
<Table verticalSpacing="xs" highlightOnHover>

<Table verticalSpacing="xs" highlightOnHover withBorder>
<thead>
<tr>
<th>Name</th>
Expand Down
64 changes: 40 additions & 24 deletions frontend/src/pages/Journals.tsx
Original file line number Diff line number Diff line change
@@ -1,40 +1,53 @@
import { Button, Group, Pagination, Table } from '@mantine/core';
import { Button, CloseButton, Group, Input, Pagination, Table } from '@mantine/core';
import { format } from 'date-fns';
import { groupBy } from 'lodash';
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import TableViewJournalLine from '../components/journalLines/tableView/TableViewJournalLine';
import { LoadingState } from '../rest-model';
import { useAppDispatch, useAppSelector } from '../states';
import { fetchJournals, journalsSlice } from '../states/journals';
import { Heading } from '../components/basic/Heading';
import { useTranslation } from 'react-i18next';
import { useDebouncedValue } from '@mantine/hooks';
import { IconFilter } from '@tabler/icons';

function Journals() {
const { t } = useTranslation();
const [filter, setFilter] = useState('');
const [debouncedFilter] = useDebouncedValue(filter, 200);
const { current_page, status: journalStatus, items, total_number, total_page } = useAppSelector((state) => state.journals);
const dispatch = useAppDispatch();
useEffect(() => {
if (journalStatus === LoadingState.NotReady) {
dispatch(fetchJournals());
dispatch(fetchJournals(debouncedFilter));
}
}, [dispatch, journalStatus]);
}, [dispatch, journalStatus, debouncedFilter]);

useEffect(() => {
dispatch(fetchJournals(debouncedFilter));
}, [dispatch, debouncedFilter]);

const onPage = (page: number) => {
dispatch(journalsSlice.actions.setPage({ current_page: page }));
dispatch(fetchJournals());
dispatch(fetchJournals(debouncedFilter));
};

if (journalStatus === LoadingState.Loading || journalStatus === LoadingState.NotReady) return <>loading</>;

const groupedRecords = groupBy(items, (record) => format(new Date(record.datetime), 'yyyy-MM-dd'));

return (
<>
<Heading title={`${total_number} Journals`}></Heading>
<Group my="lg" px="sm">
<Button variant="outline" color="gray" radius="xl" size="xs" onClick={() => dispatch(fetchJournals())}>
<Button variant="outline" color="gray" radius="xl" size="xs" onClick={() => dispatch(fetchJournals(filter))}>
{t('REFRESH')}
</Button>
<Input
icon={<IconFilter size="1rem" />}
placeholder={t('ACCOUNT_FILTER_PLACEHOLDER')}
value={filter}
onChange={(event: any) => setFilter(event.currentTarget.value)}
rightSection={<CloseButton aria-label={t('ACCOUNT_FILTER_CLOSE_BUTTON_ARIA')} onClick={() => setFilter('')} />}
/>
</Group>
<Table verticalSpacing="xs" withBorder>
<thead>
Expand All @@ -48,24 +61,27 @@ function Journals() {
</tr>
</thead>
<tbody>
{Object.entries(groupedRecords).map((entry) => {
return (
<>
<tr>
<td colSpan={6}>
<b>{entry[0]}</b>
</td>
</tr>
{entry[1].map((journal) => (
<>
<TableViewJournalLine key={journal.id} data={journal} />
</>
))}
</>
);
})}
{journalStatus === LoadingState.Success &&
Object.entries(groupedRecords).map((entry) => {
return (
<>
<tr>
<td colSpan={6}>
<b>{entry[0]}</b>
</td>
</tr>
{entry[1].map((journal) => (
<>
<TableViewJournalLine key={journal.id} data={journal} />
</>
))}
</>
);
})}
</tbody>
</Table>

{(journalStatus === LoadingState.Loading || journalStatus === LoadingState.NotReady) && <p>loading</p>}
<Pagination my="xs" total={total_page} value={current_page} onChange={onPage} position="center" />
</>
);
Expand Down
2 changes: 0 additions & 2 deletions frontend/src/pages/Report.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ export default function Report() {
]);

useEffect(() => {
console.log('value is ', value);
if (value[0] !== null && value[1] !== null) {
console.log('update value', value);
setDateRange([value[0], value[1]]);
}
}, [value]);
Expand Down
11 changes: 9 additions & 2 deletions frontend/src/states/account.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,18 @@ export const accountsSlice = createSlice({
});

export const getAccountByName = (name: string) => (state: RootState) => state.accounts.dataMap[name];
export const getAccountsTrie = (hideClosedAccount: boolean) => (state: RootState) => {
export const getAccountsTrie = (hideClosedAccount: boolean, filterKeyword: string) => (state: RootState) => {
const data = state.accounts.data;
let trie = new AccountTrie();
for (let account of data.filter((it) => (hideClosedAccount ? it.status === AccountStatus.Open : true))) {
trie.insert(account);
let trimmedKeyword = filterKeyword.trim();
if (trimmedKeyword !== '') {
if (account.name.toLowerCase().includes(trimmedKeyword.toLowerCase()) || (account.alias?.toLowerCase() ?? '').includes(trimmedKeyword.toLowerCase())) {
trie.insert(account);
}
} else {
trie.insert(account);
}
}
return trie;
};
Expand Down
5 changes: 3 additions & 2 deletions frontend/src/states/journals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { RootState } from '.';
import { fetcher } from '..';
import { LoadingState } from '../rest-model';

export const fetchJournals = createAsyncThunk('journals/fetch', async (args, { getState }) => {
export const fetchJournals = createAsyncThunk('journals/fetch', async (keyword: string, { getState }) => {
const current_page = (getState() as RootState).journals.current_page;
const ret = await fetcher(`/api/journals?page=${current_page}`);
const url = keyword.trim() === '' ? `/api/journals?page=${current_page}` : `/api/journals?page=${current_page}&keyword=${keyword.trim()}`;
const ret = await fetcher(url);
return ret;
});

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
option "operating_currency" "CNY"

1970-01-01 open Assets:BankCard CNY

1970-01-01 open Expenses:Food CNY

2023-12-02 "KFC" "VME50 Package"
Assets:BankCard -50 CNY
Expenses:Food

0 comments on commit d614313

Please sign in to comment.