mirror of
https://github.com/actualbudget/actual.git
synced 2026-03-21 15:36:50 -05:00
feat(CommandBar): display account balances (#5355)
* feat(CommandBar): display account balances * add release note * rename release note
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import {
|
||||
type ComponentType,
|
||||
type ReactNode,
|
||||
type SVGProps,
|
||||
useCallback,
|
||||
useEffect,
|
||||
@@ -22,18 +23,41 @@ import {
|
||||
SvgCalendar3,
|
||||
SvgNotesPaperText,
|
||||
} from '@actual-app/components/icons/v2';
|
||||
import { styles } from '@actual-app/components/styles';
|
||||
import { Text } from '@actual-app/components/text';
|
||||
import { View } from '@actual-app/components/view';
|
||||
import { css } from '@emotion/css';
|
||||
import { Command } from 'cmdk';
|
||||
|
||||
import { CellValue, CellValueText } from './spreadsheet/CellValue';
|
||||
|
||||
import { useAccounts } from '@desktop-client/hooks/useAccounts';
|
||||
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
|
||||
import { useModalState } from '@desktop-client/hooks/useModalState';
|
||||
import { useNavigate } from '@desktop-client/hooks/useNavigate';
|
||||
import { useReports } from '@desktop-client/hooks/useReports';
|
||||
import type {
|
||||
Binding,
|
||||
SheetFields,
|
||||
SheetNames,
|
||||
} from '@desktop-client/spreadsheet';
|
||||
import {
|
||||
accountBalance,
|
||||
allAccountBalance,
|
||||
offBudgetAccountBalance,
|
||||
onBudgetAccountBalance,
|
||||
} from '@desktop-client/spreadsheet/bindings';
|
||||
|
||||
type SearchableItem = {
|
||||
id: string;
|
||||
/** The name to display and use for searching */
|
||||
name: string;
|
||||
/**
|
||||
* The item content to display. If not provided, {@link SearchableItem.name `name`} will be used.
|
||||
*
|
||||
* Meant for complex items that want to display more than just static text.
|
||||
*/
|
||||
content?: ReactNode;
|
||||
Icon: ComponentType<SVGProps<SVGSVGElement>>;
|
||||
};
|
||||
|
||||
@@ -44,6 +68,38 @@ type SearchSection = {
|
||||
onSelect: (item: Pick<SearchableItem, 'id'>) => void;
|
||||
};
|
||||
|
||||
function BalanceRow<
|
||||
SheetName extends SheetNames,
|
||||
FieldName extends SheetFields<SheetName>,
|
||||
>({
|
||||
label,
|
||||
binding,
|
||||
}: {
|
||||
label: string;
|
||||
binding: Binding<SheetName, FieldName>;
|
||||
}) {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexDirection: 'row',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
flex: 1,
|
||||
}}
|
||||
>
|
||||
<Text>{label}</Text>
|
||||
<CellValue binding={binding} type="financial">
|
||||
{props => (
|
||||
<CellValueText
|
||||
{...props}
|
||||
style={{ ...styles.tnum, whiteSpace: 'nowrap', opacity: 0.9 }}
|
||||
/>
|
||||
)}
|
||||
</CellValue>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
export function CommandBar() {
|
||||
const { t } = useTranslation();
|
||||
const [open, setOpen] = useState(false);
|
||||
@@ -75,6 +131,12 @@ export function CommandBar() {
|
||||
id: 'accounts',
|
||||
name: t('All Accounts'),
|
||||
path: '/accounts',
|
||||
content: (
|
||||
<BalanceRow<'account', 'accounts-balance'>
|
||||
label={t('All Accounts')}
|
||||
binding={allAccountBalance()}
|
||||
/>
|
||||
),
|
||||
Icon: SvgLibrary,
|
||||
},
|
||||
],
|
||||
@@ -120,11 +182,7 @@ export function CommandBar() {
|
||||
{
|
||||
key: 'navigation',
|
||||
heading: t('Navigation'),
|
||||
items: navigationItems.map(({ id, name, Icon }) => ({
|
||||
id,
|
||||
name,
|
||||
Icon,
|
||||
})),
|
||||
items: navigationItems,
|
||||
onSelect: ({ id }) => {
|
||||
const item = navigationItems.find(item => item.id === id);
|
||||
if (!!item) handleNavigate(item.path);
|
||||
@@ -137,15 +195,33 @@ export function CommandBar() {
|
||||
{
|
||||
id: 'onbudget',
|
||||
name: t('On Budget'),
|
||||
content: (
|
||||
<BalanceRow<'account', 'onbudget-accounts-balance'>
|
||||
label={t('On Budget')}
|
||||
binding={onBudgetAccountBalance()}
|
||||
/>
|
||||
),
|
||||
Icon: SvgLibrary,
|
||||
},
|
||||
{
|
||||
id: 'offbudget',
|
||||
name: t('Off Budget'),
|
||||
content: (
|
||||
<BalanceRow<'account', 'offbudget-accounts-balance'>
|
||||
label={t('Off Budget')}
|
||||
binding={offBudgetAccountBalance()}
|
||||
/>
|
||||
),
|
||||
Icon: SvgLibrary,
|
||||
},
|
||||
...accounts.map(account => ({
|
||||
...account,
|
||||
content: (
|
||||
<BalanceRow<'account', 'balance'>
|
||||
label={account.name}
|
||||
binding={accountBalance(account.id)}
|
||||
/>
|
||||
),
|
||||
Icon: SvgPiggyBank,
|
||||
})),
|
||||
],
|
||||
@@ -245,7 +321,7 @@ export function CommandBar() {
|
||||
},
|
||||
})}
|
||||
>
|
||||
{section.items.map(({ id, name, Icon }) => (
|
||||
{section.items.map(({ id, name, Icon, content }) => (
|
||||
<Command.Item
|
||||
key={id}
|
||||
onSelect={() => section.onSelect({ id })}
|
||||
@@ -274,7 +350,7 @@ export function CommandBar() {
|
||||
})}
|
||||
>
|
||||
<Icon width={16} height={16} />
|
||||
{name}
|
||||
{content || name}
|
||||
</Command.Item>
|
||||
))}
|
||||
</Command.Group>
|
||||
|
||||
Reference in New Issue
Block a user