Compare commits

...

9 Commits

Author SHA1 Message Date
Matiss Janis Aboltins
d8639a2a71 🔖 (24.2.0) cleared transaction improvements; experimental simplefin bank-sync (#2311) 2024-02-02 19:20:53 +00:00
Matiss Janis Aboltins
734191424b 🐛 (goCardless) patch incomplete migration (#2308) 2024-02-01 16:40:12 +00:00
shall0pass
5d4fcfde00 [Enhancement] Goal Target with cleanup template (#2282)
* update goal target after montly cleanup

* release note
2024-01-31 13:42:05 -06:00
Joel Jeremy Marquez
54d7e5460a [Cleanup] useSingleActiveEditForm hook on mobile budget table (#2263)
* useSingleActiveEditForm on mobile budget table

* Release notes

* Remove unused variables
2024-01-30 14:37:06 -08:00
youngcw
43ebe9e0fd fix bad account sort order in demo (#2279)
* fix bad sort order in demo

* note

* add async back

* fix tests

* fix2

* fix3

* update vrt

* fix image
2024-01-26 09:12:54 -07:00
youngcw
515bdf5a74 update vrt instructions (#2287)
* update

* note

* revise
2024-01-26 09:06:12 -07:00
Ed
018714610a Updating link for GoCardless Bank Account Sync (#2276)
* Updating link for GoCardless Bank Account Sync

* Adding release notes

* Update URL to point to docs

* Fixing lint
2024-01-26 07:11:18 -08:00
Joel Jeremy Marquez
00ee165f8e Mobile Off Budget category label (#2284)
* Mobile Off Budget category label

* Release notes

* Fix error

* Fix release notes
2024-01-26 07:09:29 -08:00
Neil
68442ae9e6 Custom reports: hide "show ..." checkboxes in menu (#2174)
* Add Toggles

* budget table

* testing

* updates

* updates

* fixes

* updates

* fix Menu

* lint fixes

* fix keybindings

* revert budget menu changes

* notes

* remove default exports

* fixes

* disabled fix

* add style option

* lint fix

* remove css

* lint fixes

* color updates

* merge menu with togglemenu

* host

* menu fixes

* fix regression

* remove host

* adjustments

* onToggle

* vrt fix

* typecheck

* merge fixes

* colors

* lint fix
2024-01-24 21:49:12 +00:00
144 changed files with 343 additions and 731 deletions

View File

@@ -1,6 +1,6 @@
{
"name": "@actual-app/api",
"version": "6.4.0",
"version": "6.5.0",
"license": "MIT",
"description": "An API for Actual",
"engines": {

View File

@@ -32,11 +32,12 @@ Prerequisites:
#### Running against the local server
First start the dev server:
First start a dev instance:
```sh
HTTPS=true yarn start
```
Note the network IP address and port the dev instance is listening on.
Next, navigate to the root of your project folder, run the standartised docker container, and launch the visual regression tests from within it.
@@ -47,11 +48,11 @@ docker run --rm --network host -v $(pwd):/work/ -w /work/ -it mcr.microsoft.com/
# If you receive an error such as "docker: invalid reference format", please instead use the following command:
docker run --rm --network host -v ${pwd}:/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.41.1-jammy /bin/bash
# Run the VRT tests: important - they MUST be ran against a HTTPS server
E2E_START_URL=https://192.168.0.178:3001 yarn vrt
# Run the VRT tests: important - they MUST be ran against a HTTPS server. Use the ip and port noted earlier
E2E_START_URL=https://ip:port yarn vrt
# To update snapshots, use the following command:
E2E_START_URL=https://192.168.0.178:3001 yarn vrt --update-snapshots
E2E_START_URL=https://ip:port yarn vrt --update-snapshots
```
#### Running against a remote server

Binary file not shown.

Before

Width:  |  Height:  |  Size: 131 KiB

After

Width:  |  Height:  |  Size: 131 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 129 KiB

After

Width:  |  Height:  |  Size: 129 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 117 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 55 KiB

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -49,7 +49,7 @@ test.describe('Mobile', () => {
test('opens the accounts page and asserts on balances', async () => {
const accountsPage = await navigation.goToAccountsPage();
const account = await accountsPage.getNthAccount(0);
const account = await accountsPage.getNthAccount(1);
await expect(account.name).toHaveText('Ally Savings');
await expect(account.balance).toHaveText('7,653.00');
@@ -58,7 +58,7 @@ test.describe('Mobile', () => {
test('opens individual account page and checks that filtering is working', async () => {
const accountsPage = await navigation.goToAccountsPage();
const accountPage = await accountsPage.openNthAccount(1);
const accountPage = await accountsPage.openNthAccount(0);
await expect(accountPage.heading).toHaveText('Bank of America');
expect(await accountPage.getBalance()).toBeGreaterThan(0);

Binary file not shown.

Before

Width:  |  Height:  |  Size: 33 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 34 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 58 KiB

After

Width:  |  Height:  |  Size: 58 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 66 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 67 KiB

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 71 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

After

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 77 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 77 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 78 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 107 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 108 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

After

Width:  |  Height:  |  Size: 104 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 106 KiB

After

Width:  |  Height:  |  Size: 106 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

View File

@@ -1,6 +1,6 @@
{
"name": "@actual-app/web",
"version": "24.1.0",
"version": "24.2.0",
"license": "MIT",
"files": [
"build"

View File

@@ -83,11 +83,14 @@ export function AccountHeader({
const [menuOpen, setMenuOpen] = useState(false);
const searchInput = useRef(null);
const splitsExpanded = useSplitsExpanded();
const syncServerStatus = useSyncServerStatus();
const isUsingServer = syncServerStatus !== 'no-server';
const isServerOffline = syncServerStatus === 'offline';
let canSync = account && account.account_id;
let canSync = account && account.account_id && isUsingServer;
if (!account) {
// All accounts - check for any syncable account
canSync = !!accounts.find(account => !!account.account_id);
canSync = !!accounts.find(account => !!account.account_id) && isUsingServer;
}
function onToggleSplits() {
@@ -210,7 +213,11 @@ export function AccountHeader({
style={{ marginTop: 12 }}
>
{((account && !account.closed) || canSync) && (
<Button type="bare" onClick={canSync ? onSync : onImport}>
<Button
type="bare"
onClick={canSync ? onSync : onImport}
disabled={canSync && isServerOffline}
>
{canSync ? (
<>
<AnimatedRefresh
@@ -222,7 +229,7 @@ export function AccountHeader({
}
style={{ marginRight: 4 }}
/>{' '}
Sync
{isServerOffline ? 'Sync offline' : 'Sync'}
</>
) : (
<>

View File

@@ -199,11 +199,11 @@ export function AccountDetails({
</View>
<PullToRefresh onRefresh={onRefresh}>
<TransactionList
account={account}
transactions={allTransactions}
categories={categories}
accounts={accounts}
payees={payees}
showCategory={!account.offbudget}
isNew={isNewTransaction}
onLoadMore={onLoadMore}
onSelect={onSelectTransaction}

View File

@@ -22,9 +22,6 @@ import { SyncRefresh } from '../SyncRefresh';
import { BudgetTable } from './MobileBudgetTable';
import { prewarmMonth, switchBudgetType } from './util';
const CATEGORY_BUDGET_EDIT_ACTION = 'category-budget';
const BALANCE_MENU_OPEN_ACTION = 'balance-menu';
type BudgetInnerProps = {
categories: CategoryEntity[];
categoryGroups: CategoryGroupEntity[];
@@ -76,8 +73,6 @@ function BudgetInner(props: BudgetInnerProps) {
const [currentMonth, setCurrentMonth] = useState(currMonth);
const [initialized, setInitialized] = useState(false);
const [editMode, setEditMode] = useState(false);
const [editingBudgetCategoryId, setEditingBudgetCategoryId] = useState(null);
const [openBalanceActionMenuId, setOpenBalanceActionMenuId] = useState(null);
useEffect(() => {
async function init() {
@@ -360,29 +355,6 @@ function BudgetInner(props: BudgetInnerProps) {
});
};
const onEditCategoryBudget = id => {
onEdit(CATEGORY_BUDGET_EDIT_ACTION, id);
};
const onOpenBalanceActionMenu = id => {
onEdit(BALANCE_MENU_OPEN_ACTION, id);
};
const onEdit = (action, id) => {
// Do not allow editing if another field is currently being edited.
// Cancel the currently editing field in that case.
const currentlyEditing = editingBudgetCategoryId || openBalanceActionMenuId;
setEditingBudgetCategoryId(
action === CATEGORY_BUDGET_EDIT_ACTION && !currentlyEditing ? id : null,
);
setOpenBalanceActionMenuId(
action === BALANCE_MENU_OPEN_ACTION && !currentlyEditing ? id : null,
);
return { action, editingId: !currentlyEditing ? id : null };
};
const numberFormat = prefs?.numberFormat || 'comma-dot';
const hideFraction = prefs?.hideFraction || false;
@@ -438,10 +410,6 @@ function BudgetInner(props: BudgetInnerProps) {
pushModal={pushModal}
onEditGroup={onEditGroup}
onEditCategory={onEditCategory}
editingBudgetCategoryId={editingBudgetCategoryId}
onEditCategoryBudget={onEditCategoryBudget}
openBalanceActionMenuId={openBalanceActionMenuId}
onOpenBalanceActionMenu={onOpenBalanceActionMenu}
/>
)}
</SyncRefresh>

View File

@@ -1,4 +1,4 @@
import React, { memo, useEffect, useRef, useState } from 'react';
import React, { memo, useRef, useState } from 'react';
import { useSelector } from 'react-redux';
import memoizeOne from 'memoize-one';
@@ -7,6 +7,10 @@ import { rolloverBudget, reportBudget } from 'loot-core/src/client/queries';
import * as monthUtils from 'loot-core/src/shared/months';
import { useFeatureFlag } from '../../hooks/useFeatureFlag';
import {
SingleActiveEditFormProvider,
useSingleActiveEditForm,
} from '../../hooks/useSingleActiveEditForm';
import {
SvgArrowThinLeft,
SvgArrowThinRight,
@@ -134,6 +138,7 @@ function BudgetCell({
month,
onBudgetAction,
onEdit,
onBlur,
isEditing,
}) {
const sheetValue = useSheetValue(binding);
@@ -146,7 +151,7 @@ function BudgetCell({
}
function onAmountClick() {
onEdit?.(categoryId);
onEdit?.();
}
return (
@@ -162,7 +167,7 @@ function BudgetCell({
focused={isEditing}
textStyle={{ ...styles.smallText, ...textStyle }}
onUpdate={updateBudgetAmount}
onBlur={() => onEdit?.(null)}
onBlur={onBlur}
/>
<View
role="button"
@@ -244,10 +249,6 @@ const ExpenseCategory = memo(function ExpenseCategory({
style,
month,
onEdit,
isEditingBudget,
onEditBudget,
isBalanceActionMenuOpen,
onOpenBalanceActionMenu,
onBudgetAction,
show3Cols,
showBudgetedCol,
@@ -255,11 +256,22 @@ const ExpenseCategory = memo(function ExpenseCategory({
const opacity = blank ? 0 : 1;
const balanceTooltip = useTooltip();
useEffect(() => {
if (isBalanceActionMenuOpen) {
const [isEditingBudget, setIsEditingBudget] = useState(false);
const { onRequestActiveEdit, onClearActiveEdit } = useSingleActiveEditForm();
const onEditBudget = () => {
onRequestActiveEdit(`${category.id}-budget`, () => {
setIsEditingBudget(true);
return () => setIsEditingBudget(false);
});
};
const onOpenBalanceActionMenu = () => {
onRequestActiveEdit(`${category.id}-balance`, () => {
balanceTooltip.open();
}
}, [isBalanceActionMenuOpen, balanceTooltip]);
return () => balanceTooltip.close();
});
};
const listItemRef = useRef();
@@ -317,6 +329,7 @@ const ExpenseCategory = memo(function ExpenseCategory({
onBudgetAction={onBudgetAction}
isEditing={isEditingBudget}
onEdit={onEditBudget}
onBlur={onClearActiveEdit}
/>
<View
style={{
@@ -349,7 +362,7 @@ const ExpenseCategory = memo(function ExpenseCategory({
>
<span
role="button"
onPointerUp={() => onOpenBalanceActionMenu?.(category.id)}
onPointerUp={() => onOpenBalanceActionMenu?.()}
onPointerDown={e => e.preventDefault()}
>
<BalanceWithCarryover
@@ -371,7 +384,7 @@ const ExpenseCategory = memo(function ExpenseCategory({
monthIndex={monthUtils.getMonthIndex(month)}
onBudgetAction={_onBudgetAction}
onClose={() => {
onOpenBalanceActionMenu?.(null);
onClearActiveEdit();
}}
/>
) : (
@@ -382,7 +395,7 @@ const ExpenseCategory = memo(function ExpenseCategory({
monthIndex={monthUtils.getMonthIndex(month)}
onBudgetAction={_onBudgetAction}
onClose={() => {
onOpenBalanceActionMenu?.(null);
onClearActiveEdit();
}}
/>
))}
@@ -795,10 +808,6 @@ const ExpenseGroup = memo(function ExpenseGroup({
editMode,
onEditGroup,
onEditCategory,
editingBudgetCategoryId,
onEditCategoryBudget,
openBalanceActionMenuId,
onOpenBalanceActionMenu,
// gestures,
month,
// onReorderCategory,
@@ -873,10 +882,6 @@ const ExpenseGroup = memo(function ExpenseGroup({
{group.categories
.filter(category => !category.hidden || showHiddenCategories)
.map((category, index) => {
const isEditingCategoryBudget =
editingBudgetCategoryId === category.id;
const isBalanceActionMenuOpen =
openBalanceActionMenuId === category.id;
return (
<ExpenseCategory
key={category.id}
@@ -915,10 +920,6 @@ const ExpenseGroup = memo(function ExpenseGroup({
showBudgetedCol={showBudgetedCol}
editMode={editMode}
onEdit={onEditCategory}
isEditingBudget={isEditingCategoryBudget}
onEditBudget={onEditCategoryBudget}
isBalanceActionMenuOpen={isBalanceActionMenuOpen}
onOpenBalanceActionMenu={onOpenBalanceActionMenu}
// gestures={gestures}
month={month}
// onReorder={onReorderCategory}
@@ -1019,10 +1020,6 @@ function BudgetGroups({
categoryGroups,
onEditGroup,
onEditCategory,
editingBudgetCategoryId,
onEditCategoryBudget,
openBalanceActionMenuId,
onOpenBalanceActionMenu,
editMode,
gestures,
month,
@@ -1048,71 +1045,67 @@ function BudgetGroups({
const { incomeGroup, expenseGroups } = separateGroups(categoryGroups);
return (
<View
data-testid="budget-groups"
style={{ flex: '1 0 auto', overflowY: 'auto', paddingBottom: 15 }}
>
{expenseGroups
.filter(group => !group.hidden || showHiddenCategories)
.map(group => {
return (
<ExpenseGroup
key={group.id}
type={type}
group={group}
showBudgetedCol={showBudgetedCol}
gestures={gestures}
month={month}
editMode={editMode}
onEditGroup={onEditGroup}
onEditCategory={onEditCategory}
editingBudgetCategoryId={editingBudgetCategoryId}
onEditCategoryBudget={onEditCategoryBudget}
openBalanceActionMenuId={openBalanceActionMenuId}
onOpenBalanceActionMenu={onOpenBalanceActionMenu}
onSaveCategory={onSaveCategory}
onDeleteCategory={onDeleteCategory}
onAddCategory={onAddCategory}
onReorderCategory={onReorderCategory}
onReorderGroup={onReorderGroup}
onBudgetAction={onBudgetAction}
show3Cols={show3Cols}
showHiddenCategories={showHiddenCategories}
pushModal={pushModal}
/>
);
})}
<SingleActiveEditFormProvider formName="mobile-budget-table">
<View
style={{
alignItems: 'flex-start',
justifyContent: 'flex-start',
}}
data-testid="budget-groups"
style={{ flex: '1 0 auto', overflowY: 'auto', paddingBottom: 15 }}
>
<Button onClick={onAddGroup} style={{ fontSize: 12, margin: 10 }}>
Add Group
</Button>
</View>
{expenseGroups
.filter(group => !group.hidden || showHiddenCategories)
.map(group => {
return (
<ExpenseGroup
key={group.id}
type={type}
group={group}
showBudgetedCol={showBudgetedCol}
gestures={gestures}
month={month}
editMode={editMode}
onEditGroup={onEditGroup}
onEditCategory={onEditCategory}
onSaveCategory={onSaveCategory}
onDeleteCategory={onDeleteCategory}
onAddCategory={onAddCategory}
onReorderCategory={onReorderCategory}
onReorderGroup={onReorderGroup}
onBudgetAction={onBudgetAction}
show3Cols={show3Cols}
showHiddenCategories={showHiddenCategories}
pushModal={pushModal}
/>
);
})}
{incomeGroup && (
<IncomeGroup
type={type}
group={incomeGroup}
month={month}
onAddCategory={onAddCategory}
onSaveCategory={onSaveCategory}
onDeleteCategory={onDeleteCategory}
showHiddenCategories={showHiddenCategories}
editMode={editMode}
onEditGroup={onEditGroup}
onEditCategory={onEditCategory}
editingBudgetCategoryId={editingBudgetCategoryId}
onEditCategoryBudget={onEditCategoryBudget}
onBudgetAction={onBudgetAction}
pushModal={pushModal}
/>
)}
</View>
<View
style={{
alignItems: 'flex-start',
justifyContent: 'flex-start',
}}
>
<Button onClick={onAddGroup} style={{ fontSize: 12, margin: 10 }}>
Add Group
</Button>
</View>
{incomeGroup && (
<IncomeGroup
type={type}
group={incomeGroup}
month={month}
onAddCategory={onAddCategory}
onSaveCategory={onSaveCategory}
onDeleteCategory={onDeleteCategory}
showHiddenCategories={showHiddenCategories}
editMode={editMode}
onEditGroup={onEditGroup}
onEditCategory={onEditCategory}
onBudgetAction={onBudgetAction}
pushModal={pushModal}
/>
)}
</View>
</SingleActiveEditFormProvider>
);
}
@@ -1143,10 +1136,6 @@ export function BudgetTable({
pushModal,
onEditGroup,
onEditCategory,
editingBudgetCategoryId,
onEditCategoryBudget,
openBalanceActionMenuId,
onOpenBalanceActionMenu,
}) {
const { width } = useResponsive();
const show3Cols = width >= 360;
@@ -1385,10 +1374,6 @@ export function BudgetTable({
editMode={editMode}
onEditGroup={onEditGroup}
onEditCategory={onEditCategory}
editingBudgetCategoryId={editingBudgetCategoryId}
onEditCategoryBudget={onEditCategoryBudget}
openBalanceActionMenuId={openBalanceActionMenuId}
onOpenBalanceActionMenu={onOpenBalanceActionMenu}
onSaveCategory={onSaveCategory}
onDeleteCategory={onDeleteCategory}
onAddCategory={onAddCategory}
@@ -1423,10 +1408,6 @@ export function BudgetTable({
editMode={editMode}
onEditGroup={onEditGroup}
onEditCategory={onEditCategory}
editingBudgetCategoryId={editingBudgetCategoryId}
onEditCategoryBudget={onEditCategoryBudget}
openBalanceActionMenuId={openBalanceActionMenuId}
onOpenBalanceActionMenu={onOpenBalanceActionMenu}
onSaveCategory={onSaveCategory}
onDeleteCategory={onDeleteCategory}
onAddCategory={onAddCategory}

View File

@@ -10,6 +10,7 @@ import {
import { type CSSProperties, theme } from '../../style';
import { Text } from './Text';
import { Toggle } from './Toggle';
import { View } from './View';
type KeybindingProps = {
@@ -33,6 +34,8 @@ type MenuItem = {
text: string;
key?: string;
style?: CSSProperties;
toggle?: boolean;
tooltip?: string;
};
type MenuProps<T extends MenuItem = MenuItem> = {
@@ -164,23 +167,48 @@ export function Menu<T extends MenuItem>({
onMouseEnter={() => setHoveredIndex(idx)}
onMouseLeave={() => setHoveredIndex(null)}
onClick={() =>
!item.disabled && onMenuSelect && onMenuSelect(item.name)
!item.disabled &&
onMenuSelect &&
item.toggle === undefined &&
onMenuSelect(item.name)
}
>
{/* Force it to line up evenly */}
<Text style={{ lineHeight: 0 }}>
{item.icon &&
createElement(item.icon, {
width: item.iconSize || 10,
height: item.iconSize || 10,
style: {
marginRight: 7,
width: item.iconSize || 10,
},
})}
</Text>
<Text>{item.text}</Text>
<View style={{ flex: 1 }} />
{item.toggle === undefined ? (
<>
<Text style={{ lineHeight: 0 }}>
{item.icon &&
createElement(item.icon, {
width: item.iconSize || 10,
height: item.iconSize || 10,
style: {
marginRight: 7,
width: item.iconSize || 10,
},
})}
</Text>
<Text title={item.tooltip}>{item.text}</Text>
<View style={{ flex: 1 }} />
</>
) : (
<>
<label htmlFor={item.name} title={item.tooltip}>
{item.text}
</label>
<View style={{ flex: 1 }} />
<Toggle
id={item.name}
checked={item.toggle}
onColor={theme.pageTextPositive}
style={{ marginLeft: 5, ...item.style }}
onToggle={() =>
!item.disabled &&
item.toggle !== undefined &&
onMenuSelect(item.name)
}
/>
</>
)}
{item.key && <Keybinding keyName={item.key} />}
</View>
);

View File

@@ -0,0 +1,75 @@
import React from 'react';
import { css } from 'glamor';
import { theme, type CSSProperties } from '../../style';
type ToggleProps = {
id: string;
checked: boolean;
onToggle?: () => void;
onColor?: string;
style?: CSSProperties;
};
export const Toggle = ({
id,
checked,
onToggle,
onColor,
style,
}: ToggleProps) => {
return (
<div style={{ marginTop: -20, ...style }}>
<input
id={id}
checked={checked}
onChange={onToggle}
className={`${css({
height: 0,
width: 0,
visibility: 'hidden',
})}`}
type="checkbox"
/>
<label
style={{
background: checked ? onColor : theme.checkboxToggleBackground,
}}
className={`${css({
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
cursor: 'pointer',
width: '32px',
height: '16px',
borderRadius: '100px',
position: 'relative',
transition: 'background-color .2s',
})}`}
htmlFor={id}
>
<span
className={`${css(
{
content: '',
position: 'absolute',
top: '2px',
left: '2px',
width: '12px',
height: '12px',
borderRadius: '100px',
transition: '0.2s',
background: '#fff',
boxShadow: '0 0 2px 0 rgba(10, 10, 10, 0.29)',
},
checked && {
left: 'calc(100% - 2px)',
transform: 'translateX(-100%)',
},
)}`}
/>
</label>
</div>
);
};

View File

@@ -58,7 +58,10 @@ export const GoCardlessInitialise = ({
In order to enable bank-sync via GoCardless (only for EU banks) you
will need to create access credentials. This can be done by creating
an account with{' '}
<ExternalLink to="https://gocardless.com/" linkColor="purple">
<ExternalLink
to="https://actualbudget.org/docs/advanced/bank-sync/"
linkColor="purple"
>
GoCardless
</ExternalLink>
.

View File

@@ -1,12 +1,14 @@
import React from 'react';
import React, { useState } from 'react';
import * as monthUtils from 'loot-core/src/shared/months';
import { theme } from '../../style';
import { Button } from '../common/Button';
import { Menu } from '../common/Menu';
import { Select } from '../common/Select';
import { Text } from '../common/Text';
import { View } from '../common/View';
import { Checkbox } from '../forms';
import { Tooltip } from '../tooltips';
import { CategorySelector } from './CategorySelector';
import {
@@ -39,6 +41,7 @@ export function ReportSidebar({
onChangeDates,
onChangeViews,
}) {
const [menuOpen, setMenuOpen] = useState(false);
const onSelectRange = cond => {
setDateRange(cond);
switch (cond) {
@@ -242,70 +245,62 @@ export function ReportSidebar({
}}
>
<Text style={{ width: 40, textAlign: 'right', marginRight: 5 }} />
<Checkbox
id="show-empty-columns"
checked={customReportItems.showEmpty}
value={customReportItems.showEmpty}
onChange={() => setShowEmpty(!customReportItems.showEmpty)}
/>
<label
htmlFor="show-empty-columns"
title="Show rows that are zero or blank"
style={{ fontSize: 12 }}
<Button
onClick={() => {
setMenuOpen(true);
}}
style={{
color: 'currentColor',
padding: '5px 10px',
}}
>
Show Empty Rows
</label>
</View>
<View
style={{
flexDirection: 'row',
padding: 5,
alignItems: 'center',
}}
>
<Text style={{ width: 40, textAlign: 'right', marginRight: 5 }} />
<Checkbox
id="show-hidden-columns"
checked={customReportItems.showOffBudgetHidden}
value={customReportItems.showOffBudgetHidden}
onChange={() =>
setShowOffBudgetHidden(!customReportItems.showOffBudgetHidden)
}
/>
<label
htmlFor="show-hidden-columns"
title="Show off budget accounts and hidden categories"
style={{ fontSize: 12 }}
>
Off Budget Items
</label>
</View>
<View
style={{
flexDirection: 'row',
padding: 5,
alignItems: 'center',
}}
>
<Text style={{ width: 40, textAlign: 'right', marginRight: 5 }} />
<Checkbox
id="show-uncategorized"
checked={customReportItems.showUncategorized}
value={customReportItems.showUncategorized}
onChange={() =>
setShowUncategorized(!customReportItems.showUncategorized)
}
/>
<label
htmlFor="show-uncategorized"
title="Show uncategorized transactions"
style={{ fontSize: 12 }}
>
Uncategorized
</label>
Options
{menuOpen && (
<Tooltip
position="bottom-left"
style={{ padding: 0 }}
onClose={() => {
setMenuOpen(false);
}}
>
<Menu
onMenuSelect={type => {
if (type === 'show-hidden-categories') {
setShowOffBudgetHidden(
!customReportItems.showOffBudgetHidden,
);
} else if (type === 'show-empty-rows') {
setShowEmpty(!customReportItems.showEmpty);
} else if (type === 'show-uncategorized') {
setShowUncategorized(
!customReportItems.showUncategorized,
);
}
}}
items={[
{
name: 'show-empty-rows',
text: 'Show Empty Rows',
tooltip: 'Show rows that are zero or blank',
toggle: customReportItems.showEmpty,
},
{
name: 'show-hidden-categories',
text: 'Show Off Budget',
tooltip: 'Show off budget accounts and hidden categories',
toggle: customReportItems.showOffBudgetHidden,
},
{
name: 'show-uncategorized',
text: 'Show Uncategorized',
tooltip: 'Show uncategorized transactions',
toggle: customReportItems.showUncategorized,
},
]}
/>
</Tooltip>
)}
</Button>
</View>
<View
style={{

View File

@@ -7,7 +7,7 @@ export const adjustTextSize = (
let source;
switch (type) {
case 'variable':
source = variableLookup.find(({ value }) => values > value).arr;
source = variableLookup.find(({ value }) => values >= value).arr;
break;
case 'donut':
source = donutLookup;

View File

@@ -1131,10 +1131,10 @@ export const TransactionEdit = props => {
const Transaction = memo(function Transaction({
transaction,
account,
accounts,
categories,
payees,
showCategory,
added,
onSelect,
style,
@@ -1169,11 +1169,15 @@ const Transaction = memo(function Transaction({
payee,
transferAcct,
);
const prettyCategory = transferAcct
? 'Transfer'
: isParent
? 'Split'
: categoryName;
const specialCategory = account?.offbudget
? 'Off Budget'
: transferAcct
? 'Transfer'
: isParent
? 'Split'
: null;
const prettyCategory = specialCategory || categoryName;
const isPreview = isPreviewId(id);
const isReconciled = transaction.reconciled;
@@ -1260,22 +1264,21 @@ const Transaction = memo(function Transaction({
}}
/>
)}
{showCategory && (
<TextOneLine
style={{
fontSize: 11,
marginTop: 1,
fontWeight: '400',
color: prettyCategory
? theme.tableTextSelected
: theme.menuItemTextSelected,
fontStyle: prettyCategory ? null : 'italic',
textAlign: 'left',
}}
>
{prettyCategory || 'Uncategorized'}
</TextOneLine>
)}
<TextOneLine
style={{
fontSize: 11,
marginTop: 1,
fontWeight: '400',
color: prettyCategory
? theme.tableTextSelected
: theme.menuItemTextSelected,
fontStyle:
specialCategory || !prettyCategory ? 'italic' : undefined,
textAlign: 'left',
}}
>
{prettyCategory || 'Uncategorized'}
</TextOneLine>
</View>
)}
</View>
@@ -1296,11 +1299,11 @@ const Transaction = memo(function Transaction({
});
export function TransactionList({
account,
accounts,
categories,
payees,
transactions,
showCategory,
isNew,
onSelect,
scrollProps = {},
@@ -1384,10 +1387,10 @@ export function TransactionList({
>
<Transaction
transaction={transaction}
account={account}
categories={categories}
accounts={accounts}
payees={payees}
showCategory={showCategory}
added={isNew(transaction.id)}
onSelect={onSelect} // onSelect(transaction)}
/>

View File

@@ -174,6 +174,7 @@ export const checkboxText = tableText;
export const checkboxBackgroundSelected = colorPalette.purple300;
export const checkboxBorderSelected = colorPalette.purple300;
export const checkboxShadowSelected = colorPalette.purple500;
export const checkboxToggleBackground = colorPalette.gray700;
export const pillBackground = colorPalette.navy800;
export const pillBackgroundLight = colorPalette.navy900;

View File

@@ -174,6 +174,7 @@ export const checkboxText = tableBackground;
export const checkboxBackgroundSelected = colorPalette.blue500;
export const checkboxBorderSelected = colorPalette.blue500;
export const checkboxShadowSelected = colorPalette.blue300;
export const checkboxToggleBackground = colorPalette.gray400;
export const pillBackground = colorPalette.navy150;
export const pillBackgroundLight = colorPalette.navy100;

View File

@@ -3,7 +3,7 @@
"author": "Actual",
"productName": "Actual",
"description": "A simple and powerful personal finance system",
"version": "24.1.0",
"version": "24.2.0",
"scripts": {
"clean": "rm -rf dist",
"update-client": "bin/update-client",

View File

@@ -2,10 +2,4 @@ BEGIN TRANSACTION;
ALTER TABLE accounts ADD COLUMN account_sync_source TEXT;
UPDATE accounts SET
account_sync_source = CASE
WHEN account_id THEN 'goCardless'
ELSE NULL
END;
COMMIT;

View File

@@ -0,0 +1,9 @@
BEGIN TRANSACTION;
UPDATE accounts
SET
account_sync_source = 'goCardless'
WHERE account_id IS NOT NULL
AND account_sync_source IS NULL;
COMMIT;

View File

@@ -589,13 +589,12 @@ export async function createTestBudget(handlers: Handlers) {
{ name: 'House Asset', offBudget: true },
{ name: 'Roth IRA', offBudget: true },
];
await runMutator(() =>
batchMessages(async () => {
for (const account of accounts) {
account.id = await handlers['account-create'](account);
}
}),
);
await runMutator(async () => {
for (const account of accounts) {
account.id = await handlers['account-create'](account);
}
});
const payees: Array<MockPayeeEntity> = [
{ name: 'Starting Balance' },

View File

@@ -3,7 +3,7 @@ import { Notification } from '../../client/state-types/notifications';
import * as monthUtils from '../../shared/months';
import * as db from '../db';
import { setBudget, getSheetValue } from './actions';
import { setBudget, getSheetValue, setGoal } from './actions';
import { parse } from './cleanup-template.pegjs';
export function cleanupTemplate({ month }: { month: string }) {
@@ -35,11 +35,20 @@ async function processCleanup(month: string): Promise<Notification> {
sheetName,
`budget-${category.id}`,
);
const spent = await getSheetValue(
sheetName,
`sum-amount-${category.id}`,
);
await setBudget({
category: category.id,
month,
amount: budgeted - balance,
});
await setGoal({
category: category.id,
month,
goal: -spent,
});
num_sources += 1;
}
if (template.filter(t => t.type === 'sink').length > 0) {

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [twk3]
---
Bundle loot-core types into the API

View File

@@ -1,6 +0,0 @@
---
category: Enhancements
authors: [HansiWursti]
---
Added cleared and uncleared Balances to Account Mobile View

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [MatissJanis]
---
Fixing TypeScript issues when enabling `strictFunctionTypes` (pt.5).

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
Refactored MobileBudget component to TypeScript

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [twk3]
---
Switch desktop-client to the Vite JS framework.

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [shall0pass]
---
Goals: Refactor schedules file into functions and improve the readability of the code.

View File

@@ -1,6 +0,0 @@
---
category: Enhancements
authors: [davidkus]
---
Adding filter for reconciled transactions.

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 2

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 3

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 4

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 5

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 6

View File

@@ -1,6 +0,0 @@
---
category: Enhancements
authors: [carkom]
---
Enabling and formatting "viewLabels" button for custom reports page

View File

@@ -1,6 +0,0 @@
---
category: Bugfix
authors: [jasonmichalski]
---
Fix net worth graph to show more detail in compact card view

View File

@@ -1,6 +0,0 @@
---
category: Enhancements
authors: [Jackenmen]
---
Ask for confirmation when editing date of a locked transaction

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [twk3]
---
TypeScript: Add proper types to runHandler

View File

@@ -1,6 +0,0 @@
---
category: Enhancements
authors: [kstockk]
---
Add cleared column in csv export

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [MatissJanis]
---
Fixing TypeScript issues when enabling `strictFunctionTypes` (pt.4).

View File

@@ -1,6 +0,0 @@
---
category: Bugfix
authors: [jasonmichalski]
---
Fix when pressing Enter adds an extra split transaction when no split remains

View File

@@ -1,6 +0,0 @@
---
category: Features
authors: [NikxDa]
---
Add "Distribute" button to distribute remaining split amount across empty splits.

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [carkom]
---
Reorganize tableGraph files for custom reports.

View File

@@ -1,6 +0,0 @@
---
category: Enhancements
authors: [ScottFries, blakegearin, carkom]
---
Add ability to import categories from CSV

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [twk3]
---
Add api tests for payees and transactions

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 7

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 8

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 9

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 10

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 11

View File

@@ -1,6 +0,0 @@
---
category: Enhancements
authors: [rjwonder]
---
Update sync.ts with additionalInformation as last resort fallback to prevent Payee being empty

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [MatissJanis]
---
migration: rename `nordigen_*` secrets to `gocardless_*`

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [MatissJanis]
---
Electron-app app store (osx) release

View File

@@ -1,6 +0,0 @@
---
category: Maintenance
authors: [joel-jeremy]
---
eslint: no default exports - part 12

Some files were not shown because too many files have changed in this diff Show More