Skip to content

Commit

Permalink
feat(TransactionsImport): match contribution
Browse files Browse the repository at this point in the history
  • Loading branch information
Betree committed Jun 20, 2024
1 parent 0cea006 commit 06a5938
Show file tree
Hide file tree
Showing 3 changed files with 338 additions and 13 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,290 @@
import React from 'react';
import { startCase } from 'lodash';
import { Ban, ExternalLink, Filter, ListPlus, RefreshCcw, Save, ScanSearch, Search } from 'lucide-react';

Check failure on line 3 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

'ListPlus' is defined but never used

Check failure on line 3 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

'RefreshCcw' is defined but never used

Check failure on line 3 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

'ScanSearch' is defined but never used

Check failure on line 3 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

'Search' is defined but never used
import { FormattedMessage } from 'react-intl';
import { z } from 'zod';

import type { FilterComponentConfigs, FiltersToVariables } from '../../../../lib/filters/filter-types';
import { integer } from '../../../../lib/filters/schemas';
import type { Account, TransactionsImportRow } from '../../../../lib/graphql/types/v2/graphql';
import useQueryFilter from '../../../../lib/hooks/useQueryFilter';

import Avatar from '../../../Avatar';

Check failure on line 12 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

'Avatar' is defined but never used
import FormattedMoneyAmount from '../../../FormattedMoneyAmount';
import Link from '../../../Link';

Check failure on line 14 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

'Link' is defined but never used
import LinkCollective from '../../../LinkCollective';
import StyledLink from '../../../StyledLink';

Check failure on line 16 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

'StyledLink' is defined but never used
import { DataTable } from '../../../table/DataTable';
import { Button } from '../../../ui/Button';
import { Checkbox } from '../../../ui/Checkbox';

Check failure on line 19 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

'Checkbox' is defined but never used
import { Dialog, DialogContent, DialogHeader } from '../../../ui/Dialog';
import { Input } from '../../../ui/Input';

Check failure on line 21 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

'Input' is defined but never used
import { RadioGroup, RadioGroupItem } from '../../../ui/RadioGroup';
import { amountFilter } from '../../filters/AmountFilter';
import { AmountFilterType } from '../../filters/AmountFilter/schema';
import { dateFilter, expectedDateFilter } from '../../filters/DateFilter';
import { expectedFundsFilter } from '../../filters/ExpectedFundsFilter';
import { Filterbar } from '../../filters/Filterbar';
import { orderByFilter } from '../../filters/OrderFilter';
import { SearchFilter, searchFilter } from '../../filters/SearchFilter';

Check failure on line 29 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / lint

'SearchFilter' is defined but never used

Check failure on line 29 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / typescript

'"../../filters/SearchFilter"' has no exported member named 'SearchFilter'. Did you mean 'searchFilter'?
import type { FilterMeta } from '../contributions/filters';
import { OrderTypeFilter } from '../contributions/filters';
import { FilterValues } from '../transactions/HostTransactions';

const MOCK_MATCHES = [
{
id: 45624,
description: 'Contribution from John Doe to Webpack',
date: 'June 1, 2023',
account: {
id: 12345,
slug: 'webpack',
name: 'Webpack',
},
amount: {
valueInCents: 1000,
currency: 'USD',
},
},
{
id: 78945,
description: 'Contribution from Toto to Babel',
date: 'June 1, 2023',
account: {
id: 67890,
slug: 'babel',
name: 'Babel',
},
amount: {
valueInCents: 500,
currency: 'USD',
},
},
{
id: 12345,
description: 'Contribution from Mike to Webpack',
date: 'June 1, 2023',
account: {
id: 12345,
slug: 'webpack',
name: 'Webpack',
},
amount: {
valueInCents: 1000,
currency: 'USD',
},
},
];

const NB_CONTRIBUTIONS_DISPLAYED = 5;

export const filtersSchema = z.object({
limit: integer.default(NB_CONTRIBUTIONS_DISPLAYED),
offset: integer.default(0),
orderBy: orderByFilter.schema,
searchTerm: searchFilter.schema,
date: dateFilter.schema,
expectedDate: expectedDateFilter.schema,
expectedFundsFilter: expectedFundsFilter.schema,
amount: amountFilter.schema,
// type: z.nativeEnum(OrderTypeFilter).optional(),
// paymentMethod: z.string().optional(),
});

// TODO change any to query variables
const toVariables: FiltersToVariables<z.infer<typeof filtersSchema>, any, FilterMeta> = {
orderBy: orderByFilter.toVariables,
date: dateFilter.toVariables,
expectedDate: expectedDateFilter.toVariables,
amount: amountFilter.toVariables,
// type: (value: OrderTypeFilter) => {
// switch (value) {
// case OrderTypeFilter.RECURRING:
// return {
// onlySubscriptions: true,
// };
// case OrderTypeFilter.ONETIME:
// return {
// frequency: ContributionFrequency.ONETIME,
// };
// }
// },
// paymentMethod: (value: string) => {
// if (value) {
// return { paymentMethod: { id: value } };
// }

// return null;
// },
};

const filters: FilterComponentConfigs<z.infer<typeof filtersSchema>, FilterMeta> = {
searchTerm: searchFilter.filter,
date: dateFilter.filter,
expectedDate: expectedDateFilter.filter,
expectedFundsFilter: expectedFundsFilter.filter,
amount: amountFilter.filter,
};

export const MatchContributionFromImportRowDialog = ({
host,
row,
onClose,

Check failure on line 132 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / typescript

Property 'onClose' does not exist on type '{ host: Account; row: TransactionsImportRow; } & DialogProps'.
...props
}: {
host: Account;
row: TransactionsImportRow;
} & React.ComponentProps<typeof Dialog>) => {
const [selectedRow, setSelectedRow] = React.useState(null);
const [loading, setLoading] = React.useState(false);

const queryFilter = useQueryFilter({
schema: filtersSchema,
toVariables,
filters,
meta: { currency: row.amount.currency },
});

React.useEffect(() => {
setLoading(true);
setTimeout(() => setLoading(false), 1000);
}, [queryFilter.values]);

return (
<Dialog open onOpenChange={onClose} {...props}>
<DialogContent className="sm:max-w-4xl">
<DialogHeader>
<h2 className="text-xl font-bold">
<FormattedMessage defaultMessage="Match contribution" id="c7INEq" />
</h2>
</DialogHeader>
<div className="text-sm">
<Filterbar hideSeparator {...queryFilter} />
<RadioGroup value={selectedRow?.id}>
<DataTable
className="mt-3"
loading={loading}
nbPlaceholders={3}
onClickRow={({ original }) => setSelectedRow(original)}
getRowClassName={({ original }) =>
selectedRow?.id === original.id
? 'bg-blue-50 font-semibold shadow-inner shadow-blue-100 !border-l-2 border-l-blue-500'
: ''
}
columns={[
{
id: 'select',
cell: ({ row }) => <RadioGroupItem value={row.original.id} />,

Check failure on line 177 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / typescript

Type 'number' is not assignable to type 'string'.
meta: { className: 'w-[20px]' },
},
{
header: 'Date',
accessorKey: 'date',
},
{
header: 'Amount',
accessorKey: 'amount',
cell: ({ cell }) => {
const value = cell.getValue();
return (
<FormattedMoneyAmount
amount={value.valueInCents}

Check failure on line 191 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / typescript

Property 'valueInCents' does not exist on type 'unknown'.
currency={value.currency}

Check failure on line 192 in components/dashboard/sections/transactions-imports/MatchContributionDialog.tsx

View workflow job for this annotation

GitHub Actions / typescript

Property 'currency' does not exist on type 'unknown'.
showCurrencyCode={false}
/>
);
},
},
{
header: 'Description',
accessorKey: 'description',
cell: ({ cell }) => <div className="flex items-center gap-1">{cell.getValue() as string}</div>,
},
]}
data={MOCK_MATCHES}
/>
</RadioGroup>
</div>
<div className="mt-2 grid grid-cols-2 gap-4 text-sm">
<div className="rounded-lg border border-gray-200 p-4">
<strong className="text-base text-gray-700">
<FormattedMessage defaultMessage="Source data" id="dNaFoq" />
</strong>

<ul className="mt-2 list-inside list-disc">
{Object.entries(row.rawValue)
.filter(([, value]) => Boolean(value))
.map(([key, value]) => (
<li key={key}>
<strong>{startCase(key)}</strong>: {value.toString()}{' '}
<Button
variant="ghost"
size="icon-xs"
className="inline-block h-4 w-4 p-0 text-neutral-500 hover:bg-white hover:text-neutral-700"
onClick={() => {
if (key === 'Amount') {
const intValue = parseFloat(value.toString());
const minValue = Math.floor(intValue - intValue / 20);
const maxValue = Math.ceil(intValue + intValue / 20);
queryFilter.setFilter('amount', {
gte: minValue * 100,
lte: maxValue * 100,
type: AmountFilterType.IS_BETWEEN,
});
} else {
queryFilter.setFilter('searchTerm', value.toString());
}
}}
>
<Filter size={16} className="inline" />
</Button>
</li>
))}
</ul>
</div>
{selectedRow && (
<div className="rounded-lg border border-gray-200 p-4">
<div className="flex items-center gap-1 text-base font-semibold text-neutral-700">
<FormattedMessage defaultMessage="Selected match" id="bQndkF" />:
<span className="flex cursor-pointer items-center gap-1 underline hover:text-neutral-900">
contribution #{selectedRow.id}
<ExternalLink size={14} />
</span>
</div>

<ul className="mt-2 list-inside list-disc">
<li>
<strong>Description</strong>: {selectedRow.description}
</li>
<li>
<strong>Date</strong>: {selectedRow.date}
</li>
<li>
<strong>Amount</strong>:{' '}
<FormattedMoneyAmount
amount={selectedRow.amount.valueInCents}
amountStyles={null}
currency={selectedRow.amount.currency}
/>
</li>
<li>
<strong>Account</strong>: <LinkCollective collective={selectedRow.account} />
</li>
</ul>
</div>
)}
</div>
<div className="mt-5 flex justify-end gap-2">
<Button variant="outline" onClick={onClose}>
<Ban size={16} className="mr-2" />
Cancel
</Button>
<Button disabled={!selectedRow}>
<Save size={16} className="mr-2" />
Save
</Button>{' '}
</div>
</DialogContent>
</Dialog>
);
};
Loading

0 comments on commit 06a5938

Please sign in to comment.