Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feat/link and tags #368

Merged
merged 5 commits into from
Sep 10, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 8 additions & 8 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@
"@emotion/serialize": "^1.1.4",
"@emotion/styled": "^11.11.5",
"@emotion/utils": "^1.2.1",
"@mantine/charts": "^7.9.1",
"@mantine/core": "^7.9.1",
"@mantine/dates": "^7.9.1",
"@mantine/dropzone": "^7.9.1",
"@mantine/emotion": "^7.9.1",
"@mantine/hooks": "^7.9.1",
"@mantine/modals": "^7.9.1",
"@mantine/notifications": "^7.9.1",
"@mantine/charts": "^7.12.0",
"@mantine/core": "^7.12.0",
"@mantine/dates": "^7.12.0",
"@mantine/dropzone": "^7.12.0",
"@mantine/emotion": "^7.12.0",
"@mantine/hooks": "^7.12.0",
"@mantine/modals": "^7.12.0",
"@mantine/notifications": "^7.12.0",
"@tabler/icons-react": "^3.3.0",
"@uiw/react-codemirror": "^4.22.0",
"axios": "^1.6.8",
Expand Down
160 changes: 84 additions & 76 deletions frontend/pnpm-lock.yaml

Large diffs are not rendered by default.

17 changes: 8 additions & 9 deletions frontend/src/components/ReportGraph.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function ChartTooltip({
</Text>
{payload.map((item: any) => (
<Text key={item.name} c={item.color} fz="sm">
{item.name}: <Amount amount={item.name !== 'total' ? item.value - total_min : item.value} currency={commodity}></Amount>
{item.name}: <Amount amount={item.value} currency={commodity}></Amount>
</Text>
))}
</Paper>
Expand All @@ -44,17 +44,16 @@ export default function ReportGraph(props: Props) {
const target_day = props.data.balances[date];
return new BigNumber(target_day.calculated.number).toNumber();
});
let total_domain = [min(total_dataset) ?? 0, max(total_dataset) ?? 0];
let total_domain = [(min(total_dataset) ?? 0) * 0.999, (max(total_dataset) ?? 0) * 1.001];

const income_dataset = sequencedDate
.map((date) => props.data.changes[date]?.[AccountType.Income])
.map((amount) => -1 * new BigNumber(amount?.calculated.number ?? '0').toNumber())
.map((amount) => amount + total_domain[0]);
.map((amount) => -1 * new BigNumber(amount?.calculated.number ?? '0').toNumber());

const expense_dataset = sequencedDate
.map((date) => props.data.changes[date]?.[AccountType.Expenses])
.map((amount) => new BigNumber(amount?.calculated.number ?? '0').toNumber())
.map((amount) => amount + total_domain[0]);
.map((amount) => new BigNumber(amount?.calculated.number ?? '0').toNumber());

const data = labels.map((label, idx) => ({
date: label,
total: total_dataset[idx],
Expand All @@ -69,7 +68,7 @@ export default function ReportGraph(props: Props) {
data={data}
dataKey="date"
withDots={false}
withYAxis={false}
withRightYAxis
withLegend
tooltipProps={{
content: ({ label, payload }) => (
Expand All @@ -79,8 +78,8 @@ export default function ReportGraph(props: Props) {
yAxisProps={{ type: 'number', scale: 'log', domain: total_domain }}
series={[
{ name: 'total', color: 'violet.6' },
{ name: 'income', color: 'indigo.6' },
{ name: 'expense', color: 'pink' },
{ name: 'income', color: 'indigo.6', yAxisId: 'right' },
{ name: 'expense', color: 'pink', yAxisId: 'right' },
]}
connectNulls
curveType="bump"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ActionIcon, Badge, Group, Table } from '@mantine/core';
import { ActionIcon, Badge, Group, Stack, Table, Text } from '@mantine/core';
import { IconFile, IconPencil, IconZoomExclamation } from '@tabler/icons-react';
import { format } from 'date-fns';
import { JournalTransactionItem } from '../../../rest-model';
Expand All @@ -7,6 +7,8 @@ import Amount from '../../Amount';
import { openContextModal } from '@mantine/modals';
import PayeeNarration from '../../basic/PayeeNarration';
import { createStyles, getStylesRef } from '@mantine/emotion';
import { journalLinksAtom, journalTagsAtom } from '../../../states/journals';
import { useAtom } from 'jotai';

const useStyles = createStyles((theme, _, u) => ({
positiveAmount: {
Expand Down Expand Up @@ -51,6 +53,26 @@ export default function TableViewTransactionLine({ data }: Props) {

const time = format(new Date(data.datetime), 'HH:mm:ss');

const [, setJournalTags] = useAtom(journalTagsAtom);
const [, setJournalLinks] = useAtom(journalLinksAtom);

const handleTagClick = (tag: string) => () => {
setJournalTags((prevTags) => {
if (prevTags.includes(tag)) {
return prevTags;
}
return [...prevTags, tag];
});
};
const handleLinkClick = (link: string) => () => {
setJournalLinks((prevLinks) => {
if (prevLinks.includes(link)) {
return prevLinks;
}
return [...prevLinks, link];
});
};

const openPreviewModal = (e: any) => {
openContextModal({
modal: 'transactionPreviewModal',
Expand Down Expand Up @@ -85,10 +107,24 @@ export default function TableViewTransactionLine({ data }: Props) {
</Badge>
</Table.Td>
<Table.Td>
<Group align="center" gap="xs">
<PayeeNarration payee={data.payee} narration={data.narration} />
{hasDocuments && <IconFile size="1rem" color={'gray'} stroke={1}></IconFile>}
</Group>
<Stack gap={'xs'}>
<Group align="center" gap="xs">
<PayeeNarration payee={data.payee} narration={data.narration} />
{data.links &&
data.links.map((it) => (
<Badge key={it} color="blue" variant="outline" size="xs" onClick={() => handleLinkClick(it)()}>
^{it}
</Badge>
))}
{data.tags &&
data.tags.map((tag) => (
<Badge key={tag} color="blue" variant="outline" size="xs" onClick={() => handleTagClick(tag)()}>
#{tag}
</Badge>
))}
{hasDocuments && <IconFile size="1rem" color={'gray'} stroke={1}></IconFile>}
</Group>
</Stack>
</Table.Td>
<Table.Td>
{Array.from(summary.values()).map((each) => (
Expand Down
29 changes: 27 additions & 2 deletions frontend/src/pages/Journals.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { Button, CloseButton, Group, Input, Pagination, Table, Text } from '@mantine/core';
import { Badge, Button, CloseButton, Group, Input, Pagination, Pill, Table, Text } from '@mantine/core';
import { useEffect, useMemo, useState } from 'react';
import TableViewJournalLine from '../components/journalLines/tableView/TableViewJournalLine';
import { Heading } from '../components/basic/Heading';
Expand All @@ -8,7 +8,7 @@ import { IconFilter } from '@tabler/icons-react';
import { JournalListSkeleton } from '../components/skeletons/journalListSkeleton';
import { useAtomValue } from 'jotai/index';
import { titleAtom } from '../states/basic';
import { groupedJournalsAtom, journalAtom, journalFetcher, journalKeywordAtom, journalPageAtom } from '../states/journals';
import { groupedJournalsAtom, journalAtom, journalFetcher, journalKeywordAtom, journalLinksAtom, journalPageAtom, journalTagsAtom } from '../states/journals';
import { useAtom, useSetAtom } from 'jotai';
import { loadable_unwrap } from '../states';
import { selectAtom } from 'jotai/utils';
Expand All @@ -29,6 +29,19 @@ function Journals() {
const total_count = useAtomValue(useMemo(() => selectAtom(journalAtom, (val) => loadable_unwrap(val, 0, (val) => val.total_count)), []));
const total_page = useAtomValue(useMemo(() => selectAtom(journalAtom, (val) => loadable_unwrap(val, 0, (val) => val.total_page)), []));

const [journalTags, setJournalTags] = useAtom(journalTagsAtom);
const [journalLinks, setJournalLinks] = useAtom(journalLinksAtom);

const removeTag = (tagToRemove: string) => {
let newTags = journalTags.filter((tag) => tag !== tagToRemove);
setJournalTags(newTags);
};

const removeLink = (linkToRemove: string) => {
let newLinks = journalLinks.filter((tag) => tag !== linkToRemove);
setJournalLinks(newLinks);
};

useEffect(() => {
setKeyword(debouncedFilter);
}, [setKeyword, debouncedFilter]);
Expand All @@ -52,6 +65,18 @@ function Journals() {
rightSection={<CloseButton aria-label={t('ACCOUNT_FILTER_CLOSE_BUTTON_ARIA')} onClick={() => setFilter('')} />}
/>
</Group>
<Group my="lg" px="sm">
{journalTags.map((tag, index) => (
<Pill key={index} withRemoveButton onRemove={() => removeTag(tag)}>
#{tag}
</Pill>
))}
{journalLinks.map((link, index) => (
<Pill key={index} withRemoveButton onRemove={() => removeLink(link)}>
^{link}
</Pill>
))}
</Group>
<Table verticalSpacing="xs" withTableBorder>
<Table.Thead>
<Table.Tr>
Expand Down
20 changes: 19 additions & 1 deletion frontend/src/states/journals.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,29 @@ import { format } from 'date-fns';

export const journalKeywordAtom = atom('');
export const journalPageAtom = atom(1);
export const journalTagsAtom = atom<string[]>([]);
export const journalLinksAtom = atom<string[]>([]);

export const journalFetcher = atomWithRefresh(async (get) => {
const page = get(journalPageAtom);
const keyword = get(journalKeywordAtom);
const url = keyword.trim() === '' ? `/api/journals?page=${page}` : `/api/journals?page=${page}&keyword=${keyword.trim()}`;
const tags = get(journalTagsAtom);
const links = get(journalLinksAtom);

let url = `/api/journals?page=${page}`;

if (keyword.trim() !== '') {
url += `&keyword=${encodeURIComponent(keyword.trim())}`;
}

if (tags.length > 0) {
url += tags.map((tag) => `&tags[]=${encodeURIComponent(tag)}`).join('');
}

if (links.length > 0) {
url += links.map((link) => `&links[]=${encodeURIComponent(link)}`).join('');
}

const ret = await fetcher<Pageable<JournalItem>>(url);
return ret;
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,8 @@ option "operating_currency" "CNY"
2023-12-02 "KFC" "VME50 Package"
Assets:BankCard -50 CNY
Expenses:Food


2023-12-02 "MC" "VME50 Package"
Assets:BankCard -50 CNY
Expenses:Food
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"validations": [
[
"$.data.records.length()",
1
2
],
[
"$.data.records[0].postings.length()",
Expand Down Expand Up @@ -43,7 +43,7 @@
"validations": [
[
"$.data.records.length()",
1
2
],
[
"$.data.records[0].postings.length()",
Expand All @@ -56,7 +56,7 @@
"validations": [
[
"$.data.records.length()",
1
2
],
[
"$.data.records[0].postings.length()",
Expand All @@ -69,15 +69,15 @@
"validations": [
[
"$.data.records.length()",
1
2
],
[
"$.data.records[0].postings.length()",
2
],
[
"$.data.total_count",
1
2
]
]
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
option "operating_currency" "CNY"

1970-01-01 open Assets:BankCard CNY

1970-01-01 open Expenses:Food CNY

2023-12-02 "KFC" "VME50 Package" #tag1 #tag2 ^link1 ^link2
Assets:BankCard -50 CNY
Expenses:Food


2023-12-02 "MC" "VME50 Package" #tag1 ^link1
Assets:BankCard -50 CNY
Expenses:Food
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
[
{
"uri": "/api/journals",
"validations": [
[
"$.data.records.length()",
2
],
[
"$.data.records[0].postings.length()",
2
]
]
},
{
"uri": "/api/journals?tags[]=tag1",
"validations": [
[
"$.data.records.length()",
2
]
]
},
{
"uri": "/api/journals?tags[]=tag2",
"validations": [
[
"$.data.records.length()",
1
]
]
}
]
Loading
Loading