Compare commits
1 Commits
non-chrome
...
revert-329
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d6b1508d6 |
27
.eslintignore
Normal file
@@ -0,0 +1,27 @@
|
||||
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-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/*
|
||||
603
.eslintrc.js
Normal file
@@ -0,0 +1,603 @@
|
||||
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: 'Don’t 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',
|
||||
'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 Actual’s 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/rollover/HoldMenu.tsx',
|
||||
'./packages/desktop-client/src/components/budget/rollover/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,
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
12
.github/ISSUE_TEMPLATE/bug-report.yml
vendored
@@ -12,7 +12,7 @@ body:
|
||||
id: intro-md
|
||||
attributes:
|
||||
value: |
|
||||
**IMPORTANT:** we use GitHub Issues only for BUG REPORTS and FEATURE REQUESTS. If you are looking for help/support - please reach out to the [community on Discord](https://discord.gg/pRYNYr4W5A). All non-bug and non-feature-request issues will be closed.
|
||||
**IMPORTANT:** we use Github Issues only for BUG REPORTS and FEATURE REQUESTS. If you are looking for help/support - please reach out to the [community on Discord](https://discord.gg/pRYNYr4W5A). All non-bug and non-feature-request issues will be closed.
|
||||
|
||||
**Bank-sync problems (SimpleFin / GoCardless)?** Reach out via the [community Discord](https://discord.gg/pRYNYr4W5A) first and open an issue only if the community deems the issue to be a legitimate bug in Actual.
|
||||
- type: checkboxes
|
||||
@@ -23,6 +23,8 @@ 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
|
||||
@@ -34,14 +36,6 @@ 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:
|
||||
|
||||
3
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -6,6 +6,3 @@ contact_links:
|
||||
- name: Support
|
||||
url: https://discord.gg/pRYNYr4W5A
|
||||
about: Need help with something? Having troubles setting up? Or perhaps issues using the API? Reach out to the community on Discord.
|
||||
- name: Translations
|
||||
url: https://hosted.weblate.org/projects/actualbudget/actual/
|
||||
about: Found a string that needs a better translation? Add your suggestion or upvote an existing one in Weblate.
|
||||
|
||||
30
.github/actions/bump-package-versions
vendored
@@ -1,30 +0,0 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
if [ "$#" -gt 0 ]; then
|
||||
version="${1#v}"
|
||||
else
|
||||
version=""
|
||||
fi
|
||||
|
||||
files_to_bump=(
|
||||
packages/api/package.json
|
||||
packages/desktop-client/package.json
|
||||
packages/desktop-electron/package.json
|
||||
)
|
||||
|
||||
for file in "${files_to_bump[@]}"; do
|
||||
if [ -z "$version" ]; then
|
||||
# version format: YY.MM.patch
|
||||
# logic: if before the 25th, bump patch, else set minor/major to next month
|
||||
version="$(jq -r .version "$file" | perl -e '($y,$m,$p)=split/\./,<>;$d=(localtime)[3];$d>25?($p=0,++$m,$m>12&&($m=1,++$y)):$p++;print"$y.$m.$p\n"')"
|
||||
if [ -z "$version" ]; then
|
||||
echo "Error: Failed to calculate new version" >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
echo "Bumping $file to version $version"
|
||||
jq '.version = "'"$version"'"' "$file" > "$file.tmp"
|
||||
mv "$file.tmp" "$file"
|
||||
done
|
||||
29
.github/actions/setup/action.yml
vendored
@@ -1,15 +1,5 @@
|
||||
name: Setup
|
||||
|
||||
inputs:
|
||||
working-directory:
|
||||
description: 'Working directory to run in, default .'
|
||||
required: false
|
||||
default: '.'
|
||||
download-translations:
|
||||
description: 'Whether to download translations as part of setup, default true'
|
||||
required: false
|
||||
default: 'true'
|
||||
|
||||
runs:
|
||||
using: composite
|
||||
steps:
|
||||
@@ -17,28 +7,13 @@ 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
|
||||
with:
|
||||
path: ${{ format('{0}/**/node_modules', inputs.working-directory) }}
|
||||
key: yarn-v1-${{ runner.os }}-${{ hashFiles(format('{0}/.nvmrc', inputs.working-directory)) }}-${{ hashFiles(format('{0}/**/yarn.lock', inputs.working-directory)) }}
|
||||
path: '**/node_modules'
|
||||
key: yarn-v1-${{ runner.os }}-${{ hashFiles('.nvmrc') }}-${{ hashFiles('**/yarn.lock') }}
|
||||
- name: Install
|
||||
working-directory: ${{ inputs.working-directory }}
|
||||
run: yarn --immutable
|
||||
shell: bash
|
||||
if: steps.cache.outputs.cache-hit != 'true'
|
||||
- name: Download translations
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
repository: actualbudget/translations
|
||||
path: ${{ inputs.working-directory }}/packages/desktop-client/locale
|
||||
if: ${{ inputs.download-translations == 'true' }}
|
||||
- name: Remove untranslated languages
|
||||
run: packages/desktop-client/bin/remove-untranslated-languages
|
||||
shell: bash
|
||||
if: ${{ inputs.download-translations == 'true' }}
|
||||
|
||||
14
.github/workflows/build.yml
vendored
@@ -68,17 +68,3 @@ jobs:
|
||||
with:
|
||||
name: build-stats
|
||||
path: packages/desktop-client/build-stats
|
||||
|
||||
server:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Build Server
|
||||
run: cd packages/sync-server && yarn build
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: sync-server
|
||||
path: packages/sync-server/build
|
||||
|
||||
90
.github/workflows/docker-edge.yml
vendored
@@ -1,90 +0,0 @@
|
||||
name: Build Edge Docker Image
|
||||
|
||||
# Edge Docker images are built for every commit, and daily
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'packages/sync-server/**'
|
||||
pull_request:
|
||||
branches:
|
||||
- master
|
||||
paths:
|
||||
- 'packages/sync-server/**'
|
||||
schedule:
|
||||
- cron: '0 0 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
packages: write
|
||||
|
||||
env:
|
||||
IMAGES: |
|
||||
actualbudget/actual-server
|
||||
ghcr.io/actualbudget/actual-server
|
||||
ghcr.io/actualbudget/actual
|
||||
|
||||
# Creates the following tags:
|
||||
# - actual-server:edge
|
||||
TAGS: |
|
||||
type=edge,value=edge
|
||||
type=sha
|
||||
|
||||
jobs:
|
||||
build:
|
||||
if: ${{ github.event.repository.fork == false }}
|
||||
name: Build Docker image
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu, alpine]
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
# Push to both Docker Hub and Github Container Registry
|
||||
images: ${{ env.IMAGES }}
|
||||
flavor: ${{ matrix.os != 'ubuntu' && format('suffix=-{0}', matrix.os) || '' }}
|
||||
tags: ${{ env.TAGS }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
if: github.event_name != 'pull_request'
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Download artifacts
|
||||
run: ./packages/sync-server/docker/download-artifacts.sh
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: ${{ github.event_name != 'pull_request' }}
|
||||
file: packages/sync-server/docker/edge-${{ matrix.os }}.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7${{ matrix.os == 'alpine' && ',linux/arm/v6' || '' }}
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
build-args: |
|
||||
GITHUB_TOKEN=${{ secrets.GITHUB_TOKEN }}
|
||||
89
.github/workflows/docker-release.yml
vendored
@@ -1,89 +0,0 @@
|
||||
name: Build Stable Docker Image
|
||||
|
||||
# Stable Docker images are built for every new tag
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'v*.*.*'
|
||||
paths-ignore:
|
||||
- README.md
|
||||
- LICENSE.txt
|
||||
|
||||
env:
|
||||
IMAGES: |
|
||||
actualbudget/actual-server
|
||||
ghcr.io/actualbudget/actual-server
|
||||
ghcr.io/actualbudget/actual
|
||||
|
||||
# Creates the following tags:
|
||||
# - actual-server:latest (see docker/metadata-action flavor inputs, below)
|
||||
# - actual-server:1.3
|
||||
# - actual-server:1.3.7
|
||||
# - actual-server:sha-90dd603
|
||||
TAGS: |
|
||||
type=semver,pattern={{version}}
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: Build Docker image
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Set up QEMU
|
||||
uses: docker/setup-qemu-action@v3
|
||||
|
||||
- name: Set up Docker Buildx
|
||||
uses: docker/setup-buildx-action@v3
|
||||
|
||||
- name: Docker meta
|
||||
id: meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
# Push to both Docker Hub and Github Container Registry
|
||||
images: ${{ env.IMAGES }}
|
||||
# Automatically update :latest
|
||||
flavor: latest=true
|
||||
tags: ${{ env.TAGS }}
|
||||
|
||||
- name: Docker meta for Alpine image
|
||||
id: alpine-meta
|
||||
uses: docker/metadata-action@v5
|
||||
with:
|
||||
images: ${{ env.IMAGES }}
|
||||
# Automatically update :latest
|
||||
flavor: |
|
||||
latest=true
|
||||
suffix=-alpine,onlatest=true
|
||||
tags: ${{ env.TAGS }}
|
||||
|
||||
- name: Login to Docker Hub
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
username: ${{ secrets.DOCKERHUB_USERNAME }}
|
||||
password: ${{ secrets.DOCKERHUB_TOKEN }}
|
||||
|
||||
- name: Login to GitHub Container Registry
|
||||
uses: docker/login-action@v3
|
||||
with:
|
||||
registry: ghcr.io
|
||||
username: ${{ github.repository_owner }}
|
||||
password: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Build and push ubuntu image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: packages/sync-server/docker/stable-ubuntu.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
tags: ${{ steps.meta.outputs.tags }}
|
||||
|
||||
- name: Build and push alpine image
|
||||
uses: docker/build-push-action@v5
|
||||
with:
|
||||
context: .
|
||||
push: true
|
||||
file: packages/sync-server/docker/stable-alpine.Dockerfile
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7,linux/arm/v6
|
||||
tags: ${{ steps.alpine-meta.outputs.tags }}
|
||||
3
.github/workflows/e2e-test.yml
vendored
@@ -1,7 +1,6 @@
|
||||
name: E2E Tests
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
on: [pull_request]
|
||||
|
||||
env:
|
||||
GITHUB_PR_NUMBER: ${{github.event.pull_request.number}}
|
||||
|
||||
12
.github/workflows/electron-master.yml
vendored
@@ -40,7 +40,6 @@ jobs:
|
||||
python3 -m pip install setuptools
|
||||
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install flatpak -y
|
||||
sudo apt-get install flatpak-builder -y
|
||||
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
@@ -49,17 +48,13 @@ jobs:
|
||||
sudo flatpak install org.electronjs.Electron2.BaseApp/x86_64/23.08 -y
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Build Electron for Mac
|
||||
if: ${{ startsWith(matrix.os, 'macos') }}
|
||||
- name: Build Electron
|
||||
run: ./bin/package-electron
|
||||
env:
|
||||
CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
# CSC_KEY_PASSWORD: ${{ secrets.CSC_KEY_PASSWORD }}
|
||||
# CSC_LINK: ${{ secrets.CSC_LINK }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
|
||||
- name: Build Electron
|
||||
if: ${{ ! startsWith(matrix.os, 'macos') }}
|
||||
run: ./bin/package-electron
|
||||
- name: Upload Build
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
@@ -80,7 +75,6 @@ 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
|
||||
|
||||
1
.github/workflows/electron-pr.yml
vendored
@@ -35,7 +35,6 @@ jobs:
|
||||
python3 -m pip install setuptools
|
||||
- if: ${{ startsWith(matrix.os, 'ubuntu') }}
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install flatpak -y
|
||||
sudo apt-get install flatpak-builder -y
|
||||
sudo flatpak remote-add --if-not-exists flathub https://flathub.org/repo/flathub.flatpakrepo
|
||||
|
||||
35
.github/workflows/generate-release-pr.yml
vendored
@@ -1,35 +0,0 @@
|
||||
name: Generate release PR
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
ref:
|
||||
description: 'Commit or branch to release'
|
||||
required: true
|
||||
default: 'master'
|
||||
version:
|
||||
description: 'Version number for the release (optional)'
|
||||
required: false
|
||||
default: ''
|
||||
|
||||
jobs:
|
||||
generate-release-pr:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Checkout
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ github.event.inputs.ref }}
|
||||
- name: Bump package versions
|
||||
id: bump_package_versions
|
||||
shell: bash
|
||||
run: |
|
||||
.github/actions/bump-package-versions ${{ github.event.inputs.version }}
|
||||
echo "version=$(jq -r .version packages/desktop-client/package.json)" > $GITHUB_OUTPUT
|
||||
- name: Create PR
|
||||
uses: peter-evans/create-pull-request@v7
|
||||
with:
|
||||
commit-message: '🔖 (${{ steps.bump_package_versions.outputs.version }})'
|
||||
title: '🔖 (${{ steps.bump_package_versions.outputs.version }})'
|
||||
body: 'Generated by [generate-release-pr.yml](../tree/master/.github/workflows/generate-release-pr.yml)'
|
||||
branch: 'release/v${{ steps.bump_package_versions.outputs.version }}'
|
||||
87
.github/workflows/i18n-string-extract-master.yml
vendored
@@ -1,87 +0,0 @@
|
||||
name: Extract and upload i18n strings
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# 4am UTC
|
||||
- cron: "0 4 * * *"
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
extract-and-upload-i18n-strings:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.repository == 'actualbudget/actual'
|
||||
steps:
|
||||
- name: Check out main repository
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
path: actual
|
||||
- name: Set up environment
|
||||
uses: ./actual/.github/actions/setup
|
||||
with:
|
||||
working-directory: actual
|
||||
download-translations: false # As we'll manually clone instead
|
||||
- name: Configure Git config
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "41898282+github-actions[bot]@users.noreply.github.com"
|
||||
- name: Configure i18n client
|
||||
run: |
|
||||
pip install wlc
|
||||
|
||||
- name: Lock translations
|
||||
run: |
|
||||
wlc \
|
||||
--url https://hosted.weblate.org/api/ \
|
||||
--key "${{ secrets.WEBLATE_API_KEY_CI_STRINGS }}" \
|
||||
lock \
|
||||
actualbudget/actual
|
||||
|
||||
- name: Update VCS with latest translations
|
||||
run: |
|
||||
wlc \
|
||||
--url https://hosted.weblate.org/api/ \
|
||||
--key "${{ secrets.WEBLATE_API_KEY_CI_STRINGS }}" \
|
||||
push \
|
||||
actualbudget/actual
|
||||
- name: Check out updated translations
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ssh-key: ${{ secrets.STRING_IMPORT_DEPLOY_KEY }}
|
||||
repository: actualbudget/translations
|
||||
path: translations
|
||||
- name: Generate i18n strings
|
||||
working-directory: actual
|
||||
run: |
|
||||
mkdir -p packages/desktop-client/locale/
|
||||
cp ../translations/en.json packages/desktop-client/locale/
|
||||
yarn generate:i18n
|
||||
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
|
||||
- name: Check in new i18n strings
|
||||
working-directory: translations
|
||||
run: |
|
||||
cp ../actual/packages/desktop-client/locale/en.json .
|
||||
git add .
|
||||
if git commit -m "Update source strings"; then
|
||||
git push
|
||||
else
|
||||
echo "No changes to commit"
|
||||
fi
|
||||
- name: Update Weblate with latest translations
|
||||
run: |
|
||||
wlc \
|
||||
--url https://hosted.weblate.org/api/ \
|
||||
--key "${{ secrets.WEBLATE_API_KEY_CI_STRINGS }}" \
|
||||
pull \
|
||||
actualbudget/actual
|
||||
|
||||
- name: Unlock translations
|
||||
if: always() # Clean up even on failure
|
||||
run: |
|
||||
wlc \
|
||||
--url https://hosted.weblate.org/api/ \
|
||||
--key "${{ secrets.WEBLATE_API_KEY_CI_STRINGS }}" \
|
||||
unlock \
|
||||
actualbudget/actual
|
||||
16
.github/workflows/size-compare.yml
vendored
@@ -13,9 +13,6 @@ name: Compare Sizes
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
paths:
|
||||
- 'packages/**'
|
||||
- '!packages/sync-server/**'
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
||||
@@ -28,7 +25,7 @@ jobs:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Wait for ${{github.base_ref}} build to succeed
|
||||
uses: fountainhead/action-wait-for-check@v1.2.0
|
||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
||||
id: master-build
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -36,7 +33,7 @@ jobs:
|
||||
ref: ${{github.base_ref}}
|
||||
|
||||
- name: Wait for PR build to succeed
|
||||
uses: fountainhead/action-wait-for-check@v1.2.0
|
||||
uses: fountainhead/action-wait-for-check@v1.1.0
|
||||
id: wait-for-build
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -49,7 +46,7 @@ jobs:
|
||||
echo "Build failed on PR branch or ${{github.base_ref}}"
|
||||
exit 1
|
||||
- name: Download build artifact from ${{github.base_ref}}
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
id: pr-build
|
||||
with:
|
||||
branch: ${{github.base_ref}}
|
||||
@@ -58,13 +55,12 @@ jobs:
|
||||
path: base
|
||||
|
||||
- name: Download build artifact from PR
|
||||
uses: dawidd6/action-download-artifact@v6
|
||||
uses: dawidd6/action-download-artifact@v3
|
||||
with:
|
||||
pr: ${{github.event.pull_request.number}}
|
||||
workflow: build.yml
|
||||
name: build-stats
|
||||
path: head
|
||||
allow_forks: true
|
||||
|
||||
- name: Strip content hashes from stats files
|
||||
run: |
|
||||
@@ -74,14 +70,14 @@ jobs:
|
||||
sed -i -E 's/index\.[0-9a-zA-Z_-]{8,}\./index./g' ./base/web-stats.json
|
||||
sed -i -E 's/\.[0-9a-zA-Z_-]{8,}\.chunk\././g' ./base/web-stats.json
|
||||
sed -i -E 's/\.[0-9a-f]{8,}\././g' ./base/*.json
|
||||
- uses: twk3/rollup-size-compare-action@v1.1.1
|
||||
- uses: twk3/rollup-size-compare-action@v1.0.0
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
current-stats-json-path: ./head/web-stats.json
|
||||
base-stats-json-path: ./base/web-stats.json
|
||||
title: desktop-client
|
||||
|
||||
- uses: github/webpack-bundlesize-compare-action@v2.1.0
|
||||
- uses: github/webpack-bundlesize-compare-action@v1.8.2
|
||||
with:
|
||||
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
current-stats-json-path: ./head/loot-core-stats.json
|
||||
|
||||
115
.github/workflows/update-vrt.yml
vendored
@@ -1,115 +0,0 @@
|
||||
name: /update-vrt
|
||||
on:
|
||||
issue_comment:
|
||||
types: [ created ]
|
||||
|
||||
permissions:
|
||||
pull-requests: read
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: ${{ github.workflow }}-${{ github.event.issue.number }}-${{ contains(github.event.comment.body, '/update-vrt') }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
update-vrt:
|
||||
name: Update VRT
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/update-vrt')
|
||||
container:
|
||||
image: mcr.microsoft.com/playwright:v1.41.1-jammy
|
||||
steps:
|
||||
- name: Get PR branch
|
||||
# Until https://github.com/xt0rted/pull-request-comment-branch/issues/322 is resolved we use the forked version
|
||||
uses: gotson/pull-request-comment-branch@head-repo-owner-dist
|
||||
id: comment-branch
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ steps.comment-branch.outputs.head_owner }}/${{ steps.comment-branch.outputs.head_repo }}
|
||||
ref: ${{ steps.comment-branch.outputs.head_ref }}
|
||||
- name: Set up environment
|
||||
uses: ./.github/actions/setup
|
||||
- name: Wait for Netlify build to finish
|
||||
id: netlify
|
||||
env:
|
||||
COMMIT_SHA: ${{ steps.comment-branch.outputs.head_sha }}
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: ./.github/actions/netlify-wait-for-build
|
||||
- name: Run VRT Tests on Netlify URL
|
||||
run: yarn vrt --update-snapshots
|
||||
env:
|
||||
E2E_START_URL: ${{ steps.netlify.outputs.url }}
|
||||
- name: Create patch
|
||||
run: |
|
||||
git config --system --add safe.directory "*"
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git reset
|
||||
git add "**/*.png"
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
git commit -m "Update VRT"
|
||||
git format-patch -1 HEAD --stdout > Update-VRT.patch
|
||||
- uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: patch
|
||||
path: Update-VRT.patch
|
||||
|
||||
push-patch:
|
||||
runs-on: ubuntu-latest
|
||||
needs: update-vrt
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: Get PR branch
|
||||
# Until https://github.com/xt0rted/pull-request-comment-branch/issues/322 is resolved we use the forked version
|
||||
uses: gotson/pull-request-comment-branch@head-repo-owner-dist
|
||||
id: comment-branch
|
||||
- uses: actions/checkout@v4
|
||||
with:
|
||||
repository: ${{ steps.comment-branch.outputs.head_owner }}/${{ steps.comment-branch.outputs.head_repo }}
|
||||
ref: ${{ steps.comment-branch.outputs.head_ref }}
|
||||
- uses: actions/download-artifact@v4
|
||||
continue-on-error: true
|
||||
with:
|
||||
name: patch
|
||||
- name: Apply patch and push
|
||||
env:
|
||||
BRANCH_NAME: ${{ steps.comment-branch.outputs.head_ref }}
|
||||
run: |
|
||||
git config --global user.name "github-actions[bot]"
|
||||
git config --global user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git apply Update-VRT.patch
|
||||
git add "**/*.png"
|
||||
if git diff --staged --quiet; then
|
||||
echo "No changes to commit"
|
||||
exit 0
|
||||
fi
|
||||
git commit -m "Update VRT"
|
||||
git push origin HEAD:${BRANCH_NAME}
|
||||
- name: Add finished reaction
|
||||
uses: dkershner6/reaction-action@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commentId: ${{ github.event.comment.id }}
|
||||
reaction: "rocket"
|
||||
|
||||
add-starting-reaction:
|
||||
runs-on: ubuntu-latest
|
||||
if: |
|
||||
github.event.issue.pull_request &&
|
||||
contains(github.event.comment.body, '/update-vrt')
|
||||
permissions:
|
||||
pull-requests: write
|
||||
steps:
|
||||
- name: React to comment
|
||||
uses: dkershner6/reaction-action@v2
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
commentId: ${{ github.event.comment.id }}
|
||||
reaction: "+1"
|
||||
6
.gitignore
vendored
@@ -50,9 +50,3 @@ bundle.mobile.js.map
|
||||
|
||||
# Local Netlify folder
|
||||
.netlify
|
||||
|
||||
# build output
|
||||
package.tgz
|
||||
|
||||
# Fly.io configuration
|
||||
fly.toml
|
||||
|
||||
@@ -2,8 +2,6 @@ compressionLevel: mixed
|
||||
|
||||
enableGlobalCache: false
|
||||
|
||||
enableTransparentWorkspaces: false
|
||||
|
||||
nodeLinker: node-modules
|
||||
|
||||
yarnPath: .yarn/releases/yarn-4.3.1.cjs
|
||||
|
||||
@@ -74,6 +74,6 @@ Make Actual Budget accessible to more people by helping with the [Internationali
|
||||
|
||||
## Sponsors
|
||||
|
||||
Thanks to our wonderful sponsors who make Actual Budget possible!
|
||||
Thanks to our wonderful sponsors who make Actual budget possible!
|
||||
|
||||
<a href="https://www.netlify.com"> <img src="https://www.netlify.com/v3/img/components/netlify-color-accent.svg" alt="Deploys by Netlify" /> </a>
|
||||
|
||||
@@ -4,16 +4,6 @@ ROOT=`dirname $0`
|
||||
|
||||
cd "$ROOT/.."
|
||||
|
||||
echo "Updating translations..."
|
||||
if ! [ -d packages/desktop-client/locale ]; then
|
||||
git clone https://github.com/actualbudget/translations packages/desktop-client/locale
|
||||
fi
|
||||
pushd packages/desktop-client/locale > /dev/null
|
||||
git checkout .
|
||||
git pull
|
||||
popd > /dev/null
|
||||
packages/desktop-client/bin/remove-untranslated-languages
|
||||
|
||||
yarn workspace loot-core build:browser
|
||||
yarn workspace @actual-app/web build:browser
|
||||
|
||||
|
||||
@@ -36,18 +36,7 @@ fi
|
||||
|
||||
yarn workspace loot-core build:node
|
||||
|
||||
# Get translations
|
||||
echo "Updating translations..."
|
||||
if ! [ -d packages/desktop-client/locale ]; then
|
||||
git clone https://github.com/actualbudget/translations packages/desktop-client/locale
|
||||
fi
|
||||
pushd packages/desktop-client/locale > /dev/null
|
||||
git checkout .
|
||||
git pull
|
||||
popd > /dev/null
|
||||
packages/desktop-client/bin/remove-untranslated-languages
|
||||
|
||||
yarn workspace @actual-app/web build --mode=desktop # electron specific build
|
||||
yarn workspace @actual-app/web build --mode=desktop
|
||||
|
||||
yarn workspace desktop-electron update-client
|
||||
|
||||
|
||||
@@ -28,5 +28,5 @@ echo "Running VRT tests with the following parameters:"
|
||||
echo "E2E_START_URL: $E2E_START_URL"
|
||||
echo "VRT_ARGS: $VRT_ARGS"
|
||||
|
||||
MSYS_NO_PATHCONV=1 docker run --rm --network host -v "$(pwd)":/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.41.1-jammy /bin/bash \
|
||||
docker run --rm --network host -v "$(pwd)":/work/ -w /work/ -it mcr.microsoft.com/playwright:v1.41.1-jammy /bin/bash \
|
||||
-c "E2E_START_URL=$E2E_START_URL yarn vrt $VRT_ARGS"
|
||||
|
||||
@@ -1,840 +0,0 @@
|
||||
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/component-library/src/icons/**/*',
|
||||
'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/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 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',
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: 'react-router-dom',
|
||||
importNames: ['useNavigate'],
|
||||
message:
|
||||
"Please import Actual's useNavigate() hook from `src/hooks` instead.",
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/desktop-client/**/*', 'packages/loot-core/**/*'],
|
||||
ignores: ['packages/desktop-client/src/redux/index.{ts,tsx}'],
|
||||
|
||||
rules: {
|
||||
'no-restricted-imports': [
|
||||
'warn',
|
||||
{
|
||||
paths: [
|
||||
{
|
||||
name: 'react-redux',
|
||||
importNames: ['useDispatch'],
|
||||
message:
|
||||
"Please import Actual's useDispatch() hook from `src/redux` instead.",
|
||||
},
|
||||
{
|
||||
name: 'react-redux',
|
||||
importNames: ['useSelector'],
|
||||
message:
|
||||
"Please import Actual's useSelector() hook from `src/redux` instead.",
|
||||
},
|
||||
{
|
||||
name: 'react-redux',
|
||||
importNames: ['useStore'],
|
||||
message:
|
||||
"Please import Actual's useStore() hook from `src/redux` 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/*`',
|
||||
},
|
||||
{
|
||||
group: ['@actual-app/web/*'],
|
||||
message: 'Please do not import `@actual-app/web` in `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',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/api/index.ts'],
|
||||
rules: {
|
||||
'import/no-unresolved': '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/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/component-library/src/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',
|
||||
],
|
||||
|
||||
rules: {
|
||||
'react-hooks/exhaustive-deps': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'eslint.config.mjs',
|
||||
'**/*.test.js',
|
||||
'**/*.test.ts',
|
||||
'**/*.test.jsx',
|
||||
'**/*.test.tsx',
|
||||
'**/*.spec.js',
|
||||
],
|
||||
|
||||
rules: {
|
||||
'rulesdir/typography': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: [
|
||||
'packages/desktop-client/**/*.{ts,tsx}',
|
||||
'packages/loot-core/src/client/**/*.{ts,tsx}',
|
||||
],
|
||||
ignores: ['**/**/globals.d.ts'],
|
||||
rules: {
|
||||
// enforce type over interface
|
||||
'@typescript-eslint/consistent-type-definitions': ['warn', 'type'],
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/sync-server/**/*'],
|
||||
// TODO: fix the issues in these files
|
||||
rules: {
|
||||
'import/extensions': 'off',
|
||||
'rulesdir/typography': 'off',
|
||||
},
|
||||
},
|
||||
{
|
||||
files: ['packages/sync-server/src/app-gocardless/banks/*.js'],
|
||||
rules: {
|
||||
'import/no-anonymous-default-export': 'off',
|
||||
'import/no-default-export': 'off',
|
||||
// can be re-enabled after https://github.com/actualbudget/actual/pull/4253
|
||||
'@typescript-eslint/no-unused-vars': 'off',
|
||||
},
|
||||
},
|
||||
];
|
||||
26
package.json
@@ -19,9 +19,6 @@
|
||||
},
|
||||
"scripts": {
|
||||
"start": "yarn start:browser",
|
||||
"start:server": "yarn workspace @actual-app/sync-server start",
|
||||
"start:server-monitor": "yarn workspace @actual-app/sync-server start-monitor",
|
||||
"start:server-dev": "NODE_ENV=development BROWSER_OPEN=localhost:5006 yarn npm-run-all --parallel 'start:server-monitor' 'start'",
|
||||
"start:desktop": "yarn rebuild-electron && npm-run-all --parallel 'start:desktop-*'",
|
||||
"start:desktop-node": "yarn workspace loot-core watch:node",
|
||||
"start:desktop-client": "yarn workspace @actual-app/web watch",
|
||||
@@ -41,34 +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",
|
||||
"lint": "eslint . --max-warnings 0 --ext .js,.jsx,.ts,.tsx",
|
||||
"lint:verbose": "DEBUG=eslint:cli-engine eslint . --max-warnings 0",
|
||||
"install:server": "yarn workspaces focus @actual-app/sync-server --production",
|
||||
"typecheck": "yarn tsc && tsc-strict",
|
||||
"jq": "./node_modules/node-jq/bin/jq",
|
||||
"prepare": "husky"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@typescript-eslint/parser": "^8.18.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.1.0",
|
||||
"@typescript-eslint/parser": "^8.1.0",
|
||||
"confusing-browser-globals": "^1.0.11",
|
||||
"cross-env": "^7.0.3",
|
||||
"eslint": "^9.17.0",
|
||||
"eslint": "^8.57.0",
|
||||
"eslint-config-prettier": "^9.1.0",
|
||||
"eslint-import-resolver-typescript": "^3.7.0",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-import-resolver-typescript": "3.6.1",
|
||||
"eslint-plugin-import": "2.29.1",
|
||||
"eslint-plugin-jsx-a11y": "^6.9.0",
|
||||
"eslint-plugin-prettier": "5.2.1",
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react": "7.35.0",
|
||||
"eslint-plugin-react-hooks": "^4.6.2",
|
||||
"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.4.2",
|
||||
"prettier": "3.3.3",
|
||||
"source-map-support": "^0.5.21",
|
||||
"typescript": "^5.5.4",
|
||||
"typescript-eslint": "^8.18.1",
|
||||
"typescript-strict-plugin": "^2.4.4"
|
||||
},
|
||||
"resolutions": {
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import type {
|
||||
RequestInfo as FetchInfo,
|
||||
RequestInit as FetchInit,
|
||||
} from 'node-fetch';
|
||||
// @ts-ignore: false-positive commonjs module error on build until typescript 5.3
|
||||
} from 'node-fetch'; // with { 'resolution-mode': 'import' };
|
||||
|
||||
// loot-core types
|
||||
import type { InitConfig } from 'loot-core/server/main';
|
||||
@@ -15,6 +16,9 @@ import { validateNodeVersion } from './validateNodeVersion';
|
||||
let actualApp: null | typeof bundle.lib;
|
||||
export const internal = bundle.lib;
|
||||
|
||||
// DEPRECATED: remove the next line in @actual-app/api v7
|
||||
export * as methods from './methods';
|
||||
|
||||
export * from './methods';
|
||||
export * as utils from './utils';
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// @ts-strict-ignore
|
||||
import type { Handlers } from 'loot-core/types/handlers';
|
||||
import type { Handlers } from 'loot-core/src/types/handlers';
|
||||
|
||||
import * as injected from './injected';
|
||||
|
||||
@@ -85,22 +85,8 @@ export function addTransactions(
|
||||
});
|
||||
}
|
||||
|
||||
export interface ImportTransactionsOpts {
|
||||
defaultCleared?: boolean;
|
||||
}
|
||||
|
||||
export function importTransactions(
|
||||
accountId,
|
||||
transactions,
|
||||
opts: ImportTransactionsOpts = {
|
||||
defaultCleared: true,
|
||||
},
|
||||
) {
|
||||
return send('api/transactions-import', {
|
||||
accountId,
|
||||
transactions,
|
||||
opts,
|
||||
});
|
||||
export function importTransactions(accountId, transactions) {
|
||||
return send('api/transactions-import', { accountId, transactions });
|
||||
}
|
||||
|
||||
export function getTransactions(accountId, startDate, endDate) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@actual-app/api",
|
||||
"version": "25.3.1",
|
||||
"version": "6.9.0",
|
||||
"license": "MIT",
|
||||
"description": "An API for Actual",
|
||||
"engines": {
|
||||
@@ -23,7 +23,7 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@actual-app/crdt": "workspace:^",
|
||||
"better-sqlite3": "^11.7.0",
|
||||
"better-sqlite3": "^9.6.0",
|
||||
"compare-versions": "^6.1.0",
|
||||
"node-fetch": "^3.3.2",
|
||||
"uuid": "^9.0.1"
|
||||
|
||||
@@ -5,13 +5,13 @@
|
||||
// the latest Node 16.x release supports all of the features
|
||||
"target": "ES2021",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node10",
|
||||
"noEmit": false,
|
||||
"declaration": true,
|
||||
"outDir": "dist",
|
||||
"declarationDir": "@types",
|
||||
"paths": {
|
||||
"loot-core/*": ["./@types/loot-core/*"]
|
||||
"loot-core/src/*": ["./loot-core/*"],
|
||||
"loot-core/*": ["./@types/loot-core/*"],
|
||||
}
|
||||
},
|
||||
"include": ["."],
|
||||
|
||||
@@ -1,51 +0,0 @@
|
||||
{
|
||||
"name": "@actual-app/components",
|
||||
"version": "0.0.1",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": ">=18.2"
|
||||
},
|
||||
"dependencies": {
|
||||
"@emotion/css": "^11.13.4",
|
||||
"react-aria-components": "^1.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@svgr/cli": "^8.1.0",
|
||||
"@types/react": "^18.2.0",
|
||||
"react": "18.2.0"
|
||||
},
|
||||
"exports": {
|
||||
"./icons/logo": "./src/icons/logo/index.ts",
|
||||
"./icons/v0": "./src/icons/v0/index.ts",
|
||||
"./icons/v1": "./src/icons/v1/index.ts",
|
||||
"./icons/v2": "./src/icons/v2/index.ts",
|
||||
"./icons/AnimatedLoading": "./src/icons/AnimatedLoading.tsx",
|
||||
"./icons/Loading": "./src/icons/Loading.tsx",
|
||||
"./aligned-text": "./src/AlignedText.tsx",
|
||||
"./block": "./src/Block.tsx",
|
||||
"./button": "./src/Button.tsx",
|
||||
"./card": "./src/Card.tsx",
|
||||
"./form-error": "./src/FormError.tsx",
|
||||
"./initial-focus": "./src/InitialFocus.ts",
|
||||
"./inline-field": "./src/InlineField.tsx",
|
||||
"./input": "./src/Input.tsx",
|
||||
"./label": "./src/Label.tsx",
|
||||
"./menu": "./src/Menu.tsx",
|
||||
"./paragraph": "./src/Paragraph.tsx",
|
||||
"./popover": "./src/Popover.tsx",
|
||||
"./select": "./src/Select.tsx",
|
||||
"./space-between": "./src/SpaceBetween.tsx",
|
||||
"./stack": "./src/Stack.tsx",
|
||||
"./styles": "./src/styles.ts",
|
||||
"./text": "./src/Text.tsx",
|
||||
"./text-one-line": "./src/TextOneLine.tsx",
|
||||
"./theme": "./src/theme.ts",
|
||||
"./tokens": "./src/tokens.ts",
|
||||
"./toggle": "./src/Toggle.tsx",
|
||||
"./tooltip": "./src/Tooltip.tsx",
|
||||
"./view": "./src/View.tsx"
|
||||
},
|
||||
"scripts": {
|
||||
"generate:icons": "rm src/icons/*/*.tsx; cd src/icons && svgr --template template.ts --index-template index-template.ts --typescript --expand-props start -d . ."
|
||||
}
|
||||
}
|
||||
@@ -1,57 +0,0 @@
|
||||
import { type ComponentProps, useCallback, useEffect, useRef } from 'react';
|
||||
import { Popover as ReactAriaPopover } from 'react-aria-components';
|
||||
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { styles } from './styles';
|
||||
|
||||
type PopoverProps = ComponentProps<typeof ReactAriaPopover>;
|
||||
|
||||
export const Popover = ({
|
||||
style = {},
|
||||
shouldCloseOnInteractOutside,
|
||||
...props
|
||||
}: PopoverProps) => {
|
||||
const ref = useRef<HTMLElement>(null);
|
||||
|
||||
const handleFocus = useCallback(
|
||||
(e: FocusEvent) => {
|
||||
if (!ref.current?.contains(e.relatedTarget as Node)) {
|
||||
props.onOpenChange?.(false);
|
||||
}
|
||||
},
|
||||
[props],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!props.isNonModal) return;
|
||||
if (props.isOpen) {
|
||||
ref.current?.addEventListener('focusout', handleFocus);
|
||||
} else {
|
||||
ref.current?.removeEventListener('focusout', handleFocus);
|
||||
}
|
||||
}, [handleFocus, props.isNonModal, props.isOpen]);
|
||||
|
||||
return (
|
||||
<ReactAriaPopover
|
||||
ref={ref}
|
||||
placement="bottom end"
|
||||
offset={1}
|
||||
className={css({
|
||||
...styles.tooltip,
|
||||
...styles.lightScrollbar,
|
||||
padding: 0,
|
||||
userSelect: 'none',
|
||||
...style,
|
||||
})}
|
||||
shouldCloseOnInteractOutside={element => {
|
||||
if (shouldCloseOnInteractOutside) {
|
||||
return shouldCloseOnInteractOutside(element);
|
||||
}
|
||||
|
||||
return true;
|
||||
}}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
@@ -1,31 +0,0 @@
|
||||
import React, { type CSSProperties, type ReactNode } from 'react';
|
||||
|
||||
import { View } from './View';
|
||||
|
||||
type SpaceBetweenProps = {
|
||||
direction?: 'horizontal' | 'vertical';
|
||||
gap?: number;
|
||||
style?: CSSProperties;
|
||||
children: ReactNode;
|
||||
};
|
||||
|
||||
export const SpaceBetween = ({
|
||||
direction = 'horizontal',
|
||||
gap = 15,
|
||||
style,
|
||||
children,
|
||||
}: SpaceBetweenProps) => {
|
||||
return (
|
||||
<View
|
||||
style={{
|
||||
flexWrap: 'wrap',
|
||||
flexDirection: direction === 'horizontal' ? 'row' : 'column',
|
||||
alignItems: 'center',
|
||||
gap,
|
||||
...style,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -1,88 +0,0 @@
|
||||
import React, { type CSSProperties } from 'react';
|
||||
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
import { theme } from './theme';
|
||||
import { View } from './View';
|
||||
|
||||
type ToggleProps = {
|
||||
id: string;
|
||||
isOn: boolean;
|
||||
isDisabled?: boolean;
|
||||
onToggle?: (isOn: boolean) => void;
|
||||
className?: string;
|
||||
style?: CSSProperties;
|
||||
};
|
||||
|
||||
export const Toggle = ({
|
||||
id,
|
||||
isOn,
|
||||
isDisabled = false,
|
||||
onToggle,
|
||||
className,
|
||||
style,
|
||||
}: ToggleProps) => {
|
||||
return (
|
||||
<View style={style} className={className}>
|
||||
<input
|
||||
id={id}
|
||||
checked={isOn}
|
||||
disabled={isDisabled}
|
||||
onChange={e => onToggle?.(e.target.checked)}
|
||||
className={css({
|
||||
height: 0,
|
||||
width: 0,
|
||||
visibility: 'hidden',
|
||||
})}
|
||||
type="checkbox"
|
||||
/>
|
||||
<label
|
||||
data-toggle-container
|
||||
data-on={isOn}
|
||||
className={String(
|
||||
css({
|
||||
display: 'flex',
|
||||
alignItems: 'center',
|
||||
justifyContent: 'space-between',
|
||||
cursor: 'pointer',
|
||||
width: '32px',
|
||||
height: '16px',
|
||||
borderRadius: '100px',
|
||||
position: 'relative',
|
||||
transition: 'background-color .2s',
|
||||
backgroundColor: isOn
|
||||
? theme.checkboxToggleBackgroundSelected
|
||||
: theme.checkboxToggleBackground,
|
||||
}),
|
||||
)}
|
||||
htmlFor={id}
|
||||
>
|
||||
<span
|
||||
data-toggle
|
||||
data-on={isOn}
|
||||
className={css(
|
||||
{
|
||||
// eslint-disable-next-line rulesdir/typography
|
||||
content: '" "',
|
||||
position: 'absolute',
|
||||
top: '2px',
|
||||
left: '2px',
|
||||
width: '12px',
|
||||
height: '12px',
|
||||
borderRadius: '100px',
|
||||
transition: '0.2s',
|
||||
boxShadow: '0 0 2px 0 rgba(10, 10, 10, 0.29)',
|
||||
backgroundColor: isDisabled
|
||||
? theme.checkboxToggleDisabled
|
||||
: '#fff',
|
||||
},
|
||||
isOn && {
|
||||
left: 'calc(100% - 2px)',
|
||||
transform: 'translateX(-100%)',
|
||||
},
|
||||
)}
|
||||
/>
|
||||
</label>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
@@ -1 +0,0 @@
|
||||
export { SvgLogo } from './Logo';
|
||||
@@ -1,9 +0,0 @@
|
||||
export { SvgAdd } from './Add';
|
||||
export { SvgDelete } from './Delete';
|
||||
export { SvgExpandArrow } from './ExpandArrow';
|
||||
export { SvgLeftArrow2 } from './LeftArrow2';
|
||||
export { SvgMath } from './Math';
|
||||
export { SvgRightArrow2 } from './RightArrow2';
|
||||
export { SvgSubtract } from './Subtract';
|
||||
export { SvgMerge } from './Merge';
|
||||
export { SvgSplit } from './Split';
|
||||
@@ -1,311 +0,0 @@
|
||||
export { SvgAddOutline } from './AddOutline';
|
||||
export { SvgAddSolid } from './AddSolid';
|
||||
export { SvgAdd } from './Add';
|
||||
export { SvgAdjust } from './Adjust';
|
||||
export { SvgAirplane } from './Airplane';
|
||||
export { SvgAlbum } from './Album';
|
||||
export { SvgAlignCenter } from './AlignCenter';
|
||||
export { SvgAlignJustified } from './AlignJustified';
|
||||
export { SvgAlignLeft } from './AlignLeft';
|
||||
export { SvgAlignRight } from './AlignRight';
|
||||
export { SvgAnchor } from './Anchor';
|
||||
export { SvgAnnouncement } from './Announcement';
|
||||
export { SvgApparel } from './Apparel';
|
||||
export { SvgArrowDown } from './ArrowDown';
|
||||
export { SvgArrowLeft } from './ArrowLeft';
|
||||
export { SvgArrowOutlineDown } from './ArrowOutlineDown';
|
||||
export { SvgArrowOutlineLeft } from './ArrowOutlineLeft';
|
||||
export { SvgArrowOutlineRight } from './ArrowOutlineRight';
|
||||
export { SvgArrowOutlineUp } from './ArrowOutlineUp';
|
||||
export { SvgArrowRight } from './ArrowRight';
|
||||
export { SvgArrowThickDown } from './ArrowThickDown';
|
||||
export { SvgArrowThickLeft } from './ArrowThickLeft';
|
||||
export { SvgArrowThickRight } from './ArrowThickRight';
|
||||
export { SvgArrowThickUp } from './ArrowThickUp';
|
||||
export { SvgArrowThinDown } from './ArrowThinDown';
|
||||
export { SvgArrowThinLeft } from './ArrowThinLeft';
|
||||
export { SvgArrowThinRight } from './ArrowThinRight';
|
||||
export { SvgArrowThinUp } from './ArrowThinUp';
|
||||
export { SvgArrowUp } from './ArrowUp';
|
||||
export { SvgArtist } from './Artist';
|
||||
export { SvgAtSymbol } from './AtSymbol';
|
||||
export { SvgAttachment } from './Attachment';
|
||||
export { SvgBackspace } from './Backspace';
|
||||
export { SvgBackwardStep } from './BackwardStep';
|
||||
export { SvgBackward } from './Backward';
|
||||
export { SvgBadge } from './Badge';
|
||||
export { SvgBatteryFull } from './BatteryFull';
|
||||
export { SvgBatteryHalf } from './BatteryHalf';
|
||||
export { SvgBatteryLow } from './BatteryLow';
|
||||
export { SvgBeverage } from './Beverage';
|
||||
export { SvgBlock } from './Block';
|
||||
export { SvgBluetooth } from './Bluetooth';
|
||||
export { SvgBolt } from './Bolt';
|
||||
export { SvgBookReference } from './BookReference';
|
||||
export { SvgBookmarkCopy2 } from './BookmarkCopy2';
|
||||
export { SvgBookmarkCopy3 } from './BookmarkCopy3';
|
||||
export { SvgBookmarkOutlineAdd } from './BookmarkOutlineAdd';
|
||||
export { SvgBookmarkOutline } from './BookmarkOutline';
|
||||
export { SvgBookmark } from './Bookmark';
|
||||
export { SvgBorderAll } from './BorderAll';
|
||||
export { SvgBorderBottom } from './BorderBottom';
|
||||
export { SvgBorderHorizontal } from './BorderHorizontal';
|
||||
export { SvgBorderInner } from './BorderInner';
|
||||
export { SvgBorderLeft } from './BorderLeft';
|
||||
export { SvgBorderNone } from './BorderNone';
|
||||
export { SvgBorderOuter } from './BorderOuter';
|
||||
export { SvgBorderRight } from './BorderRight';
|
||||
export { SvgBorderTop } from './BorderTop';
|
||||
export { SvgBorderVertical } from './BorderVertical';
|
||||
export { SvgBox } from './Box';
|
||||
export { SvgBrightnessDown } from './BrightnessDown';
|
||||
export { SvgBrightnessUp } from './BrightnessUp';
|
||||
export { SvgBrowserWindowNew } from './BrowserWindowNew';
|
||||
export { SvgBrowserWindowOpen } from './BrowserWindowOpen';
|
||||
export { SvgBrowserWindow } from './BrowserWindow';
|
||||
export { SvgBug } from './Bug';
|
||||
export { SvgBuoy } from './Buoy';
|
||||
export { SvgCalculator } from './Calculator';
|
||||
export { SvgCalendar } from './Calendar';
|
||||
export { SvgCamera } from './Camera';
|
||||
export { SvgChartArea } from './ChartArea';
|
||||
export { SvgChartBar } from './ChartBar';
|
||||
export { SvgChartPie } from './ChartPie';
|
||||
export { SvgChart } from './Chart';
|
||||
export { SvgChatBubbleDots } from './ChatBubbleDots';
|
||||
export { SvgCheckAlternative } from './CheckAlternative';
|
||||
export { SvgCheckmarkOutline } from './CheckmarkOutline';
|
||||
export { SvgCheckmark } from './Checkmark';
|
||||
export { SvgCheveronDown } from './CheveronDown';
|
||||
export { SvgCheveronLeft } from './CheveronLeft';
|
||||
export { SvgCheveronOutlineDown } from './CheveronOutlineDown';
|
||||
export { SvgCheveronOutlineLeft } from './CheveronOutlineLeft';
|
||||
export { SvgCheveronOutlineRight } from './CheveronOutlineRight';
|
||||
export { SvgCheveronOutlineUp } from './CheveronOutlineUp';
|
||||
export { SvgCheveronRight } from './CheveronRight';
|
||||
export { SvgCheveronUp } from './CheveronUp';
|
||||
export { SvgClipboard } from './Clipboard';
|
||||
export { SvgCloseOutline } from './CloseOutline';
|
||||
export { SvgCloseSolid } from './CloseSolid';
|
||||
export { SvgClose } from './Close';
|
||||
export { SvgCloudCheck } from './CloudCheck';
|
||||
export { SvgCloudDownload } from './CloudDownload';
|
||||
export { SvgCloudUpload } from './CloudUpload';
|
||||
export { SvgCloudWarning } from './CloudWarning';
|
||||
export { SvgCloud } from './Cloud';
|
||||
export { SvgCode } from './Code';
|
||||
export { SvgCoffee } from './Coffee';
|
||||
export { SvgCog } from './Cog';
|
||||
export { SvgColorPalette } from './ColorPalette';
|
||||
export { SvgCompose } from './Compose';
|
||||
export { SvgComputerDesktop } from './ComputerDesktop';
|
||||
export { SvgComputerLaptop } from './ComputerLaptop';
|
||||
export { SvgConversation } from './Conversation';
|
||||
export { SvgCopy } from './Copy';
|
||||
export { SvgCreditCard } from './CreditCard';
|
||||
export { SvgCurrencyDollar } from './CurrencyDollar';
|
||||
export { SvgDashboard } from './Dashboard';
|
||||
export { SvgDateAdd } from './DateAdd';
|
||||
export { SvgDialPad } from './DialPad';
|
||||
export { SvgDirections } from './Directions';
|
||||
export { SvgDocumentAdd } from './DocumentAdd';
|
||||
export { SvgDocument } from './Document';
|
||||
export { SvgDotsHorizontalDouble } from './DotsHorizontalDouble';
|
||||
export { SvgDotsHorizontalTriple } from './DotsHorizontalTriple';
|
||||
export { SvgDownload } from './Download';
|
||||
export { SvgDuplicate } from './Duplicate';
|
||||
export { SvgEditCopy } from './EditCopy';
|
||||
export { SvgEditCrop } from './EditCrop';
|
||||
export { SvgEditCut } from './EditCut';
|
||||
export { SvgEditPencil } from './EditPencil';
|
||||
export { SvgEducation } from './Education';
|
||||
export { SvgEnvelope } from './Envelope';
|
||||
export { SvgEquals } from './Equals';
|
||||
export { SvgExclamationOutline } from './ExclamationOutline';
|
||||
export { SvgExclamationSolid } from './ExclamationSolid';
|
||||
export { SvgExplore } from './Explore';
|
||||
export { SvgFactory } from './Factory';
|
||||
export { SvgFastForward } from './FastForward';
|
||||
export { SvgFastRewind } from './FastRewind';
|
||||
export { SvgFileDouble } from './FileDouble';
|
||||
export { SvgFilm } from './Film';
|
||||
export { SvgFilter } from './Filter';
|
||||
export { SvgFlag } from './Flag';
|
||||
export { SvgFlashlight } from './Flashlight';
|
||||
export { SvgFolderOutlineAdd } from './FolderOutlineAdd';
|
||||
export { SvgFolderOutline } from './FolderOutline';
|
||||
export { SvgFolder } from './Folder';
|
||||
export { SvgFormatBold } from './FormatBold';
|
||||
export { SvgFormatFontSize } from './FormatFontSize';
|
||||
export { SvgFormatItalic } from './FormatItalic';
|
||||
export { SvgFormatTextSize } from './FormatTextSize';
|
||||
export { SvgFormatUnderline } from './FormatUnderline';
|
||||
export { SvgForwardStep } from './ForwardStep';
|
||||
export { SvgForward } from './Forward';
|
||||
export { SvgGift } from './Gift';
|
||||
export { SvgGlobe } from './Globe';
|
||||
export { SvgHandStop } from './HandStop';
|
||||
export { SvgHardDrive } from './HardDrive';
|
||||
export { SvgHeadphones } from './Headphones';
|
||||
export { SvgHeart } from './Heart';
|
||||
export { SvgHome } from './Home';
|
||||
export { SvgHot } from './Hot';
|
||||
export { SvgHourGlass } from './HourGlass';
|
||||
export { SvgInboxCheck } from './InboxCheck';
|
||||
export { SvgInboxDownload } from './InboxDownload';
|
||||
export { SvgInboxFull } from './InboxFull';
|
||||
export { SvgInbox } from './Inbox';
|
||||
export { SvgIndentDecrease } from './IndentDecrease';
|
||||
export { SvgIndentIncrease } from './IndentIncrease';
|
||||
export { SvgInformationOutline } from './InformationOutline';
|
||||
export { SvgInformationSolid } from './InformationSolid';
|
||||
export { SvgKey } from './Key';
|
||||
export { SvgKeyboard } from './Keyboard';
|
||||
export { SvgLayers } from './Layers';
|
||||
export { SvgLibrary } from './Library';
|
||||
export { SvgLightBulb } from './LightBulb';
|
||||
export { SvgLink } from './Link';
|
||||
export { SvgListAdd } from './ListAdd';
|
||||
export { SvgListBullet } from './ListBullet';
|
||||
export { SvgList } from './List';
|
||||
export { SvgLoadBalancer } from './LoadBalancer';
|
||||
export { SvgLocationCurrent } from './LocationCurrent';
|
||||
export { SvgLocationFood } from './LocationFood';
|
||||
export { SvgLocationGasStation } from './LocationGasStation';
|
||||
export { SvgLocationHotel } from './LocationHotel';
|
||||
export { SvgLocationMarina } from './LocationMarina';
|
||||
export { SvgLocationPark } from './LocationPark';
|
||||
export { SvgLocationRestroom } from './LocationRestroom';
|
||||
export { SvgLocationShopping } from './LocationShopping';
|
||||
export { SvgLocation } from './Location';
|
||||
export { SvgLockClosed } from './LockClosed';
|
||||
export { SvgLockOpen } from './LockOpen';
|
||||
export { SvgMap } from './Map';
|
||||
export { SvgMenu } from './Menu';
|
||||
export { SvgMic } from './Mic';
|
||||
export { SvgMinusOutline } from './MinusOutline';
|
||||
export { SvgMinusSolid } from './MinusSolid';
|
||||
export { SvgMobileDevices } from './MobileDevices';
|
||||
export { SvgMoneyBag } from './MoneyBag';
|
||||
export { SvgMoodHappyOutline } from './MoodHappyOutline';
|
||||
export { SvgMoodHappySolid } from './MoodHappySolid';
|
||||
export { SvgMoodNeutralOutline } from './MoodNeutralOutline';
|
||||
export { SvgMoodNeutralSolid } from './MoodNeutralSolid';
|
||||
export { SvgMoodSadOutline } from './MoodSadOutline';
|
||||
export { SvgMoodSadSolid } from './MoodSadSolid';
|
||||
export { SvgMouse } from './Mouse';
|
||||
export { SvgMoveBack } from './MoveBack';
|
||||
export { SvgMusicAlbum } from './MusicAlbum';
|
||||
export { SvgMusicArtist } from './MusicArtist';
|
||||
export { SvgMusicNotes } from './MusicNotes';
|
||||
export { SvgMusicPlaylist } from './MusicPlaylist';
|
||||
export { SvgNavigationMore } from './NavigationMore';
|
||||
export { SvgNetwork } from './Network';
|
||||
export { SvgNewsPaper } from './NewsPaper';
|
||||
export { SvgNotification } from './Notification';
|
||||
export { SvgNotificationsOutline } from './NotificationsOutline';
|
||||
export { SvgNotifications } from './Notifications';
|
||||
export { SvgPaste } from './Paste';
|
||||
export { SvgPauseOutline } from './PauseOutline';
|
||||
export { SvgPauseSolid } from './PauseSolid';
|
||||
export { SvgPause } from './Pause';
|
||||
export { SvgPenTool } from './PenTool';
|
||||
export { SvgPencilWrite } from './PencilWrite';
|
||||
export { SvgPhone } from './Phone';
|
||||
export { SvgPhoto } from './Photo';
|
||||
export { SvgPhpElephant } from './PhpElephant';
|
||||
export { SvgPiggyBank } from './PiggyBank';
|
||||
export { SvgPin } from './Pin';
|
||||
export { SvgPlayOutline } from './PlayOutline';
|
||||
export { SvgPlay } from './Play';
|
||||
export { SvgPlaylist } from './Playlist';
|
||||
export { SvgPlugin } from './Plugin';
|
||||
export { SvgPortfolio } from './Portfolio';
|
||||
export { SvgPrinter } from './Printer';
|
||||
export { SvgPylon } from './Pylon';
|
||||
export { SvgQuestion } from './Question';
|
||||
export { SvgQueue } from './Queue';
|
||||
export { SvgRadarCopy2 } from './RadarCopy2';
|
||||
export { SvgRadar } from './Radar';
|
||||
export { SvgRadio } from './Radio';
|
||||
export { SvgRefresh } from './Refresh';
|
||||
export { SvgReload } from './Reload';
|
||||
export { SvgReplyAll } from './ReplyAll';
|
||||
export { SvgReply } from './Reply';
|
||||
export { SvgReports } from './Reports';
|
||||
export { SvgRepost } from './Repost';
|
||||
export { SvgSaveDisk } from './SaveDisk';
|
||||
export { SvgScreenFull } from './ScreenFull';
|
||||
export { SvgSearch } from './Search';
|
||||
export { SvgSend } from './Send';
|
||||
export { SvgServers } from './Servers';
|
||||
export { SvgShare01 } from './Share01';
|
||||
export { SvgShareAlt } from './ShareAlt';
|
||||
export { SvgShare } from './Share';
|
||||
export { SvgShield } from './Shield';
|
||||
export { SvgShoppingCart } from './ShoppingCart';
|
||||
export { SvgShowSidebar } from './ShowSidebar';
|
||||
export { SvgShuffle } from './Shuffle';
|
||||
export { SvgStandBy } from './StandBy';
|
||||
export { SvgStarFull } from './StarFull';
|
||||
export { SvgStation } from './Station';
|
||||
export { SvgStepBackward } from './StepBackward';
|
||||
export { SvgStepForward } from './StepForward';
|
||||
export { SvgStethoscope } from './Stethoscope';
|
||||
export { SvgStoreFront } from './StoreFront';
|
||||
export { SvgStrokeWidth } from './StrokeWidth';
|
||||
export { SvgSubdirectoryLeft } from './SubdirectoryLeft';
|
||||
export { SvgSubdirectoryRight } from './SubdirectoryRight';
|
||||
export { SvgSubtract } from './Subtract';
|
||||
export { SvgSwap } from './Swap';
|
||||
export { SvgTablet } from './Tablet';
|
||||
export { SvgTag } from './Tag';
|
||||
export { SvgTarget } from './Target';
|
||||
export { SvgTextBox } from './TextBox';
|
||||
export { SvgTextDecoration } from './TextDecoration';
|
||||
export { SvgThermometer } from './Thermometer';
|
||||
export { SvgThumbsDown } from './ThumbsDown';
|
||||
export { SvgThumbsUp } from './ThumbsUp';
|
||||
export { SvgTicket } from './Ticket';
|
||||
export { SvgTime } from './Time';
|
||||
export { SvgTimer } from './Timer';
|
||||
export { SvgToolsCopy } from './ToolsCopy';
|
||||
export { SvgTranslate } from './Translate';
|
||||
export { SvgTrash } from './Trash';
|
||||
export { SvgTravelBus } from './TravelBus';
|
||||
export { SvgTravelCar } from './TravelCar';
|
||||
export { SvgTravelCase } from './TravelCase';
|
||||
export { SvgTravelTaxiCab } from './TravelTaxiCab';
|
||||
export { SvgTravelTrain } from './TravelTrain';
|
||||
export { SvgTravelWalk } from './TravelWalk';
|
||||
export { SvgTravel } from './Travel';
|
||||
export { SvgTrophy } from './Trophy';
|
||||
export { SvgTuning } from './Tuning';
|
||||
export { SvgUpload } from './Upload';
|
||||
export { SvgUsb } from './Usb';
|
||||
export { SvgUserAdd } from './UserAdd';
|
||||
export { SvgUserGroup } from './UserGroup';
|
||||
export { SvgUserSolidCircle } from './UserSolidCircle';
|
||||
export { SvgUserSolidSquare } from './UserSolidSquare';
|
||||
export { SvgUser } from './User';
|
||||
export { SvgVector } from './Vector';
|
||||
export { SvgVideoCamera } from './VideoCamera';
|
||||
export { SvgViewCarousel } from './ViewCarousel';
|
||||
export { SvgViewColumn } from './ViewColumn';
|
||||
export { SvgViewHide } from './ViewHide';
|
||||
export { SvgViewList } from './ViewList';
|
||||
export { SvgViewShow } from './ViewShow';
|
||||
export { SvgViewTile } from './ViewTile';
|
||||
export { SvgVolumeDown } from './VolumeDown';
|
||||
export { SvgVolumeMute } from './VolumeMute';
|
||||
export { SvgVolumeOff } from './VolumeOff';
|
||||
export { SvgVolumeUp } from './VolumeUp';
|
||||
export { SvgWallet } from './Wallet';
|
||||
export { SvgWatch } from './Watch';
|
||||
export { SvgWindowNew } from './WindowNew';
|
||||
export { SvgWindowOpen } from './WindowOpen';
|
||||
export { SvgWindow } from './Window';
|
||||
export { SvgWrench } from './Wrench';
|
||||
export { SvgYinYang } from './YinYang';
|
||||
export { SvgZoomIn } from './ZoomIn';
|
||||
export { SvgZoomOut } from './ZoomOut';
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 17 56.7" style="enable-background:new 0 0 17 56.7;" xml:space="preserve">
|
||||
<path d="M1.9,2.2L1.9,2.2c1.3,0.2,2.4,0.7,3.4,1.5c0.8,0.8,1.5,1.8,2,2.9c0.9,2.3,3.1,8.4,3.1,21.8S8.2,47.8,7.3,50.1
|
||||
c-0.5,1.1-1.2,2.1-2,2.9c-1,0.8-2.1,1.3-3.3,1.5H1.9c-0.4,0.1-0.6,0.4-0.6,0.8c0.1,0.3,0.3,0.6,0.6,0.6c1.6,0.2,3.2-0.1,4.7-0.8
|
||||
c1.4-0.8,2.7-1.9,3.6-3.2c1.7-2.6,2.9-5.4,3.6-8.4l0.1-0.3c2.5-9.7,2.5-19.8,0-29.5l-0.1-0.3c-0.7-3-1.9-5.8-3.6-8.4
|
||||
C9.3,3.5,8,2.4,6.6,1.6C5.1,0.9,3.5,0.7,1.9,0.9c-0.4,0-0.7,0.4-0.6,0.7C1.3,1.9,1.5,2.2,1.9,2.2L1.9,2.2z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 817 B |
@@ -1,22 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import type { SVGProps } from 'react';
|
||||
export const SvgCloseParenthesis = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 17 56.7"
|
||||
preserveAspectRatio="none"
|
||||
style={{
|
||||
color: 'inherit',
|
||||
...props.style,
|
||||
}}
|
||||
>
|
||||
<path
|
||||
d="M1.9,2.2L1.9,2.2c1.3,0.2,2.4,0.7,3.4,1.5c0.8,0.8,1.5,1.8,2,2.9c0.9,2.3,3.1,8.4,3.1,21.8S8.2,47.8,7.3,50.1
|
||||
c-0.5,1.1-1.2,2.1-2,2.9c-1,0.8-2.1,1.3-3.3,1.5H1.9c-0.4,0.1-0.6,0.4-0.6,0.8c0.1,0.3,0.3,0.6,0.6,0.6c1.6,0.2,3.2-0.1,4.7-0.8
|
||||
c1.4-0.8,2.7-1.9,3.6-3.2c1.7-2.6,2.9-5.4,3.6-8.4l0.1-0.3c2.5-9.7,2.5-19.8,0-29.5l-0.1-0.3c-0.7-3-1.9-5.8-3.6-8.4
|
||||
C9.3,3.5,8,2.4,6.6,1.6C5.1,0.9,3.5,0.7,1.9,0.9c-0.4,0-0.7,0.4-0.6,0.7C1.3,1.9,1.5,2.2,1.9,2.2L1.9,2.2z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1,19 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import type { SVGProps } from 'react';
|
||||
export const SvgHelp = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
style={{
|
||||
color: 'inherit',
|
||||
...props.style,
|
||||
}}
|
||||
>
|
||||
<path
|
||||
fill="currentColor"
|
||||
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM7.92 9.234v.102a.5.5 0 0 0 .5.5h.997a.499.499 0 0 0 .499-.499c0-1.29.998-1.979 2.34-1.979 1.308 0 2.168.689 2.168 1.67 0 .928-.482 1.359-1.686 1.91l-.344.154C11.379 11.54 11 12.21 11 13.381v.119a.5.5 0 0 0 .5.5h.997a.499.499 0 0 0 .499-.499c0-.516.138-.723.55-.912l.345-.155c1.445-.654 2.529-1.514 2.529-3.39v-.103c0-1.978-1.72-3.441-4.164-3.441-2.478 0-4.336 1.428-4.336 3.734zm2.58 7.757c0 .867.659 1.509 1.491 1.509.85 0 1.509-.642 1.509-1.509 0-.867-.659-1.491-1.509-1.491-.832 0-1.491.624-1.491 1.491z"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1,9 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 17 56.7" style="enable-background:new 0 0 17 56.7;" xml:space="preserve">
|
||||
<path d="M15.1,54.5L15.1,54.5c-1.3-0.2-2.4-0.7-3.4-1.5c-0.8-0.8-1.5-1.8-2-2.9c-0.9-2.3-3.1-8.4-3.1-21.8S8.8,8.9,9.8,6.6
|
||||
c0.5-1.1,1.2-2.1,2-2.9c1-0.8,2.1-1.3,3.3-1.5h0.1c0.4-0.1,0.6-0.4,0.6-0.8c-0.1-0.3-0.3-0.6-0.6-0.6c-1.6-0.2-3.2,0.1-4.7,0.8
|
||||
C9,2.4,7.8,3.5,6.8,4.8c-1.7,2.6-2.9,5.4-3.6,8.4l-0.1,0.3c-2.5,9.7-2.5,19.8,0,29.5l0.1,0.3c0.7,3,1.9,5.8,3.6,8.4
|
||||
c0.9,1.3,2.2,2.4,3.6,3.2c1.5,0.7,3.1,0.9,4.7,0.8c0.4,0,0.7-0.4,0.6-0.7C15.7,54.8,15.5,54.5,15.1,54.5L15.1,54.5z"/>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 829 B |
@@ -1,22 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import type { SVGProps } from 'react';
|
||||
export const SvgOpenParenthesis = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 17 56.7"
|
||||
preserveAspectRatio="none"
|
||||
style={{
|
||||
color: 'inherit',
|
||||
...props.style,
|
||||
}}
|
||||
>
|
||||
<path
|
||||
d="M15.1,54.5L15.1,54.5c-1.3-0.2-2.4-0.7-3.4-1.5c-0.8-0.8-1.5-1.8-2-2.9c-0.9-2.3-3.1-8.4-3.1-21.8S8.8,8.9,9.8,6.6
|
||||
c0.5-1.1,1.2-2.1,2-2.9c1-0.8,2.1-1.3,3.3-1.5h0.1c0.4-0.1,0.6-0.4,0.6-0.8c-0.1-0.3-0.3-0.6-0.6-0.6c-1.6-0.2-3.2,0.1-4.7,0.8
|
||||
C9,2.4,7.8,3.5,6.8,4.8c-1.7,2.6-2.9,5.4-3.6,8.4l-0.1,0.3c-2.5,9.7-2.5,19.8,0,29.5l0.1,0.3c0.7,3,1.9,5.8,3.6,8.4
|
||||
c0.9,1.3,2.2,2.4,3.6,3.2c1.5,0.7,3.1,0.9,4.7,0.8c0.4,0,0.7-0.4,0.6-0.7C15.7,54.8,15.5,54.5,15.1,54.5L15.1,54.5z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1,11 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.1.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" baseProfile="basic" id="Layer_1"
|
||||
xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 28.3 28.3"
|
||||
xml:space="preserve">
|
||||
<g>
|
||||
<path d="M23.2,10.1c1.1,0,2-0.9,2-2V2.2c0-1.1-0.9-2-2-2h-18c-1.1,0-2,0.9-2,2c0,0.4,0.1,0.9,0.4,1.2l8.1,10.8L3.6,25
|
||||
c-0.7,0.9-0.5,2.1,0.4,2.8c0.3,0.3,0.8,0.4,1.2,0.4h18c1.1,0,2-0.9,2-2v-5.8c0-1.1-0.9-2-2-2s-2,0.9-2,2v3.8h-12l6.6-8.8
|
||||
c0.5-0.7,0.5-1.7,0-2.4L9.2,4.2h12v3.9C21.2,9.2,22.1,10.1,23.2,10.1z"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 654 B |
@@ -1,20 +0,0 @@
|
||||
import * as React from 'react';
|
||||
import type { SVGProps } from 'react';
|
||||
export const SvgSum = (props: SVGProps<SVGSVGElement>) => (
|
||||
<svg
|
||||
{...props}
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
viewBox="0 0 28.3 28.3"
|
||||
style={{
|
||||
color: 'inherit',
|
||||
...props.style,
|
||||
}}
|
||||
>
|
||||
<path
|
||||
d="M23.2,10.1c1.1,0,2-0.9,2-2V2.2c0-1.1-0.9-2-2-2h-18c-1.1,0-2,0.9-2,2c0,0.4,0.1,0.9,0.4,1.2l8.1,10.8L3.6,25
|
||||
c-0.7,0.9-0.5,2.1,0.4,2.8c0.3,0.3,0.8,0.4,1.2,0.4h18c1.1,0,2-0.9,2-2v-5.8c0-1.1-0.9-2-2-2s-2,0.9-2,2v3.8h-12l6.6-8.8
|
||||
c0.5-0.7,0.5-1.7,0-2.4L9.2,4.2h12v3.9C21.2,9.2,22.1,10.1,23.2,10.1z"
|
||||
fill="currentColor"
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
@@ -1,9 +0,0 @@
|
||||
<svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="SVGRepo_bgCarrier" stroke-width="0"></g>
|
||||
<g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
|
||||
<g id="SVGRepo_iconCarrier">
|
||||
<path
|
||||
d="M12 22C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10-4.477 10-10 10zM7.92 9.234v.102a.5.5 0 0 0 .5.5h.997a.499.499 0 0 0 .499-.499c0-1.29.998-1.979 2.34-1.979 1.308 0 2.168.689 2.168 1.67 0 .928-.482 1.359-1.686 1.91l-.344.154C11.379 11.54 11 12.21 11 13.381v.119a.5.5 0 0 0 .5.5h.997a.499.499 0 0 0 .499-.499c0-.516.138-.723.55-.912l.345-.155c1.445-.654 2.529-1.514 2.529-3.39v-.103c0-1.978-1.72-3.441-4.164-3.441-2.478 0-4.336 1.428-4.336 3.734zm2.58 7.757c0 .867.659 1.509 1.491 1.509.85 0 1.509-.642 1.509-1.509 0-.867-.659-1.491-1.509-1.491-.832 0-1.491.624-1.491 1.491z"
|
||||
fill="inherit"></path>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 905 B |
@@ -1,51 +0,0 @@
|
||||
export { SvgAlertTriangle } from './AlertTriangle';
|
||||
export { SvgArrowButtonDown1 } from './ArrowButtonDown1';
|
||||
export { SvgArrowButtonLeft1 } from './ArrowButtonLeft1';
|
||||
export { SvgArrowButtonRight1 } from './ArrowButtonRight1';
|
||||
export { SvgArrowButtonUp1 } from './ArrowButtonUp1';
|
||||
export { SvgArrowsExpand3 } from './ArrowsExpand3';
|
||||
export { SvgArrowsShrink3 } from './ArrowsShrink3';
|
||||
export { SvgArrowsSynchronize } from './ArrowsSynchronize';
|
||||
export { SvgCalendar } from './Calendar';
|
||||
export { SvgCalendar3 } from './Calendar3';
|
||||
export { SvgCheck } from './Check';
|
||||
export { SvgCheckAll } from './CheckAll';
|
||||
export { SvgCheckCircle1 } from './CheckCircle1';
|
||||
export { SvgCheckCircleHollow } from './CheckCircleHollow';
|
||||
export { SvgCloseParenthesis } from './CloseParenthesis';
|
||||
export { SvgCloudUnknown } from './CloudUnknown';
|
||||
export { SvgCloudUpload } from './CloudUpload';
|
||||
export { SvgCustomNotesPaper } from './CustomNotesPaper';
|
||||
export { SvgDownAndRightArrow } from './DownAndRightArrow';
|
||||
export { SvgDownloadThickBottom } from './DownloadThickBottom';
|
||||
export { SvgEditSkull1 } from './EditSkull1';
|
||||
export { SvgFavoriteStar } from './FavoriteStar';
|
||||
export { SvgFilter2 } from './Filter2';
|
||||
export { SvgHelp } from './Help';
|
||||
export { SvgHyperlink2 } from './Hyperlink2';
|
||||
export { SvgHyperlink3 } from './Hyperlink3';
|
||||
export { SvgInformationCircle } from './InformationCircle';
|
||||
export { SvgKey } from './Key';
|
||||
export { SvgLockClosed } from './LockClosed';
|
||||
export { SvgMoonStars } from './MoonStars';
|
||||
export { SvgNavigationMenu } from './NavigationMenu';
|
||||
export { SvgNotesPaper } from './NotesPaper';
|
||||
export { SvgNotesPaperText } from './NotesPaperText';
|
||||
export { SvgOpenParenthesis } from './OpenParenthesis';
|
||||
export { SvgPencil1 } from './Pencil1';
|
||||
export { SvgPencilWriteAlternate } from './PencilWriteAlternate';
|
||||
export { SvgRefreshArrow } from './RefreshArrow';
|
||||
export { SvgRemove } from './Remove';
|
||||
export { SvgRemoveAlternate } from './RemoveAlternate';
|
||||
export { SvgSearch1 } from './Search1';
|
||||
export { SvgSearchAlternate } from './SearchAlternate';
|
||||
export { SvgSettingsSliderAlternate } from './SettingsSliderAlternate';
|
||||
export { SvgSubtract } from './Subtract';
|
||||
export { SvgSum } from './Sum';
|
||||
export { SvgSun } from './Sun';
|
||||
export { SvgSystem } from './System';
|
||||
export { SvgUncheckAll } from './UncheckAll';
|
||||
export { SvgUploadThickBottom } from './UploadThickBottom';
|
||||
export { SvgValidationCheck } from './ValidationCheck';
|
||||
export { SvgViewHide } from './ViewHide';
|
||||
export { SvgViewShow } from './ViewShow';
|
||||
@@ -1,157 +0,0 @@
|
||||
import { keyframes } from '@emotion/css';
|
||||
|
||||
import { theme } from './theme';
|
||||
import { tokens } from './tokens';
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export type CSSProperties = Record<string, any>;
|
||||
|
||||
const MOBILE_MIN_HEIGHT = 40;
|
||||
|
||||
const shadowLarge = {
|
||||
boxShadow: '0 15px 30px 0 rgba(0,0,0,0.11), 0 5px 15px 0 rgba(0,0,0,0.08)',
|
||||
};
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
export const styles: Record<string, any> = {
|
||||
incomeHeaderHeight: 70,
|
||||
cardShadow: '0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)',
|
||||
monthRightPadding: 5,
|
||||
menuBorderRadius: 4,
|
||||
mobileMinHeight: MOBILE_MIN_HEIGHT,
|
||||
mobileMenuItem: {
|
||||
fontSize: 17,
|
||||
fontWeight: 400,
|
||||
paddingTop: 8,
|
||||
paddingBottom: 8,
|
||||
height: MOBILE_MIN_HEIGHT,
|
||||
minHeight: MOBILE_MIN_HEIGHT,
|
||||
},
|
||||
mobileEditingPadding: 12,
|
||||
altMenuMaxHeight: 250,
|
||||
altMenuText: {
|
||||
fontSize: 13,
|
||||
},
|
||||
altMenuHeaderText: {
|
||||
fontSize: 13,
|
||||
fontWeight: 700,
|
||||
},
|
||||
veryLargeText: {
|
||||
fontSize: 30,
|
||||
fontWeight: 600,
|
||||
},
|
||||
largeText: {
|
||||
fontSize: 20,
|
||||
fontWeight: 700,
|
||||
letterSpacing: 0.5,
|
||||
},
|
||||
mediumText: {
|
||||
fontSize: 15,
|
||||
fontWeight: 500,
|
||||
},
|
||||
smallText: {
|
||||
fontSize: 13,
|
||||
},
|
||||
verySmallText: {
|
||||
fontSize: 12,
|
||||
},
|
||||
tinyText: {
|
||||
fontSize: 10,
|
||||
},
|
||||
page: {
|
||||
flex: 1,
|
||||
'@media (max-height: 550px)': {
|
||||
minHeight: 700, // ensure we can scroll on small screens
|
||||
},
|
||||
paddingTop: 8, // height of the titlebar
|
||||
[`@media (min-width: ${tokens.breakpoint_small})`]: {
|
||||
paddingTop: 36,
|
||||
},
|
||||
},
|
||||
pageContent: {
|
||||
paddingLeft: 2,
|
||||
paddingRight: 2,
|
||||
[`@media (min-width: ${tokens.breakpoint_small})`]: {
|
||||
paddingLeft: 20,
|
||||
paddingRight: 20,
|
||||
},
|
||||
},
|
||||
settingsPageContent: {
|
||||
padding: 20,
|
||||
[`@media (min-width: ${tokens.breakpoint_small})`]: {
|
||||
padding: 'inherit',
|
||||
},
|
||||
},
|
||||
staticText: {
|
||||
cursor: 'default',
|
||||
userSelect: 'none',
|
||||
},
|
||||
shadow: {
|
||||
boxShadow: '0 2px 4px 0 rgba(0,0,0,0.1)',
|
||||
},
|
||||
shadowLarge,
|
||||
tnum: {
|
||||
// eslint-disable-next-line rulesdir/typography
|
||||
fontFeatureSettings: '"tnum"',
|
||||
},
|
||||
notFixed: { fontFeatureSettings: '' },
|
||||
text: {
|
||||
fontSize: 16,
|
||||
// lineHeight: 22.4 // TODO: This seems like trouble, but what's the right value?
|
||||
},
|
||||
delayedFadeIn: {
|
||||
animationName: keyframes({
|
||||
'0%': { opacity: 0 },
|
||||
'100%': { opacity: 1 },
|
||||
}),
|
||||
animationDuration: '1s',
|
||||
animationFillMode: 'both',
|
||||
animationDelay: '0.5s',
|
||||
},
|
||||
underlinedText: {
|
||||
borderBottom: `2px solid`,
|
||||
},
|
||||
noTapHighlight: {
|
||||
WebkitTapHighlightColor: 'transparent',
|
||||
':focus': {
|
||||
outline: 'none',
|
||||
},
|
||||
},
|
||||
lineClamp: (lines: number) => {
|
||||
return {
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: lines,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
wordBreak: 'break-word',
|
||||
};
|
||||
},
|
||||
tooltip: {
|
||||
padding: 5,
|
||||
...shadowLarge,
|
||||
borderWidth: 2,
|
||||
borderRadius: 4,
|
||||
borderStyle: 'solid',
|
||||
borderColor: theme.tooltipBorder,
|
||||
backgroundColor: theme.tooltipBackground,
|
||||
color: theme.tooltipText,
|
||||
overflow: 'auto',
|
||||
},
|
||||
popover: {
|
||||
border: 'none',
|
||||
backgroundColor: theme.menuBackground,
|
||||
color: theme.menuItemText,
|
||||
},
|
||||
// Dynamically set
|
||||
horizontalScrollbar: null as CSSProperties | null,
|
||||
lightScrollbar: null as CSSProperties | null,
|
||||
darkScrollbar: null as CSSProperties | null,
|
||||
scrollbarWidth: null as number | null,
|
||||
editorPill: {
|
||||
color: theme.pillText,
|
||||
backgroundColor: theme.pillBackground,
|
||||
borderRadius: 4,
|
||||
padding: '3px 5px',
|
||||
},
|
||||
};
|
||||
@@ -1,203 +0,0 @@
|
||||
export const theme = {
|
||||
pageBackground: 'var(--color-pageBackground)',
|
||||
pageBackgroundModalActive: 'var(--color-pageBackgroundModalActive)',
|
||||
pageBackgroundTopLeft: 'var(--color-pageBackgroundTopLeft)',
|
||||
pageBackgroundBottomRight: 'var(--color-pageBackgroundBottomRight)',
|
||||
pageBackgroundLineTop: 'var(--color-pageBackgroundLineTop)',
|
||||
pageBackgroundLineMid: 'var(--color-pageBackgroundLineMid)',
|
||||
pageBackgroundLineBottom: 'var(--color-pageBackgroundLineBottom)',
|
||||
pageText: 'var(--color-pageText)',
|
||||
pageTextLight: 'var(--color-pageTextLight)',
|
||||
pageTextSubdued: 'var(--color-pageTextSubdued)',
|
||||
pageTextDark: 'var(--color-pageTextDark)',
|
||||
pageTextPositive: 'var(--color-pageTextPositive)',
|
||||
pageTextLink: 'var(--color-pageTextLink)',
|
||||
pageTextLinkLight: 'var(--color-pageTextLinkLight)',
|
||||
cardBackground: 'var(--color-cardBackground)',
|
||||
cardBorder: 'var(--color-cardBorder)',
|
||||
cardShadow: 'var(--color-cardShadow)',
|
||||
tableBackground: 'var(--color-tableBackground)',
|
||||
tableRowBackgroundHover: 'var(--color-tableRowBackgroundHover)',
|
||||
tableText: 'var(--color-tableText)',
|
||||
tableTextLight: 'var(--color-tableTextLight)',
|
||||
tableTextSubdued: 'var(--color-tableTextSubdued)',
|
||||
tableTextSelected: 'var(--color-tableTextSelected)',
|
||||
tableTextHover: 'var(--color-tableTextHover)',
|
||||
tableTextInactive: 'var(--color-tableTextInactive)',
|
||||
tableHeaderText: 'var(--color-tableHeaderText)',
|
||||
tableHeaderBackground: 'var(--color-tableHeaderBackground)',
|
||||
tableBorder: 'var(--color-tableBorder)',
|
||||
tableBorderSelected: 'var(--color-tableBorderSelected)',
|
||||
tableBorderHover: 'var(--color-tableBorderHover)',
|
||||
tableBorderSeparator: 'var(--color-tableBorderSeparator)',
|
||||
tableRowBackgroundHighlight: 'var(--color-tableRowBackgroundHighlight)',
|
||||
tableRowBackgroundHighlightText:
|
||||
'var(--color-tableRowBackgroundHighlightText)',
|
||||
tableRowHeaderBackground: 'var(--color-tableRowHeaderBackground)',
|
||||
tableRowHeaderText: 'var(--color-tableRowHeaderText)',
|
||||
sidebarBackground: 'var(--color-sidebarBackground)',
|
||||
sidebarItemBackgroundPending: 'var(--color-sidebarItemBackgroundPending)',
|
||||
sidebarItemBackgroundPositive: 'var(--color-sidebarItemBackgroundPositive)',
|
||||
sidebarItemBackgroundFailed: 'var(--color-sidebarItemBackgroundFailed)',
|
||||
sidebarItemAccentSelected: 'var(--color-sidebarItemAccentSelected)',
|
||||
sidebarItemBackgroundHover: 'var(--color-sidebarItemBackgroundHover)',
|
||||
sidebarItemText: 'var(--color-sidebarItemText)',
|
||||
sidebarItemTextSelected: 'var(--color-sidebarItemTextSelected)',
|
||||
menuBackground: 'var(--color-menuBackground)',
|
||||
menuItemBackground: 'var(--color-menuItemBackground)',
|
||||
menuItemBackgroundHover: 'var(--color-menuItemBackgroundHover)',
|
||||
menuItemText: 'var(--color-menuItemText)',
|
||||
menuItemTextHover: 'var(--color-menuItemTextHover)',
|
||||
menuItemTextSelected: 'var(--color-menuItemTextSelected)',
|
||||
menuItemTextHeader: 'var(--color-menuItemTextHeader)',
|
||||
menuBorder: 'var(--color-menuBorder)',
|
||||
menuBorderHover: 'var(--color-menuBorderHover)',
|
||||
menuKeybindingText: 'var(--color-menuKeybindingText)',
|
||||
menuAutoCompleteBackground: 'var(--color-menuAutoCompleteBackground)',
|
||||
menuAutoCompleteBackgroundHover:
|
||||
'var(--color-menuAutoCompleteBackgroundHover)',
|
||||
menuAutoCompleteText: 'var(--color-menuAutoCompleteText)',
|
||||
menuAutoCompleteTextHover: 'var(--color-menuAutoCompleteTextHover)',
|
||||
menuAutoCompleteTextHeader: 'var(--color-menuAutoCompleteTextHeader)',
|
||||
menuAutoCompleteItemTextHover: 'var(--color-menuAutoCompleteItemTextHover)',
|
||||
menuAutoCompleteItemText: 'var(--color-menuAutoCompleteItemText)',
|
||||
modalBackground: 'var(--color-modalBackground)',
|
||||
modalBorder: 'var(--color-modalBorder)',
|
||||
mobileHeaderBackground: 'var(--color-mobileHeaderBackground)',
|
||||
mobileHeaderText: 'var(--color-mobileHeaderText)',
|
||||
mobileHeaderTextSubdued: 'var(--color-mobileHeaderTextSubdued)',
|
||||
mobileHeaderTextHover: 'var(--color-mobileHeaderTextHover)',
|
||||
mobilePageBackground: 'var(--color-mobilePageBackground)',
|
||||
mobileNavBackground: 'var(--color-mobileNavBackground)',
|
||||
mobileNavItem: 'var(--color-mobileNavItem)',
|
||||
mobileNavItemSelected: 'var(--color-mobileNavItemSelected)',
|
||||
mobileAccountShadow: 'var(--color-mobileAccountShadow)',
|
||||
mobileAccountText: 'var(--color-mobileAccountText)',
|
||||
mobileTransactionSelected: 'var(--color-mobileTransactionSelected)',
|
||||
mobileViewTheme: 'var(--color-mobileViewTheme)',
|
||||
mobileConfigServerViewTheme: 'var(--color-mobileConfigServerViewTheme)',
|
||||
markdownNormal: 'var(--color-markdownNormal)',
|
||||
markdownDark: 'var(--color-markdownDark)',
|
||||
markdownLight: 'var(--color-markdownLight)',
|
||||
buttonMenuText: 'var(--color-buttonMenuText)',
|
||||
buttonMenuTextHover: 'var(--color-buttonMenuTextHover)',
|
||||
buttonMenuBackground: 'var(--color-buttonMenuBackground)',
|
||||
buttonMenuBackgroundHover: 'var(--color-buttonMenuBackgroundHover)',
|
||||
buttonMenuBorder: 'var(--color-buttonMenuBorder)',
|
||||
buttonMenuSelectedText: 'var(--color-buttonMenuSelectedText)',
|
||||
buttonMenuSelectedTextHover: 'var(--color-buttonMenuSelectedTextHover)',
|
||||
buttonMenuSelectedBackground: 'var(--color-buttonMenuSelectedBackground)',
|
||||
buttonMenuSelectedBackgroundHover:
|
||||
'var(--color-buttonMenuSelectedBackgroundHover)',
|
||||
buttonMenuSelectedBorder: 'var(--color-buttonMenuSelectedBorder)',
|
||||
buttonPrimaryText: 'var(--color-buttonPrimaryText)',
|
||||
buttonPrimaryTextHover: 'var(--color-buttonPrimaryTextHover)',
|
||||
buttonPrimaryBackground: 'var(--color-buttonPrimaryBackground)',
|
||||
buttonPrimaryBackgroundHover: 'var(--color-buttonPrimaryBackgroundHover)',
|
||||
buttonPrimaryBorder: 'var(--color-buttonPrimaryBorder)',
|
||||
buttonPrimaryShadow: 'var(--color-buttonPrimaryShadow)',
|
||||
buttonPrimaryDisabledText: 'var(--color-buttonPrimaryDisabledText)',
|
||||
buttonPrimaryDisabledBackground:
|
||||
'var(--color-buttonPrimaryDisabledBackground)',
|
||||
buttonPrimaryDisabledBorder: 'var(--color-buttonPrimaryDisabledBorder)',
|
||||
buttonNormalText: 'var(--color-buttonNormalText)',
|
||||
buttonNormalTextHover: 'var(--color-buttonNormalTextHover)',
|
||||
buttonNormalBackground: 'var(--color-buttonNormalBackground)',
|
||||
buttonNormalBackgroundHover: 'var(--color-buttonNormalBackgroundHover)',
|
||||
buttonNormalBorder: 'var(--color-buttonNormalBorder)',
|
||||
buttonNormalShadow: 'var(--color-buttonNormalShadow)',
|
||||
buttonNormalSelectedText: 'var(--color-buttonNormalSelectedText)',
|
||||
buttonNormalSelectedBackground: 'var(--color-buttonNormalSelectedBackground)',
|
||||
buttonNormalDisabledText: 'var(--color-buttonNormalDisabledText)',
|
||||
buttonNormalDisabledBackground: 'var(--color-buttonNormalDisabledBackground)',
|
||||
buttonNormalDisabledBorder: 'var(--color-buttonNormalDisabledBorder)',
|
||||
buttonBareText: 'var(--color-buttonBareText)',
|
||||
buttonBareTextHover: 'var(--color-buttonBareTextHover)',
|
||||
buttonBareBackground: 'var(--color-buttonBareBackground)',
|
||||
buttonBareBackgroundHover: 'var(--color-buttonBareBackgroundHover)',
|
||||
buttonBareBackgroundActive: 'var(--color-buttonBareBackgroundActive)',
|
||||
buttonBareDisabledText: 'var(--color-buttonBareDisabledText)',
|
||||
buttonBareDisabledBackground: 'var(--color-buttonBareDisabledBackground)',
|
||||
calendarText: 'var(--color-calendarText)',
|
||||
calendarBackground: 'var(--color-calendarBackground)',
|
||||
calendarItemText: 'var(--color-calendarItemText)',
|
||||
calendarItemBackground: 'var(--color-calendarItemBackground)',
|
||||
calendarSelectedBackground: 'var(--color-calendarSelectedBackground)',
|
||||
noticeBackground: 'var(--color-noticeBackground)',
|
||||
noticeBackgroundLight: 'var(--color-noticeBackgroundLight)',
|
||||
noticeBackgroundDark: 'var(--color-noticeBackgroundDark)',
|
||||
noticeText: 'var(--color-noticeText)',
|
||||
noticeTextLight: 'var(--color-noticeTextLight)',
|
||||
noticeTextDark: 'var(--color-noticeTextDark)',
|
||||
noticeTextMenu: 'var(--color-noticeTextMenu)',
|
||||
noticeTextMenuHover: 'var(--color-noticeTextMenuHover)',
|
||||
noticeBorder: 'var(--color-noticeBorder)',
|
||||
warningBackground: 'var(--color-warningBackground)',
|
||||
warningText: 'var(--color-warningText)',
|
||||
warningTextLight: 'var(--color-warningTextLight)',
|
||||
warningTextDark: 'var(--color-warningTextDark)',
|
||||
warningBorder: 'var(--color-warningBorder)',
|
||||
errorBackground: 'var(--color-errorBackground)',
|
||||
errorText: 'var(--color-errorText)',
|
||||
errorTextDark: 'var(--color-errorTextDark)',
|
||||
errorTextDarker: 'var(--color-errorTextDarker)',
|
||||
errorTextMenu: 'var(--color-errorTextMenu)',
|
||||
errorBorder: 'var(--color-errorBorder)',
|
||||
upcomingBackground: 'var(--color-upcomingBackground)',
|
||||
upcomingText: 'var(--color-upcomingText)',
|
||||
upcomingBorder: 'var(--color-upcomingBorder)',
|
||||
formLabelText: 'var(--color-formLabelText)',
|
||||
formLabelBackground: 'var(--color-formLabelBackground)',
|
||||
formInputBackground: 'var(--color-formInputBackground)',
|
||||
formInputBackgroundSelected: 'var(--color-formInputBackgroundSelected)',
|
||||
formInputBackgroundSelection: 'var(--color-formInputBackgroundSelection)',
|
||||
formInputBorder: 'var(--color-formInputBorder)',
|
||||
formInputTextReadOnlySelection: 'var(--color-formInputTextReadOnlySelection)',
|
||||
formInputBorderSelected: 'var(--color-formInputBorderSelected)',
|
||||
formInputText: 'var(--color-formInputText)',
|
||||
formInputTextSelected: 'var(--color-formInputTextSelected)',
|
||||
formInputTextPlaceholder: 'var(--color-formInputTextPlaceholder)',
|
||||
formInputTextPlaceholderSelected:
|
||||
'var(--color-formInputTextPlaceholderSelected)',
|
||||
formInputTextSelection: 'var(--color-formInputTextSelection)',
|
||||
formInputShadowSelected: 'var(--color-formInputShadowSelected)',
|
||||
formInputTextHighlight: 'var(--color-formInputTextHighlight)',
|
||||
checkboxText: 'var(--color-checkboxText)',
|
||||
checkboxBackgroundSelected: 'var(--color-checkboxBackgroundSelected)',
|
||||
checkboxBorderSelected: 'var(--color-checkboxBorderSelected)',
|
||||
checkboxShadowSelected: 'var(--color-checkboxShadowSelected)',
|
||||
checkboxToggleBackground: 'var(--color-checkboxToggleBackground)',
|
||||
checkboxToggleBackgroundSelected:
|
||||
'var(--color-checkboxToggleBackgroundSelected)',
|
||||
checkboxToggleDisabled: 'var(--color-checkboxToggleDisabled)',
|
||||
pillBackground: 'var(--color-pillBackground)',
|
||||
pillBackgroundLight: 'var(--color-pillBackgroundLight)',
|
||||
pillText: 'var(--color-pillText)',
|
||||
pillTextHighlighted: 'var(--color-pillTextHighlighted)',
|
||||
pillBorder: 'var(--color-pillBorder)',
|
||||
pillBorderDark: 'var(--color-pillBorderDark)',
|
||||
pillBackgroundSelected: 'var(--color-pillBackgroundSelected)',
|
||||
pillTextSelected: 'var(--color-pillTextSelected)',
|
||||
pillBorderSelected: 'var(--color-pillBorderSelected)',
|
||||
pillTextSubdued: 'var(--color-pillTextSubdued)',
|
||||
reportsRed: 'var(--color-reportsRed)',
|
||||
reportsBlue: 'var(--color-reportsBlue)',
|
||||
reportsGreen: 'var(--color-reportsGreen)',
|
||||
reportsGray: 'var(--color-reportsGray)',
|
||||
reportsLabel: 'var(--color-reportsLabel)',
|
||||
reportsInnerLabel: 'var(--color-reportsInnerLabel)',
|
||||
noteTagBackground: 'var(--color-noteTagBackground)',
|
||||
noteTagBackgroundHover: 'var(--color-noteTagBackgroundHover)',
|
||||
noteTagText: 'var(--color-noteTagText)',
|
||||
budgetOtherMonth: 'var(--color-budgetOtherMonth)',
|
||||
budgetCurrentMonth: 'var(--color-budgetCurrentMonth)',
|
||||
budgetHeaderOtherMonth: 'var(--color-budgetHeaderOtherMonth)',
|
||||
budgetHeaderCurrentMonth: 'var(--color-budgetHeaderCurrentMonth)',
|
||||
floatingActionBarBackground: 'var(--color-floatingActionBarBackground)',
|
||||
floatingActionBarBorder: 'var(--color-floatingActionBarBorder)',
|
||||
floatingActionBarText: 'var(--color-floatingActionBarText)',
|
||||
tooltipText: 'var(--color-tooltipText)',
|
||||
tooltipBackground: 'var(--color-tooltipBackground)',
|
||||
tooltipBorder: 'var(--color-tooltipBorder)',
|
||||
calendarCellBackground: 'var(--color-calendarCellBackground)',
|
||||
};
|
||||
@@ -1,35 +0,0 @@
|
||||
enum BreakpointNames {
|
||||
small = 'small',
|
||||
medium = 'medium',
|
||||
wide = 'wide',
|
||||
}
|
||||
|
||||
type NumericBreakpoints = {
|
||||
[key in BreakpointNames]: number;
|
||||
};
|
||||
|
||||
export const breakpoints: NumericBreakpoints = {
|
||||
small: 512,
|
||||
medium: 730,
|
||||
wide: 1100,
|
||||
};
|
||||
|
||||
type BreakpointsPx = {
|
||||
[B in keyof NumericBreakpoints as `breakpoint_${B}`]: string;
|
||||
};
|
||||
|
||||
// Provide the same breakpoints in a form usable by CSS media queries
|
||||
// {
|
||||
// breakpoint_small: '512px',
|
||||
// breakpoint_medium: '740px',
|
||||
// breakpoint_wide: '1100px',
|
||||
// }
|
||||
export const tokens: BreakpointsPx = Object.entries(
|
||||
breakpoints,
|
||||
).reduce<BreakpointsPx>(
|
||||
(acc, [key, val]) => ({
|
||||
...acc,
|
||||
[`breakpoint_${key}`]: `${val}px`,
|
||||
}),
|
||||
{} as BreakpointsPx,
|
||||
);
|
||||
@@ -5,7 +5,6 @@
|
||||
// the latest Node 16.x release supports all of the features
|
||||
"target": "ES2021",
|
||||
"module": "CommonJS",
|
||||
"moduleResolution": "node10",
|
||||
"noEmit": false,
|
||||
"declaration": true,
|
||||
"strict": true,
|
||||
|
||||
5
packages/desktop-client/.gitignore
vendored
@@ -6,11 +6,9 @@ node_modules
|
||||
# testing
|
||||
coverage
|
||||
test-results
|
||||
playwright-report
|
||||
|
||||
# production
|
||||
build
|
||||
build-electron
|
||||
build-stats
|
||||
stats.json
|
||||
|
||||
@@ -25,6 +23,3 @@ public/kcab
|
||||
public/data
|
||||
public/data-file-index.txt
|
||||
public/*.wasm
|
||||
|
||||
# translations
|
||||
locale/
|
||||
|
||||
@@ -1,53 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
const fs = require('fs');
|
||||
const path = require('path');
|
||||
|
||||
// Local path to the cloned translations repository
|
||||
const localRepoPath = './packages/desktop-client/locale';
|
||||
|
||||
// Compare JSON files and delete incomplete ones
|
||||
const processTranslations = () => {
|
||||
try {
|
||||
const files = fs.readdirSync(localRepoPath);
|
||||
const enJsonPath = path.join(localRepoPath, 'en.json');
|
||||
|
||||
if (!fs.existsSync(enJsonPath)) {
|
||||
throw new Error('en.json not found in the repository.');
|
||||
}
|
||||
|
||||
const enJson = JSON.parse(fs.readFileSync(enJsonPath, 'utf8'));
|
||||
const enKeysCount = Object.keys(enJson).length;
|
||||
|
||||
console.log(`en.json has ${enKeysCount} keys.`);
|
||||
|
||||
files.forEach((file) => {
|
||||
if (file === 'en.json' || path.extname(file) !== '.json') return;
|
||||
|
||||
if (file.startsWith('en-')) {
|
||||
console.log(`Keeping ${file} as it's an English language.`);
|
||||
return;
|
||||
}
|
||||
|
||||
const filePath = path.join(localRepoPath, file);
|
||||
const jsonData = JSON.parse(fs.readFileSync(filePath, 'utf8'));
|
||||
const fileKeysCount = Object.keys(jsonData).length;
|
||||
|
||||
// Calculate the percentage of keys present compared to en.json
|
||||
const percentage = (fileKeysCount / enKeysCount) * 100;
|
||||
console.log(`${file} has ${fileKeysCount} keys (${percentage.toFixed(2)}%).`);
|
||||
|
||||
if (percentage < 50) {
|
||||
fs.unlinkSync(filePath);
|
||||
console.log(`Deleted ${file} due to insufficient keys.`);
|
||||
} else {
|
||||
console.log(`Keeping ${file}.`);
|
||||
}
|
||||
});
|
||||
|
||||
console.log('Processing completed.');
|
||||
} catch (error) {
|
||||
console.error(`Error: ${error.message}`);
|
||||
}
|
||||
};
|
||||
|
||||
processTranslations();
|
||||
@@ -1,65 +0,0 @@
|
||||
import { type Page } from '@playwright/test';
|
||||
|
||||
import { expect, test } from './fixtures';
|
||||
import { ConfigurationPage } from './page-models/configuration-page';
|
||||
import { MobileNavigation } from './page-models/mobile-navigation';
|
||||
|
||||
test.describe('Mobile Accounts', () => {
|
||||
let page: Page;
|
||||
let navigation: MobileNavigation;
|
||||
let configurationPage: ConfigurationPage;
|
||||
|
||||
test.beforeEach(async ({ browser }) => {
|
||||
page = await browser.newPage();
|
||||
navigation = new MobileNavigation(page);
|
||||
configurationPage = new ConfigurationPage(page);
|
||||
|
||||
await page.setViewportSize({
|
||||
width: 350,
|
||||
height: 600,
|
||||
});
|
||||
await page.goto('/');
|
||||
await configurationPage.createTestFile();
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
test('opens the accounts page and asserts on balances', async () => {
|
||||
const accountsPage = await navigation.goToAccountsPage();
|
||||
await accountsPage.waitFor();
|
||||
|
||||
const account = await accountsPage.getNthAccount(1);
|
||||
|
||||
await expect(account.name).toHaveText('Ally Savings');
|
||||
await expect(account.balance).toHaveText('7,653.00');
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test('opens individual account page and checks that filtering is working', async () => {
|
||||
const accountsPage = await navigation.goToAccountsPage();
|
||||
await accountsPage.waitFor();
|
||||
|
||||
const accountPage = await accountsPage.openNthAccount(0);
|
||||
await accountPage.waitFor();
|
||||
|
||||
await expect(accountPage.heading).toHaveText('Bank of America');
|
||||
await expect(accountPage.transactionList).toBeVisible();
|
||||
await expect(await accountPage.getBalance()).toBeGreaterThan(0);
|
||||
await expect(accountPage.noTransactionsMessage).not.toBeVisible();
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
|
||||
await accountPage.searchByText('nothing should be found');
|
||||
await expect(accountPage.noTransactionsMessage).toBeVisible();
|
||||
await expect(accountPage.transactions).toHaveCount(0);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
|
||||
await accountPage.clearSearch();
|
||||
await expect(accountPage.transactions).not.toHaveCount(0);
|
||||
|
||||
await accountPage.searchByText('Kroger');
|
||||
await expect(accountPage.transactions).not.toHaveCount(0);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
});
|
||||
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 28 KiB |
|
Before Width: | Height: | Size: 34 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 33 KiB |
@@ -1,19 +1,15 @@
|
||||
import { join } from 'path';
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
import { type Page } from '@playwright/test';
|
||||
|
||||
import { expect, test } from './fixtures';
|
||||
import { type AccountPage } from './page-models/account-page';
|
||||
import { ConfigurationPage } from './page-models/configuration-page';
|
||||
import { Navigation } from './page-models/navigation';
|
||||
|
||||
test.describe('Accounts', () => {
|
||||
let page: Page;
|
||||
let navigation: Navigation;
|
||||
let configurationPage: ConfigurationPage;
|
||||
let accountPage: AccountPage;
|
||||
let page;
|
||||
let navigation;
|
||||
let configurationPage;
|
||||
let accountPage;
|
||||
|
||||
test.beforeEach(async ({ browser }) => {
|
||||
test.beforeAll(async ({ browser }) => {
|
||||
page = await browser.newPage();
|
||||
navigation = new Navigation(page);
|
||||
configurationPage = new ConfigurationPage(page);
|
||||
@@ -22,7 +18,7 @@ test.describe('Accounts', () => {
|
||||
await configurationPage.createTestFile();
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
test.afterAll(async () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
@@ -56,17 +52,15 @@ test.describe('Accounts', () => {
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test.describe('On Budget Accounts', () => {
|
||||
test.describe('Budgeted Accounts', () => {
|
||||
// Reset filters
|
||||
test.afterEach(async () => {
|
||||
await accountPage.removeFilter(0);
|
||||
});
|
||||
|
||||
test('creates a transfer from two existing transactions', async () => {
|
||||
accountPage = await navigation.goToAccountPage('On budget');
|
||||
await accountPage.waitFor();
|
||||
|
||||
await expect(accountPage.accountName).toHaveText('On Budget Accounts');
|
||||
accountPage = await navigation.goToAccountPage('For budget');
|
||||
await expect(accountPage.accountName).toHaveText('Budgeted Accounts');
|
||||
|
||||
await accountPage.filterByNote('Test Acc Transfer');
|
||||
|
||||
@@ -105,61 +99,4 @@ test.describe('Accounts', () => {
|
||||
await expect(transaction.account).toHaveText('Ally Savings');
|
||||
});
|
||||
});
|
||||
|
||||
test.describe('Import Transactions', () => {
|
||||
test.beforeEach(async () => {
|
||||
accountPage = await navigation.createAccount({
|
||||
name: 'CSV import',
|
||||
offBudget: false,
|
||||
balance: 0,
|
||||
});
|
||||
await accountPage.waitFor();
|
||||
});
|
||||
|
||||
async function importCsv(screenshot = false) {
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
await accountPage.page.getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(join(__dirname, 'data/test.csv'));
|
||||
|
||||
if (screenshot) await expect(page).toMatchThemeScreenshots();
|
||||
|
||||
const importButton = accountPage.page.getByRole('button', {
|
||||
name: /Import \d+ transactions/,
|
||||
});
|
||||
await importButton.click();
|
||||
|
||||
await expect(importButton).not.toBeVisible();
|
||||
}
|
||||
|
||||
test('imports transactions from a CSV file', async () => {
|
||||
await importCsv(true);
|
||||
});
|
||||
|
||||
test('import csv file twice', async () => {
|
||||
await importCsv(false);
|
||||
|
||||
const fileChooserPromise = page.waitForEvent('filechooser');
|
||||
await accountPage.page.getByRole('button', { name: 'Import' }).click();
|
||||
|
||||
const fileChooser = await fileChooserPromise;
|
||||
await fileChooser.setFiles(join(__dirname, 'data/test.csv'));
|
||||
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
|
||||
const importButton = accountPage.page.getByRole('button', {
|
||||
name: /Import \d+ transactions/,
|
||||
});
|
||||
|
||||
await expect(importButton).toBeDisabled();
|
||||
await expect(await importButton.innerText()).toMatch(
|
||||
/Import 0 transactions/,
|
||||
);
|
||||
|
||||
await accountPage.page.getByRole('button', { name: 'Close' }).click();
|
||||
|
||||
await expect(importButton).not.toBeVisible();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
After Width: | Height: | Size: 197 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 190 KiB |
|
After Width: | Height: | Size: 117 KiB |
|
After Width: | Height: | Size: 116 KiB |
|
After Width: | Height: | Size: 118 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 55 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
Before Width: | Height: | Size: 171 KiB |
|
Before Width: | Height: | Size: 166 KiB |
|
Before Width: | Height: | Size: 169 KiB |
|
Before Width: | Height: | Size: 153 KiB |
|
Before Width: | Height: | Size: 152 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 197 KiB |
|
Before Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 190 KiB |
|
Before Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 116 KiB |
|
Before Width: | Height: | Size: 117 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 56 KiB |
@@ -1,411 +0,0 @@
|
||||
import { type Page } from '@playwright/test';
|
||||
|
||||
import * as monthUtils from 'loot-core/shared/months';
|
||||
import { amountToCurrency, currencyToAmount } from 'loot-core/shared/util';
|
||||
|
||||
import { expect, test } from './fixtures';
|
||||
import { ConfigurationPage } from './page-models/configuration-page';
|
||||
import { type MobileBudgetPage } from './page-models/mobile-budget-page';
|
||||
import { MobileNavigation } from './page-models/mobile-navigation';
|
||||
|
||||
const copyLastMonthBudget = async (
|
||||
budgetPage: MobileBudgetPage,
|
||||
categoryName: string,
|
||||
) => {
|
||||
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
|
||||
await budgetMenuModal.copyLastMonthBudget();
|
||||
await budgetMenuModal.close();
|
||||
};
|
||||
|
||||
const setTo3MonthAverage = async (
|
||||
budgetPage: MobileBudgetPage,
|
||||
categoryName: string,
|
||||
) => {
|
||||
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
|
||||
await budgetMenuModal.setTo3MonthAverage();
|
||||
await budgetMenuModal.close();
|
||||
};
|
||||
|
||||
const setTo6MonthAverage = async (
|
||||
budgetPage: MobileBudgetPage,
|
||||
categoryName: string,
|
||||
) => {
|
||||
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
|
||||
await budgetMenuModal.setTo6MonthAverage();
|
||||
await budgetMenuModal.close();
|
||||
};
|
||||
|
||||
const setToYearlyAverage = async (
|
||||
budgetPage: MobileBudgetPage,
|
||||
categoryName: string,
|
||||
) => {
|
||||
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
|
||||
await budgetMenuModal.setToYearlyAverage();
|
||||
await budgetMenuModal.close();
|
||||
};
|
||||
|
||||
async function setBudgetAverage(
|
||||
budgetPage: MobileBudgetPage,
|
||||
categoryName: string,
|
||||
numberOfMonths: number,
|
||||
setBudgetAverageFn: (
|
||||
budgetPage: MobileBudgetPage,
|
||||
categoryName: string,
|
||||
numberOfMonths: number,
|
||||
) => Promise<void>,
|
||||
) {
|
||||
let totalSpent = 0;
|
||||
|
||||
for (let i = 0; i < numberOfMonths; i++) {
|
||||
await budgetPage.goToPreviousMonth();
|
||||
const spentButton = await budgetPage.getButtonForSpent(categoryName);
|
||||
const spent = await spentButton.textContent();
|
||||
if (!spent) {
|
||||
throw new Error('Failed to get spent amount');
|
||||
}
|
||||
totalSpent += currencyToAmount(spent) ?? 0;
|
||||
}
|
||||
|
||||
// Calculate average amount
|
||||
const averageSpent = totalSpent / numberOfMonths;
|
||||
|
||||
// Go back to the current month
|
||||
for (let i = 0; i < numberOfMonths; i++) {
|
||||
await budgetPage.goToNextMonth();
|
||||
}
|
||||
|
||||
await setBudgetAverageFn(budgetPage, categoryName, numberOfMonths);
|
||||
|
||||
return averageSpent;
|
||||
}
|
||||
|
||||
const budgetTypes = ['Envelope', 'Tracking'] as const;
|
||||
|
||||
budgetTypes.forEach(budgetType => {
|
||||
test.describe(`Mobile Budget [${budgetType}]`, () => {
|
||||
let page: Page;
|
||||
let navigation: MobileNavigation;
|
||||
let configurationPage: ConfigurationPage;
|
||||
let previousGlobalIsTesting: boolean;
|
||||
|
||||
test.beforeAll(() => {
|
||||
// TODO: Hack, properly mock the currentMonth function
|
||||
previousGlobalIsTesting = global.IS_TESTING;
|
||||
global.IS_TESTING = true;
|
||||
});
|
||||
|
||||
test.afterAll(() => {
|
||||
// TODO: Hack, properly mock the currentMonth function
|
||||
global.IS_TESTING = previousGlobalIsTesting;
|
||||
});
|
||||
|
||||
test.beforeEach(async ({ browser }) => {
|
||||
page = await browser.newPage();
|
||||
navigation = new MobileNavigation(page);
|
||||
configurationPage = new ConfigurationPage(page);
|
||||
|
||||
await page.setViewportSize({
|
||||
width: 350,
|
||||
height: 600,
|
||||
});
|
||||
await page.goto('/');
|
||||
await configurationPage.createTestFile();
|
||||
|
||||
const settingsPage = await navigation.goToSettingsPage();
|
||||
await settingsPage.useBudgetType(budgetType);
|
||||
});
|
||||
|
||||
test.afterEach(async () => {
|
||||
await page.close();
|
||||
});
|
||||
|
||||
test('loads the budget page with budgeted amounts', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
await expect(budgetPage.categoryNames).toHaveText([
|
||||
'Food',
|
||||
'Restaurants',
|
||||
'Entertainment',
|
||||
'Clothing',
|
||||
'General',
|
||||
'Gift',
|
||||
'Medical',
|
||||
'Savings',
|
||||
'Cell',
|
||||
'Internet',
|
||||
'Mortgage',
|
||||
'Water',
|
||||
'Power',
|
||||
'Starting Balances',
|
||||
'Misc',
|
||||
'Income',
|
||||
]);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
// Page Header Tests
|
||||
|
||||
test('checks that clicking the Actual logo in the page header opens the budget page menu', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
await budgetPage.openBudgetPageMenu();
|
||||
|
||||
const budgetPageMenuModal = page.getByRole('dialog');
|
||||
|
||||
await expect(budgetPageMenuModal).toBeVisible();
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test("checks that clicking the left arrow in the page header shows the previous month's budget", async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const selectedMonth = await budgetPage.getSelectedMonth();
|
||||
const displayMonth = monthUtils.format(
|
||||
selectedMonth,
|
||||
budgetPage.MONTH_HEADER_DATE_FORMAT,
|
||||
);
|
||||
|
||||
await expect(budgetPage.heading).toHaveText(displayMonth);
|
||||
|
||||
const previousMonth = await budgetPage.goToPreviousMonth();
|
||||
const previousDisplayMonth = monthUtils.format(
|
||||
previousMonth,
|
||||
budgetPage.MONTH_HEADER_DATE_FORMAT,
|
||||
);
|
||||
|
||||
await expect(budgetPage.heading).toHaveText(previousDisplayMonth);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test('checks that clicking the month in the page header opens the month menu modal', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const selectedMonth = await budgetPage.getSelectedMonth();
|
||||
|
||||
await budgetPage.openMonthMenu();
|
||||
|
||||
const monthMenuModal = page.getByRole('dialog');
|
||||
const monthMenuModalHeading = monthMenuModal.getByRole('heading');
|
||||
|
||||
const displayMonth = monthUtils.format(
|
||||
selectedMonth,
|
||||
budgetPage.MONTH_HEADER_DATE_FORMAT,
|
||||
);
|
||||
await expect(monthMenuModalHeading).toHaveText(displayMonth);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test("checks that clicking the right arrow in the page header shows the next month's budget", async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const selectedMonth = await budgetPage.getSelectedMonth();
|
||||
const displayMonth = monthUtils.format(
|
||||
selectedMonth,
|
||||
budgetPage.MONTH_HEADER_DATE_FORMAT,
|
||||
);
|
||||
|
||||
await expect(budgetPage.heading).toHaveText(displayMonth);
|
||||
|
||||
const nextMonth = await budgetPage.goToNextMonth();
|
||||
const nextDisplayMonth = monthUtils.format(
|
||||
nextMonth,
|
||||
budgetPage.MONTH_HEADER_DATE_FORMAT,
|
||||
);
|
||||
|
||||
await expect(budgetPage.heading).toHaveText(nextDisplayMonth);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
// Category / Category Group Menu Tests
|
||||
|
||||
test('checks that clicking the category group name opens the category group menu modal', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const categoryGroupName = await budgetPage.getCategoryGroupNameForRow(0);
|
||||
await budgetPage.openCategoryGroupMenu(categoryGroupName);
|
||||
|
||||
const categoryMenuModalHeading = page
|
||||
.getByRole('dialog')
|
||||
.getByRole('heading');
|
||||
|
||||
await expect(categoryMenuModalHeading).toHaveText(categoryGroupName);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test('checks that clicking the category name opens the category menu modal', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const categoryName = await budgetPage.getCategoryNameForRow(0);
|
||||
const categoryMenuModal = await budgetPage.openCategoryMenu(categoryName);
|
||||
|
||||
await expect(categoryMenuModal.heading).toHaveText(categoryName);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
// Budgeted Cell Tests
|
||||
|
||||
test('checks that clicking the budgeted cell opens the budget menu modal', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const categoryName = await budgetPage.getCategoryNameForRow(0);
|
||||
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
|
||||
|
||||
await expect(budgetMenuModal.heading).toHaveText(categoryName);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test('updates the budgeted amount', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const categoryName = await budgetPage.getCategoryNameForRow(0);
|
||||
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
|
||||
|
||||
const budgetAmount = 123;
|
||||
|
||||
// Set to 123.00
|
||||
await budgetMenuModal.setBudgetAmount(`${budgetAmount}00`);
|
||||
|
||||
const budgetedButton =
|
||||
await budgetPage.getButtonForBudgeted(categoryName);
|
||||
|
||||
await expect(budgetedButton).toHaveText(amountToCurrency(budgetAmount));
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
test(`copies last month's budget`, async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const categoryName = await budgetPage.getCategoryNameForRow(3);
|
||||
const budgetedButton =
|
||||
await budgetPage.getButtonForBudgeted(categoryName);
|
||||
|
||||
await budgetPage.goToPreviousMonth();
|
||||
|
||||
const lastMonthBudget = await budgetedButton.textContent();
|
||||
|
||||
if (!lastMonthBudget) {
|
||||
throw new Error('Failed to get last month budget');
|
||||
}
|
||||
|
||||
await budgetPage.goToNextMonth();
|
||||
|
||||
await copyLastMonthBudget(budgetPage, categoryName);
|
||||
|
||||
await expect(budgetedButton).toHaveText(lastMonthBudget);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
(
|
||||
[
|
||||
[3, setTo3MonthAverage],
|
||||
[6, setTo6MonthAverage],
|
||||
[12, setToYearlyAverage],
|
||||
] as const
|
||||
).forEach(([numberOfMonths, setBudgetAverageFn]) => {
|
||||
test(`set budget to ${numberOfMonths} month average`, async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const categoryName = await budgetPage.getCategoryNameForRow(3);
|
||||
|
||||
const averageSpent = await setBudgetAverage(
|
||||
budgetPage,
|
||||
categoryName,
|
||||
numberOfMonths,
|
||||
setBudgetAverageFn,
|
||||
);
|
||||
|
||||
const budgetedButton =
|
||||
await budgetPage.getButtonForBudgeted(categoryName);
|
||||
|
||||
await expect(budgetedButton).toHaveText(
|
||||
amountToCurrency(Math.abs(averageSpent)),
|
||||
);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
});
|
||||
|
||||
test(`applies budget template`, async () => {
|
||||
const settingsPage = await navigation.goToSettingsPage();
|
||||
await settingsPage.enableExperimentalFeature('Goal templates');
|
||||
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const categoryName = await budgetPage.getCategoryNameForRow(1);
|
||||
|
||||
const amountToTemplate = 123;
|
||||
|
||||
const categoryMenuModal = await budgetPage.openCategoryMenu(categoryName);
|
||||
const editNotesModal = await categoryMenuModal.editNotes();
|
||||
const templateNotes = `#template ${amountToTemplate}`;
|
||||
await editNotesModal.updateNotes(templateNotes);
|
||||
await editNotesModal.close();
|
||||
|
||||
const budgetedButton =
|
||||
await budgetPage.getButtonForBudgeted(categoryName);
|
||||
|
||||
const budgetMenuModal = await budgetPage.openBudgetMenu(categoryName);
|
||||
await budgetMenuModal.applyBudgetTemplate();
|
||||
await budgetMenuModal.close();
|
||||
|
||||
await expect(budgetedButton).toHaveText(
|
||||
amountToCurrency(amountToTemplate),
|
||||
);
|
||||
const notification = page.getByRole('alert').first();
|
||||
await expect(notification).toContainText(templateNotes);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
// Spent Cell Tests
|
||||
|
||||
test('checks that clicking spent cell redirects to the category transactions page', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const categoryName = await budgetPage.getCategoryNameForRow(0);
|
||||
const accountPage = await budgetPage.openSpentPage(categoryName);
|
||||
|
||||
await expect(accountPage.heading).toContainText(categoryName);
|
||||
await expect(accountPage.transactionList).toBeVisible();
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
// Balance Cell Tests
|
||||
|
||||
test('checks that clicking the balance cell opens the balance menu modal', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const categoryName = await budgetPage.getCategoryNameForRow(0);
|
||||
const balanceMenuModal = await budgetPage.openBalanceMenu(categoryName);
|
||||
|
||||
await expect(balanceMenuModal.heading).toHaveText(categoryName);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
|
||||
if (budgetType === 'Envelope') {
|
||||
test('checks that clicking the To Budget/Overbudgeted amount opens the budget summary menu modal', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const envelopeBudgetSummaryModal =
|
||||
await budgetPage.openEnvelopeBudgetSummary();
|
||||
|
||||
await expect(envelopeBudgetSummaryModal.heading).toHaveText(
|
||||
'Budget Summary',
|
||||
);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
}
|
||||
|
||||
if (budgetType === 'Tracking') {
|
||||
test('checks that clicking the Saved/Projected Savings/Overspent amount opens the budget summary menu modal', async () => {
|
||||
const budgetPage = await navigation.goToBudgetPage();
|
||||
|
||||
const trackingBudgetSummaryModal =
|
||||
await budgetPage.openTrackingBudgetSummary();
|
||||
|
||||
await expect(trackingBudgetSummaryModal.heading).toHaveText(
|
||||
'Budget Summary',
|
||||
);
|
||||
await expect(page).toMatchThemeScreenshots();
|
||||
});
|
||||
}
|
||||
});
|
||||
});
|
||||
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 33 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 27 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 35 KiB |
|
Before Width: | Height: | Size: 31 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 29 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 30 KiB |