mirror of
https://github.com/actualbudget/actual.git
synced 2026-05-11 17:48:17 -05:00
Compare commits
5 Commits
fix-react-
...
feat/auto-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d0c00252e3 | ||
|
|
f53d3c662d | ||
|
|
a8a3355354 | ||
|
|
9d51ef7ce8 | ||
|
|
e6d63f620e |
@@ -28,7 +28,7 @@ try {
|
||||
}
|
||||
|
||||
const data = JSON.stringify({
|
||||
model: 'gpt-4o-mini',
|
||||
model: 'gpt-4.1-mini',
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
@@ -71,30 +71,26 @@ try {
|
||||
const rawContent = response.choices[0].message.content.trim();
|
||||
console.log('Raw content from OpenAI:', rawContent);
|
||||
|
||||
let category;
|
||||
try {
|
||||
category = JSON.parse(rawContent);
|
||||
console.log('Parsed category:', category);
|
||||
} catch (parseError) {
|
||||
console.log(
|
||||
'JSON parse error, using raw content:',
|
||||
parseError.message,
|
||||
);
|
||||
category = rawContent;
|
||||
}
|
||||
//CHANGED HOW IT READS THE CATEGORY TO AVOID ERRORS WHEN LLM DOESNT ANSWER A JSON
|
||||
|
||||
// Validate the category response
|
||||
const validCategories = [
|
||||
'Features',
|
||||
'Bugfixes',
|
||||
'Enhancements',
|
||||
'Maintenance',
|
||||
];
|
||||
if (validCategories.includes(category)) {
|
||||
const lowerContent = rawContent.toLowerCase();
|
||||
const category = validCategories.find(cat =>
|
||||
lowerContent.includes(cat.toLowerCase()),
|
||||
);
|
||||
if (category) {
|
||||
console.log('OpenAI categorized as:', category);
|
||||
setOutput('result', category);
|
||||
} else {
|
||||
console.log('Invalid category from OpenAI:', category);
|
||||
console.log(
|
||||
'No valid category found in OpenAI response:',
|
||||
rawContent,
|
||||
);
|
||||
console.log('Valid categories are:', validCategories);
|
||||
setOutput('result', 'null');
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ try {
|
||||
console.log('CodeRabbit comment body:', commentBody);
|
||||
|
||||
const data = JSON.stringify({
|
||||
model: 'gpt-4o-mini',
|
||||
model: 'gpt-4.1-mini',
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
|
||||
159
.github/scripts/count-points.mjs
vendored
159
.github/scripts/count-points.mjs
vendored
@@ -8,13 +8,6 @@ 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 },
|
||||
@@ -38,116 +31,6 @@ 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.
|
||||
@@ -206,7 +89,6 @@ 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,
|
||||
@@ -320,28 +202,6 @@ 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 (
|
||||
@@ -433,7 +293,7 @@ async function countContributorPoints() {
|
||||
// Print all statistics
|
||||
printStats(
|
||||
'Code Review Statistics',
|
||||
stats => stats.codeReviews.reduce((sum, r) => sum + r.points, 0),
|
||||
stats => stats.codeReviews.length,
|
||||
(user, count) =>
|
||||
`${user}: ${count} (PRs: ${stats
|
||||
.get(user)
|
||||
@@ -448,7 +308,7 @@ async function countContributorPoints() {
|
||||
|
||||
printStats(
|
||||
'Docs Review Statistics',
|
||||
stats => stats.docsReviews.reduce((sum, r) => sum + r.points, 0),
|
||||
stats => stats.docsReviews.length,
|
||||
(user, count) =>
|
||||
`${user}: ${count} (PRs: ${stats
|
||||
.get(user)
|
||||
@@ -456,27 +316,16 @@ 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 * CONFIG.POINTS_PER_ISSUE_TRIAGE_ACTION,
|
||||
stats => stats.labelRemovals.length,
|
||||
(user, count) =>
|
||||
`${user}: ${count} (Issues: ${stats.get(user).labelRemovals.join(', ')})`,
|
||||
);
|
||||
|
||||
printStats(
|
||||
'Issue Closing Statistics',
|
||||
stats =>
|
||||
stats.issueClosings.length * CONFIG.POINTS_PER_ISSUE_CLOSING_ACTION,
|
||||
stats => stats.issueClosings.length,
|
||||
(user, count) =>
|
||||
`${user}: ${count} (Issues: ${stats.get(user).issueClosings.join(', ')})`,
|
||||
);
|
||||
|
||||
15
.github/workflows/ai-generated-release-notes.yml
vendored
15
.github/workflows/ai-generated-release-notes.yml
vendored
@@ -83,11 +83,22 @@ jobs:
|
||||
PR_DETAILS: ${{ steps.pr-details.outputs.result }}
|
||||
SUMMARY_DATA: ${{ steps.generate-summary.outputs.result }}
|
||||
|
||||
# Internal bot doesn't trigger workflows on commit; switching to Actual Bot App
|
||||
- name: Generate GitHub App token
|
||||
if: steps.check-first-comment.outputs.result == 'true' && steps.check-release-notes-exists.outputs.result == 'false' && steps.generate-summary.outputs.result != 'null' && steps.determine-category.outputs.result != 'null' && steps.determine-category.outputs.result != ''
|
||||
id: app-token
|
||||
uses: actions/create-github-app-token@v2
|
||||
with:
|
||||
app-id: ${{ vars.ACTUAL_BOT_APP_ID }}
|
||||
private-key: ${{ secrets.ACTUAL_BOT_PRIVATE_KEY }}
|
||||
owner: actualbudget
|
||||
repositories: actual
|
||||
|
||||
- name: Create and commit release notes file via GitHub API
|
||||
if: steps.check-first-comment.outputs.result == 'true' && steps.check-release-notes-exists.outputs.result == 'false' && steps.generate-summary.outputs.result != 'null' && steps.determine-category.outputs.result != 'null' && steps.determine-category.outputs.result != ''
|
||||
run: node .github/actions/ai-generated-release-notes/create-release-notes-file.js
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.ACTIONS_UPDATE_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
SUMMARY_DATA: ${{ steps.generate-summary.outputs.result }}
|
||||
@@ -97,7 +108,7 @@ jobs:
|
||||
if: steps.check-first-comment.outputs.result == 'true' && steps.check-release-notes-exists.outputs.result == 'false' && steps.generate-summary.outputs.result != 'null' && steps.determine-category.outputs.result != 'null' && steps.determine-category.outputs.result != ''
|
||||
run: node .github/actions/ai-generated-release-notes/comment-on-pr.js
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
GITHUB_TOKEN: ${{ steps.app-token.outputs.token }}
|
||||
GITHUB_REPOSITORY: ${{ github.repository }}
|
||||
GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }}
|
||||
SUMMARY_DATA: ${{ steps.generate-summary.outputs.result }}
|
||||
|
||||
1
.github/workflows/electron-master.yml
vendored
1
.github/workflows/electron-master.yml
vendored
@@ -199,7 +199,6 @@ 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 }}.
|
||||
|
||||
@@ -101,7 +101,6 @@
|
||||
"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",
|
||||
@@ -112,7 +111,7 @@
|
||||
"import/no-duplicates": [
|
||||
"warn",
|
||||
{
|
||||
"prefer-inline": false
|
||||
"prefer-inline": true
|
||||
}
|
||||
],
|
||||
|
||||
@@ -390,12 +389,6 @@
|
||||
"typescript-paths/absolute-import": ["error", { "enableAlias": false }]
|
||||
}
|
||||
},
|
||||
{
|
||||
"files": ["packages/desktop-client/src/style/themes/*"],
|
||||
"rules": {
|
||||
"eslint/no-restricted-imports": "off"
|
||||
}
|
||||
},
|
||||
// TODO: enable these
|
||||
{
|
||||
"files": [
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -356,143 +356,6 @@ describe('API CRUD operations', () => {
|
||||
);
|
||||
});
|
||||
|
||||
// apis: createTag, getTags, updateTag, deleteTag
|
||||
test('Tags: successfully complete tag operations', async () => {
|
||||
// Create tags
|
||||
const tagId1 = await api.createTag({ tag: 'test-tag1', color: '#ff0000' });
|
||||
const tagId2 = await api.createTag({
|
||||
tag: 'test-tag2',
|
||||
description: 'A test tag',
|
||||
});
|
||||
|
||||
let tags = await api.getTags();
|
||||
expect(tags).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: tagId1,
|
||||
tag: 'test-tag1',
|
||||
color: '#ff0000',
|
||||
}),
|
||||
expect.objectContaining({
|
||||
id: tagId2,
|
||||
tag: 'test-tag2',
|
||||
description: 'A test tag',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
// Update tag
|
||||
await api.updateTag(tagId1, { tag: 'updated-tag', color: '#00ff00' });
|
||||
tags = await api.getTags();
|
||||
expect(tags).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: tagId1,
|
||||
tag: 'updated-tag',
|
||||
color: '#00ff00',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
// Delete tag
|
||||
await api.deleteTag(tagId2);
|
||||
tags = await api.getTags();
|
||||
expect(tags).not.toEqual(
|
||||
expect.arrayContaining([expect.objectContaining({ id: tagId2 })]),
|
||||
);
|
||||
});
|
||||
|
||||
test('Tags: create tag with minimal fields', async () => {
|
||||
const tagId = await api.createTag({ tag: 'minimal-tag' });
|
||||
const tags = await api.getTags();
|
||||
expect(tags).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: tagId,
|
||||
tag: 'minimal-tag',
|
||||
color: null,
|
||||
description: null,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('Tags: update single field only', async () => {
|
||||
const tagId = await api.createTag({ tag: 'original', color: '#ff0000' });
|
||||
|
||||
// Update only color, tag and description should remain unchanged
|
||||
await api.updateTag(tagId, { color: '#00ff00' });
|
||||
|
||||
const tags = await api.getTags();
|
||||
expect(tags).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: tagId,
|
||||
tag: 'original',
|
||||
color: '#00ff00',
|
||||
description: null,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('Tags: handle null values correctly', async () => {
|
||||
const tagId = await api.createTag({
|
||||
tag: 'with-nulls',
|
||||
color: null,
|
||||
description: null,
|
||||
});
|
||||
|
||||
const tags = await api.getTags();
|
||||
expect(tags).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: tagId,
|
||||
color: null,
|
||||
description: null,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
test('Tags: clear optional field', async () => {
|
||||
const tagId = await api.createTag({
|
||||
tag: 'clearable',
|
||||
color: '#ff0000',
|
||||
description: 'will be cleared',
|
||||
});
|
||||
|
||||
// Clear color by setting to null
|
||||
await api.updateTag(tagId, { color: null });
|
||||
|
||||
let tags = await api.getTags();
|
||||
expect(tags).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: tagId,
|
||||
tag: 'clearable',
|
||||
color: null,
|
||||
description: 'will be cleared',
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
// Clear description by setting to null
|
||||
await api.updateTag(tagId, { description: null });
|
||||
|
||||
tags = await api.getTags();
|
||||
expect(tags).toEqual(
|
||||
expect.arrayContaining([
|
||||
expect.objectContaining({
|
||||
id: tagId,
|
||||
tag: 'clearable',
|
||||
color: null,
|
||||
description: null,
|
||||
}),
|
||||
]),
|
||||
);
|
||||
});
|
||||
|
||||
// apis: getRules, getPayeeRules, createRule, updateRule, deleteRule
|
||||
test('Rules: successfully update rules', async () => {
|
||||
await api.createPayee({ name: 'test-payee' });
|
||||
|
||||
@@ -5,7 +5,6 @@ import type {
|
||||
APIFileEntity,
|
||||
APIPayeeEntity,
|
||||
APIScheduleEntity,
|
||||
APITagEntity,
|
||||
} from 'loot-core/server/api-models';
|
||||
import type { Query } from 'loot-core/shared/query';
|
||||
import type { Handlers } from 'loot-core/types/handlers';
|
||||
@@ -275,25 +274,6 @@ export function deletePayee(id: APIPayeeEntity['id']) {
|
||||
return send('api/payee-delete', { id });
|
||||
}
|
||||
|
||||
export function getTags() {
|
||||
return send('api/tags-get');
|
||||
}
|
||||
|
||||
export function createTag(tag: Omit<APITagEntity, 'id'>) {
|
||||
return send('api/tag-create', { tag });
|
||||
}
|
||||
|
||||
export function updateTag(
|
||||
id: APITagEntity['id'],
|
||||
fields: Partial<Omit<APITagEntity, 'id'>>,
|
||||
) {
|
||||
return send('api/tag-update', { id, fields });
|
||||
}
|
||||
|
||||
export function deleteTag(id: APITagEntity['id']) {
|
||||
return send('api/tag-delete', { id });
|
||||
}
|
||||
|
||||
export function mergePayees(
|
||||
targetId: APIPayeeEntity['id'],
|
||||
mergeIds: APIPayeeEntity['id'][],
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ComponentProps, CSSProperties, ReactNode } from 'react';
|
||||
import { type ComponentProps, type CSSProperties, type ReactNode } from 'react';
|
||||
|
||||
import { Block } from './Block';
|
||||
import { View } from './View';
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import type { HTMLProps, Ref } from 'react';
|
||||
import { type HTMLProps, type 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>;
|
||||
|
||||
@@ -1,5 +1,10 @@
|
||||
import React, { forwardRef, useMemo } from 'react';
|
||||
import type { ComponentPropsWithoutRef, CSSProperties, ReactNode } from 'react';
|
||||
import React, {
|
||||
forwardRef,
|
||||
useMemo,
|
||||
type ComponentPropsWithoutRef,
|
||||
type CSSProperties,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import { Button as ReactAriaButton } from 'react-aria-components';
|
||||
|
||||
import { css, cx } from '@emotion/css';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { forwardRef } from 'react';
|
||||
import type { ComponentProps } from 'react';
|
||||
import { forwardRef, type ComponentProps } from 'react';
|
||||
|
||||
import { theme } from './theme';
|
||||
import { View } from './View';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ChangeEvent, ReactNode } from 'react';
|
||||
import { type ChangeEvent, type ReactNode } from 'react';
|
||||
import {
|
||||
ColorPicker as AriaColorPicker,
|
||||
ColorSwatch as AriaColorSwatch,
|
||||
@@ -8,10 +8,8 @@ import {
|
||||
Dialog,
|
||||
DialogTrigger,
|
||||
parseColor,
|
||||
} from 'react-aria-components';
|
||||
import type {
|
||||
ColorPickerProps as AriaColorPickerProps,
|
||||
ColorSwatchProps,
|
||||
type ColorPickerProps as AriaColorPickerProps,
|
||||
type ColorSwatchProps,
|
||||
} from 'react-aria-components';
|
||||
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { CSSProperties, ReactNode } from 'react';
|
||||
import { type CSSProperties, type ReactNode } from 'react';
|
||||
|
||||
import { View } from './View';
|
||||
|
||||
|
||||
@@ -4,8 +4,10 @@ import {
|
||||
isValidElement,
|
||||
useEffect,
|
||||
useRef,
|
||||
type ReactElement,
|
||||
type Ref,
|
||||
type RefObject,
|
||||
} from 'react';
|
||||
import type { ReactElement, Ref, RefObject } from 'react';
|
||||
|
||||
type InitialFocusProps<T extends HTMLElement> = {
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import * as React from 'react';
|
||||
import { forwardRef } from 'react';
|
||||
import type { Ref } from 'react';
|
||||
import { forwardRef, type Ref } from 'react';
|
||||
|
||||
import { render } from '@testing-library/react';
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React from 'react';
|
||||
import type {
|
||||
ChangeEvent,
|
||||
ComponentPropsWithRef,
|
||||
FocusEvent,
|
||||
KeyboardEvent,
|
||||
import React, {
|
||||
type ChangeEvent,
|
||||
type ComponentPropsWithRef,
|
||||
type FocusEvent,
|
||||
type KeyboardEvent,
|
||||
} from 'react';
|
||||
import { Input as ReactAriaInput } from 'react-aria-components';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { forwardRef } from 'react';
|
||||
import type { CSSProperties, ReactNode } from 'react';
|
||||
import { forwardRef, type CSSProperties, type ReactNode } from 'react';
|
||||
|
||||
import { styles } from './styles';
|
||||
import { Text } from './Text';
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
import { useEffect, useRef, useState } from 'react';
|
||||
import type {
|
||||
ComponentProps,
|
||||
ComponentType,
|
||||
CSSProperties,
|
||||
KeyboardEvent,
|
||||
ReactNode,
|
||||
SVGProps,
|
||||
import {
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
type ComponentProps,
|
||||
type ComponentType,
|
||||
type CSSProperties,
|
||||
type KeyboardEvent,
|
||||
type ReactNode,
|
||||
type SVGProps,
|
||||
} from 'react';
|
||||
|
||||
import { Button } from './Button';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useCallback, useEffect, useRef } from 'react';
|
||||
import type { ComponentProps } from 'react';
|
||||
import { useCallback, useEffect, useRef, type ComponentProps } from 'react';
|
||||
import { Popover as ReactAriaPopover } from 'react-aria-components';
|
||||
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { useRef, useState } from 'react';
|
||||
import type { CSSProperties } from 'react';
|
||||
import { useRef, useState, type CSSProperties } from 'react';
|
||||
|
||||
import { Button } from './Button';
|
||||
import { SvgExpandArrow } from './icons/v0';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { type ReactNode } from 'react';
|
||||
|
||||
import type { CSSProperties } from './styles';
|
||||
import { type CSSProperties } from './styles';
|
||||
import { View } from './View';
|
||||
|
||||
type SpaceBetweenProps = {
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
import React, { forwardRef } from 'react';
|
||||
import type { HTMLProps, ReactNode, Ref } from 'react';
|
||||
import React, {
|
||||
forwardRef,
|
||||
type HTMLProps,
|
||||
type ReactNode,
|
||||
type 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>;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { ComponentProps } from 'react';
|
||||
import { type ComponentProps } from 'react';
|
||||
|
||||
import { Text } from './Text';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import type { CSSProperties } from 'react';
|
||||
import React, { type CSSProperties } from 'react';
|
||||
|
||||
import { css } from '@emotion/css';
|
||||
|
||||
|
||||
@@ -1,5 +1,11 @@
|
||||
import React, { useCallback, useEffect, useRef, useState } from 'react';
|
||||
import type { ComponentProps, ReactNode } from 'react';
|
||||
import React, {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useRef,
|
||||
useState,
|
||||
type ComponentProps,
|
||||
type ReactNode,
|
||||
} from 'react';
|
||||
import { Tooltip as AriaTooltip, TooltipTrigger } from 'react-aria-components';
|
||||
|
||||
import { styles } from './styles';
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
import React, { forwardRef } from 'react';
|
||||
import type { HTMLProps, Ref } from 'react';
|
||||
import React, { forwardRef, type HTMLProps, type 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;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React from 'react';
|
||||
import type { SVGProps } from 'react';
|
||||
import React, { type SVGProps } from 'react';
|
||||
|
||||
import { css, keyframes } from '@emotion/css';
|
||||
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import type { SVGProps } from 'react';
|
||||
import React, { useState, type SVGProps } from 'react';
|
||||
|
||||
export const SvgLoading = (props: SVGProps<SVGSVGElement>) => {
|
||||
const { color = 'currentColor' } = props;
|
||||
|
||||
@@ -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 },
|
||||
|
||||
@@ -12,7 +12,8 @@ const shadowLarge = {
|
||||
boxShadow: '0 15px 30px 0 rgba(0,0,0,0.11), 0 5px 15px 0 rgba(0,0,0,0.08)',
|
||||
};
|
||||
|
||||
export const styles: CSSProperties = {
|
||||
// oxlint-disable-next-line typescript/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,
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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';
|
||||
|
||||
|
||||
@@ -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 (
|
||||
|
||||
@@ -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', () => {
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import { expect as baseExpect } from '@playwright/test';
|
||||
import type { Locator } from '@playwright/test';
|
||||
import { expect as baseExpect, type Locator } from '@playwright/test';
|
||||
|
||||
export { test } from '@playwright/test';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { CloseAccountModal } from './close-account-modal';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Page } from '@playwright/test';
|
||||
import { type Page } from '@playwright/test';
|
||||
|
||||
export class BankSyncPage {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { AccountPage } from './account-page';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class CloseAccountModal {
|
||||
readonly locator: Locator;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { AccountPage } from './account-page';
|
||||
import { BudgetPage } from './budget-page';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class CustomReportPage {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
type ConditionsEntry = {
|
||||
field: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { MobileTransactionEntryPage } from './mobile-transaction-entry-page';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { MobileAccountPage } from './mobile-account-page';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class BalanceMenuModal {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class MobileBankSyncPage {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class BudgetMenuModal {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { MobileAccountPage } from './mobile-account-page';
|
||||
import { BalanceMenuModal } from './mobile-balance-menu-modal';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { EditNotesModal } from './mobile-edit-notes-modal';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class EditNotesModal {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class EnvelopeBudgetSummaryModal {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { MobileAccountPage } from './mobile-account-page';
|
||||
import { MobileAccountsPage } from './mobile-accounts-page';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class MobilePayeesPage {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class MobileReportsPage {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class MobileRulesPage {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class MobileSchedulesPage {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class TrackingBudgetSummaryModal {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { MobileAccountPage } from './mobile-account-page';
|
||||
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class PayeesPage {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { CustomReportPage } from './custom-report-page';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { EditRuleModal } from './edit-rule-modal';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
type ScheduleEntry = {
|
||||
scheduleName?: string;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
import { ScheduleEditModal } from './schedule-edit-modal';
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { Locator, Page } from '@playwright/test';
|
||||
import { type Locator, type Page } from '@playwright/test';
|
||||
|
||||
export class SettingsPage {
|
||||
readonly page: Page;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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';
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
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';
|
||||
|
||||
|
||||
@@ -1,24 +1,23 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
||||
import memoizeOne from 'memoize-one';
|
||||
|
||||
import { send } from 'loot-core/platform/client/fetch';
|
||||
import type { SyncResponseWithErrors } from 'loot-core/server/accounts/app';
|
||||
import { type SyncResponseWithErrors } from 'loot-core/server/accounts/app';
|
||||
import { groupById } from 'loot-core/shared/util';
|
||||
import type {
|
||||
AccountEntity,
|
||||
CategoryEntity,
|
||||
SyncServerGoCardlessAccount,
|
||||
SyncServerPluggyAiAccount,
|
||||
SyncServerSimpleFinAccount,
|
||||
TransactionEntity,
|
||||
import {
|
||||
type AccountEntity,
|
||||
type CategoryEntity,
|
||||
type SyncServerGoCardlessAccount,
|
||||
type SyncServerPluggyAiAccount,
|
||||
type SyncServerSimpleFinAccount,
|
||||
type TransactionEntity,
|
||||
} from 'loot-core/types/models';
|
||||
|
||||
import { resetApp } from '@desktop-client/app/appSlice';
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { markPayeesDirty } from '@desktop-client/payees/payeesSlice';
|
||||
import { createAppAsyncThunk } from '@desktop-client/redux';
|
||||
import type { AppDispatch } from '@desktop-client/redux/store';
|
||||
import { type AppDispatch } from '@desktop-client/redux/store';
|
||||
import { setNewTransactions } from '@desktop-client/transactions/transactionsSlice';
|
||||
|
||||
const sliceName = 'account';
|
||||
|
||||
@@ -1,10 +1,13 @@
|
||||
import { createAction, createSlice } from '@reduxjs/toolkit';
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import {
|
||||
createAction,
|
||||
createSlice,
|
||||
type PayloadAction,
|
||||
} from '@reduxjs/toolkit';
|
||||
|
||||
import { send } from 'loot-core/platform/client/fetch';
|
||||
import { getUploadError } from 'loot-core/shared/errors';
|
||||
import type { AccountEntity } from 'loot-core/types/models';
|
||||
import type { AtLeastOne } from 'loot-core/types/util';
|
||||
import { type AccountEntity } from 'loot-core/types/models';
|
||||
import { type AtLeastOne } from 'loot-core/types/util';
|
||||
|
||||
import { syncAccounts } from '@desktop-client/accounts/accountsSlice';
|
||||
import { pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import React, { createContext, useContext } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { createContext, useContext, type ReactNode } from 'react';
|
||||
|
||||
import type { Permissions } from './types';
|
||||
import { type Permissions } from './types';
|
||||
|
||||
import { useServerURL } from '@desktop-client/components/ServerContext';
|
||||
import { useSelector } from '@desktop-client/redux';
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
import { useEffect, useState, type ReactElement } from 'react';
|
||||
import { Trans } from 'react-i18next';
|
||||
|
||||
import { View } from '@actual-app/components/view';
|
||||
|
||||
import type { RemoteFile, SyncedLocalFile } from 'loot-core/types/file';
|
||||
import { type RemoteFile, type SyncedLocalFile } from 'loot-core/types/file';
|
||||
|
||||
import { useAuth } from './AuthProvider';
|
||||
import type { Permissions } from './types';
|
||||
import { type Permissions } from './types';
|
||||
|
||||
import { useMetadataPref } from '@desktop-client/hooks/useMetadataPref';
|
||||
import { useSelector } from '@desktop-client/redux';
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
import { useMutation, useQueryClient } from '@tanstack/react-query';
|
||||
import type { QueryClient, QueryKey } from '@tanstack/react-query';
|
||||
import type { TFunction } from 'i18next';
|
||||
import {
|
||||
useMutation,
|
||||
useQueryClient,
|
||||
type QueryClient,
|
||||
type QueryKey,
|
||||
} from '@tanstack/react-query';
|
||||
import { type TFunction } from 'i18next';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
import { sendCatch } from 'loot-core/platform/client/fetch';
|
||||
import type { send } from 'loot-core/platform/client/fetch';
|
||||
import { sendCatch, type send } from 'loot-core/platform/client/fetch';
|
||||
import { logger } from 'loot-core/platform/server/log';
|
||||
import type { IntegerAmount } from 'loot-core/shared/util';
|
||||
import type {
|
||||
CategoryEntity,
|
||||
CategoryGroupEntity,
|
||||
import { type IntegerAmount } from 'loot-core/shared/util';
|
||||
import {
|
||||
type CategoryEntity,
|
||||
type CategoryGroupEntity,
|
||||
} from 'loot-core/types/models';
|
||||
|
||||
import { categoryQueries } from '.';
|
||||
@@ -19,7 +22,7 @@ import { categoryQueries } from '.';
|
||||
import { pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
import { addNotification } from '@desktop-client/notifications/notificationsSlice';
|
||||
import { useDispatch } from '@desktop-client/redux';
|
||||
import type { AppDispatch } from '@desktop-client/redux/store';
|
||||
import { type AppDispatch } from '@desktop-client/redux/store';
|
||||
|
||||
const sendThrow: typeof send = async (name, args) => {
|
||||
const { error, data } = await sendCatch(name, args);
|
||||
|
||||
@@ -2,9 +2,9 @@ import { queryOptions } from '@tanstack/react-query';
|
||||
import i18n from 'i18next';
|
||||
|
||||
import { send } from 'loot-core/platform/client/fetch';
|
||||
import type {
|
||||
CategoryEntity,
|
||||
CategoryGroupEntity,
|
||||
import {
|
||||
type CategoryEntity,
|
||||
type CategoryGroupEntity,
|
||||
} from 'loot-core/types/models';
|
||||
|
||||
type CategoryViews = {
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
import type { PayloadAction } from '@reduxjs/toolkit';
|
||||
import { createSlice, type PayloadAction } from '@reduxjs/toolkit';
|
||||
import { t } from 'i18next';
|
||||
|
||||
import { send } from 'loot-core/platform/client/fetch';
|
||||
import type { RemoteFile } from 'loot-core/server/cloud-storage';
|
||||
import { type RemoteFile } from 'loot-core/server/cloud-storage';
|
||||
import { getDownloadError, getSyncError } from 'loot-core/shared/errors';
|
||||
import type { Budget } from 'loot-core/types/budget';
|
||||
import type { File } from 'loot-core/types/file';
|
||||
import type { Handlers } from 'loot-core/types/handlers';
|
||||
import { type Budget } from 'loot-core/types/budget';
|
||||
import { type File } from 'loot-core/types/file';
|
||||
import { type Handlers } from 'loot-core/types/handlers';
|
||||
|
||||
import { resetApp, setAppState } from '@desktop-client/app/appSlice';
|
||||
import { closeModal, pushModal } from '@desktop-client/modals/modalsSlice';
|
||||
@@ -100,11 +99,10 @@ export const loadBudget = createAppAsyncThunk(
|
||||
|
||||
export const closeBudget = createAppAsyncThunk(
|
||||
`${sliceName}/closeBudget`,
|
||||
async (_, { dispatch, getState, extra: { queryClient } }) => {
|
||||
async (_, { dispatch, getState }) => {
|
||||
const prefs = getState().prefs.local;
|
||||
if (prefs && prefs.id) {
|
||||
await dispatch(resetApp());
|
||||
queryClient.clear();
|
||||
await dispatch(setAppState({ loadingText: t('Closing...') }));
|
||||
await send('close-budget');
|
||||
await dispatch(setAppState({ loadingText: null }));
|
||||
@@ -117,11 +115,10 @@ export const closeBudget = createAppAsyncThunk(
|
||||
|
||||
export const closeBudgetUI = createAppAsyncThunk(
|
||||
`${sliceName}/closeBudgetUI`,
|
||||
async (_, { dispatch, getState, extra: { queryClient } }) => {
|
||||
async (_, { dispatch, getState }) => {
|
||||
const prefs = getState().prefs.local;
|
||||
if (prefs && prefs.id) {
|
||||
await dispatch(resetApp());
|
||||
queryClient.clear();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
// @ts-strict-ignore
|
||||
import React from 'react';
|
||||
import type { CSSProperties } from 'react';
|
||||
import React, { type CSSProperties } from 'react';
|
||||
|
||||
import { SvgRefresh } from '@actual-app/components/icons/v1';
|
||||
import { View } from '@actual-app/components/view';
|
||||
|
||||
@@ -2,8 +2,11 @@
|
||||
import React, { useEffect, useState } from 'react';
|
||||
import { DndProvider } from 'react-dnd';
|
||||
import { HTML5Backend } from 'react-dnd-html5-backend';
|
||||
import { ErrorBoundary, useErrorBoundary } from 'react-error-boundary';
|
||||
import type { FallbackProps } from 'react-error-boundary';
|
||||
import {
|
||||
ErrorBoundary,
|
||||
useErrorBoundary,
|
||||
type FallbackProps,
|
||||
} from 'react-error-boundary';
|
||||
import { HotkeysProvider } from 'react-hotkeys-hook';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { BrowserRouter } from 'react-router';
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { useCallback, useEffect, useMemo, useState } from 'react';
|
||||
import type { ComponentType, ReactNode, SVGProps } from 'react';
|
||||
import {
|
||||
useCallback,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useState,
|
||||
type ComponentType,
|
||||
type ReactNode,
|
||||
type SVGProps,
|
||||
} from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import {
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import React, { useState } from 'react';
|
||||
import type { ReactNode } from 'react';
|
||||
import React, { useState, type ReactNode } from 'react';
|
||||
import { Trans, useTranslation } from 'react-i18next';
|
||||
|
||||
import { Block } from '@actual-app/components/block';
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
// @ts-strict-ignore
|
||||
import React, { useEffect, useEffectEvent, useRef } from 'react';
|
||||
import type { ReactElement } from 'react';
|
||||
import React, {
|
||||
useEffect,
|
||||
useEffectEvent,
|
||||
useRef,
|
||||
type ReactElement,
|
||||
} from 'react';
|
||||
import { useTranslation } from 'react-i18next';
|
||||
import { Navigate, Route, Routes, useHref, useLocation } from 'react-router';
|
||||
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import React from 'react';
|
||||
import type { ComponentPropsWithoutRef, ElementType } from 'react';
|
||||
import React, { type ComponentPropsWithoutRef, type ElementType } from 'react';
|
||||
|
||||
import { styles } from '@actual-app/components/styles';
|
||||
import type { CSSProperties } from '@actual-app/components/styles';
|
||||
import { styles, type CSSProperties } from '@actual-app/components/styles';
|
||||
import { Text } from '@actual-app/components/text';
|
||||
|
||||
type FinancialTextProps<T extends ElementType = typeof Text> = {
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
// @ts-strict-ignore
|
||||
import { createRef, PureComponent } from 'react';
|
||||
import type { CSSProperties, ReactNode, Ref, RefObject, UIEvent } from 'react';
|
||||
import {
|
||||
createRef,
|
||||
PureComponent,
|
||||
type CSSProperties,
|
||||
type ReactNode,
|
||||
type Ref,
|
||||
type RefObject,
|
||||
type UIEvent,
|
||||
} from 'react';
|
||||
|
||||
import { View } from '@actual-app/components/view';
|
||||
import memoizeOne from 'memoize-one';
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user