Compare commits

...

57 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
3c4f0fff58 Add default data to usePayees usages using inline destructuring
Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com>
2026-02-10 19:31:56 +00:00
copilot-swe-agent[bot]
acadee2a5c Initial plan 2026-02-10 19:15:46 +00:00
Joel Jeremy Marquez
1a1c7447ad Fix lint errors 2026-02-10 18:33:15 +00:00
Joel Jeremy Marquez
c8d7e4bc92 Replace usage of logger in desktop-client with console 2026-02-10 18:13:14 +00:00
github-actions[bot]
7b19600b29 Add release notes for PR #6880 2026-02-10 18:13:14 +00:00
Joel Jeremy Marquez
9804625b57 Move redux state to react-query - payees states 2026-02-10 18:13:14 +00:00
Joel Jeremy Marquez
10374316db Clear react query on closing of budget file similar to redux resetApp action 2026-02-10 18:12:56 +00:00
Joel Jeremy Marquez
bbd039e572 Fix lint errors 2026-02-10 16:30:19 +00:00
Joel Jeremy Marquez
c5db712481 Replace logger calls in desktop-client to console 2026-02-10 16:16:19 +00:00
Joel Jeremy Marquez
11837ddad5 [skip ci] Change category to Maintenance and update migration text 2026-02-10 16:16:04 +00:00
Joel Jeremy Marquez
0830540168 Fix onbudget and offbudget displaying closed accounts 2026-02-10 16:16:04 +00:00
Joel Jeremy Marquez
c22d445ae0 Fix TestProviders 2026-02-10 16:16:04 +00:00
autofix-ci[bot]
1cbacdd192 [autofix.ci] apply automated fixes 2026-02-10 16:16:04 +00:00
Joel Jeremy Marquez
e678a69c70 Cleanup 2026-02-10 16:16:04 +00:00
Joel Jeremy Marquez
6c75b4545d Coderabbit feedback 2026-02-10 16:16:04 +00:00
Joel Jeremy Marquez
c42390ca9f Fix TestProviders 2026-02-10 16:16:04 +00:00
Joel Jeremy Marquez
ac8d247def Fix lint error 2026-02-10 16:16:04 +00:00
github-actions[bot]
34f0c6c2e7 Add release notes for PR #6140 2026-02-10 16:16:04 +00:00
Joel Jeremy Marquez
63de281d13 TestProviders 2026-02-10 16:16:04 +00:00
Joel Jeremy Marquez
751e886796 Move redux state to react-query - account states 2026-02-10 16:15:52 +00:00
Joel Jeremy Marquez
89d5b7b6ad Fix typecheck errors 2026-02-10 16:13:43 +00:00
Matiss Janis Aboltins
c8aa0cf1d3 Refactor: extract tooltip components and clean up lint suppressions (#6721)
* Refactor: extract tooltip components and clean up lint suppressions

Extract CustomTooltip components from CrossoverGraph and NetWorthGraph
to module level to fix unstable nested components lint warnings. Also
consolidate theme file lint rule into oxlintrc.json and add proper
typing to styles object.

* Add release notes for maintenance updates addressing lint violations

* Remove style prop from CustomTooltip to prevent container layout styles from affecting tooltip

Co-authored-by: matiss <matiss@mja.lv>

* Refactor NetWorthGraph component by extracting TrendTooltip and StackedTooltip into separate functions for improved readability and maintainability. Update tooltip props to include necessary parameters for rendering. Clean up unused code and enhance tooltip styling.

* Refactor NetWorthGraph component to streamline tooltip handling

- Removed unnecessary prop passing for translation function in TrendTooltip.
- Adjusted import statements for better clarity and consistency.
- Cleaned up code to enhance readability and maintainability.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2026-02-10 15:12:22 +00:00
Matiss Janis Aboltins
84cebed20b Enforce consistent TypeScript type import style (#6805) 2026-02-10 13:33:20 +00:00
Matiss Janis Aboltins
0c3b54ee7d Points: pay for contributions (#6481)
* Add point totals display to all statistics sections in count-points script

* Update funding contributors documentation and add release notes for contributor point counting script

* Refactor contributor points calculation and enhance PR category determination

- Updated point values for PR contributions: reduced points for Features, Enhancements, Bugfix, and Maintenance, and added a new category for Unknown contributions.
- Introduced a new function to retrieve the last commit SHA before a specified date to improve accuracy in reading release notes.
- Modified the getPRCategoryAndPoints function to accept a monthEnd parameter for better context in point assignment.

* Update contributor points values in count-points script to reflect new scoring system

* Add new blog post on funding contributors' next steps

This post outlines plans to expand the contributor compensation system, including broader rewards for project involvement, targeted donations, and a points-based system for feature work. It emphasizes transparency and community feedback in shaping future funding strategies.

* Increase Bugfix points from 2 to 3

* Change points awarded for Features to 2
2026-02-10 12:42:52 +00:00
Michael Clark
eb9f9b3a73 :electron: Flathub PR to be draft on release (#6910)
* make flathub pr draft so that we dont attract attention

* release notes

* grammar
2026-02-09 19:10:37 +00:00
Diego Palacios
5c31aa03ba Fix feedback link for budget analysis report experimental flag (#6914) 2026-02-09 18:28:43 +00:00
xaviuzz
266e7f9cac Fix Ctrl+Enter losing amount value when adding transaction (#6911)
* Fix Ctrl+Enter losing amount value when adding transaction

Fixes #6901

When using Ctrl+Enter to add a transaction immediately after typing
in the amount field, the value wasn't being committed before the
transaction was saved, resulting in a zero amount.

The fix wraps the add-and-close logic in an afterSave() callback
to ensure field values are committed before adding the transaction.

Added regression tests for both debit and credit fields to verify
the fix works correctly.

* Add release notes for PR #6911

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.5 <noreply@anthropic.com>
2026-02-08 21:39:25 +00:00
Michael Clark
e951e21fe1 🎨 Storybook docs for block, card, colorpicker, formerror (#6874)
* storybook docs for block, card, colorpicker, formerror

* release notes
2026-02-07 09:40:30 +00:00
Stephen Brown II
1a26253457 Update the Create Linked Account workflow to prompt for Starting Date and Balance (#6629)
* feat: Add optional starting date and balance for bank sync accounts

Adds the ability to specify a custom starting date and balance when
linking new bank sync accounts in the Select Linked Accounts modal.

Addresses: https://discord.com/channels/937901803608096828/1402270361625563186

Changes:
- Frontend: Added inline date and amount input fields in the account
  linking table for new accounts
- Redux: Extended link account actions to accept startingDate and
  startingBalance parameters
- Backend: Updated account linking handlers to pass custom values to
  sync logic
- Sync: Modified syncAccount and processBankSyncDownload to use custom
  starting date/balance for initial sync transactions

Features:
- Only displays starting options when creating new accounts (not upgrades)
- AmountInput with smart sign detection based on account balance
  (negative for credit cards/loans)
- Defaults to 90 days ago for date and 0 for balance
- Mobile-responsive with separate AccountCard layout
- Works across all sync providers: GoCardless, SimpleFIN, Pluggy.ai

The custom starting balance is used directly for the starting balance
transaction, and the custom starting date determines both the sync
start date and the transaction date for the starting balance entry.

* refactor: Extract shared types and components for starting balance inputs

- Create CustomStartingSettings type to replace repeated inline type definitions
- Extract StartingOptionsInput component to consolidate duplicate UI between mobile/desktop views
- Create LinkAccountBasePayload type shared across GoCardless, SimpleFIN, and PluggyAI link functions
- Apply same base type pattern to server-side link account handlers

This simplifies the code introduced for custom starting date/balance when linking bank accounts.

[autofix.ci] apply automated fixes

* allow explicit zero values

* refactor: add type guard for BankSyncError to remove oxlint-disable

- Create isBankSyncError() type guard function with proper type narrowing
- Remove oxlint-disable-next-line comment that suppressed the no-explicit-any rule
- Add JSDoc comments for both isBankSyncError and handleSyncError functions
- Remove redundant type assertion now that type guard narrows correctly

* refactor: address code review nitpicks for SelectLinkedAccountsModal

- Use locale-aware date formatting instead of toISOString()
- Extract isNewAccountOption helper to reduce duplication
- Align AccountCardProps type definition pattern with TableRowProps

* Add placeholder date/balance for already linked accounts

* [autofix.ci] apply automated fixes

* Use StartingBalanceInfo only, and add mobile view

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-06 21:44:30 +00:00
distantvapor
e72f18c5db Add new theme 'You Need A Dark Mode' to catalog (#6891)
* Add new theme 'You Need A Dark Mode' to catalog

* Add 'You Need A Dark Mode' theme to catalog
2026-02-06 20:18:12 +00:00
Christian Speich
5deb2cf790 Add bank sync option to update dates. (#6850)
Signed-off-by: Christian Speich <christian@spei.ch>
2026-02-06 20:16:32 +00:00
Joel Jeremy Marquez
111e01449d Add build-electron to tsconfig excludes (#6883)
* Add build-electron to tsconfig excludes

* Add release notes for PR #6883

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-06 19:20:03 +00:00
Joel Jeremy Marquez
c0bd920c26 Fix react-hooks/exhaustive-deps in DateSelect (#6864)
* Fix react-hooks/exhaustive-deps in DateSelect

* Add release notes for PR #6864

* Fix remaining suppressions

* Change category to Maintenance and fix linting issues

Updated category from Enhancements to Maintenance and fixed linting issues related to react-hooks/exhaustive-deps in DateSelect.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-06 18:03:31 +00:00
Stephen Brown II
b695af66c0 Avoid duplicate category import errors in YNAB5 importer (#6878)
* avoid duplicate category import errors

Add normalizeError helper function

* Add release notes file
2026-02-06 16:15:56 +00:00
Noah
650521f05b Add Catppuccin Themes to custom theme catalog (#6857)
* Add Catppuccin Themes to theme catalog

* [autofix.ci] apply automated fixes

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-06 16:07:45 +00:00
tabedzki
738a8cda7c Fix date range calculation in BudgetAnalysisCard (#6875)
* fix: corrected date range calculation in BudgetAnalysisCard using calculateTimeRange

* add release note

* fix: ensure correct date formatting in BudgetAnalysisCard for start and end dates

* fix: rename release note file
2026-02-06 15:58:31 +00:00
Stephen Brown II
deadd9aefc Apply tag colors to YNAB flag tags (#6866)
* Apply tag colors to match YNAB flags

* Update tag colors to match YNAB, add description on import

* Tighten types

* Use custom colors

* Use Actual palette equivalents for tag colors

* Nitpick fixes

* Fix nitpick 'fix'

* Handle YNAB flag tag conflicts

* Handle YNAB flag tag conflicts without creating separate color tags

* Simplify

* Reorganize
2026-02-05 22:22:01 +00:00
Joel Jeremy Marquez
16ec636358 Fix react-hooks/exhaustive-deps in ImportTransactionsModal (#6868)
* Fix react-hooks/exhaustive-deps in ImportTransactionsModal

* [autofix.ci] apply automated fixes

* Add release notes for PR #6868

* Update category to Maintenance and fix warnings

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 17:09:04 +00:00
Joel Jeremy Marquez
b271de32b6 Fix react-hooks/exhaustive-deps in CustomReport (#6867)
* Fix react-hooks/exhaustive-deps in CustomReport

* Add release notes for PR #6867

* Fix typecheck errors

* [autofix.ci] apply automated fixes

* Change category to Maintenance and update description

* [autofix.ci] apply automated fixes

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-05 17:06:54 +00:00
Joel Jeremy Marquez
2fb98156f6 Fix react/exhaustive-deps in PayeeTable (#6863)
* Fix react/exhaustive-deps in Modals

* Fix react/exhaustive-deps in PayeeTable

* Add release notes for PR #6863

* Change category and fix dependency management in PayeeTable

Updated category from 'Enhancements' to 'Maintenance' and fixed dependency management in PayeeTable.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 17:05:23 +00:00
Joel Jeremy Marquez
2f86bafd1f Fix react/exhaustive-deps in Modals (#6862)
* Fix react/exhaustive-deps in Modals

* Add release notes for PR #6862

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-05 17:05:09 +00:00
Joel Jeremy Marquez
7f6f4d5def Move redux state to react-query - category states (#5977)
* Move redux state to react query - category states

* Fix typecheck errors

* Fix typecheck errors

* Fix typecheck errors

* Remove t argument

* [autofix.ci] apply automated fixes

* Coderabbot suggestion

* Code review feedback

* Fix type

* Coderabbit

* Delete useCategoryActions

* Fix lint

* Use categories from react query cache

* Fix typecheck error

* Update to use useDeleteCategoryGroupMutation

* Coderabbit feedback

* Break up useCategoryActions

* [autofix.ci] apply automated fixes

* Fix typecheck errors

* Fix typecheck error

* Fix typecheck error

* await nested mutations

* Await deleteCategory

* Rename to sendThrow

* Fix lint errors

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-05 00:42:49 +00:00
Tyler Durr
c57260a504 Remove comma from all instances of month-year strings (#6748)
* Remove comma from all instances of month-year strings, e.g., "January 2026" rather than "January, 2026"

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #6748

* All updated calls now include `locale` as an argument

* Update dependencies in `useEffect` to include `locale`

* Reorganized new import

* Remove double colon

* Consistent trailing commas

* Include locale in other dependency arrays

* Reorder imports

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #6748

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-02-05 00:27:00 +00:00
Saahil Jaffer
1452ecfeb7 changes token expiry handling to be automatic sign out (#6798)
* changes token expiry handling to be automatic sign out

* add release notes

---------

Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-02-05 00:01:53 +00:00
Matthias Benaets
264cc9fb0e fix: isConcise state handling for CashFlow report (#6765) 2026-02-04 23:50:22 +00:00
Juulz
554d0b6150 Use consistent color variables on Budget Page. (#6820)
* Change color of budget table scrollbar.

* Update budget sidebar to use budget colors.

* Update fontWeight for 'Category' to match rest of table

* Update to use budget background

* Update ExpenseGroup to use budget color

* Update IncomeGroup to use budget color

* Update SidebarCategory colors

Change drag/drop to buttonPrimaryBackground so it will always be visible in any custom theme.

Background to budget color.

* Update SidebarGroup background to budget color

* Update EnvelopeBudgetComponents

Add 'budgetNumberNeutral' to cheveronDown in budgeted field instead of using default bare button text incases where bare button is set to normal button text and normal button is inverted. remove mobile color variable for shadow

* Update BudgetTotals.tsx

Use tableHeaderText for header.

* Update BudgetSummary.tsx

Use budget colors

* Update MonthPicker.tsx

change selected months at top from tableBorderHover color to buttonPrimaryBackground.

* [autofix.ci] apply automated fixes

* Update EnvelopeBudgetComponents.tsx

* Update BudgetTotals.tsx

revert

* Update SidebarCategory.tsx

* Update TrackingBudgetComponents to use budget colors

* [autofix.ci] apply automated fixes

* Update BudgetSummary to use budget colors

* Update BudgetTotal.tsx

* Update ExpenseProgress.tsx

* Update IncomeProgress.tsx

* Update Saved.tsx

* Use consistent color variables on budget pages.

* Update IncomeProgress.tsx

* Update ExpenseProgress.tsx

* Update VRT screenshots

Auto-generated by VRT workflow

PR: #6820

* Update EnvelopeBudgetComponents

Budget total header to follow current/other month style.

* Update EnvelopeBudgetComponents.tsx

* [autofix.ci] apply automated fixes

* Update EnvelopeBudgetComponents.tsx

* Update EnvelopeBudgetComponents.tsx

* [autofix.ci] apply automated fixes

* Update EnvelopeBudgetComponents.tsx

* Revert EnvelopeBudgetComponents.tsx

* [autofix.ci] apply automated fixes

* Update EnvelopeBudgetComponents.tsx

* Update EnvelopeBudgetComponents.tsx

* Update EnvelopeBudgetComponents.tsx

* Revert again :) EnvelopeBudgetComponents.tsx

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-04 23:26:18 +00:00
Joel Jeremy Marquez
11d0b9d824 Update findSortUp and findSortDown parameter to be more generic (#6861)
* findSortUp and findSortDown is also used for CategoryEntity. Updating type to be more generic.

* Generics

* Add release notes for PR #6861

* Adjust category to Maintenance based on review feedback (#6865)

* Initial plan

* Change release notes category to Maintenance

Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com>

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: joel-jeremy <20313680+joel-jeremy@users.noreply.github.com>
2026-02-04 22:50:10 +00:00
Stephen Brown II
323c2beb0a Include scheduled transactions in nYNAB imports (#6844)
* Include scheduled transactions in nYNAB imports

* Remove logs and restore schedule name from transaction memo

* Simplify rule actions

* Create schedules with unique names

* Set the note rather than append

* Update ynab5 demo budget and e2e test
2026-02-04 21:40:40 +00:00
Matt Fiddaman
dc5ce6ae96 switch to node alpine docker image (#6840)
* switch to node alpine image

* note
2026-02-04 20:08:17 +00:00
Adam
d8afc6b2be fix(i18n): respect browser preferred languages when supported (#6812)
* fix(i18n): respect browser preferred languages when supported

Instead of relying on the first browser language, the application now
iterates through navigator.languages and selects the first supported
locale, with a fallback to English.

* chore: add the release notes related file

* fix(i18n): check region locales support before falling back to base locale

* fix(i18n): make the unit test aligned with the region locales checking
2026-02-04 20:04:28 +00:00
Joshua Granick
7732fac8b6 Fix sync server migrations (#6346)
* Fix sync-server migrations to use ESM loader

* Add release notes

* Apply CodeRabbit suggestions

* [autofix.ci] apply automated fixes

* Add file extension filter to sync-server migrations import

* [autofix.ci] apply automated fixes

* Ensure migrations occur synchronously

* [autofix.ci] apply automated fixes

* Minor cleanup

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Matiss Janis Aboltins <matiss@mja.lv>
Co-authored-by: Matt Fiddaman <github@m.fiddaman.uk>
2026-02-04 20:00:42 +00:00
Matthias Benaets
6da6f505e6 fix: report widget tooltip z-index (#6849) 2026-02-04 19:38:04 +00:00
Stephen Brown II
4674916d3e Avoid negative zero in budget summary amounts (#6843) 2026-02-04 19:36:55 +00:00
Nam
5388a115e9 Fix translation issue #6828 (#6845)
* Fix translation issue #6828

* Add release note

* [autofix.ci] apply automated fixes

* update release number

---------

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-02-04 19:36:37 +00:00
Matiss Janis Aboltins
06d31ce035 Improve bug report template with better structure and requirements (#6784)
* Improve bug report template with better structure and requirements

* Fix: Remove empty value fields from textarea inputs in bug report template

* Add release notes for PR #6784

* Update 6784.md

* Update bug report template to request a screenshot of the import screen along with a redacted file version for better issue resolution.

* Update bug report template for import issues

Clarified instructions for reporting import issues.

---------

Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-02-04 19:29:21 +00:00
Matiss Janis Aboltins
9585a92cda Add oxlint rule against direct theme imports; fix Login OpenID button styles (#6796)
Co-authored-by: Michael Clark <5285928+MikesGlitch@users.noreply.github.com>
2026-02-04 19:29:20 +00:00
Matiss Janis Aboltins
a0a490c14c Typescript: make arithmetic.ts strict (#6801)
* Make arithmetic.ts strict TypeScript compliant

- Add type definitions for ParserState, Operator, OperatorNode, and AstNode
- Add explicit type annotations to all function parameters
- Fix null/undefined handling in parsePrimary function
- Remove type assertion in makeOperatorParser by using explicit Operator type
- Handle null return from currencyToAmount function
- All functions now have proper return type annotations

* Add test for ignoring leftover characters in evalArithmetic function
2026-02-04 19:29:19 +00:00
768 changed files with 8295 additions and 5709 deletions

View File

@@ -8,35 +8,66 @@ body:
attributes:
value: |
Thanks for taking the time to fill out this bug report! Please ensure you provide as much information as possible to better assist in confirming and identifying a fix for the bug.
⚠️ **CRITICAL:** Bug reports without clear, step-by-step reproduction instructions will be closed. We cannot investigate or fix bugs without being able to reproduce them. Please take the time to provide detailed reproduction steps.
- type: markdown
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.
**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
id: existing-issue
attributes:
label: 'Verified issue does not already exist?'
description: 'Please search to see if an issue already exists for the issue you encountered.'
options:
- label: 'I have searched and found no existing issue'
required: true
- type: textarea
id: what-happened
attributes:
label: What happened?
description: Also tell us, what did you expect to happen? If you're reporting an issue with imports, please attach a (redacted) version of the file you're having trouble importing. You may need to zip it before uploading.
placeholder: Tell us what you see!
value: 'A bug happened!'
description: |
Describe the bug clearly and concisely. Include:
- What you were trying to do
- What you expected to happen
- What actually happened instead
- Any error messages (copy/paste the exact text)
If you're reporting an issue with imports, please include a (redacted) version of the file, and a screenshot of the import screen. You may need to zip it before uploading.
placeholder: |
I was trying to [action] when [context].
Expected: [expected behavior]
Actual: [actual behavior]
Error message: [if any]
validations:
required: true
- type: markdown
attributes:
value: |
## Reproduction Steps
**REQUIRED:** Without clear reproduction steps, we cannot investigate or fix the bug. Please provide detailed, step-by-step instructions that anyone can follow to reproduce the issue.
- 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?'
description: |
**This field is mandatory and must be filled out completely.**
Provide numbered, step-by-step instructions that allow us to reproduce the bug. Include:
- Specific actions you took (e.g., "Click on the Budget tab", "Enter $100 in the amount field")
- What you expected to happen
- What actually happened instead
Example format:
1. Navigate to [specific page/section]
2. Click on [specific button/link]
3. Enter [specific data] in [specific field]
4. Click [action]
5. Observe [expected vs actual behavior]
If the issue involves importing data, please attach a (redacted) sample file. You may need to zip it before uploading.
placeholder: |
1. Go to [specific location]
2. Click [specific element]
3. Enter [specific data]
4. Click [action]
5. Expected: [what should happen]
Actual: [what actually happens]
validations:
required: true
- type: markdown

View File

@@ -8,6 +8,13 @@ const CONFIG = {
POINTS_PER_ISSUE_TRIAGE_ACTION: 1,
POINTS_PER_ISSUE_CLOSING_ACTION: 1,
POINTS_PER_RELEASE_PR: 4, // Awarded to whoever merges the release PR
PR_CONTRIBUTION_POINTS: {
Features: 2,
Enhancements: 2,
Bugfix: 3,
Maintenance: 2,
Unknown: 2,
},
// Point tiers for code changes (non-docs)
CODE_PR_REVIEW_POINT_TIERS: [
{ minChanges: 500, points: 8 },
@@ -31,6 +38,116 @@ const CONFIG = {
DOCS_FILES_PATTERN: 'packages/docs/**/*',
};
/**
* Parse category from release notes file content.
* @param {string} content - The content of the release notes file.
* @returns {string|null} The category or null if not found.
*/
function parseReleaseNotesCategory(content) {
if (!content) return null;
// Extract YAML front matter
const frontMatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---/);
if (!frontMatterMatch) return null;
// Extract category from front matter
const categoryMatch = frontMatterMatch[1].match(/^category:\s*(.+)$/m);
if (!categoryMatch) return null;
return categoryMatch[1].trim();
}
/**
* Get the last commit SHA on or before a given date.
* @param {Octokit} octokit - The Octokit instance.
* @param {string} owner - Repository owner.
* @param {string} repo - Repository name.
* @param {Date} beforeDate - The date to find the last commit before.
* @returns {Promise<string|null>} The commit SHA or null if not found.
*/
async function getLastCommitBeforeDate(octokit, owner, repo, beforeDate) {
try {
// Get the default branch from the repository
const { data: repoData } = await octokit.repos.get({ owner, repo });
const defaultBranch = repoData.default_branch;
const { data: commits } = await octokit.repos.listCommits({
owner,
repo,
sha: defaultBranch,
until: beforeDate.toISOString(),
per_page: 1,
});
if (commits.length > 0) {
return commits[0].sha;
}
} catch {
// If error occurs, return null to fall back to default branch
}
return null;
}
/**
* Get the category and points for a PR by reading its release notes file.
* @param {Octokit} octokit - The Octokit instance.
* @param {string} owner - Repository owner.
* @param {string} repo - Repository name.
* @param {number} prNumber - PR number.
* @param {Date} monthEnd - The end date of the month to use as base revision.
* @returns {Object} Object with category and points, or null if error.
*/
async function getPRCategoryAndPoints(
octokit,
owner,
repo,
prNumber,
monthEnd,
) {
const releaseNotesPath = `upcoming-release-notes/${prNumber}.md`;
try {
// Get the last commit of the month to use as base revision
const commitSha = await getLastCommitBeforeDate(
octokit,
owner,
repo,
monthEnd,
);
// Try to read the release notes file from the last commit of the month
const { data: fileContent } = await octokit.repos.getContent({
owner,
repo,
path: releaseNotesPath,
ref: commitSha || undefined, // Use commit SHA if available, otherwise default branch
});
if (fileContent.content) {
// Decode base64 content
const content = Buffer.from(fileContent.content, 'base64').toString(
'utf-8',
);
const category = parseReleaseNotesCategory(content);
if (category && CONFIG.PR_CONTRIBUTION_POINTS[category]) {
return {
category,
points: CONFIG.PR_CONTRIBUTION_POINTS[category],
};
}
}
} catch {
// Do nothing
}
return {
category: 'Unknown',
points: CONFIG.PR_CONTRIBUTION_POINTS.Unknown,
};
}
/**
* Get the start and end dates for the last month.
* @returns {Object} An object containing the start and end dates.
@@ -89,6 +206,7 @@ async function countContributorPoints() {
{
codeReviews: [], // Will store objects with PR number and points for main repo changes
docsReviews: [], // Will store objects with PR number and points for docs changes
prContributions: [], // Will store objects with PR number, category, and points for PR author contributions
labelRemovals: [],
issueClosings: [],
points: 0,
@@ -202,6 +320,28 @@ async function countContributorPoints() {
mergerStats.points += CONFIG.POINTS_PER_RELEASE_PR;
}
} else {
// Award points to PR author if they are a core maintainer
const prAuthor = pr.user?.login;
if (prAuthor && orgMemberLogins.has(prAuthor)) {
const categoryAndPoints = await getPRCategoryAndPoints(
octokit,
owner,
repo,
pr.number,
until,
);
if (categoryAndPoints) {
const authorStats = stats.get(prAuthor);
authorStats.prContributions.push({
pr: pr.number.toString(),
category: categoryAndPoints.category,
points: categoryAndPoints.points,
});
authorStats.points += categoryAndPoints.points;
}
}
const uniqueReviewers = new Set();
reviews.data.forEach(review => {
if (
@@ -293,7 +433,7 @@ async function countContributorPoints() {
// Print all statistics
printStats(
'Code Review Statistics',
stats => stats.codeReviews.length,
stats => stats.codeReviews.reduce((sum, r) => sum + r.points, 0),
(user, count) =>
`${user}: ${count} (PRs: ${stats
.get(user)
@@ -308,7 +448,7 @@ async function countContributorPoints() {
printStats(
'Docs Review Statistics',
stats => stats.docsReviews.length,
stats => stats.docsReviews.reduce((sum, r) => sum + r.points, 0),
(user, count) =>
`${user}: ${count} (PRs: ${stats
.get(user)
@@ -316,16 +456,27 @@ async function countContributorPoints() {
.join(', ')})`,
);
printStats(
'PR Contribution Statistics',
stats => stats.prContributions.reduce((sum, r) => sum + r.points, 0),
(user, count) =>
`${user}: ${count} (PRs: ${stats
.get(user)
.prContributions.map(r => `#${r.pr} (${r.points}pts - ${r.category})`)
.join(', ')})`,
);
printStats(
'"Needs Triage" Label Removal Statistics',
stats => stats.labelRemovals.length,
stats => stats.labelRemovals.length * CONFIG.POINTS_PER_ISSUE_TRIAGE_ACTION,
(user, count) =>
`${user}: ${count} (Issues: ${stats.get(user).labelRemovals.join(', ')})`,
);
printStats(
'Issue Closing Statistics',
stats => stats.issueClosings.length,
stats =>
stats.issueClosings.length * CONFIG.POINTS_PER_ISSUE_CLOSING_ACTION,
(user, count) =>
`${user}: ${count} (Issues: ${stats.get(user).issueClosings.join(', ')})`,
);

View File

@@ -199,6 +199,7 @@ jobs:
token: ${{ secrets.FLATHUB_GITHUB_TOKEN }}
commit-message: 'Update Actual flatpak to version ${{ needs.build.outputs.version }}'
branch: 'release/${{ needs.build.outputs.version }}'
draft: true
title: 'Update Actual flatpak to version ${{ needs.build.outputs.version }}'
body: |
This PR updates the Actual desktop flatpak to version ${{ needs.build.outputs.version }}.

View File

@@ -101,6 +101,7 @@
"typescript/no-var-requires": "warn",
// Import rules
"import/consistent-type-specifier-style": "warn",
"import/first": "error",
"import/no-amd": "error",
"import/no-default-export": "warn",
@@ -111,7 +112,7 @@
"import/no-duplicates": [
"warn",
{
"prefer-inline": true
"prefer-inline": false
}
],
@@ -119,7 +120,7 @@
"react/exhaustive-deps": [
"warn",
{
"additionalHooks": "(useQuery|useEffectAfterMount)"
"additionalHooks": "(^useQuery$|^useEffectAfterMount$)"
}
],
"react/jsx-curly-brace-presence": "warn",
@@ -333,6 +334,10 @@
"importNames": ["colors"],
"message": "Please use themes instead of colors"
},
{
"group": ["**/style/themes/*"],
"message": "Please do not import theme files directly"
},
{
"group": ["@actual-app/web/**/*"],
"message": "Please do not import `@actual-app/web` in `loot-core`"
@@ -385,6 +390,12 @@
"typescript-paths/absolute-import": ["error", { "enableAlias": false }]
}
},
{
"files": ["packages/desktop-client/src/style/themes/*"],
"rules": {
"eslint/no-restricted-imports": "off"
}
},
// TODO: enable these
{
"files": [

View File

@@ -1,7 +1,7 @@
import * as fs from 'fs/promises';
import * as path from 'path';
import { type RuleEntity } from 'loot-core/types/models';
import type { RuleEntity } from 'loot-core/types/models';
import * as api from './index';

View File

@@ -26,6 +26,7 @@ const config: StorybookConfig = {
core: {
disableTelemetry: true,
},
staticDirs: ['./public'],
async viteFinal(config) {
const { mergeConfig } = await import('vite');

View File

@@ -0,0 +1,9 @@
# /assets folder contain processed assets with a file hash
# They are safe for immutable caching, as filename change when content change
/assets/*
Cache-Control: public
Cache-Control: max-age=365000000
Cache-Control: immutable

View File

@@ -0,0 +1,139 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { AlignedText } from './AlignedText';
const meta = {
title: 'AlignedText',
component: AlignedText,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof AlignedText>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
left: 'Label',
right: 'Value',
style: { width: 300, display: 'flex' },
},
parameters: {
docs: {
description: {
story:
'AlignedText displays two pieces of content aligned on opposite sides.',
},
},
},
};
export const TruncateLeft: Story = {
args: {
left: 'This is a very long label that should be truncated on the left side',
right: '$100.00',
truncate: 'left',
style: { width: 250, display: 'flex' },
},
parameters: {
docs: {
description: {
story:
'When `truncate="left"`, the left content is truncated with ellipsis.',
},
},
},
};
export const TruncateRight: Story = {
args: {
left: 'Short Label',
right:
'This is a very long value that should be truncated on the right side',
truncate: 'right',
style: { width: 250, display: 'flex' },
},
parameters: {
docs: {
description: {
story:
'When `truncate="right"`, the right content is truncated with ellipsis.',
},
},
},
};
export const FinancialAmount: Story = {
args: {
left: 'Groceries',
right: '$1,234.56',
style: { width: 300, display: 'flex' },
rightStyle: { fontWeight: 'bold' },
},
parameters: {
docs: {
description: {
story:
'Example showing AlignedText used for displaying financial data.',
},
},
},
};
export const WithCustomStyles: Story = {
args: {
left: 'Category',
right: 'Amount',
style: {
width: 300,
padding: 10,
backgroundColor: '#f5f5f5',
borderRadius: 4,
display: 'flex',
},
leftStyle: { color: '#666', fontStyle: 'italic' },
rightStyle: { color: '#333', fontWeight: 'bold' },
},
};
export const MultipleRows: Story = {
args: {
left: 'Income',
right: '$5,000.00',
},
render: () => (
<div
style={{ width: 300, display: 'flex', flexDirection: 'column', gap: 8 }}
>
<AlignedText
left="Income"
right="$5,000.00"
rightStyle={{ color: 'green' }}
style={{ display: 'flex' }}
/>
<AlignedText
left="Expenses"
right="-$3,200.00"
rightStyle={{ color: 'red' }}
style={{ display: 'flex' }}
/>
<AlignedText
left="Balance"
right="$1,800.00"
style={{ borderTop: '1px solid #ccc', paddingTop: 8, display: 'flex' }}
rightStyle={{ fontWeight: 'bold' }}
/>
</div>
),
parameters: {
docs: {
description: {
story:
'Multiple AlignedText components stacked to create a summary view.',
},
},
},
};

View File

@@ -1,4 +1,4 @@
import { type ComponentProps, type CSSProperties, type ReactNode } from 'react';
import type { ComponentProps, CSSProperties, ReactNode } from 'react';
import { Block } from './Block';
import { View } from './View';

View File

@@ -0,0 +1,111 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Block } from './Block';
import { theme } from './theme';
const meta = {
title: 'Block',
component: Block,
parameters: {
layout: 'centered',
},
} satisfies Meta<typeof Block>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
children: 'This is a Block component',
},
parameters: {
docs: {
description: {
story:
'Block is a basic div wrapper that accepts Emotion CSS styles via the `style` prop.',
},
},
},
tags: ['autodocs'],
};
export const WithStyles: Story = {
args: {
children: 'Styled Block',
style: {
padding: 20,
backgroundColor: theme.cardBackground,
borderRadius: 8,
border: `1px solid ${theme.cardBorder}`,
color: theme.pageText,
},
},
};
export const WithFlexLayout: Story = {
render: () => (
<Block
style={{
display: 'flex',
gap: 10,
padding: 15,
borderRadius: 4,
color: theme.pageText,
}}
>
<Block
style={{
padding: 10,
backgroundColor: theme.cardBackground,
borderRadius: 4,
border: `1px solid ${theme.cardBorder}`,
}}
>
Item 1
</Block>
<Block
style={{
padding: 10,
backgroundColor: theme.cardBackground,
borderRadius: 4,
border: `1px solid ${theme.cardBorder}`,
}}
>
Item 2
</Block>
<Block
style={{
padding: 10,
backgroundColor: theme.cardBackground,
borderRadius: 4,
border: `1px solid ${theme.cardBorder}`,
}}
>
Item 3
</Block>
</Block>
),
parameters: {
docs: {
description: {
story: 'Block components can be nested and styled with flexbox.',
},
},
},
};
export const AsContainer: Story = {
args: {
children: 'Container Block',
style: {
width: 300,
padding: 25,
textAlign: 'center',
backgroundColor: theme.cardBackground,
border: `2px dashed ${theme.cardBorder}`,
borderRadius: 8,
color: theme.pageText,
},
},
};

View File

@@ -1,8 +1,8 @@
import { type HTMLProps, type Ref } from 'react';
import type { HTMLProps, Ref } from 'react';
import { css, cx } from '@emotion/css';
import { type CSSProperties } from './styles';
import type { CSSProperties } from './styles';
type BlockProps = HTMLProps<HTMLDivElement> & {
innerRef?: Ref<HTMLDivElement>;

View File

@@ -20,7 +20,6 @@ export default meta;
type Story = StoryObj<typeof meta>;
// More on writing stories with args: https://storybook.js.org/docs/writing-stories/args
export const Primary: Story = {
args: {
variant: 'primary',

View File

@@ -1,10 +1,5 @@
import React, {
forwardRef,
useMemo,
type ComponentPropsWithoutRef,
type CSSProperties,
type ReactNode,
} from 'react';
import React, { forwardRef, useMemo } from 'react';
import type { ComponentPropsWithoutRef, CSSProperties, ReactNode } from 'react';
import { Button as ReactAriaButton } from 'react-aria-components';
import { css, cx } from '@emotion/css';

View File

@@ -0,0 +1,82 @@
import { styles } from '@actual-app/components/styles';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { Card } from './Card';
import { Paragraph } from './Paragraph';
import { theme } from './theme';
const meta = {
title: 'Card',
component: Card,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof Card>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
children: 'Card content goes here',
style: {
padding: 20,
width: 300,
color: theme.pageText,
},
},
parameters: {
docs: {
description: {
story: `
Default Card component uses the following theme CSS variables:
- \`--color-cardBackground\`
- \`--color-cardBorder\`
`,
},
},
},
};
export const WithCustomContent: Story = {
args: {
style: {
padding: 20,
width: 300,
color: theme.pageText,
},
},
render: args => (
<Card {...args}>
<h3 style={{ ...styles.largeText }}>Card Title</h3>
<Paragraph style={{ margin: 0 }}>
This is a card with more complex content including a title and
paragraph.
</Paragraph>
</Card>
),
};
export const Narrow: Story = {
args: {
children: 'Narrow card',
style: {
padding: 15,
width: 150,
color: theme.pageText,
},
},
};
export const Wide: Story = {
args: {
children: 'Wide card with more content space',
style: {
padding: 25,
width: 500,
color: theme.pageText,
},
},
};

View File

@@ -1,4 +1,5 @@
import { forwardRef, type ComponentProps } from 'react';
import { forwardRef } from 'react';
import type { ComponentProps } from 'react';
import { theme } from './theme';
import { View } from './View';

View File

@@ -0,0 +1,108 @@
import { useState } from 'react';
import { ColorSwatch } from 'react-aria-components';
import type { Meta, StoryObj } from '@storybook/react-vite';
import { fn } from 'storybook/test';
import { Button } from './Button';
import { ColorPicker } from './ColorPicker';
const meta = {
title: 'ColorPicker',
component: ColorPicker,
parameters: {
layout: 'centered',
},
args: {
onChange: fn(),
children: <Button>Pick a color</Button>,
},
tags: ['autodocs'],
} satisfies Meta<typeof ColorPicker>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
defaultValue: '#690CB0',
children: <Button>Pick a color</Button>,
},
};
export const WithColorSwatch: Story = {
args: {
defaultValue: '#1976D2',
children: (
<Button style={{ padding: 4 }}>
<ColorSwatch
style={{
width: 24,
height: 24,
borderRadius: 4,
boxShadow: 'inset 0 0 0 1px rgba(0, 0, 0, 0.1)',
}}
/>
</Button>
),
},
};
export const CustomColorSet: Story = {
args: {
defaultValue: '#FF0000',
columns: 4,
colorset: [
'#FF0000',
'#00FF00',
'#0000FF',
'#FFFF00',
'#FF00FF',
'#00FFFF',
'#FFA500',
'#800080',
],
children: <Button>Custom Colors</Button>,
},
parameters: {
docs: {
description: {
story:
'ColorPicker with a custom color set and different column layout.',
},
},
},
};
export const Controlled: Story = {
args: {
children: <Button>Pick a color</Button>,
},
render: () => {
const [color, setColor] = useState('#388E3C');
return (
<div style={{ display: 'flex', flexDirection: 'column', gap: 10 }}>
<ColorPicker value={color} onChange={c => setColor(c.toString('hex'))}>
<Button style={{ padding: 4 }}>
<ColorSwatch
style={{
width: 24,
height: 24,
borderRadius: 4,
}}
/>
</Button>
</ColorPicker>
<span>Selected: {color}</span>
</div>
);
},
parameters: {
docs: {
description: {
story: 'Controlled ColorPicker with external state management.',
},
},
},
};

View File

@@ -1,4 +1,4 @@
import { type ChangeEvent, type ReactNode } from 'react';
import type { ChangeEvent, ReactNode } from 'react';
import {
ColorPicker as AriaColorPicker,
ColorSwatch as AriaColorSwatch,
@@ -8,8 +8,10 @@ import {
Dialog,
DialogTrigger,
parseColor,
type ColorPickerProps as AriaColorPickerProps,
type ColorSwatchProps,
} from 'react-aria-components';
import type {
ColorPickerProps as AriaColorPickerProps,
ColorSwatchProps,
} from 'react-aria-components';
import { css } from '@emotion/css';

View File

@@ -0,0 +1,90 @@
import type { Meta, StoryObj } from '@storybook/react-vite';
import { FormError } from './FormError';
import { Input } from './Input';
import { View } from './View';
const meta = {
title: 'FormError',
component: FormError,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
} satisfies Meta<typeof FormError>;
export default meta;
type Story = StoryObj<typeof meta>;
export const Default: Story = {
args: {
children: 'This field is required',
},
parameters: {
docs: {
description: {
story: 'FormError displays validation error messages in red text.',
},
},
},
};
export const InFormContext: Story = {
render: () => (
<View
style={{ display: 'flex', flexDirection: 'column', gap: 5, width: 250 }}
>
<Input placeholder="Email address" style={{ borderColor: 'red' }} />
<FormError>Please enter a valid email address</FormError>
</View>
),
parameters: {
docs: {
description: {
story:
'FormError displayed below an input field with validation error.',
},
},
},
};
export const MultipleErrors: Story = {
render: () => (
<View style={{ display: 'flex', flexDirection: 'column', gap: 3 }}>
<FormError>Password must be at least 8 characters</FormError>
<FormError>Password must contain a number</FormError>
<FormError>Password must contain a special character</FormError>
</View>
),
parameters: {
docs: {
description: {
story:
'Multiple FormError components for displaying several validation errors.',
},
},
},
};
export const CustomStyle: Story = {
args: {
children: 'Custom styled error message',
style: {
fontSize: 14,
fontWeight: 'bold',
padding: 10,
backgroundColor: '#ffebee',
borderRadius: 4,
border: '1px solid red',
},
},
};
export const LongErrorMessage: Story = {
args: {
children:
'This is a longer error message that explains the validation issue in more detail. Please correct the input and try again.',
style: { maxWidth: 300 },
},
};

View File

@@ -1,4 +1,4 @@
import { type CSSProperties, type ReactNode } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import { View } from './View';

View File

@@ -4,10 +4,8 @@ import {
isValidElement,
useEffect,
useRef,
type ReactElement,
type Ref,
type RefObject,
} from 'react';
import type { ReactElement, Ref, RefObject } from 'react';
type InitialFocusProps<T extends HTMLElement> = {
/**

View File

@@ -1,5 +1,6 @@
import * as React from 'react';
import { forwardRef, type Ref } from 'react';
import { forwardRef } from 'react';
import type { Ref } from 'react';
import { render } from '@testing-library/react';

View File

@@ -1,8 +1,8 @@
import { type ReactNode } from 'react';
import type { ReactNode } from 'react';
import { css } from '@emotion/css';
import { type CSSProperties } from './styles';
import type { CSSProperties } from './styles';
type InlineFieldProps = {
label: ReactNode;

View File

@@ -1,8 +1,9 @@
import React, {
type ChangeEvent,
type ComponentPropsWithRef,
type FocusEvent,
type KeyboardEvent,
import React from 'react';
import type {
ChangeEvent,
ComponentPropsWithRef,
FocusEvent,
KeyboardEvent,
} from 'react';
import { Input as ReactAriaInput } from 'react-aria-components';

View File

@@ -1,4 +1,5 @@
import { forwardRef, type CSSProperties, type ReactNode } from 'react';
import { forwardRef } from 'react';
import type { CSSProperties, ReactNode } from 'react';
import { styles } from './styles';
import { Text } from './Text';

View File

@@ -1,13 +1,11 @@
import {
useEffect,
useRef,
useState,
type ComponentProps,
type ComponentType,
type CSSProperties,
type KeyboardEvent,
type ReactNode,
type SVGProps,
import { useEffect, useRef, useState } from 'react';
import type {
ComponentProps,
ComponentType,
CSSProperties,
KeyboardEvent,
ReactNode,
SVGProps,
} from 'react';
import { Button } from './Button';

View File

@@ -1,8 +1,8 @@
import { type HTMLProps } from 'react';
import type { HTMLProps } from 'react';
import { css } from '@emotion/css';
import { type CSSProperties } from './styles';
import type { CSSProperties } from './styles';
type ParagraphProps = HTMLProps<HTMLDivElement> & {
style?: CSSProperties;

View File

@@ -1,4 +1,5 @@
import { useCallback, useEffect, useRef, type ComponentProps } from 'react';
import { useCallback, useEffect, useRef } from 'react';
import type { ComponentProps } from 'react';
import { Popover as ReactAriaPopover } from 'react-aria-components';
import { css } from '@emotion/css';

View File

@@ -1,4 +1,5 @@
import { useRef, useState, type CSSProperties } from 'react';
import { useRef, useState } from 'react';
import type { CSSProperties } from 'react';
import { Button } from './Button';
import { SvgExpandArrow } from './icons/v0';

View File

@@ -1,6 +1,7 @@
import React, { type ReactNode } from 'react';
import React from 'react';
import type { ReactNode } from 'react';
import { type CSSProperties } from './styles';
import type { CSSProperties } from './styles';
import { View } from './View';
type SpaceBetweenProps = {

View File

@@ -1,13 +1,9 @@
import React, {
forwardRef,
type HTMLProps,
type ReactNode,
type Ref,
} from 'react';
import React, { forwardRef } from 'react';
import type { HTMLProps, ReactNode, Ref } from 'react';
import { css, cx } from '@emotion/css';
import { type CSSProperties } from './styles';
import type { CSSProperties } from './styles';
type TextProps = HTMLProps<HTMLSpanElement> & {
innerRef?: Ref<HTMLSpanElement>;

View File

@@ -1,4 +1,4 @@
import { type ComponentProps } from 'react';
import type { ComponentProps } from 'react';
import { Text } from './Text';

View File

@@ -1,4 +1,5 @@
import React, { type CSSProperties } from 'react';
import React from 'react';
import type { CSSProperties } from 'react';
import { css } from '@emotion/css';

View File

@@ -1,11 +1,5 @@
import React, {
useCallback,
useEffect,
useRef,
useState,
type ComponentProps,
type ReactNode,
} from 'react';
import React, { useCallback, useEffect, useRef, useState } from 'react';
import type { ComponentProps, ReactNode } from 'react';
import { Tooltip as AriaTooltip, TooltipTrigger } from 'react-aria-components';
import { styles } from './styles';

View File

@@ -1,8 +1,9 @@
import React, { forwardRef, type HTMLProps, type Ref } from 'react';
import React, { forwardRef } from 'react';
import type { HTMLProps, Ref } from 'react';
import { css, cx } from '@emotion/css';
import { type CSSProperties } from './styles';
import type { CSSProperties } from './styles';
type ViewProps = HTMLProps<HTMLDivElement> & {
className?: string;

View File

@@ -1,4 +1,5 @@
import React, { type SVGProps } from 'react';
import React from 'react';
import type { SVGProps } from 'react';
import { css, keyframes } from '@emotion/css';

View File

@@ -1,4 +1,5 @@
import React, { useState, type SVGProps } from 'react';
import React, { useState } from 'react';
import type { SVGProps } from 'react';
export const SvgLoading = (props: SVGProps<SVGSVGElement>) => {
const { color = 'currentColor' } = props;

View File

@@ -1,4 +1,4 @@
import { type Config } from '@svgr/core';
import type { Config } from '@svgr/core';
const tmpl: Config['template'] = (
{ imports, interfaces, componentName, props, jsx },

View File

@@ -12,8 +12,7 @@ const shadowLarge = {
boxShadow: '0 15px 30px 0 rgba(0,0,0,0.11), 0 5px 15px 0 rgba(0,0,0,0.08)',
};
// oxlint-disable-next-line typescript/no-explicit-any
export const styles: Record<string, any> = {
export const styles: CSSProperties = {
incomeHeaderHeight: 70,
cardShadow: '0 1px 3px rgba(0,0,0,0.12), 0 1px 2px rgba(0,0,0,0.24)',
monthRightPadding: 5,

View File

@@ -7,7 +7,7 @@
// * Need to check to make sure if account exists when handling
// * transaction changes in syncing
import { type Timestamp } from './timestamp';
import type { Timestamp } from './timestamp';
/**
* Represents a node within a trinary radix trie.

View File

@@ -1,7 +1,7 @@
import murmurhash from 'murmurhash';
import { v4 as uuidv4 } from 'uuid';
import { type TrieNode } from './merkle';
import type { TrieNode } from './merkle';
/**
* Hybrid Unique Logical Clock (HULC) timestamp generator

View File

@@ -1,4 +1,4 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';

View File

@@ -1,9 +1,9 @@
import { join } from 'path';
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { type AccountPage } from './page-models/account-page';
import type { AccountPage } from './page-models/account-page';
import { ConfigurationPage } from './page-models/configuration-page';
import { Navigation } from './page-models/navigation';

View File

@@ -1,8 +1,8 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';
import { type MobileBankSyncPage } from './page-models/mobile-bank-sync-page';
import type { MobileBankSyncPage } from './page-models/mobile-bank-sync-page';
import { MobileNavigation } from './page-models/mobile-navigation';
test.describe('Mobile Bank Sync', () => {

View File

@@ -1,7 +1,7 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { type BankSyncPage } from './page-models/bank-sync-page';
import type { BankSyncPage } from './page-models/bank-sync-page';
import { ConfigurationPage } from './page-models/configuration-page';
import { Navigation } from './page-models/navigation';

View File

@@ -1,11 +1,11 @@
import { type Page } from '@playwright/test';
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 type { MobileBudgetPage } from './page-models/mobile-budget-page';
import { MobileNavigation } from './page-models/mobile-navigation';
const copyLastMonthBudget = async (

View File

@@ -1,7 +1,7 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { type BudgetPage } from './page-models/budget-page';
import type { BudgetPage } from './page-models/budget-page';
import { ConfigurationPage } from './page-models/configuration-page';
test.describe('Budget', () => {

View File

@@ -1,4 +1,4 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';

View File

@@ -108,6 +108,12 @@
"name": "Work",
"transfer_account_id": null,
"deleted": false
},
{
"id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"name": "Schedule Payee",
"transfer_account_id": null,
"deleted": false
}
],
"payee_locations": [],
@@ -167,6 +173,12 @@
"hidden": false,
"deleted": false
},
{
"id": "6d28a243-3670-4c96-8334-216e31ea9468",
"name": "Category Group",
"hidden": false,
"deleted": false
},
{
"id": "F5751985-3290-41E7-B17F-6DBE979F315D",
"name": "Bills",
@@ -726,6 +738,30 @@
"goal_overall_funded": null,
"goal_overall_left": null,
"deleted": false
},
{
"id": "419ae801-27c8-424b-8f39-9611825803db",
"category_group_id": "6d28a243-3670-4c96-8334-216e31ea9468",
"name": "Category",
"hidden": false,
"original_category_group_id": null,
"note": null,
"budgeted": 0,
"activity": 0,
"balance": 0,
"goal_type": null,
"goal_day": null,
"goal_cadence": null,
"goal_cadence_frequency": null,
"goal_creation_month": null,
"goal_target": 0,
"goal_target_month": null,
"goal_percentage_complete": null,
"goal_months_to_budget": null,
"goal_under_funded": null,
"goal_overall_funded": null,
"goal_overall_left": null,
"deleted": false
}
],
"months": [
@@ -1674,7 +1710,8 @@
"memo": "sending to savings",
"cleared": "cleared",
"approved": true,
"flag_color": null,
"flag_color": "blue",
"flag_name": "Savings",
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "8d3017e0-2aa6-4fe2-b011-c53c9f147eb6",
"category_id": null,
@@ -1694,7 +1731,8 @@
"memo": null,
"cleared": "cleared",
"approved": true,
"flag_color": null,
"flag_color": "red",
"flag_name": "One-off",
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "2a20470a-634f-4efa-a7f6-f1c0b0bdda41",
"category_id": "225be370-37da-4cf8-8b6b-4c6d61a0dd95",
@@ -1714,7 +1752,8 @@
"memo": "getting paid",
"cleared": "reconciled",
"approved": true,
"flag_color": null,
"flag_color": "green",
"flag_name": "JOB",
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "c843e030-5a77-4dc5-9b93-f8acc64b74f8",
"category_id": "36120d44-6c61-4402-985a-891a8d267858",
@@ -1734,7 +1773,8 @@
"memo": null,
"cleared": "cleared",
"approved": true,
"flag_color": null,
"flag_color": "purple",
"flag_name": "Savings",
"account_id": "125f339b-2a63-481e-84c0-f04d898905d2",
"payee_id": "c843e030-5a77-4dc5-9b93-f8acc64b74f8",
"category_id": "36120d44-6c61-4402-985a-891a8d267858",
@@ -1870,8 +1910,245 @@
"deleted": false
}
],
"scheduled_transactions": [],
"scheduled_subtransactions": []
"scheduled_transactions": [
{
"id": "1db8beb8-ef31-4a07-b9a5-0648b1e3071a",
"date_first": "2025-08-05",
"date_next": "2025-08-05",
"frequency": "every4Weeks",
"amount": -100000,
"memo": "Scheduled - repeated every four weeks",
"flag_color": null,
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "2cb5676a-9b6e-4fff-aaf5-7ace218bb918",
"date_first": "2025-08-03",
"date_next": "2025-08-17",
"frequency": "everyOtherWeek",
"amount": -100000,
"memo": "Scheduled - repeated every other week",
"flag_color": null,
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "34157157-8ad5-46b4-aa67-36f2035478ce",
"date_first": "2025-08-05",
"date_next": "2025-08-05",
"frequency": "everyOtherYear",
"amount": -100000,
"memo": "Scheduled - repeated every other year",
"flag_color": null,
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "64a5e1ee-ac5f-4fd7-b955-818ed97c0886",
"date_first": "2025-08-05",
"date_next": "2025-08-05",
"frequency": "every4Months",
"amount": -100000,
"memo": "Scheduled - repeated every four months",
"flag_color": null,
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "6a77929b-a54f-4401-9fc0-e3be672fe946",
"date_first": "2025-08-03",
"date_next": "2025-08-18",
"frequency": "twiceAMonth",
"amount": -100000,
"memo": "Scheduled - repeated twice a month",
"flag_color": null,
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "700739ce-35a2-4fb5-9522-70d152b73a81",
"date_first": "2025-08-05",
"date_next": "2025-08-05",
"frequency": "monthly",
"amount": -100000,
"memo": "Scheduled - repeated monthly",
"flag_color": "purple",
"flag_name": "Savings",
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "8afb5da0-e189-46bc-b41a-c3603588a950",
"date_first": "2025-08-03",
"date_next": "2025-08-10",
"frequency": "weekly",
"amount": -100000,
"memo": "Scheduled - repeated weekly",
"flag_color": "blue",
"flag_name": "Savings",
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "906ca596-9f93-4c73-aaf2-9ca1f8db8a86",
"date_first": "2025-08-04",
"date_next": "2025-08-04",
"frequency": "never",
"amount": -100000,
"memo": "Scheduled - not repeated",
"flag_color": "red",
"flag_name": "One-off",
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "a06f9cef-ec00-4561-9546-22513e0e11bb",
"date_first": "2025-08-05",
"date_next": "2025-08-05",
"frequency": "twiceAYear",
"amount": -100000,
"memo": "Scheduled - repeated twice a year",
"flag_color": null,
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "d5bf68a6-5026-47a8-a40f-7ecd2f9ba4da",
"date_first": "2025-08-05",
"date_next": "2025-08-05",
"frequency": "yearly",
"amount": -100000,
"memo": "Scheduled - repeated yearly",
"flag_color": null,
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "e842e6b8-096f-4152-8acd-9566cbee293b",
"date_first": "2025-08-05",
"date_next": "2025-08-05",
"frequency": "everyOtherMonth",
"amount": -100000,
"memo": "Scheduled - repeated every other month",
"flag_color": null,
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "ec72c5af-00c9-4ea0-aaa8-6863471beea8",
"date_first": "2025-08-05",
"date_next": "2025-08-05",
"frequency": "every3Months",
"amount": -100000,
"memo": "Scheduled - repeated every three months",
"flag_color": null,
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "f80e4d81-b640-4cac-a50c-39e6400e23a6",
"date_first": "2025-08-03",
"date_next": "2025-08-04",
"frequency": "daily",
"amount": -100000,
"memo": "Scheduled - repeated daily",
"flag_color": null,
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "4b8f0a2e-9c7a-4f8e-9dcb-6a20b3d54f0e",
"date_first": "2025-08-06",
"date_next": "2025-09-06",
"frequency": "monthly",
"amount": -100000,
"memo": "Scheduled - split categories monthly",
"flag_color": "yellow",
"flag_name": "Split",
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": null,
"transfer_account_id": null,
"deleted": false
},
{
"id": "6e9dcaa6-0e96-4b08-90f7-2f8f12b7e6b6",
"date_first": "2025-08-07",
"date_next": "2025-08-21",
"frequency": "everyOtherWeek",
"amount": -50000,
"memo": "Scheduled - transfer to Saving",
"flag_color": "orange",
"flag_name": "Transfer",
"account_id": "bc1d862f-bab0-41c3-bd1e-6cee8c688e32",
"payee_id": "8d3017e0-2aa6-4fe2-b011-c53c9f147eb6",
"category_id": null,
"transfer_account_id": "125f339b-2a63-481e-84c0-f04d898905d2",
"deleted": false
}
],
"scheduled_subtransactions": [
{
"id": "2b5c23f6-109c-4f0f-8ee5-8b76407fc99f",
"scheduled_transaction_id": "4b8f0a2e-9c7a-4f8e-9dcb-6a20b3d54f0e",
"amount": -60000,
"memo": "split part a",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "419ae801-27c8-424b-8f39-9611825803db",
"transfer_account_id": null,
"deleted": false
},
{
"id": "24a4b78f-5a83-4891-8205-b8bb3f9ddf34",
"scheduled_transaction_id": "4b8f0a2e-9c7a-4f8e-9dcb-6a20b3d54f0e",
"amount": -40000,
"memo": "split part b",
"payee_id": "0f0899e3-242f-42e6-aae9-a751060d878e",
"category_id": "36120d44-6c61-4402-985a-891a8d267858",
"transfer_account_id": null,
"deleted": false
}
]
},
"server_knowledge": 58
}

View File

@@ -1,4 +1,5 @@
import { expect as baseExpect, type Locator } from '@playwright/test';
import { expect as baseExpect } from '@playwright/test';
import type { Locator } from '@playwright/test';
export { test } from '@playwright/test';

View File

@@ -1,4 +1,4 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';

View File

@@ -1,6 +1,6 @@
import path from 'path';
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { AccountPage } from './page-models/account-page';
@@ -64,6 +64,32 @@ test.describe('Onboarding', () => {
await navigation.goToAccountPage('Saving');
await expect(accountPage.accountBalance).toHaveText('250.00');
await navigation.goToSchedulesPage();
const scheduleRows = page.getByTestId('table').getByTestId('row');
const scheduleNames = [
'Scheduled - repeated every four weeks',
'Scheduled - repeated every other week',
'Scheduled - repeated every other year',
'Scheduled - repeated every four months',
'Scheduled - repeated twice a month',
'Scheduled - repeated monthly',
'Scheduled - repeated weekly',
'Scheduled - not repeated',
'Scheduled - repeated twice a year',
'Scheduled - repeated yearly',
'Scheduled - repeated every other month',
'Scheduled - repeated every three months',
'Scheduled - repeated daily',
'Scheduled - split categories monthly',
'Scheduled - transfer to Saving',
];
for (const scheduleName of scheduleNames) {
await expect(scheduleRows.filter({ hasText: scheduleName })).toHaveCount(
1,
);
}
});
test('creates a new budget file by importing Actual budget', async () => {

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { CloseAccountModal } from './close-account-modal';

View File

@@ -1,4 +1,4 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
export class BankSyncPage {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { AccountPage } from './account-page';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class CloseAccountModal {
readonly locator: Locator;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { AccountPage } from './account-page';
import { BudgetPage } from './budget-page';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class CustomReportPage {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
type ConditionsEntry = {
field: string;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { MobileTransactionEntryPage } from './mobile-transaction-entry-page';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { MobileAccountPage } from './mobile-account-page';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class BalanceMenuModal {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class MobileBankSyncPage {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class BudgetMenuModal {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { MobileAccountPage } from './mobile-account-page';
import { BalanceMenuModal } from './mobile-balance-menu-modal';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { EditNotesModal } from './mobile-edit-notes-modal';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class EditNotesModal {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class EnvelopeBudgetSummaryModal {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { MobileAccountPage } from './mobile-account-page';
import { MobileAccountsPage } from './mobile-accounts-page';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class MobilePayeesPage {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class MobileReportsPage {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class MobileRulesPage {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class MobileSchedulesPage {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class TrackingBudgetSummaryModal {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { MobileAccountPage } from './mobile-account-page';

View File

@@ -1,4 +1,4 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { AccountPage } from './account-page';
import { BankSyncPage } from './bank-sync-page';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class PayeesPage {
readonly page: Page;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { CustomReportPage } from './custom-report-page';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { EditRuleModal } from './edit-rule-modal';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
type ScheduleEntry = {
scheduleName?: string;

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
import { ScheduleEditModal } from './schedule-edit-modal';

View File

@@ -1,4 +1,4 @@
import { type Locator, type Page } from '@playwright/test';
import type { Locator, Page } from '@playwright/test';
export class SettingsPage {
readonly page: Page;

View File

@@ -1,9 +1,9 @@
import { type Page } from '@playwright/test';
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';
import { type MobilePayeesPage } from './page-models/mobile-payees-page';
import type { MobilePayeesPage } from './page-models/mobile-payees-page';
test.describe('Mobile Payees', () => {
let page: Page;

View File

@@ -1,9 +1,9 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';
import { Navigation } from './page-models/navigation';
import { type PayeesPage } from './page-models/payees-page';
import type { PayeesPage } from './page-models/payees-page';
test.describe('Payees', () => {
let page: Page;

View File

@@ -1,10 +1,10 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';
import { type CustomReportPage } from './page-models/custom-report-page';
import type { CustomReportPage } from './page-models/custom-report-page';
import { Navigation } from './page-models/navigation';
import { type ReportsPage } from './page-models/reports-page';
import type { ReportsPage } from './page-models/reports-page';
test.describe.parallel('Reports', () => {
let page: Page;

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 83 KiB

After

Width:  |  Height:  |  Size: 83 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 82 KiB

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

After

Width:  |  Height:  |  Size: 92 KiB

View File

@@ -1,9 +1,9 @@
import { type Page } from '@playwright/test';
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';
import { type MobileRulesPage } from './page-models/mobile-rules-page';
import type { MobileRulesPage } from './page-models/mobile-rules-page';
test.describe('Mobile Rules', () => {
let page: Page;

View File

@@ -1,9 +1,9 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';
import { Navigation } from './page-models/navigation';
import { type RulesPage } from './page-models/rules-page';
import type { RulesPage } from './page-models/rules-page';
test.describe('Rules', () => {
let page: Page;

View File

@@ -1,9 +1,9 @@
import { type Page } from '@playwright/test';
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';
import { type MobileSchedulesPage } from './page-models/mobile-schedules-page';
import type { MobileSchedulesPage } from './page-models/mobile-schedules-page';
test.describe('Mobile Schedules', () => {
let page: Page;

View File

@@ -1,9 +1,9 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';
import { Navigation } from './page-models/navigation';
import { type SchedulesPage } from './page-models/schedules-page';
import type { SchedulesPage } from './page-models/schedules-page';
test.describe('Schedules', () => {
let page: Page;

View File

@@ -1,4 +1,4 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';

View File

@@ -1,9 +1,9 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';
import { Navigation } from './page-models/navigation';
import { type SettingsPage } from './page-models/settings-page';
import type { SettingsPage } from './page-models/settings-page';
test.describe('Settings', () => {
let page: Page;

View File

@@ -1,4 +1,4 @@
import { type Page } from '@playwright/test';
import type { Page } from '@playwright/test';
import { expect, test } from './fixtures';
import { ConfigurationPage } from './page-models/configuration-page';

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