Compare commits

...

73 Commits

Author SHA1 Message Date
Joel Jeremy Marquez
c285392c3c Coderabbit 2025-01-08 13:09:33 -08:00
github-actions[bot]
3fe5b5aaf0 Update VRT 2025-01-08 19:38:17 +00:00
Joel Jeremy Marquez
4fc91c6b4c Fix typecheck 2025-01-08 11:11:42 -08:00
github-actions[bot]
45777cad8d Update VRT 2025-01-08 00:46:28 -08:00
Joel Jeremy Marquez
72644d3d51 Remove non-existent SelectedProviderWithItems 2025-01-08 00:46:28 -08:00
Joel Jeremy Marquez
5e805085b0 Ignore unused useActions 2025-01-08 00:46:28 -08:00
Joel Jeremy Marquez
4e6331c7f0 useTransactionsFilter hook 2025-01-08 00:46:28 -08:00
Joel Jeremy Marquez
2ec46d8dec Fix running balances by excluding child preview transactions 2025-01-08 00:46:12 -08:00
Joel Jeremy Marquez
bb17f6a6f1 Fix test 2025-01-08 00:46:12 -08:00
Joel Jeremy Marquez
ba70fca304 Fix test 2025-01-08 00:46:12 -08:00
Joel Jeremy Marquez
3ace7d199d Coderabbit feedback 2025-01-08 00:46:11 -08:00
Joel Jeremy Marquez
2a93b173e0 Fix default expand splits 2025-01-08 00:44:53 -08:00
Joel Jeremy Marquez
e9175951dd useTransactionsSearch 2025-01-08 00:44:53 -08:00
Joel Jeremy Marquez
3ba94642d9 Remove unused headerContent 2025-01-08 00:44:53 -08:00
Joel Jeremy Marquez
bc3b96c9b2 Release notes 2025-01-08 00:44:53 -08:00
Joel Jeremy Marquez
ccff8412d3 Fix preview transactions 2025-01-08 00:44:53 -08:00
Joel Jeremy Marquez
df61e42fda Initial commit 2025-01-08 00:44:53 -08:00
Joel Jeremy Marquez
0726760084 Code review feedback and improve schedules loading 2025-01-08 00:15:45 -08:00
Joel Jeremy Marquez
a7f65532fb Coderabbit feedback + make useSchedules consistent with query pattern used in useTransactions 2025-01-08 00:15:45 -08:00
Joel Jeremy Marquez
89059bf5da Code rabbit suggestions 2025-01-08 00:15:45 -08:00
Joel Jeremy Marquez
b9c167d5d6 Update useTransactions 2025-01-08 00:15:45 -08:00
Joel Jeremy Marquez
60dac66898 Apply coderabbit suggestions 2025-01-08 00:15:45 -08:00
Joel Jeremy Marquez
b9a43b992a Fix tests 2025-01-08 00:15:45 -08:00
Joel Jeremy Marquez
a7b90a0945 Fx flaky test 2025-01-08 00:15:45 -08:00
Joel Jeremy Marquez
fbc2ccd2e7 useTransactions hook to load transactions 2025-01-08 00:15:45 -08:00
Travis Lesicka
ce0ca60bcf ♻️ (typescript) Refactor Accounts/Balances to tsx and Remove ts-strict-ignore from Accounts/Account (#4047)
* Convert Balance.jsx to Balance.tsx

* Removed @ts-strict-ignore from Account.tsx

* Create 4047.md

* Fix typo

* Added Translation helpers to aria-labels

* Clarified canCalculateBalance return value logic
2025-01-08 08:59:22 +01:00
douugdev
3fbe6d05c8 fix: any rule not accounting for empty filter (#4051)
* fix: any rule not accounting for empty filter

* chore: add changelog
2025-01-08 00:37:50 +00:00
Joel Jeremy Marquez
cc1c11aac9 [Redux Toolkit Migration] Use new Redux Toolkit configureStore API (#4000)
* Initial upgrade to redux toolkit, more fixes needed e.g. removing non-serializable values from the state

* Fix typecheck and lint

* Fix lint and typecheck errors

* Fix lint and typecheck errors

* Fix typecheck error

* Cleanup

* Remove useAppStore

* Cleanup

* Undo renames

* Code review feedback

* UndoState type

* UndoState type

* yarn install
2025-01-07 16:34:21 -08:00
Robert Dyer
7dad36528c Add Copy last 6/12 months to budget menu (#4096)
* Add Copy last 12 months to budget menu

* add release note

* Make sure budget month actions use showUndoNotification
2025-01-08 00:33:03 +00:00
Julian Dominguez-Schatz
c956f8003b Mark release as draft initially (#4105)
* Mark release as draft initially

* Add release notes
2025-01-07 18:35:44 -05:00
sveselinovic
a5d591fed7 fix: creating new payee with 'one of'-condition broken (#4099)
* fix: creating new payee with 'one of'-condition broken

* change author and description of release note
2025-01-07 21:57:53 +01:00
Koen van Staveren
1f44903e4b enhance: net bar graph show net instead of two separate bars (#4033)
* enhance: net bar graph show net instead of two separate bars

* chore: note
2025-01-07 19:57:56 +01:00
Matt Fiddaman
bd77dfd111 🔧 Migrate to ESLint v9 (#3993) 2025-01-07 18:51:59 +00:00
Matt Fiddaman
39cfa11b25 🌍 improve translation strings - part 1 (#4041) 2025-01-07 18:47:13 +00:00
Matiss Janis Aboltins
af0a14ce3d ♻️ (typescript) refactor ScheduleDetails to tsx (#3964) 2025-01-07 18:45:52 +00:00
Matiss Janis Aboltins
1f2155053f 🔥 remove unused permissions prop from Button components (#4085) 2025-01-07 18:25:05 +00:00
Matiss Janis Aboltins
d5ebcced38 🔥 remove unused report prop from Link component (#4083) 2025-01-07 18:24:49 +00:00
Robert Dyer
7c2408daa6 Do not show undo notifications on desktop (#4097)
* Do not show undo notifications on desktop

* add release note

* fix linter

* Update packages/desktop-client/src/hooks/useUndo.ts

Co-authored-by: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>

* Update useUndo.ts

* fix code pasted on wrong line

* drive-by fix typo

* Update 4097.md

---------

Co-authored-by: Joel Jeremy Marquez <joeljeremy.marquez@gmail.com>
2025-01-07 08:08:14 -07:00
Julian Dominguez-Schatz
82e1922bee 🔖 (25.1.0) (#4095)
* 🔖 (25.1.0)

* Remove used release notes

---------

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
2025-01-07 01:06:34 -05:00
gabe
8f66605994 add buttons for combined-account transaction pages on mobile (#3734)
* add buttons for combined-account transaction pages (#2333)

* add release note for #3734

* add accessibility label to mobile combined-account header button

* increase touch targets for combined-account buttons

* remove highlight color and add bounce to header buttons

to match the feel of the other account buttons

* update vrt screenshots for actualbudget#3734
2025-01-06 16:18:54 -08:00
Leo Lee
eadd11b7f0 (typescript) Refactoring the mobile TransactionListWithBalance component into typescript. (#4061)
* refactor: convert txListwBal to tsx

* docs: add release notes

* docs: rename notes

* refactor: fix missing cleared/uncleared balance

* refactor: use Binding type
2025-01-06 13:23:03 -08:00
Julian Dominguez-Schatz
832fd1e5d8 Fix schedule split template amounts (#4077)
* Fix incorrect argument to goals schedule function

* Add argument types to prevent similar issues

* Add release notes

* Fix test types
2025-01-02 18:01:19 -05:00
Matt Fiddaman
928260ca3a Fix calendar report day background colour in development theme (#4073)
* fix calendar background in development theme

* note
2025-01-02 17:12:27 +00:00
Julian Dominguez-Schatz
be5bfa275e Fix icon hover effect in transaction table (#4070)
* Fix icon hover effect in transaction table

* Add release notes

* Add test
2025-01-02 11:45:59 -05:00
Matt Fiddaman
1e65939147 fix mobile hold buffer initial sign (#4068) 2025-01-01 22:18:28 +00:00
youngcw
7060e4b657 [Goals]: fix repeating spend templates (#4066)
* fix repeating spend templates

* work with all repeat types

* note
2025-01-01 15:03:30 -07:00
Matt Fiddaman
da613ab673 Fix payee cell overflowing when it contains an icon (#4056) 2024-12-30 20:39:47 +00:00
Koen van Staveren
d894281465 enhance: context menu on sidebar elements (#3777)
* enhance: context menu on sidebar account

* enhance: context menu on EditableBudgetName

* chore: release note

* chore: lint

* Update packages/desktop-client/src/components/sidebar/Sidebar.tsx

* chore: fix margin

* fix: merge

* chore: use useContextMenu hook

* style: change account name field an input

* lint

---------

Co-authored-by: matt <matt@fiddaman.net>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2024-12-29 21:13:57 +00:00
Koen van Staveren
5c577aa069 enhance: context menu on custom reports page (#3776)
* enhance: context menu on custom reports page

* chore: release note

* chore: lint

* chore: use both feature flags

* chore: use both feature flags

* chore: pr feedback

* fix: changing name with context menu
2024-12-29 20:51:56 +00:00
Robert Dyer
e6aeea668b Fix issue where the UI is stuck sync'ing if no data from server (#3941)
* Fix issue where the UI is stuck sync'ing if no data from server

* add release note
2024-12-29 20:49:19 +00:00
Julian Dominguez-Schatz
ded2f39e13 Fix loading of number format on initial app startup (#4038)
* Fix loading of number format on initial app load

* Add release notes
2024-12-27 18:19:22 -05:00
Matt Fiddaman
3f6068fe88 add electron build files to eslint ignore list (#4042)
* add electron build files to eslintignore

* note
2024-12-26 01:22:53 +00:00
Julian Dominguez-Schatz
9213ed75b5 Upload translations on builds of master (#4002)
* fix: translations were not being loaded properly

* fix: support running GitHub actions locally with `act`

* feat: upload new strings on master build

* Add release notes

* PR feedback: security
2024-12-24 12:48:41 -05:00
Matt Fiddaman
93262e7fb4 extend fix splits tool to report splits with mismatched amounts (#3970) 2024-12-24 07:43:44 +00:00
Matt Fiddaman
cd8bb8e139 change feedback issue for openid (#4030) 2024-12-23 17:11:46 -07:00
Koen van Staveren
bd126b499b feat: now button at budget page (#3703)
* feat: now button on budget

* Update VRT

* chore: change to icon

* chore: rename to today

* chore: fix not being centered on multiple months

* Update VRT

* Update VRT

* Trigger Build

* fix: keep now button with monthpicker not left

* Update VRT

* fix: center MonthPicker

* Update VRT

* Trigger Build

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-23 23:11:15 +01:00
Koen van Staveren
8976ffc256 enhance: allow negatives in the budget template (#4028)
* enhance: allow negatives in the budget template

* chore: add test case

* chore: release note
2024-12-23 23:10:54 +01:00
lelemm
0b2c8ccd88 OpenId Implementation (#3878)
* OpenId implementation

* Code rabbit auto generated code applied

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

* Code rabbit suggestions round 2

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

* fixes from code rabbit round 1

* fixes from code rabbit round 2

* change variable name

* code review round 3

* Update VRT

* small fix

* Update VRT

* linter

* app.tsx

* LoggedInUser

* UserAccess

* UserAccessHeader

* UserAccessPage

* UserAccessRow

* UserDirectory

* UserDirectoryHeader

* UserDirectoryPage

* UserDirectoryRow

* BudgetList

* Bootstrap

* Login

* OpenIdForm

* CreateAccountModal

* EditAccess

* EditUser

* GoCardlessInitialiseModal

* OpenIDEnableModal

* PasswordEnableModal

* SimpleFinInitialiseModal

* TransferOwnership

* AuthSettings

* fix hooks in EditUser

* enable electron openid login

* typecheck

* linter and typecheck fixes

* Update VRT

* small fix

* linter

* small changes for file owner name and a fix for privacyfilter in the username

* linter for merge

* change the entra url and changing the electron loopback url when built

* "logged in as" was showing when had no user

* linter

* linter²

* code review

---------

Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: matt <matt@fiddaman.net>
2024-12-23 09:46:22 -07:00
youngcw
cde81da72c [Goals]: Fix applying templates in tracking budget (#4010)
* fix logic

* note
2024-12-21 19:49:29 -07:00
Matt Fiddaman
6cfb9d2a7a Fix incorrect boldening of synced accounts in the sidebar (#4009) 2024-12-19 15:13:44 +00:00
Matt Fiddaman
4ce5e2fd07 Prevent schedules with null amounts from crashing the app (#3958)
* test

* note

* add comment
2024-12-18 21:55:23 +00:00
Matt Fiddaman
11bde73fa5 🔧 upgrade better-sqlite3 (#3987)
* upgrade better-sqlite3

* note
2024-12-18 09:05:46 +00:00
Matiss Janis Aboltins
94666a2ac1 Update bug-report.yml 2024-12-16 20:21:44 +00:00
Matiss Janis Aboltins
b6fbcef6f0 Update bug-report.yml 2024-12-16 20:21:15 +00:00
Matiss Janis Aboltins
1165c4c008 Update bug-report.yml (#3992) 2024-12-16 20:20:21 +00:00
Darin Loh
8446356cc6 fix: space missing on create local account copy (#3985) 2024-12-15 12:27:39 +00:00
lelemm
ec977ee51a Calendar Report (#3828) 2024-12-14 20:19:14 +00:00
Dany Khalife (MSFT)
ef95850e93 Migrate useSplitsExpanded to TypeScript (#3945)
* useSplitsExpanded renamed to .tsx

* Some type hardening

* add release note

* lint

* typecheck

* lint

* rename expanded -> isExpanded
2024-12-11 17:52:41 -08:00
Joel Jeremy Marquez
81fc029a03 Use useTranslation hook instead of directly importing the t function (#3893)
* Use useTranslation hook instead of directly importing the t function

* Release notes

* Fix lint
2024-12-11 13:57:45 -08:00
Dany Khalife (MSFT)
9e6a486c90 Dkhalife/ts/categorytransactions (#3959)
* rename

* a bit of hardening

* release notes

* typecheck & lint

* lint
2024-12-11 21:46:55 +00:00
Marian Bäuerle
9af3539b91 Fix iOS mobile navigation tabs disappearing on bouncing top (#3962) 2024-12-11 13:28:03 -08:00
Joel Jeremy Marquez
62d8358f90 Remove use of useActions (#3911)
* Remove use of useActions

* Release notes

* Fix lint
2024-12-10 15:16:56 -08:00
Joel Jeremy Marquez
219e139d55 Consistent accounts terminology (For budget / Budgeted --> On budget) (#3903)
* Change for budget and budgeted terms to on-budget

* Release notes

* Update mobile account header

* Fix release notes

* Fix release note category

* Update VRT

* Rename variables

* Remove hyphens

* Show off budget

* Update VRT

* Dummy commit

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2024-12-10 15:15:44 -08:00
519 changed files with 13770 additions and 5372 deletions

View File

@@ -1,28 +0,0 @@
packages/api/app/bundle.api.js
packages/api/dist
packages/api/@types
packages/api/migrations
packages/crdt/dist
packages/desktop-client/bundle.browser.js
packages/desktop-client/build/
packages/desktop-client/build-stats/
packages/desktop-client/public/kcab/
packages/desktop-client/public/data/
packages/desktop-client/**/node_modules/*
packages/desktop-client/node_modules/
packages/desktop-client/src/icons/**/*
packages/desktop-client/test-results/
packages/desktop-client/playwright-report/
packages/desktop-electron/client-build/
packages/desktop-electron/dist/
packages/import-ynab4/**/node_modules/*
packages/import-ynab5/**/node_modules/*
packages/loot-core/**/node_modules/*
packages/loot-core/**/lib-dist/*
packages/loot-core/**/proto/*

View File

@@ -1,608 +0,0 @@
const path = require('path');
const rulesDirPlugin = require('eslint-plugin-rulesdir');
rulesDirPlugin.RULES_DIR = path.join(
__dirname,
'packages',
'eslint-plugin-actual',
'lib',
'rules',
);
const ruleFCMsg =
'Type the props argument and let TS infer or use ComponentType for a component prop';
const restrictedImportPatterns = [
{
group: ['*.api', '*.web', '*.electron'],
message: 'Dont directly reference imports from other platforms',
},
{
group: ['uuid'],
importNames: ['*'],
message: "Use `import { v4 as uuidv4 } from 'uuid'` instead",
},
];
const restrictedImportColors = [
{
group: ['**/style', '**/colors'],
importNames: ['colors'],
message: 'Please use themes instead of colors',
},
];
module.exports = {
root: true,
env: {
browser: true,
commonjs: true,
es6: true,
jest: true,
node: true,
},
plugins: [
'prettier',
'import',
'rulesdir',
'@typescript-eslint',
'jsx-a11y',
'react-hooks',
],
extends: [
'plugin:react/recommended',
'plugin:react/jsx-runtime',
'plugin:prettier/recommended',
'plugin:@typescript-eslint/recommended',
'plugin:import/typescript',
],
parser: '@typescript-eslint/parser',
parserOptions: { project: [path.join(__dirname, './tsconfig.json')] },
reportUnusedDisableDirectives: true,
globals: {
globalThis: false,
vi: true,
},
rules: {
// http://eslint.org/docs/rules/
'array-callback-return': 'warn',
'default-case': ['warn', { commentPattern: '^no default$' }],
'dot-location': ['warn', 'property'],
eqeqeq: ['warn', 'smart'],
'new-parens': 'warn',
'no-array-constructor': 'warn',
'no-caller': 'warn',
'no-cond-assign': ['warn', 'except-parens'],
'no-const-assign': 'warn',
'no-control-regex': 'warn',
'no-delete-var': 'warn',
'no-dupe-args': 'warn',
'no-dupe-class-members': 'warn',
'no-dupe-keys': 'warn',
'no-duplicate-case': 'warn',
'no-empty-character-class': 'warn',
'no-empty-pattern': 'warn',
'no-eval': 'warn',
'no-ex-assign': 'warn',
'no-extend-native': 'warn',
'no-extra-bind': 'warn',
'no-extra-label': 'warn',
'no-fallthrough': 'warn',
'no-func-assign': 'warn',
'no-implied-eval': 'warn',
'no-invalid-regexp': 'warn',
'no-iterator': 'warn',
'no-label-var': 'warn',
'no-labels': ['warn', { allowLoop: true, allowSwitch: false }],
'no-lone-blocks': 'warn',
'no-mixed-operators': [
'warn',
{
groups: [
['&', '|', '^', '~', '<<', '>>', '>>>'],
['==', '!=', '===', '!==', '>', '>=', '<', '<='],
['&&', '||'],
['in', 'instanceof'],
],
allowSamePrecedence: false,
},
],
'no-multi-str': 'warn',
'no-global-assign': 'warn',
'no-unsafe-negation': 'warn',
'no-new-func': 'warn',
'no-new-object': 'warn',
'no-new-symbol': 'warn',
'no-new-wrappers': 'warn',
'no-obj-calls': 'warn',
'no-octal': 'warn',
'no-octal-escape': 'warn',
'no-redeclare': 'warn',
'no-regex-spaces': 'warn',
'no-script-url': 'warn',
'no-self-assign': 'warn',
'no-self-compare': 'warn',
'no-sequences': 'warn',
'no-shadow-restricted-names': 'warn',
'no-sparse-arrays': 'warn',
'no-template-curly-in-string': 'warn',
'no-this-before-super': 'warn',
'no-throw-literal': 'warn',
'no-undef': 'error',
'no-unreachable': 'warn',
'no-unused-expressions': [
'error',
{
allowShortCircuit: true,
allowTernary: true,
allowTaggedTemplates: true,
},
],
'no-unused-labels': 'warn',
'no-use-before-define': [
'warn',
{
functions: false,
classes: false,
variables: false,
},
],
'no-useless-computed-key': 'warn',
'no-useless-concat': 'warn',
'no-useless-constructor': 'warn',
'no-useless-escape': 'warn',
'no-useless-rename': [
'warn',
{
ignoreDestructuring: false,
ignoreImport: false,
ignoreExport: false,
},
],
'no-with': 'warn',
'no-whitespace-before-property': 'warn',
'react-hooks/exhaustive-deps': [
'warn',
{
additionalHooks: '(useQuery)',
},
],
'require-yield': 'warn',
'rest-spread-spacing': ['warn', 'never'],
strict: ['warn', 'never'],
'unicode-bom': ['warn', 'never'],
'use-isnan': 'warn',
'valid-typeof': 'warn',
'no-restricted-properties': [
'error',
{
object: 'require',
property: 'ensure',
message:
'Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting',
},
{
object: 'System',
property: 'import',
message:
'Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting',
},
],
'getter-return': 'warn',
// https://github.com/benmosher/eslint-plugin-import/tree/master/docs/rules
'import/first': 'error',
'import/no-amd': 'error',
'import/no-anonymous-default-export': 'warn',
'import/no-webpack-loader-syntax': 'error',
// https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules
'react/forbid-foreign-prop-types': ['warn', { allowInPropTypes: true }],
'react/jsx-no-comment-textnodes': 'warn',
'react/jsx-no-duplicate-props': 'warn',
'react/jsx-no-target-blank': 'warn',
'react/jsx-no-undef': 'error',
'react/jsx-pascal-case': [
'warn',
{
allowAllCaps: true,
ignore: [],
},
],
'react/no-danger-with-children': 'warn',
// Disabled because of undesirable warnings
// See https://github.com/facebook/create-react-app/issues/5204 for
// blockers until its re-enabled
// 'react/no-deprecated': 'warn',
'react/no-direct-mutation-state': 'warn',
'react/no-is-mounted': 'warn',
'react/no-typos': 'error',
'react/require-render-return': 'error',
'react/style-prop-object': 'warn',
// https://github.com/evcohen/eslint-plugin-jsx-a11y/tree/master/docs/rules
'jsx-a11y/alt-text': 'warn',
'jsx-a11y/anchor-has-content': 'warn',
'jsx-a11y/anchor-is-valid': [
'warn',
{
aspects: ['noHref', 'invalidHref'],
},
],
'jsx-a11y/aria-activedescendant-has-tabindex': 'warn',
'jsx-a11y/aria-props': 'warn',
'jsx-a11y/aria-proptypes': 'warn',
'jsx-a11y/aria-role': ['warn', { ignoreNonDOM: true }],
'jsx-a11y/aria-unsupported-elements': 'warn',
'jsx-a11y/heading-has-content': 'warn',
'jsx-a11y/iframe-has-title': 'warn',
'jsx-a11y/img-redundant-alt': 'warn',
'jsx-a11y/no-access-key': 'warn',
'jsx-a11y/no-distracting-elements': 'warn',
'jsx-a11y/no-redundant-roles': 'warn',
'jsx-a11y/role-has-required-aria-props': 'warn',
'jsx-a11y/role-supports-aria-props': 'warn',
'jsx-a11y/scope': 'warn',
// https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks
'react-hooks/rules-of-hooks': 'error',
'prettier/prettier': 'warn',
// Note: base rule explicitly disabled in favor of the TS one
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
varsIgnorePattern: '^(_|React)',
ignoreRestSiblings: true,
caughtErrors: 'none',
},
],
curly: ['warn', 'multi-line', 'consistent'],
'no-restricted-globals': ['warn'].concat(
require('confusing-browser-globals').filter(g => g !== 'self'),
),
'react/jsx-filename-extension': [
'warn',
{ extensions: ['.jsx', '.tsx'], allow: 'as-needed' },
],
'react/jsx-no-useless-fragment': 'warn',
'react/self-closing-comp': 'warn',
'react/no-unstable-nested-components': [
'warn',
{ allowAsProps: true, customValidators: ['formatter'] },
],
'rulesdir/typography': 'warn',
'rulesdir/prefer-if-statement': 'warn',
// https://github.com/eslint/eslint/issues/16954
// https://github.com/eslint/eslint/issues/16953
'no-loop-func': 'off',
// Do don't need this as we're using TypeScript
'react/prop-types': 'off',
// TODO: re-enable these rules
'react/react-in-jsx-scope': 'off',
'no-var': 'warn',
'react/jsx-curly-brace-presence': 'warn',
'object-shorthand': ['warn', 'properties'],
'import/extensions': [
'warn',
'never',
{
json: 'always',
},
],
'import/no-useless-path-segments': 'warn',
'import/no-duplicates': ['warn', { 'prefer-inline': true }],
'import/no-unused-modules': ['warn', { unusedExports: true }],
'import/order': [
'warn',
{
alphabetize: {
caseInsensitive: true,
order: 'asc',
},
groups: [
'builtin', // Built-in types are first
'external',
'parent',
'sibling',
'index', // Then the index file
],
'newlines-between': 'always',
pathGroups: [
// Enforce that React (and react-related packages) is the first import
{ group: 'builtin', pattern: 'react?(-*)', position: 'before' },
// Separate imports from Actual from "real" external imports
{
group: 'external',
pattern: 'loot-{core,design}/**/*',
position: 'after',
},
],
pathGroupsExcludedImportTypes: ['react'],
},
],
'no-restricted-syntax': [
'warn',
{
// forbid React.* as they are legacy https://twitter.com/dan_abramov/status/1308739731551858689
selector:
":matches(MemberExpression[object.name='React'], TSQualifiedName[left.name='React'])",
message:
'Using default React import is discouraged, please use named exports directly instead.',
},
{
// forbid <a> in favor of <Link>
selector: 'JSXOpeningElement[name.name="a"]',
message: 'Using <a> is discouraged, please use <Link> instead.',
},
],
'no-restricted-imports': [
'warn',
{ patterns: [...restrictedImportPatterns, ...restrictedImportColors] },
],
'@typescript-eslint/ban-ts-comment': [
'error',
{ 'ts-ignore': 'allow-with-description' },
],
// Rules disable during TS migration
'@typescript-eslint/no-var-requires': 'off',
'prefer-const': 'warn',
'prefer-spread': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-require-imports': 'off',
'import/no-default-export': 'warn',
},
overrides: [
{
files: ['**/*.ts?(x)'],
parser: '@typescript-eslint/parser',
parserOptions: {
ecmaVersion: 2018,
sourceType: 'module',
ecmaFeatures: {
jsx: true,
},
// typescript-eslint specific options
warnOnUnsupportedTypeScriptVersion: true,
},
plugins: ['@typescript-eslint'],
// If adding a typescript-eslint version of an existing ESLint rule,
// make sure to disable the ESLint rule here.
rules: {
// TypeScript's `noFallthroughCasesInSwitch` option is more robust (#6906)
'default-case': 'off',
// 'tsc' already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/291)
'no-dupe-class-members': 'off',
// 'tsc' already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/477)
'no-undef': 'off',
// Add TypeScript specific rules (and turn off ESLint equivalents)
'@typescript-eslint/consistent-type-assertions': 'warn',
'no-array-constructor': 'off',
'@typescript-eslint/no-array-constructor': 'warn',
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': 'warn',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': [
'warn',
{
functions: false,
classes: false,
variables: false,
typedefs: false,
},
],
'no-unused-expressions': 'off',
'@typescript-eslint/no-unused-expressions': [
'error',
{
allowShortCircuit: true,
allowTernary: true,
allowTaggedTemplates: true,
},
],
'no-useless-constructor': 'off',
'@typescript-eslint/no-useless-constructor': 'warn',
},
},
{
files: ['.eslintrc.js', './**/.eslintrc.js'],
parserOptions: { project: null },
rules: {
'@typescript-eslint/consistent-type-exports': 'off',
},
},
{
files: [
'./packages/desktop-client/**/*.{ts,tsx}',
'./packages/loot-core/src/client/**/*.{ts,tsx}',
],
rules: {
// enforce type over interface
'@typescript-eslint/consistent-type-definitions': ['warn', 'type'],
// enforce import type
'@typescript-eslint/consistent-type-imports': [
'warn',
{ prefer: 'type-imports', fixStyle: 'inline-type-imports' },
],
'@typescript-eslint/no-restricted-types': [
'warn',
{
types: {
// forbid FC as superflous
FunctionComponent: { message: ruleFCMsg },
FC: { message: ruleFCMsg },
},
},
],
},
},
{
files: ['./packages/desktop-client/**/*'],
excludedFiles: [
'./packages/desktop-client/src/hooks/useNavigate.{ts,tsx}',
],
rules: {
'no-restricted-imports': [
'warn',
{
patterns: [
{
group: ['react-router-dom'],
importNames: ['useNavigate'],
message: 'Please use Actuals useNavigate() hook instead.',
},
],
},
],
},
},
{
files: ['./packages/loot-core/src/**/*'],
rules: {
'no-restricted-imports': [
'warn',
{
patterns: [
...restrictedImportPatterns,
{
group: ['loot-core/**'],
message:
'Please use relative imports in loot-core instead of importing from `loot-core/*`',
},
],
},
],
},
},
{
files: [
'packages/loot-core/src/types/**/*',
'packages/loot-core/src/client/state-types/**/*',
'**/icons/**/*',
'**/{mocks,__mocks__}/**/*',
// can't correctly resolve usages
'**/*.{testing,electron,browser,web,api}.ts',
],
rules: { 'import/no-unused-modules': 'off' },
},
{
files: [
'./packages/desktop-client/src/style/index.*',
'./packages/desktop-client/src/style/palette.*',
],
rules: {
'no-restricted-imports': ['off', { patterns: restrictedImportColors }],
},
},
{
files: [
'./packages/api/migrations/*',
'./packages/loot-core/migrations/*',
],
rules: {
'import/no-default-export': 'off',
},
},
{
// TODO: fix the issues in these files
files: [
'./packages/desktop-client/src/components/accounts/Account.jsx',
'./packages/desktop-client/src/components/accounts/MobileAccount.jsx',
'./packages/desktop-client/src/components/accounts/MobileAccounts.jsx',
'./packages/desktop-client/src/components/App.tsx',
'./packages/desktop-client/src/components/budget/BudgetCategories.jsx',
'./packages/desktop-client/src/components/budget/BudgetSummaries.tsx',
'./packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx',
'./packages/desktop-client/src/components/budget/index.tsx',
'./packages/desktop-client/src/components/budget/MobileBudget.tsx',
'./packages/desktop-client/src/components/budget/envelope/HoldMenu.tsx',
'./packages/desktop-client/src/components/budget/envelope/TransferMenu.tsx',
'./packages/desktop-client/src/components/common/Menu.tsx',
'./packages/desktop-client/src/components/FinancesApp.tsx',
'./packages/desktop-client/src/components/GlobalKeys.ts',
'./packages/desktop-client/src/components/LoggedInUser.tsx',
'./packages/desktop-client/src/components/manager/ManagementApp.jsx',
'./packages/desktop-client/src/components/manager/subscribe/common.tsx',
'./packages/desktop-client/src/components/ManageRules.tsx',
'./packages/desktop-client/src/components/mobile/MobileAmountInput.jsx',
'./packages/desktop-client/src/components/mobile/MobileNavTabs.tsx',
'./packages/desktop-client/src/components/Modals.tsx',
'./packages/desktop-client/src/components/modals/EditRule.jsx',
'./packages/desktop-client/src/components/modals/ImportTransactions.jsx',
'./packages/desktop-client/src/components/modals/MergeUnusedPayees.jsx',
'./packages/desktop-client/src/components/Notifications.tsx',
'./packages/desktop-client/src/components/payees/ManagePayees.jsx',
'./packages/desktop-client/src/components/payees/ManagePayeesWithData.jsx',
'./packages/desktop-client/src/components/payees/PayeeTable.tsx',
'./packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx',
'./packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx',
'./packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx',
'./packages/desktop-client/src/components/reports/reports/CustomReport.jsx',
'./packages/desktop-client/src/components/reports/reports/NetWorthCard.jsx',
'./packages/desktop-client/src/components/reports/SaveReportName.tsx',
'./packages/desktop-client/src/components/reports/useReport.ts',
'./packages/desktop-client/src/components/schedules/ScheduleDetails.jsx',
'./packages/desktop-client/src/components/schedules/SchedulesTable.tsx',
'./packages/desktop-client/src/components/select/DateSelect.tsx',
'./packages/desktop-client/src/components/sidebar/Tools.tsx',
'./packages/desktop-client/src/components/sort.tsx',
'./packages/desktop-client/src/components/spreadsheet/useSheetValue.ts',
'./packages/desktop-client/src/components/table.tsx',
'./packages/desktop-client/src/components/Titlebar.tsx',
'./packages/desktop-client/src/components/transactions/MobileTransaction.jsx',
'./packages/desktop-client/src/components/transactions/SelectedTransactions.jsx',
'./packages/desktop-client/src/components/transactions/SimpleTransactionsTable.jsx',
'./packages/desktop-client/src/components/transactions/TransactionList.jsx',
'./packages/desktop-client/src/components/transactions/TransactionsTable.jsx',
'./packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx',
'./packages/desktop-client/src/hooks/useAccounts.ts',
'./packages/desktop-client/src/hooks/useCategories.ts',
'./packages/desktop-client/src/hooks/usePayees.ts',
'./packages/desktop-client/src/hooks/useProperFocus.tsx',
'./packages/desktop-client/src/hooks/useSelected.tsx',
'./packages/loot-core/src/client/query-hooks.tsx',
],
rules: {
'react-hooks/exhaustive-deps': 'off',
},
},
{
files: [
'.eslintrc.js',
'*.test.js',
'*.test.ts',
'*.test.jsx',
'*.test.tsx',
],
rules: {
'rulesdir/typography': 'off',
},
},
],
settings: {
react: {
version: 'detect',
},
'import/resolver': {
typescript: {
alwaysTryTypes: true,
},
},
},
};

View File

@@ -23,8 +23,6 @@ body:
options:
- label: 'I have searched and found no existing issue'
required: true
- label: 'I will be providing steps how to reproduce the bug (in most cases this will also mean uploading a demo budget file)'
required: true
validations:
required: true
- type: textarea
@@ -36,6 +34,14 @@ body:
value: 'A bug happened!'
validations:
required: true
- type: textarea
id: reproduction
attributes:
label: How can we reproduce the issue?
description: Please give step-by-step instructions on how to reproduce the issue. In most cases this might also require uploading a sample budget/import file.
value: 'How can we reproduce the issue?'
validations:
required: true
- type: markdown
id: env-info
attributes:

View File

@@ -7,6 +7,10 @@ runs:
uses: actions/setup-node@v4
with:
node-version: 18.16.0
- name: Install yarn
run: npm install -g yarn
shell: bash
if: ${{ env.ACT }}
- name: Cache
uses: actions/cache@v4
id: cache

View File

@@ -80,6 +80,7 @@ jobs:
- name: Add to Release
uses: softprops/action-gh-release@v2
with:
draft: true
files: |
packages/desktop-electron/dist/*.dmg
packages/desktop-electron/dist/*.exe

View File

@@ -0,0 +1,36 @@
name: Extract and upload i18n strings
on:
push:
branches:
- master
workflow_dispatch:
jobs:
extract-and-upload-i18n-strings:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up environment
uses: ./.github/actions/setup
- name: Configure i18n client
run: |
pip install wlc
- name: Generate i18n strings
run: yarn generate:i18n
- name: Upload i18n strings
run: |
if [[ ! -f packages/desktop-client/locale/en.json ]]; then
echo "File packages/desktop-client/locale/en.json not found. Ensure the file was generated correctly."
exit 1
fi
wlc \
--url https://hosted.weblate.org/api/ \
--key "${{ secrets.WEBLATE_API_KEY_CI_STRINGS }}" \
upload \
--author-name "Actual Budget" \
--author-email "dev@actualbudget.org" \
--method add \
--input packages/desktop-client/locale/en.json \
actualbudget/actual/en
echo "Translations uploaded"

789
eslint.config.mjs Normal file
View File

@@ -0,0 +1,789 @@
import path from 'node:path';
import { fileURLToPath } from 'node:url';
import globals from 'globals';
import pluginImport from 'eslint-plugin-import';
import pluginJSXA11y from 'eslint-plugin-jsx-a11y';
import pluginPrettier from 'eslint-plugin-prettier/recommended';
import pluginReact from 'eslint-plugin-react';
import pluginReactHooks from 'eslint-plugin-react-hooks';
import pluginRulesDir from 'eslint-plugin-rulesdir';
import pluginTypescript from 'typescript-eslint';
import tsParser from '@typescript-eslint/parser';
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
pluginRulesDir.RULES_DIR = path.join(
__dirname,
'packages',
'eslint-plugin-actual',
'lib',
'rules',
);
const confusingBrowserGlobals = [
// https://github.com/facebook/create-react-app/tree/main/packages/confusing-browser-globals
'addEventListener',
'blur',
'close',
'closed',
'confirm',
'defaultStatus',
'defaultstatus',
'event',
'external',
'find',
'focus',
'frameElement',
'frames',
'history',
'innerHeight',
'innerWidth',
'length',
'location',
'locationbar',
'menubar',
'moveBy',
'moveTo',
'name',
'onblur',
'onerror',
'onfocus',
'onload',
'onresize',
'onunload',
'open',
'opener',
'opera',
'outerHeight',
'outerWidth',
'pageXOffset',
'pageYOffset',
'parent',
'print',
'removeEventListener',
'resizeBy',
'resizeTo',
'screen',
'screenLeft',
'screenTop',
'screenX',
'screenY',
'scroll',
'scrollbars',
'scrollBy',
'scrollTo',
'scrollX',
'scrollY',
'status',
'statusbar',
'stop',
'toolbar',
'top',
];
/** @type {import('eslint').Linter.Config[]} */
export default [
{
ignores: [
'packages/api/app/bundle.api.js',
'packages/api/dist',
'packages/api/@types',
'packages/api/migrations',
'packages/crdt/dist',
'packages/desktop-client/bundle.browser.js',
'packages/desktop-client/build/',
'packages/desktop-client/build-electron/',
'packages/desktop-client/build-stats/',
'packages/desktop-client/public/kcab/',
'packages/desktop-client/public/data/',
'packages/desktop-client/**/node_modules/*',
'packages/desktop-client/node_modules/',
'packages/desktop-client/src/icons/**/*',
'packages/desktop-client/test-results/',
'packages/desktop-client/playwright-report/',
'packages/desktop-electron/client-build/',
'packages/desktop-electron/build/',
'packages/desktop-electron/dist/',
'packages/import-ynab4/**/node_modules/*',
'packages/import-ynab5/**/node_modules/*',
'packages/loot-core/**/node_modules/*',
'packages/loot-core/**/lib-dist/*',
'packages/loot-core/**/proto/*',
'.yarn/*',
'.github/*',
],
},
{
linterOptions: {
reportUnusedDisableDirectives: true,
},
languageOptions: {
globals: {
...globals.browser,
...globals.commonjs,
...globals.jest,
...globals.node,
globalThis: false,
vi: true,
},
},
settings: {
react: {
version: 'detect',
},
'import/resolver': {
typescript: {
alwaysTryTypes: true,
},
},
},
},
pluginReact.configs.flat.recommended,
pluginReact.configs.flat['jsx-runtime'],
pluginPrettier,
...pluginTypescript.configs.recommended,
pluginImport.flatConfigs.recommended,
{
plugins: {
'react-hooks': pluginReactHooks,
'jsx-a11y': pluginJSXA11y,
rulesdir: pluginRulesDir,
},
},
{
files: ['**/*.{js,ts,jsx,tsx}'],
rules: {
// http://eslint.org/docs/rules/
'array-callback-return': 'warn',
'default-case': [
'warn',
{
commentPattern: '^no default$',
},
],
curly: ['warn', 'multi-line', 'consistent'],
'dot-location': ['warn', 'property'],
eqeqeq: ['warn', 'smart'],
'new-parens': 'warn',
'no-array-constructor': 'warn',
'no-caller': 'warn',
'no-cond-assign': ['warn', 'except-parens'],
'no-const-assign': 'warn',
'no-control-regex': 'warn',
'no-delete-var': 'warn',
'no-dupe-args': 'warn',
'no-dupe-class-members': 'warn',
'no-dupe-keys': 'warn',
'no-duplicate-case': 'warn',
'no-empty-character-class': 'warn',
'no-empty-pattern': 'warn',
'no-eval': 'warn',
'no-ex-assign': 'warn',
'no-extend-native': 'warn',
'no-extra-bind': 'warn',
'no-extra-label': 'warn',
'no-fallthrough': 'warn',
'no-func-assign': 'warn',
'no-implied-eval': 'warn',
'no-invalid-regexp': 'warn',
'no-iterator': 'warn',
'no-label-var': 'warn',
'no-labels': [
'warn',
{
allowLoop: true,
allowSwitch: false,
},
],
'no-lone-blocks': 'warn',
'no-mixed-operators': [
'warn',
{
groups: [
['&', '|', '^', '~', '<<', '>>', '>>>'],
['==', '!=', '===', '!==', '>', '>=', '<', '<='],
['&&', '||'],
['in', 'instanceof'],
],
allowSamePrecedence: false,
},
],
'no-multi-str': 'warn',
'no-global-assign': 'warn',
'no-unsafe-negation': 'warn',
'no-new-func': 'warn',
'no-new-object': 'warn',
'no-new-symbol': 'warn',
'no-new-wrappers': 'warn',
'no-obj-calls': 'warn',
'no-octal': 'warn',
'no-octal-escape': 'warn',
'no-redeclare': 'warn',
'no-regex-spaces': 'warn',
'no-script-url': 'warn',
'no-self-assign': 'warn',
'no-self-compare': 'warn',
'no-sequences': 'warn',
'no-shadow-restricted-names': 'warn',
'no-sparse-arrays': 'warn',
'no-template-curly-in-string': 'warn',
'no-this-before-super': 'warn',
'no-throw-literal': 'warn',
'no-undef': 'error',
'no-unreachable': 'warn',
'no-unused-expressions': [
'error',
{
allowShortCircuit: true,
allowTernary: true,
allowTaggedTemplates: true,
},
],
'no-unused-labels': 'warn',
'no-use-before-define': [
'warn',
{
functions: false,
classes: false,
variables: false,
},
],
'no-useless-computed-key': 'warn',
'no-useless-concat': 'warn',
'no-useless-constructor': 'warn',
'no-useless-escape': 'warn',
'no-useless-rename': [
'warn',
{
ignoreDestructuring: false,
ignoreImport: false,
ignoreExport: false,
},
],
'no-with': 'warn',
'no-whitespace-before-property': 'warn',
'require-yield': 'warn',
'rest-spread-spacing': ['warn', 'never'],
strict: ['warn', 'never'],
'unicode-bom': ['warn', 'never'],
'use-isnan': 'warn',
'valid-typeof': 'warn',
'no-restricted-properties': [
'error',
{
object: 'require',
property: 'ensure',
message:
'Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting',
},
{
object: 'System',
property: 'import',
message:
'Please use import() instead. More info: https://facebook.github.io/create-react-app/docs/code-splitting',
},
],
'getter-return': 'warn',
// https://github.com/benmosher/eslint-plugin-import/tree/master/docs/rules
'import/first': 'error',
'import/no-amd': 'error',
'import/no-anonymous-default-export': 'warn',
'import/no-webpack-loader-syntax': 'error',
'import/extensions': [
'warn',
'never',
{
json: 'always',
},
],
'import/no-useless-path-segments': 'warn',
'import/no-duplicates': [
'warn',
{
'prefer-inline': true,
},
],
'import/order': [
'warn',
{
alphabetize: {
caseInsensitive: true,
order: 'asc',
},
groups: ['builtin', 'external', 'parent', 'sibling', 'index'],
'newlines-between': 'always',
pathGroups: [
{
// Enforce that React (and react-related packages) is the first import
group: 'builtin',
pattern: 'react?(-*)',
position: 'before',
},
{
// Separate imports from Actual from "real" external imports
group: 'external',
pattern: 'loot-{core,design}/**/*',
position: 'after',
},
],
pathGroupsExcludedImportTypes: ['react'],
},
],
// https://github.com/yannickcr/eslint-plugin-react/tree/master/docs/rules
'react/forbid-foreign-prop-types': [
'warn',
{
allowInPropTypes: true,
},
],
'react/jsx-no-comment-textnodes': 'warn',
'react/jsx-no-duplicate-props': 'warn',
'react/jsx-no-target-blank': 'warn',
'react/jsx-no-undef': 'error',
'react/jsx-pascal-case': [
'warn',
{
allowAllCaps: true,
ignore: [],
},
],
'react/no-danger-with-children': 'warn',
// Disabled because of undesirable warnings
// See https://github.com/facebook/create-react-app/issues/5204 for
// blockers until its re-enabled
// 'react/no-deprecated': 'warn',
'react/no-direct-mutation-state': 'warn',
'react/no-is-mounted': 'warn',
'react/no-typos': 'error',
'react/require-render-return': 'error',
'react/style-prop-object': 'warn',
'react/jsx-no-useless-fragment': 'warn',
'react/self-closing-comp': 'warn',
'react/jsx-filename-extension': [
'warn',
{
extensions: ['.jsx', '.tsx'],
allow: 'as-needed',
},
],
'react/no-unstable-nested-components': [
'warn',
{
allowAsProps: true,
customValidators: ['formatter'],
},
],
// Don't need this as we're using TypeScript
'react/prop-types': 'off',
// https://github.com/evcohen/eslint-plugin-jsx-a11y/tree/master/docs/rules
'jsx-a11y/alt-text': 'warn',
'jsx-a11y/anchor-has-content': 'warn',
'jsx-a11y/anchor-is-valid': [
'warn',
{
aspects: ['noHref', 'invalidHref'],
},
],
'jsx-a11y/aria-activedescendant-has-tabindex': 'warn',
'jsx-a11y/aria-props': 'warn',
'jsx-a11y/aria-proptypes': 'warn',
'jsx-a11y/aria-role': [
'warn',
{
ignoreNonDOM: true,
},
],
'jsx-a11y/aria-unsupported-elements': 'warn',
'jsx-a11y/heading-has-content': 'warn',
'jsx-a11y/iframe-has-title': 'warn',
'jsx-a11y/img-redundant-alt': 'warn',
'jsx-a11y/no-access-key': 'warn',
'jsx-a11y/no-distracting-elements': 'warn',
'jsx-a11y/no-redundant-roles': 'warn',
'jsx-a11y/role-has-required-aria-props': 'warn',
'jsx-a11y/role-supports-aria-props': 'warn',
'jsx-a11y/scope': 'warn',
// https://github.com/facebook/react/tree/main/packages/eslint-plugin-react-hooks
'react-hooks/rules-of-hooks': 'error',
'react-hooks/exhaustive-deps': [
'warn',
{
additionalHooks: '(useQuery)',
},
],
'rulesdir/typography': 'warn',
'rulesdir/prefer-if-statement': 'warn',
// Note: base rule explicitly disabled in favor of the TS one
'no-unused-vars': 'off',
'@typescript-eslint/no-unused-vars': [
'warn',
{
varsIgnorePattern: '^(_|React)',
ignoreRestSiblings: true,
caughtErrors: 'none',
},
],
'no-restricted-globals': ['warn', ...confusingBrowserGlobals],
// https://github.com/eslint/eslint/issues/16954
// https://github.com/eslint/eslint/issues/16953
'no-loop-func': 'off',
// TODO: re-enable these rules
'react/react-in-jsx-scope': 'off',
'no-var': 'warn',
'react/jsx-curly-brace-presence': 'warn',
'object-shorthand': ['warn', 'properties'],
'no-restricted-syntax': [
'warn',
{
// forbid React.* as they are legacy https://twitter.com/dan_abramov/status/1308739731551858689
selector:
":matches(MemberExpression[object.name='React'], TSQualifiedName[left.name='React'])",
message:
'Using default React import is discouraged, please use named exports directly instead.',
},
{
// forbid <a> in favor of <Link>
selector: 'JSXOpeningElement[name.name="a"]',
message: 'Using <a> is discouraged, please use <Link> instead.',
},
],
'no-restricted-imports': [
'warn',
{
patterns: [
{
group: ['*.api', '*.web', '*.electron'],
message: "Don't directly reference imports from other platforms",
},
{
group: ['uuid'],
importNames: ['*'],
message: "Use `import { v4 as uuidv4 } from 'uuid'` instead",
},
{
group: ['**/style', '**/colors'],
importNames: ['colors'],
message: 'Please use themes instead of colors',
},
],
},
],
'@typescript-eslint/ban-ts-comment': [
'error',
{
'ts-ignore': 'allow-with-description',
},
],
// Rules disabled during TS migration
'@typescript-eslint/no-var-requires': 'off',
'prefer-const': 'warn',
'prefer-spread': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-require-imports': 'off',
'import/no-default-export': 'warn',
},
},
{
files: ['**/*.ts?(x)'],
languageOptions: {
parser: tsParser,
ecmaVersion: 2018,
sourceType: 'module',
parserOptions: {
project: [path.join(__dirname, './tsconfig.json')],
ecmaFeatures: {
jsx: true,
},
// typescript-eslint specific options
warnOnUnsupportedTypeScriptVersion: true,
},
},
// If adding a typescript-eslint version of an existing ESLint rule,
// make sure to disable the ESLint rule here.
rules: {
// TypeScript's `noFallthroughCasesInSwitch` option is more robust (#6906)
'default-case': 'off',
// 'tsc' already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/291)
'no-dupe-class-members': 'off',
// 'tsc' already handles this (https://github.com/typescript-eslint/typescript-eslint/issues/477)
'no-undef': 'off',
// Add TypeScript specific rules (and turn off ESLint equivalents)
'@typescript-eslint/consistent-type-assertions': 'warn',
'no-array-constructor': 'off',
'@typescript-eslint/no-array-constructor': 'warn',
'no-redeclare': 'off',
'@typescript-eslint/no-redeclare': 'warn',
'no-use-before-define': 'off',
'@typescript-eslint/no-use-before-define': [
'warn',
{
functions: false,
classes: false,
variables: false,
typedefs: false,
},
],
'no-unused-expressions': 'off',
'@typescript-eslint/no-unused-expressions': [
'error',
{
allowShortCircuit: true,
allowTernary: true,
allowTaggedTemplates: true,
},
],
'no-useless-constructor': 'off',
'@typescript-eslint/no-useless-constructor': 'warn',
},
},
{
files: [
'packages/desktop-client/**/*.{ts,tsx}',
'packages/loot-core/src/client/**/*.{ts,tsx}',
],
rules: {
// enforce type over interface
'@typescript-eslint/consistent-type-definitions': ['warn', 'type'],
// enforce import type
'@typescript-eslint/consistent-type-imports': [
'warn',
{
prefer: 'type-imports',
fixStyle: 'inline-type-imports',
},
],
'@typescript-eslint/no-restricted-types': [
'warn',
{
types: {
// forbid FC as superflous
FunctionComponent: {
message:
'Type the props argument and let TS infer or use ComponentType for a component prop',
},
FC: {
message:
'Type the props argument and let TS infer or use ComponentType for a component prop',
},
},
},
],
},
},
{
files: ['packages/desktop-client/**/*'],
ignores: ['packages/desktop-client/src/hooks/useNavigate.{ts,tsx}'],
rules: {
'no-restricted-imports': [
'warn',
{
patterns: [
{
group: ['react-router-dom'],
importNames: ['useNavigate'],
message: "Please use Actual's useNavigate() hook instead.",
},
],
},
],
},
},
{
files: ['packages/loot-core/src/**/*'],
rules: {
'no-restricted-imports': [
'warn',
{
patterns: [
{
group: ['*.api', '*.web', '*.electron'],
message: "Don't directly reference imports from other platforms",
},
{
group: ['uuid'],
importNames: ['*'],
message: "Use `import { v4 as uuidv4 } from 'uuid'` instead",
},
{
group: ['loot-core/**'],
message:
'Please use relative imports in loot-core instead of importing from `loot-core/*`',
},
],
},
],
},
},
{
files: [
'packages/loot-core/src/types/**/*',
'packages/loot-core/src/client/state-types/**/*',
'**/icons/**/*',
'**/{mocks,__mocks__}/**/*',
// can't correctly resolve usages
'**/*.{testing,electron,browser,web,api}.ts',
],
rules: {
'import/no-unused-modules': 'off',
},
},
{
files: [
'packages/desktop-client/src/style/index.*',
'packages/desktop-client/src/style/palette.*',
],
rules: {
'no-restricted-imports': [
'off',
{
patterns: [
{
group: ['**/style', '**/colors'],
importNames: ['colors'],
message: 'Please use themes instead of colors',
},
],
},
],
},
},
{
files: ['packages/api/migrations/*', 'packages/loot-core/migrations/*'],
rules: {
'import/no-default-export': 'off',
},
},
{},
{
// TODO: fix the issues in these files
files: [
'packages/desktop-client/src/components/accounts/Account.jsx',
'packages/desktop-client/src/components/accounts/MobileAccount.jsx',
'packages/desktop-client/src/components/accounts/MobileAccounts.jsx',
'packages/desktop-client/src/components/App.tsx',
'packages/desktop-client/src/components/budget/BudgetCategories.jsx',
'packages/desktop-client/src/components/budget/BudgetSummaries.tsx',
'packages/desktop-client/src/components/budget/DynamicBudgetTable.tsx',
'packages/desktop-client/src/components/budget/index.tsx',
'packages/desktop-client/src/components/budget/MobileBudget.tsx',
'packages/desktop-client/src/components/budget/envelope/HoldMenu.tsx',
'packages/desktop-client/src/components/budget/envelope/TransferMenu.tsx',
'packages/desktop-client/src/components/common/Menu.tsx',
'packages/desktop-client/src/components/FinancesApp.tsx',
'packages/desktop-client/src/components/GlobalKeys.ts',
'packages/desktop-client/src/components/LoggedInUser.tsx',
'packages/desktop-client/src/components/manager/ManagementApp.jsx',
'packages/desktop-client/src/components/manager/subscribe/common.tsx',
'packages/desktop-client/src/components/ManageRules.tsx',
'packages/desktop-client/src/components/mobile/MobileAmountInput.jsx',
'packages/desktop-client/src/components/mobile/MobileNavTabs.tsx',
'packages/desktop-client/src/components/Modals.tsx',
'packages/desktop-client/src/components/modals/EditRule.jsx',
'packages/desktop-client/src/components/modals/ImportTransactions.jsx',
'packages/desktop-client/src/components/modals/MergeUnusedPayees.jsx',
'packages/desktop-client/src/components/Notifications.tsx',
'packages/desktop-client/src/components/payees/ManagePayees.jsx',
'packages/desktop-client/src/components/payees/ManagePayeesWithData.jsx',
'packages/desktop-client/src/components/payees/PayeeTable.tsx',
'packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTable.tsx',
'packages/desktop-client/src/components/reports/graphs/tableGraph/ReportTableTotals.tsx',
'packages/desktop-client/src/components/reports/reports/CashFlowCard.jsx',
'packages/desktop-client/src/components/reports/reports/CustomReport.jsx',
'packages/desktop-client/src/components/reports/reports/NetWorthCard.jsx',
'packages/desktop-client/src/components/reports/SaveReportName.tsx',
'packages/desktop-client/src/components/reports/useReport.ts',
'packages/desktop-client/src/components/schedules/ScheduleDetails.jsx',
'packages/desktop-client/src/components/schedules/SchedulesTable.tsx',
'packages/desktop-client/src/components/select/DateSelect.tsx',
'packages/desktop-client/src/components/sidebar/Tools.tsx',
'packages/desktop-client/src/components/sort.tsx',
'packages/desktop-client/src/components/spreadsheet/useSheetValue.ts',
'packages/desktop-client/src/components/table.tsx',
'packages/desktop-client/src/components/Titlebar.tsx',
'packages/desktop-client/src/components/transactions/MobileTransaction.jsx',
'packages/desktop-client/src/components/transactions/SelectedTransactions.jsx',
'packages/desktop-client/src/components/transactions/SimpleTransactionsTable.jsx',
'packages/desktop-client/src/components/transactions/TransactionList.jsx',
'packages/desktop-client/src/components/transactions/TransactionsTable.jsx',
'packages/desktop-client/src/components/transactions/TransactionsTable.test.jsx',
'packages/desktop-client/src/hooks/useAccounts.ts',
'packages/desktop-client/src/hooks/useCategories.ts',
'packages/desktop-client/src/hooks/usePayees.ts',
'packages/desktop-client/src/hooks/useProperFocus.tsx',
'packages/desktop-client/src/hooks/useSelected.tsx',
'packages/loot-core/src/client/query-hooks.tsx',
],
rules: {
'react-hooks/exhaustive-deps': 'off',
},
},
{
files: [
'eslint.config.mjs',
'**/*.test.js',
'**/*.test.ts',
'**/*.test.jsx',
'**/*.test.tsx',
],
rules: {
'rulesdir/typography': 'off',
},
},
];

View File

@@ -38,33 +38,33 @@
"vrt:docker": "./bin/run-vrt",
"rebuild-electron": "./node_modules/.bin/electron-rebuild -f -m ./packages/loot-core",
"rebuild-node": "yarn workspace loot-core rebuild",
"lint": "eslint . --max-warnings 0 --ext .js,.jsx,.ts,.tsx",
"lint": "eslint . --max-warnings 0",
"lint:verbose": "DEBUG=eslint:cli-engine eslint . --max-warnings 0",
"typecheck": "yarn tsc && tsc-strict",
"jq": "./node_modules/node-jq/bin/jq",
"prepare": "husky"
},
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^8.1.0",
"@typescript-eslint/parser": "^8.1.0",
"confusing-browser-globals": "^1.0.11",
"@typescript-eslint/parser": "^8.18.1",
"cross-env": "^7.0.3",
"eslint": "^8.57.0",
"eslint": "^9.17.0",
"eslint-config-prettier": "^9.1.0",
"eslint-import-resolver-typescript": "3.6.1",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsx-a11y": "^6.9.0",
"eslint-import-resolver-typescript": "^3.7.0",
"eslint-plugin-import": "^2.31.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-prettier": "5.2.1",
"eslint-plugin-react": "7.35.0",
"eslint-plugin-react-hooks": "^4.6.2",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.1.0",
"eslint-plugin-rulesdir": "^0.2.2",
"globals": "^15.13.0",
"husky": "^9.0.11",
"lint-staged": "^15.2.9",
"node-jq": "^4.0.1",
"npm-run-all": "^4.1.5",
"prettier": "3.3.3",
"prettier": "^3.4.2",
"source-map-support": "^0.5.21",
"typescript": "^5.5.4",
"typescript-eslint": "^8.18.1",
"typescript-strict-plugin": "^2.4.4"
},
"resolutions": {

View File

@@ -8,7 +8,7 @@ import type {
import type { InitConfig } from 'loot-core/server/main';
// @ts-ignore: bundle not available until we build it
// eslint-disable-next-line import/extensions
// eslint-disable-next-line import/extensions, import/no-unresolved
import * as bundle from './app/bundle.api.js';
import * as injected from './injected';
import { validateNodeVersion } from './validateNodeVersion';

View File

@@ -86,7 +86,10 @@ export function addTransactions(
}
export function importTransactions(accountId, transactions) {
return send('api/transactions-import', { accountId, transactions });
return send('api/transactions-import', {
accountId,
transactions,
});
}
export function getTransactions(accountId, startDate, endDate) {

View File

@@ -1,6 +1,6 @@
{
"name": "@actual-app/api",
"version": "24.12.0",
"version": "25.1.0",
"license": "MIT",
"description": "An API for Actual",
"engines": {
@@ -23,7 +23,7 @@
},
"dependencies": {
"@actual-app/crdt": "workspace:^",
"better-sqlite3": "^9.6.0",
"better-sqlite3": "^11.7.0",
"compare-versions": "^6.1.0",
"node-fetch": "^3.3.2",
"uuid": "^9.0.1"

View File

@@ -25,3 +25,6 @@ public/kcab
public/data
public/data-file-index.txt
public/*.wasm
# translations
locale/

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 33 KiB

View File

@@ -54,17 +54,17 @@ test.describe('Accounts', () => {
await expect(page).toMatchThemeScreenshots();
});
test.describe('Budgeted Accounts', () => {
test.describe('On Budget Accounts', () => {
// Reset filters
test.afterEach(async () => {
await accountPage.removeFilter(0);
});
test('creates a transfer from two existing transactions', async () => {
accountPage = await navigation.goToAccountPage('For budget');
accountPage = await navigation.goToAccountPage('On budget');
await accountPage.waitFor();
await expect(accountPage.accountName).toHaveText('Budgeted Accounts');
await expect(accountPage.accountName).toHaveText('On Budget Accounts');
await accountPage.filterByNote('Test Acc Transfer');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 171 KiB

After

Width:  |  Height:  |  Size: 171 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 166 KiB

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 168 KiB

After

Width:  |  Height:  |  Size: 168 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 153 KiB

After

Width:  |  Height:  |  Size: 153 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 152 KiB

After

Width:  |  Height:  |  Size: 152 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 151 KiB

After

Width:  |  Height:  |  Size: 151 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 197 KiB

After

Width:  |  Height:  |  Size: 197 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 190 KiB

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 116 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 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: 56 KiB

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 56 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: 27 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 29 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 29 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 30 KiB

After

Width:  |  Height:  |  Size: 29 KiB

View File

@@ -27,12 +27,12 @@ test.describe('Budget', () => {
test('renders the summary information: available funds, overspent, budgeted and for next month', async () => {
const summary = budgetPage.budgetSummary.first();
await expect(summary.getByText('Available Funds')).toBeVisible({
await expect(summary.getByText('Available funds')).toBeVisible({
timeout: 10000,
});
await expect(summary.getByText(/^Overspent in /)).toBeVisible();
await expect(summary.getByText('Budgeted')).toBeVisible();
await expect(summary.getByText('For Next Month')).toBeVisible();
await expect(summary.getByText('For next month')).toBeVisible();
await expect(page).toMatchThemeScreenshots();
});

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

After

Width:  |  Height:  |  Size: 84 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: 84 KiB

After

Width:  |  Height:  |  Size: 84 KiB

View File

@@ -34,7 +34,7 @@ export class MobileBudgetPage {
name: 'Saved',
});
this.projectedSavingsButton = this.budgetTableHeader.getByRole('button', {
name: 'Projected Savings',
name: 'Projected savings',
});
this.overspentButton = this.budgetTableHeader.getByRole('button', {
name: 'Overspent',
@@ -294,7 +294,7 @@ export class MobileBudgetPage {
}
throw new Error(
'None of “Saved”, “Projected Savings”, or “Overspent” buttons could be located on the page',
'None of “Saved”, “Projected savings”, or “Overspent” buttons could be located on the page',
);
}

View File

@@ -58,7 +58,7 @@ export class Navigation {
async createAccount(data) {
await this.page.getByRole('button', { name: 'Add account' }).click();
await this.page
.getByRole('button', { name: 'Create local account' })
.getByRole('button', { name: 'Create a local account' })
.click();
// Fill the form
@@ -66,7 +66,7 @@ export class Navigation {
await this.page.getByLabel('Balance:').fill(String(data.balance));
if (data.offBudget) {
await this.page.getByLabel('Off-budget').click();
await this.page.getByLabel('Off budget').click();
}
await this.page

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 110 KiB

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 109 KiB

After

Width:  |  Height:  |  Size: 109 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 103 KiB

After

Width:  |  Height:  |  Size: 103 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: 103 KiB

After

Width:  |  Height:  |  Size: 103 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 112 KiB

After

Width:  |  Height:  |  Size: 112 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 123 KiB

After

Width:  |  Height:  |  Size: 123 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 125 KiB

After

Width:  |  Height:  |  Size: 125 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 114 KiB

After

Width:  |  Height:  |  Size: 114 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 113 KiB

After

Width:  |  Height:  |  Size: 113 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 111 KiB

After

Width:  |  Height:  |  Size: 111 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 116 KiB

After

Width:  |  Height:  |  Size: 116 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: 115 KiB

After

Width:  |  Height:  |  Size: 115 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 121 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 KiB

After

Width:  |  Height:  |  Size: 122 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

After

Width:  |  Height:  |  Size: 120 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 71 KiB

After

Width:  |  Height:  |  Size: 72 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: 70 KiB

After

Width:  |  Height:  |  Size: 70 KiB

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