Skip to content

Commit

Permalink
Custom mapping and import settings for bank sync providers (#4253)
Browse files Browse the repository at this point in the history
* barebones UI

* add saving and prefs

* add last sync functionality

* use mapping for synced transactions

* note

* jest -u

* Update VRT

* Coderabbit

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>

* add new fields

* rename migration, newer in master

* lint

* coderabbit

* update snapshots

* GoCardless handlers fallback and notes

* expose new fields from SimpleFIN

* update tests

* update instructions on GoCardless handlers

* lint

* feedback

* Update VRT

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: youngcw <[email protected]>
  • Loading branch information
4 people authored Feb 14, 2025
1 parent f90fc69 commit 9fd0042
Show file tree
Hide file tree
Showing 85 changed files with 1,366 additions and 654 deletions.
2 changes: 2 additions & 0 deletions packages/desktop-client/src/components/FinancesApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import { theme } from '../style';
import { getIsOutdated, getLatestVersion } from '../util/versions';

import { UserAccessPage } from './admin/UserAccess/UserAccessPage';
import { BankSync } from './banksync';
import { BankSyncStatus } from './BankSyncStatus';
import { View } from './common/View';
import { GlobalKeys } from './GlobalKeys';
Expand Down Expand Up @@ -248,6 +249,7 @@ export function FinancesApp() {

<Route path="/payees" element={<ManagePayeesPage />} />
<Route path="/rules" element={<ManageRulesPage />} />
<Route path="/bank-sync" element={<BankSync />} />
<Route path="/settings" element={<Settings />} />

<Route
Expand Down
4 changes: 4 additions & 0 deletions packages/desktop-client/src/components/Modals.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import { useMetadataPref } from '../hooks/useMetadataPref';
import { useModalState } from '../hooks/useModalState';
import { useDispatch } from '../redux';

import { EditSyncAccount } from './banksync/EditSyncAccount';
import { ModalTitle, ModalHeader } from './common/Modal';
import { AccountAutocompleteModal } from './modals/AccountAutocompleteModal';
import { AccountMenuModal } from './modals/AccountMenuModal';
Expand Down Expand Up @@ -384,6 +385,9 @@ export function Modals() {
case 'schedule-posts-offline-notification':
return <PostsOfflineNotification key={name} />;

case 'synced-account-edit':
return <EditSyncAccount key={name} account={options.account} />;

case 'account-menu':
return (
<AccountMenuModal
Expand Down
92 changes: 92 additions & 0 deletions packages/desktop-client/src/components/banksync/AccountRow.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import React, { memo } from 'react';
import { Trans } from 'react-i18next';

import { format } from 'loot-core/src/shared/months';
import { type AccountEntity } from 'loot-core/src/types/models';

import { useDateFormat } from '../../hooks/useDateFormat';
import { theme } from '../../style';
import { Button } from '../common/Button2';
import { Row, Cell } from '../table';

const tsToString = (ts: string | null, dateFormat: string) => {
if (!ts) return 'Unknown';

const parsed = new Date(parseInt(ts, 10));
return `${format(parsed, dateFormat)} ${format(parsed, 'HH:mm:ss')}`;
};

type AccountRowProps = {
account: AccountEntity;
hovered: boolean;
onHover: (id: AccountEntity['id'] | null) => void;
onAction: (account: AccountEntity, action: 'link' | 'edit') => void;
};

export const AccountRow = memo(
({ account, hovered, onHover, onAction }: AccountRowProps) => {
const backgroundFocus = hovered;

const dateFormat = useDateFormat() || 'MM/dd/yyyy';

const lastSync = tsToString(account.last_sync, dateFormat);

return (
<Row
height="auto"
style={{
fontSize: 13,
backgroundColor: backgroundFocus
? theme.tableRowBackgroundHover
: theme.tableBackground,
}}
collapsed={true}
onMouseEnter={() => onHover && onHover(account.id)}
onMouseLeave={() => onHover && onHover(null)}
>
<Cell
name="accountName"
width={250}
plain
style={{ color: theme.tableText, padding: '10px' }}
>
{account.name}
</Cell>

<Cell
name="bankName"
width="flex"
plain
style={{ color: theme.tableText, padding: '10px' }}
>
{account.bankName}
</Cell>

<Cell
name="lastSync"
width={200}
plain
style={{ color: theme.tableText, padding: '10px' }}
>
{account.account_sync_source ? lastSync : ''}
</Cell>

{account.account_sync_source ? (
<Cell name="edit" plain style={{ paddingRight: '10px' }}>
<Button onPress={() => onAction(account, 'edit')}>
<Trans>Edit</Trans>
</Button>
</Cell>
) : (
<Cell name="link" plain style={{ paddingRight: '10px' }}>
<Button onPress={() => onAction(account, 'link')}>
<Trans>Link account</Trans>
</Button>
</Cell>
)}
</Row>
);
},
);

AccountRow.displayName = 'AccountRow';
37 changes: 37 additions & 0 deletions packages/desktop-client/src/components/banksync/AccountsHeader.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import React from 'react';
import { useTranslation } from 'react-i18next';

import { Cell, TableHeader } from '../table';

type AccountsHeaderProps = {
unlinked: boolean;
};

export function AccountsHeader({ unlinked }: AccountsHeaderProps) {
const { t } = useTranslation();

return (
<TableHeader>
<Cell
value={t('Account')}
width={!unlinked ? 250 : 'flex'}
style={{ paddingLeft: '10px' }}
/>
{!unlinked && (
<>
<Cell
value={t('Bank')}
width="flex"
style={{ paddingLeft: '10px' }}
/>
<Cell
value={t('Last sync')}
width={160}
style={{ paddingLeft: '10px' }}
/>
<Cell value="" width={100} style={{ paddingLeft: '10px' }} />
</>
)}
</TableHeader>
);
}
43 changes: 43 additions & 0 deletions packages/desktop-client/src/components/banksync/AccountsList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import React from 'react';

import { type AccountEntity } from 'loot-core/src/types/models';

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

import { AccountRow } from './AccountRow';

type AccountsListProps = {
accounts: AccountEntity[];
hoveredAccount?: string | null;
onHover: (id: AccountEntity['id'] | null) => void;
onAction: (account: AccountEntity, action: 'link' | 'edit') => void;
};

export function AccountsList({
accounts,
hoveredAccount,
onHover,
onAction,
}: AccountsListProps) {
if (accounts.length === 0) {
return null;
}

return (
<View>
{accounts.map(account => {
const hovered = hoveredAccount === account.id;

return (
<AccountRow
key={account.id}
account={account}
hovered={hovered}
onHover={onHover}
onAction={onAction}
/>
);
})}
</View>
);
}
Loading

0 comments on commit 9fd0042

Please sign in to comment.