diff --git a/.nano-staged.js b/.nano-staged.js index 5c6ce5c2..b1861e0e 100644 --- a/.nano-staged.js +++ b/.nano-staged.js @@ -8,9 +8,7 @@ export default { * @param {string[]} filenames * @return {string[]} */ - '{package-lock.json,packages/**/{*.ts,*.tsx,tsconfig.json}}': ({ - filenames, - }) => { + '{package-lock.json,packages/**/{*.ts,*.tsx,tsconfig.json}}': ({ filenames }) => { // if dependencies was changed run type checking for all packages if (filenames.some((f) => f.endsWith('package-lock.json'))) { return ['yarn typecheck']; @@ -18,11 +16,7 @@ export default { // else run type checking for staged packages const fileNameToPackageName = (filename) => - filename - .replace(resolve(process.cwd(), 'packages') + sep, '') - .split(sep)[0]; - return [...new Set(filenames.map(fileNameToPackageName))].map( - (p) => `yarn typecheck:${p}`, - ); + filename.replace(resolve(process.cwd(), 'packages') + sep, '').split(sep)[0]; + return [...new Set(filenames.map(fileNameToPackageName))].map((p) => `yarn typecheck:${p}`); }, }; diff --git a/.prettierrc.json b/.prettierrc.json index 544138be..0981b7cc 100644 --- a/.prettierrc.json +++ b/.prettierrc.json @@ -1,3 +1,4 @@ { - "singleQuote": true + "singleQuote": true, + "printWidth": 120 } diff --git a/packages/main/src/backend/commonTypes.ts b/packages/main/src/backend/commonTypes.ts index ca73717c..920a5472 100644 --- a/packages/main/src/backend/commonTypes.ts +++ b/packages/main/src/backend/commonTypes.ts @@ -1,7 +1,4 @@ -import { - type CompanyTypes, - type ScraperCredentials, -} from 'israeli-bank-scrapers-core'; +import { type CompanyTypes, type ScraperCredentials } from 'israeli-bank-scrapers-core'; import { type Transaction } from 'israeli-bank-scrapers-core/lib/transactions'; import { type Account, type BudgetSummary } from 'ynab'; import { type EventPublisher } from './eventEmitters/EventEmitter'; @@ -38,14 +35,8 @@ export enum OutputVendorName { CSV = 'csv', } -export type OutputVendorConfigs = Exclude< - Config['outputVendors'][OutputVendorName], - undefined ->; -export type OutputVendorConfig = Exclude< - Config['outputVendors'][T], - undefined ->; +export type OutputVendorConfigs = Exclude; +export type OutputVendorConfig = Exclude; interface OutputVendorConfigBase { active: boolean; diff --git a/packages/main/src/backend/configManager/configManager.ts b/packages/main/src/backend/configManager/configManager.ts index d29541fa..4e618d5f 100644 --- a/packages/main/src/backend/configManager/configManager.ts +++ b/packages/main/src/backend/configManager/configManager.ts @@ -5,9 +5,7 @@ import { existsSync, promises as fs } from 'fs'; import configExample from './defaultConfig'; import logger from '/@/logging/logger'; -export async function getConfig( - configPath: string = configFilePath, -): Promise { +export async function getConfig(configPath: string = configFilePath): Promise { const configFromFile = await getConfigFromFile(configPath); if (configFromFile) { const decrypted = (await decrypt(configFromFile)) as string; @@ -31,10 +29,7 @@ export async function getConfig( return configExample; } -export async function updateConfig( - configPath: string, - configToUpdate: Config, -): Promise { +export async function updateConfig(configPath: string, configToUpdate: Config): Promise { const stringifiedConfig = JSON.stringify(configToUpdate, null, 2); const encryptedConfigStr = await encrypt(stringifiedConfig); await fs.writeFile(configPath, encryptedConfigStr); diff --git a/packages/main/src/backend/configManager/encryption/salt.ts b/packages/main/src/backend/configManager/encryption/salt.ts index cd49eafb..ed4d6725 100644 --- a/packages/main/src/backend/configManager/encryption/salt.ts +++ b/packages/main/src/backend/configManager/encryption/salt.ts @@ -4,8 +4,7 @@ export default async function SALT(defaultValue?: string): Promise { const existedSALT = await loadSALT(); if (existedSALT) return existedSALT; - if (!defaultValue) - throw Error('SALT not existed and no default value provided'); + if (!defaultValue) throw Error('SALT not existed and no default value provided'); await saveSALT(defaultValue); return defaultValue; diff --git a/packages/main/src/backend/eventEmitters/EventEmitter.ts b/packages/main/src/backend/eventEmitters/EventEmitter.ts index 7e3ae873..04152c19 100644 --- a/packages/main/src/backend/eventEmitters/EventEmitter.ts +++ b/packages/main/src/backend/eventEmitters/EventEmitter.ts @@ -1,8 +1,5 @@ // eslint-disable-next-line max-classes-per-file -import { - type EnrichedTransaction, - type OutputVendorName, -} from '@/backend/commonTypes'; +import { type EnrichedTransaction, type OutputVendorName } from '@/backend/commonTypes'; import Emittery from 'emittery'; import { type CompanyTypes } from 'israeli-bank-scrapers-core'; @@ -93,13 +90,7 @@ export interface ExporterEventParams { export class ExporterEvent extends BudgetTrackingEvent { allTransactions: EnrichedTransaction[]; - constructor({ - message, - allTransactions, - status, - error, - exporterName, - }: ExporterEventParams) { + constructor({ message, allTransactions, status, error, exporterName }: ExporterEventParams) { super({ message, accountType: AccountType.EXPORTER, @@ -155,7 +146,4 @@ export class BudgetTrackingEventEmitter extends Emittery {} export type EventPublisher = Pick; -export type EventSubscriber = Pick< - BudgetTrackingEventEmitter, - 'on' | 'once' | 'off' | 'onAny' | 'anyEvent' | 'offAny' ->; +export type EventSubscriber = Pick; diff --git a/packages/main/src/backend/eventEmitters/loggerEmitter.ts b/packages/main/src/backend/eventEmitters/loggerEmitter.ts index 7f8f79e7..446d4c93 100644 --- a/packages/main/src/backend/eventEmitters/loggerEmitter.ts +++ b/packages/main/src/backend/eventEmitters/loggerEmitter.ts @@ -1,7 +1,4 @@ -import { - BudgetTrackingEventEmitter, - type EventPublisher, -} from '@/backend/eventEmitters/EventEmitter'; +import { BudgetTrackingEventEmitter, type EventPublisher } from '@/backend/eventEmitters/EventEmitter'; interface Logger { info: (...params: unknown[]) => void; diff --git a/packages/main/src/backend/export/exportTransactions.ts b/packages/main/src/backend/export/exportTransactions.ts index 8d4dd5c4..a1a7728a 100644 --- a/packages/main/src/backend/export/exportTransactions.ts +++ b/packages/main/src/backend/export/exportTransactions.ts @@ -15,9 +15,7 @@ import outputVendors from '@/backend/export/outputVendors'; import _ from 'lodash'; import logger from '/@/logging/logger'; -type ExecutionResult = Partial< - Record ->; +type ExecutionResult = Partial>; export async function createTransactionsInExternalVendors( outputVendorsConfig: Config['outputVendors'], @@ -38,10 +36,7 @@ export async function createTransactionsInExternalVendors( }; await outputVendor.init?.(outputVendorsConfig); - await eventPublisher.emit( - EventNames.EXPORTER_START, - new ExporterEvent({ message: 'Starting', ...baseEvent }), - ); + await eventPublisher.emit(EventNames.EXPORTER_START, new ExporterEvent({ message: 'Starting', ...baseEvent })); try { const exportTransactionsResult = await outputVendor.exportTransactions( { @@ -57,8 +52,7 @@ export async function createTransactionsInExternalVendors( message: 'Finished', ...baseEvent, status: AccountStatus.DONE, - exportedTransactionsNum: - exportTransactionsResult.exportedTransactionsNum, + exportedTransactionsNum: exportTransactionsResult.exportedTransactionsNum, }), ); executionResult[outputVendor.name] = exportTransactionsResult; @@ -78,9 +72,7 @@ export async function createTransactionsInExternalVendors( await Promise.all(exportPromises); if (!Object.keys(executionResult).length) { - const error = new Error( - 'You need to set at least one output vendor to be active', - ); + const error = new Error('You need to set at least one output vendor to be active'); throw error; } diff --git a/packages/main/src/backend/export/outputVendors/csv/csv.ts b/packages/main/src/backend/export/outputVendors/csv/csv.ts index a77a32e7..798e016b 100644 --- a/packages/main/src/backend/export/outputVendors/csv/csv.ts +++ b/packages/main/src/backend/export/outputVendors/csv/csv.ts @@ -4,10 +4,7 @@ import { type ExportTransactionsFunction, type OutputVendor, } from '@/backend/commonTypes'; -import { - mergeTransactions, - sortByDate, -} from '@/backend/transactions/transactions'; +import { mergeTransactions, sortByDate } from '@/backend/transactions/transactions'; import { parse } from 'csv-parse/sync'; import { stringify } from 'csv-stringify/sync'; import { promises as fs } from 'fs'; @@ -86,21 +83,14 @@ export const serializeTransactions = (transactions: EnrichedTransaction[]) => { }); }; -const exportTransactions: ExportTransactionsFunction = async ({ - transactionsToCreate, - outputVendorsConfig, -}) => { +const exportTransactions: ExportTransactionsFunction = async ({ transactionsToCreate, outputVendorsConfig }) => { const { filePath } = outputVendorsConfig.csv!.options; const savedTransactions = await parseTransactionsFile(filePath); - const mergedTransactions = mergeTransactions( - savedTransactions, - transactionsToCreate, - ); + const mergedTransactions = mergeTransactions(savedTransactions, transactionsToCreate); const sorted = sortByDate(mergedTransactions); await writeCsvFile(filePath, serializeTransactions(sorted)); return { - exportedTransactionsNum: - mergedTransactions.length - savedTransactions.length, + exportedTransactionsNum: mergedTransactions.length - savedTransactions.length, }; }; diff --git a/packages/main/src/backend/export/outputVendors/googleSheets/electronGoogleOAuth2Connector.ts b/packages/main/src/backend/export/outputVendors/googleSheets/electronGoogleOAuth2Connector.ts index 9fd41853..227adbdf 100644 --- a/packages/main/src/backend/export/outputVendors/googleSheets/electronGoogleOAuth2Connector.ts +++ b/packages/main/src/backend/export/outputVendors/googleSheets/electronGoogleOAuth2Connector.ts @@ -2,18 +2,12 @@ import ElectronGoogleOAuth2 from 'electron-google-oauth2'; import { clientId, clientSecret, redirectUri, scopes } from './googleAuth'; export default () => { - if (!clientId || !clientSecret) - throw Error("No 'clientId' or 'clientSecret' for google login"); + if (!clientId || !clientSecret) throw Error("No 'clientId' or 'clientSecret' for google login"); // @ts-expect-error - The package 'electron-google-oauth2' is my own package, I don't know why it's not recognized - const electronGoogleOAuth2 = new ElectronGoogleOAuth2.default( - clientId, - clientSecret, - scopes, - { - successRedirectURL: redirectUri, - }, - ); + const electronGoogleOAuth2 = new ElectronGoogleOAuth2.default(clientId, clientSecret, scopes, { + successRedirectURL: redirectUri, + }); return electronGoogleOAuth2.openAuthWindowAndGetTokens(); }; diff --git a/packages/main/src/backend/export/outputVendors/googleSheets/googleSheets.ts b/packages/main/src/backend/export/outputVendors/googleSheets/googleSheets.ts index 3d717212..a5713c62 100644 --- a/packages/main/src/backend/export/outputVendors/googleSheets/googleSheets.ts +++ b/packages/main/src/backend/export/outputVendors/googleSheets/googleSheets.ts @@ -4,11 +4,7 @@ import { type ExportTransactionsFunction, type OutputVendor, } from '@/backend/commonTypes'; -import { - EventNames, - ExporterEvent, - type EventPublisher, -} from '@/backend/eventEmitters/EventEmitter'; +import { EventNames, ExporterEvent, type EventPublisher } from '@/backend/eventEmitters/EventEmitter'; import { filterExistedHashes } from '@/backend/transactions/transactions'; import { type Auth } from 'googleapis'; import moment from 'moment/moment'; @@ -37,39 +33,24 @@ const createTransactionsInGoogleSheets: ExportTransactionsFunction = async ( { transactionsToCreate: transactions, outputVendorsConfig }, eventPublisher, ) => { - const { spreadsheetId, credentials } = - outputVendorsConfig.googleSheets!.options; + const { spreadsheetId, credentials } = outputVendorsConfig.googleSheets!.options; if (!credentials) throw new Error("You must set the 'credentials'"); const oAuthClient = createClient(credentials); - const sheet = await googleSheets.getSheet( - spreadsheetId, - DEFAULT_SHEET_NAME, - oAuthClient, - ); + const sheet = await googleSheets.getSheet(spreadsheetId, DEFAULT_SHEET_NAME, oAuthClient); if (!sheet) { - throw new Error( - `There is no sheet called ${DEFAULT_SHEET_NAME} in the spreadsheet`, - ); + throw new Error(`There is no sheet called ${DEFAULT_SHEET_NAME} in the spreadsheet`); } - const hashesAlreadyExistingInGoogleSheets = - await googleSheets.getExistingHashes( - spreadsheetId, - DEFAULT_SHEET_NAME, - oAuthClient, - ); - const transactionsToCreate = filterExistedHashes( - transactions, - hashesAlreadyExistingInGoogleSheets, + const hashesAlreadyExistingInGoogleSheets = await googleSheets.getExistingHashes( + spreadsheetId, + DEFAULT_SHEET_NAME, + oAuthClient, ); + const transactionsToCreate = filterExistedHashes(transactions, hashesAlreadyExistingInGoogleSheets); if (transactionsToCreate.length === 0) { - await emitProgressEvent( - eventPublisher, - transactions, - 'All transactions already exist in google sheets', - ); + await emitProgressEvent(eventPublisher, transactions, 'All transactions already exist in google sheets'); return { exportedTransactionsNum: 0, }; @@ -123,23 +104,11 @@ async function emitProgressEvent( ); } -export async function createSpreadsheet( - spreadsheetTitle: string, - credentials: Auth.Credentials, -): Promise { +export async function createSpreadsheet(spreadsheetTitle: string, credentials: Auth.Credentials): Promise { const auth = createClient(credentials); - const spreadsheetId = await googleSheets.createSpreadsheet( - spreadsheetTitle, - DEFAULT_SHEET_NAME, - auth, - ); + const spreadsheetId = await googleSheets.createSpreadsheet(spreadsheetTitle, DEFAULT_SHEET_NAME, auth); - await googleSheets.appendToSpreadsheet( - spreadsheetId, - `${DEFAULT_SHEET_NAME}!A:A`, - [COLUMN_HEADERS], - auth, - ); + await googleSheets.appendToSpreadsheet(spreadsheetId, `${DEFAULT_SHEET_NAME}!A:A`, [COLUMN_HEADERS], auth); return spreadsheetId; } diff --git a/packages/main/src/backend/export/outputVendors/googleSheets/googleSheetsInternalAPI.ts b/packages/main/src/backend/export/outputVendors/googleSheets/googleSheetsInternalAPI.ts index 94475e08..ca1c7177 100644 --- a/packages/main/src/backend/export/outputVendors/googleSheets/googleSheetsInternalAPI.ts +++ b/packages/main/src/backend/export/outputVendors/googleSheets/googleSheetsInternalAPI.ts @@ -57,18 +57,12 @@ export const getAllSpreadsheets = async (auth: OAuth2Client) => { return response.data.files as Spreadsheet[]; }; -export async function getSheet( - spreadsheetId: string, - sheetName: string, - auth: OAuth2Client, -) { +export async function getSheet(spreadsheetId: string, sheetName: string, auth: OAuth2Client) { const spreadsheetResponse = await sheets.spreadsheets.get({ auth, spreadsheetId, }); - const sheet = spreadsheetResponse.data.sheets?.find( - ({ properties }) => properties?.title === sheetName, - ); + const sheet = spreadsheetResponse.data.sheets?.find(({ properties }) => properties?.title === sheetName); return sheet; } diff --git a/packages/main/src/backend/export/outputVendors/json/json.ts b/packages/main/src/backend/export/outputVendors/json/json.ts index ce5cd118..d1b721ba 100644 --- a/packages/main/src/backend/export/outputVendors/json/json.ts +++ b/packages/main/src/backend/export/outputVendors/json/json.ts @@ -4,10 +4,7 @@ import { type ExportTransactionsFunction, type OutputVendor, } from '@/backend/commonTypes'; -import { - mergeTransactions, - sortByDate, -} from '@/backend/transactions/transactions'; +import { mergeTransactions, sortByDate } from '@/backend/transactions/transactions'; import { promises as fs } from 'fs'; import logger from '/@/logging/logger'; @@ -24,21 +21,14 @@ const parseTransactionsFile = async (filename: string) => { } }; -const exportTransactions: ExportTransactionsFunction = async ({ - transactionsToCreate, - outputVendorsConfig, -}) => { +const exportTransactions: ExportTransactionsFunction = async ({ transactionsToCreate, outputVendorsConfig }) => { const { filePath } = outputVendorsConfig.json!.options; const savedTransactions = await parseTransactionsFile(filePath); - const mergedTransactions = mergeTransactions( - savedTransactions, - transactionsToCreate, - ); + const mergedTransactions = mergeTransactions(savedTransactions, transactionsToCreate); const sorted = sortByDate(mergedTransactions); await fs.writeFile(filePath, JSON.stringify(sorted, null, 4)); return { - exportedTransactionsNum: - mergedTransactions.length - savedTransactions.length, + exportedTransactionsNum: mergedTransactions.length - savedTransactions.length, }; }; diff --git a/packages/main/src/backend/export/outputVendors/ynab/ynab.test.ts b/packages/main/src/backend/export/outputVendors/ynab/ynab.test.ts index 5425aa1a..7baefbac 100644 --- a/packages/main/src/backend/export/outputVendors/ynab/ynab.test.ts +++ b/packages/main/src/backend/export/outputVendors/ynab/ynab.test.ts @@ -1,8 +1,5 @@ import { type EnrichedTransaction } from '@/backend/commonTypes'; -import { - TransactionStatuses, - TransactionTypes, -} from 'israeli-bank-scrapers-core/lib/transactions'; +import { TransactionStatuses, TransactionTypes } from 'israeli-bank-scrapers-core/lib/transactions'; import { describe, expect, test } from 'vitest'; import { SaveTransaction, type TransactionDetail } from 'ynab'; import * as ynab from './ynab'; @@ -41,27 +38,16 @@ describe('ynab', () => { cleared: ClearedEnum.Cleared, }; - expect( - ynab.isSameTransaction( - transactionFromFinancialAccount, - transferTransactionFromYnab, - ), - ).toBeTruthy(); + expect(ynab.isSameTransaction(transactionFromFinancialAccount, transferTransactionFromYnab)).toBeTruthy(); }); }); describe('areStringsEqualIgnoreCaseAndWhitespace', () => { test('should consider two strings with different casing as equal', async () => { - expect( - ynab.areStringsEqualIgnoreCaseAndWhitespace('Gett', 'GETT'), - ).toBeTruthy(); + expect(ynab.areStringsEqualIgnoreCaseAndWhitespace('Gett', 'GETT')).toBeTruthy(); }); test('should consider two strings that are the same except for whitespace as equal', async () => { - expect( - ynab.areStringsEqualIgnoreCaseAndWhitespace('Gett', 'Gett '), - ).toBeTruthy(); - expect( - ynab.areStringsEqualIgnoreCaseAndWhitespace('Gett', ' Gett '), - ).toBeTruthy(); + expect(ynab.areStringsEqualIgnoreCaseAndWhitespace('Gett', 'Gett ')).toBeTruthy(); + expect(ynab.areStringsEqualIgnoreCaseAndWhitespace('Gett', ' Gett ')).toBeTruthy(); expect( ynab.areStringsEqualIgnoreCaseAndWhitespace( 'PAYPAL *AVIDEUT 4029357733 LU', @@ -69,10 +55,7 @@ describe('ynab', () => { ), ).toBeTruthy(); expect( - ynab.areStringsEqualIgnoreCaseAndWhitespace( - 'ממלכת הצעצועים הרצליה', - 'ממלכת הצעצועים הרצליה', - ), + ynab.areStringsEqualIgnoreCaseAndWhitespace('ממלכת הצעצועים הרצליה', 'ממלכת הצעצועים הרצליה'), ).toBeTruthy(); expect( ynab.areStringsEqualIgnoreCaseAndWhitespace( @@ -82,9 +65,7 @@ describe('ynab', () => { ).toBeTruthy(); }); test('should consider two different strings as not equal', async () => { - expect( - ynab.areStringsEqualIgnoreCaseAndWhitespace('Gett', ' shmett '), - ).toBeFalsy(); + expect(ynab.areStringsEqualIgnoreCaseAndWhitespace('Gett', ' shmett ')).toBeFalsy(); }); }); describe('getPayeeName', () => { @@ -108,34 +89,22 @@ describe('ynab', () => { [' משכורת', 'המבצע: לאומי'], ['קצבת ילדים', ''], ['בזק-הוראת קבע', 'בזק - חיובי טלפון'], - ])( - 'Verify getPayeeName extracts the correct payeeName', - async (description, memo) => { - transactionSample.description = description; - transactionSample.memo = memo; - expect(ynab.getPayeeName(transactionSample)).toBe( - transactionSample.description, - ); - }, - ); + ])('Verify getPayeeName extracts the correct payeeName', async (description, memo) => { + transactionSample.description = description; + transactionSample.memo = memo; + expect(ynab.getPayeeName(transactionSample)).toBe(transactionSample.description); + }); test.each([ - [ - 'הוראת קבע', - 'לטובת: צהרון. עבור: צהרון גנים - ילד 00-000-0000000', - 'צהרון', - ], + ['הוראת קבע', 'לטובת: צהרון. עבור: צהרון גנים - ילד 00-000-0000000', 'צהרון'], ["העב' לאחר-נייד", 'לטובת: איש כלשהו. עבור: סוף חשבון', 'איש כלשהו'], ['העברה לאחר', 'לטובת: פנסיה לדוגמא. עבור: סיבה מסויימת', 'פנסיה לדוגמא'], ['העברה מהבנק', 'לטובת: אישה כלשהי. עבור: משכורת אוגוסט', 'אישה כלשהי'], // Weird bank accounts names ['הוראת קבע', 'לטובת: di. עבור: גן 092-.', 'di'], - ])( - 'Verify hapoalim transfers capture the correct payee name', - async (description, memo, expected) => { - transactionSample.description = description; - transactionSample.memo = memo; - expect(ynab.getPayeeName(transactionSample)).toBe(expected); - }, - ); + ])('Verify hapoalim transfers capture the correct payee name', async (description, memo, expected) => { + transactionSample.description = description; + transactionSample.memo = memo; + expect(ynab.getPayeeName(transactionSample)).toBe(expected); + }); }); }); diff --git a/packages/main/src/backend/export/outputVendors/ynab/ynab.ts b/packages/main/src/backend/export/outputVendors/ynab/ynab.ts index 012b316c..3394d7d0 100644 --- a/packages/main/src/backend/export/outputVendors/ynab/ynab.ts +++ b/packages/main/src/backend/export/outputVendors/ynab/ynab.ts @@ -8,11 +8,7 @@ import { type YnabConfig, type YnabFinancialAccount, } from '@/backend/commonTypes'; -import { - EventNames, - ExporterEvent, - type EventPublisher, -} from '@/backend/eventEmitters/EventEmitter'; +import { EventNames, ExporterEvent, type EventPublisher } from '@/backend/eventEmitters/EventEmitter'; import _ from 'lodash'; import moment from 'moment/moment'; import * as ynab from 'ynab'; @@ -21,10 +17,7 @@ const YNAB_DATE_FORMAT = 'YYYY-MM-DD'; const NOW = moment(); const MIN_YNAB_ACCESS_TOKEN_LENGTH = 43; -const categoriesMap = new Map< - string, - Pick ->(); +const categoriesMap = new Map>(); const transactionsFromYnab = new Map(); let ynabConfig: YnabConfig | undefined; @@ -32,9 +25,7 @@ let ynabAPI: ynab.API | undefined; export async function init(outputVendorsConfig: Config['outputVendors']) { ynabConfig = outputVendorsConfig.ynab; - initFromToken( - outputVendorsConfig[OutputVendorName.YNAB]?.options.accessToken, - ); + initFromToken(outputVendorsConfig[OutputVendorName.YNAB]?.options.accessToken); } async function initFromToken(accessToken?: string) { @@ -45,27 +36,21 @@ async function initFromToken(accessToken?: string) { } } -const createTransactions: ExportTransactionsFunction = async ( - { transactionsToCreate, startDate }, - eventPublisher, -) => { +const createTransactions: ExportTransactionsFunction = async ({ transactionsToCreate, startDate }, eventPublisher) => { if (!ynabConfig) { throw new Error('Must call init before using ynab functions'); } if (!categoriesMap.size) { await initCategories(); } - const transactionsFromFinancialAccount = transactionsToCreate.map( - convertTransactionToYnabFormat, + const transactionsFromFinancialAccount = transactionsToCreate.map(convertTransactionToYnabFormat); + let transactionsThatDontExistInYnab = await filterOnlyTransactionsThatDontExistInYnabAlready( + startDate, + transactionsFromFinancialAccount, ); - let transactionsThatDontExistInYnab = - await filterOnlyTransactionsThatDontExistInYnabAlready( - startDate, - transactionsFromFinancialAccount, - ); // Filter out transactions that are in the future - transactionsThatDontExistInYnab = transactionsThatDontExistInYnab.filter( - (transaction) => moment(transaction.date, YNAB_DATE_FORMAT).isBefore(NOW), + transactionsThatDontExistInYnab = transactionsThatDontExistInYnab.filter((transaction) => + moment(transaction.date, YNAB_DATE_FORMAT).isBefore(NOW), ); if (!transactionsThatDontExistInYnab.length) { await emitProgressEvent( @@ -84,12 +69,9 @@ const createTransactions: ExportTransactionsFunction = async ( `Creating ${transactionsThatDontExistInYnab.length} transactions in ynab`, ); try { - await ynabAPI!.transactions.createTransactions( - ynabConfig.options.budgetId, - { - transactions: transactionsThatDontExistInYnab, - }, - ); + await ynabAPI!.transactions.createTransactions(ynabConfig.options.budgetId, { + transactions: transactionsThatDontExistInYnab, + }); return { exportedTransactionsNum: transactionsThatDontExistInYnab.length, }; @@ -115,41 +97,26 @@ function getTransactions(startDate: Date): Promise { ); } -export function getPayeeName( - transaction: EnrichedTransaction, - payeeNameMaxLength = 50, -) { +export function getPayeeName(transaction: EnrichedTransaction, payeeNameMaxLength = 50) { // Specific case for Bank Hapoalim - Where we can extract the payee name from the memo. Note the "." is added // from the israeli-bank-scrapers project, and not from the bank itself - if ( - transaction.memo?.startsWith('לטובת') && - transaction.memo.includes('עבור') - ) { + if (transaction.memo?.startsWith('לטובת') && transaction.memo.includes('עבור')) { return transaction.memo.slice(7, transaction.memo.indexOf('עבור') - 2); } return transaction.description.substring(0, payeeNameMaxLength); } -function convertTransactionToYnabFormat( - originalTransaction: EnrichedTransaction, -): ynab.SaveTransaction { +function convertTransactionToYnabFormat(originalTransaction: EnrichedTransaction): ynab.SaveTransaction { const amount = Math.round(originalTransaction.chargedAmount * 1000); const date = convertTimestampToYnabDateFormat(originalTransaction); return { - account_id: getYnabAccountIdByAccountNumberFromTransaction( - originalTransaction.accountNumber, - ), + account_id: getYnabAccountIdByAccountNumberFromTransaction(originalTransaction.accountNumber), date, // "2019-01-17", amount, // "payee_id": "string", - payee_name: getPayeeName( - originalTransaction, - ynabConfig!.options.maxPayeeNameLength, - ), - category_id: getYnabCategoryIdFromCategoryName( - originalTransaction.category, - ), + payee_name: getPayeeName(originalTransaction, ynabConfig!.options.maxPayeeNameLength), + category_id: getYnabCategoryIdFromCategoryName(originalTransaction.category), memo: originalTransaction.memo, cleared: ynab.SaveTransaction.ClearedEnum.Cleared, // "approved": true, @@ -158,22 +125,15 @@ function convertTransactionToYnabFormat( }; } -function getYnabAccountIdByAccountNumberFromTransaction( - transactionAccountNumber: string, -): string { - const ynabAccountId = - ynabConfig!.options.accountNumbersToYnabAccountIds[ - transactionAccountNumber - ]; +function getYnabAccountIdByAccountNumberFromTransaction(transactionAccountNumber: string): string { + const ynabAccountId = ynabConfig!.options.accountNumbersToYnabAccountIds[transactionAccountNumber]; if (!ynabAccountId) { throw new Error(`Unhandled account number ${transactionAccountNumber}`); } return ynabAccountId; } -function convertTimestampToYnabDateFormat( - originalTransaction: EnrichedTransaction, -): string { +function convertTimestampToYnabDateFormat(originalTransaction: EnrichedTransaction): string { return moment(originalTransaction.date).format(YNAB_DATE_FORMAT); // 2018-12-29T22:00:00.000Z -> 2018-12-29 } @@ -189,9 +149,7 @@ function getYnabCategoryIdFromCategoryName(categoryName?: string) { } export async function initCategories() { - const categories = await ynabAPI!.categories.getCategories( - ynabConfig!.options.budgetId, - ); + const categories = await ynabAPI!.categories.getCategories(ynabConfig!.options.budgetId); categories.data.category_groups.forEach((categoryGroup) => { categoryGroup.categories .map((category) => ({ @@ -211,25 +169,18 @@ async function filterOnlyTransactionsThatDontExistInYnabAlready( ) { let transactionsInYnabBeforeCreatingTheseTransactions: ynab.TransactionDetail[]; if (transactionsFromYnab.has(startDate)) { - transactionsInYnabBeforeCreatingTheseTransactions = - transactionsFromYnab.get(startDate)!; + transactionsInYnabBeforeCreatingTheseTransactions = transactionsFromYnab.get(startDate)!; } else { const transactionsFromYnabResponse = await getTransactions(startDate); - transactionsInYnabBeforeCreatingTheseTransactions = - transactionsFromYnabResponse.data.transactions; - transactionsFromYnab.set( - startDate, - transactionsInYnabBeforeCreatingTheseTransactions, - ); + transactionsInYnabBeforeCreatingTheseTransactions = transactionsFromYnabResponse.data.transactions; + transactionsFromYnab.set(startDate, transactionsInYnabBeforeCreatingTheseTransactions); } - const transactionsThatDontExistInYnab = - transactionsFromFinancialAccounts.filter( - (transactionToCheck) => - !transactionsInYnabBeforeCreatingTheseTransactions.find( - (existingTransaction) => - isSameTransaction(transactionToCheck, existingTransaction), - ), - ); + const transactionsThatDontExistInYnab = transactionsFromFinancialAccounts.filter( + (transactionToCheck) => + !transactionsInYnabBeforeCreatingTheseTransactions.find((existingTransaction) => + isSameTransaction(transactionToCheck, existingTransaction), + ), + ); return transactionsThatDontExistInYnab; } @@ -244,10 +195,7 @@ export function isSameTransaction( // @ts-expect-error error TS18049: 'transactionToCreate.amount' is possibly 'null' or 'undefined' Math.abs(transactionToCreate.amount - transactionFromYnab.amount) < 1000 && // In a transfer transaction the payee name changes, but we still consider this the same transaction - (areStringsEqualIgnoreCaseAndWhitespace( - transactionToCreate.payee_name, - transactionFromYnab.payee_name, - ) || + (areStringsEqualIgnoreCaseAndWhitespace(transactionToCreate.payee_name, transactionFromYnab.payee_name) || isATransferTransaction) ); } @@ -256,10 +204,8 @@ export function areStringsEqualIgnoreCaseAndWhitespace( str1: string | null | undefined = '', str2: string | null | undefined = '', ) { - const trimmedAndLowerCaseStr1 = - str1 && normalizeWhitespace(str1.toLowerCase()); - const trimmedAndLowerCaseStr2 = - str2 && normalizeWhitespace(str2.toLowerCase()); + const trimmedAndLowerCaseStr1 = str1 && normalizeWhitespace(str1.toLowerCase()); + const trimmedAndLowerCaseStr2 = str2 && normalizeWhitespace(str2.toLowerCase()); return trimmedAndLowerCaseStr1 === trimmedAndLowerCaseStr2; } @@ -293,19 +239,14 @@ export async function getYnabAccountDetails( }; } -function doesBudgetIdExistInYnab( - budgetIdToCheck: string, - budgets?: ynab.BudgetSummary[], -): boolean { +function doesBudgetIdExistInYnab(budgetIdToCheck: string, budgets?: ynab.BudgetSummary[]): boolean { if (!budgetIdToCheck) { return false; } return !!budgets && !!budgets.find((budget) => budget.id === budgetIdToCheck); } -export async function getBudgets( - accessToken: string, -): Promise<{ id: string; name: string }[] | undefined> { +export async function getBudgets(accessToken: string): Promise<{ id: string; name: string }[] | undefined> { const localYnabApi = new ynab.API(accessToken); const budgetsResponse = await localYnabApi.budgets.getBudgets(); return budgetsResponse?.data.budgets.map((budget) => ({ @@ -336,18 +277,14 @@ async function getBudgetsAndAccountsData() { const accounts: YnabFinancialAccount[] = []; await Promise.all( budgets.map(async (budget) => { - const budgetAccountsResponse = await ynabAPI!.accounts.getAccounts( - budget.id, - ); - const budgetAccounts = budgetAccountsResponse.data.accounts.map( - ({ id, name, type, deleted, closed }) => ({ - id, - name, - type, - budgetId: budget.id, - active: !deleted && !closed, - }), - ); + const budgetAccountsResponse = await ynabAPI!.accounts.getAccounts(budget.id); + const budgetAccounts = budgetAccountsResponse.data.accounts.map(({ id, name, type, deleted, closed }) => ({ + id, + name, + type, + budgetId: budget.id, + active: !deleted && !closed, + })); accounts.push(...budgetAccounts); }), ); @@ -358,13 +295,8 @@ async function getBudgetsAndAccountsData() { } async function getYnabCategories() { - const categoriesResponse = await ynabAPI!.categories.getCategories( - ynabConfig!.options.budgetId, - ); - const categories = _.flatMap( - categoriesResponse.data.category_groups, - (categoryGroup) => categoryGroup.categories, - ); + const categoriesResponse = await ynabAPI!.categories.getCategories(ynabConfig!.options.budgetId); + const categories = _.flatMap(categoriesResponse.data.category_groups, (categoryGroup) => categoryGroup.categories); const categoryNames = categories.map((category) => category.name); return categoryNames; } diff --git a/packages/main/src/backend/import/bankScraper.ts b/packages/main/src/backend/import/bankScraper.ts index 52a00b92..7bda550d 100644 --- a/packages/main/src/backend/import/bankScraper.ts +++ b/packages/main/src/backend/import/bankScraper.ts @@ -1,10 +1,5 @@ import { type AccountToScrapeConfig } from '@/backend/commonTypes'; -import { - CompanyTypes, - createScraper, - SCRAPERS, - type ScraperOptions, -} from 'israeli-bank-scrapers-core'; +import { CompanyTypes, createScraper, SCRAPERS, type ScraperOptions } from 'israeli-bank-scrapers-core'; export const inputVendors = Object.keys(SCRAPERS) // Deprecated. see https://github.com/eshaham/israeli-bank-scrapers/blob/07ecd3de0c4aa051f119aa943493f0cda943158c/src/definitions.ts#L26-L29 @@ -22,19 +17,10 @@ interface ScrapeParameters { timeout: number; } -type EmitProgressEventFunction = ( - eventCompanyId: string, - message: string, -) => Promise; +type EmitProgressEventFunction = (eventCompanyId: string, message: string) => Promise; export async function scrape( - { - companyId, - credentials, - startDate, - timeout, - showBrowser = false, - }: ScrapeParameters, + { companyId, credentials, startDate, timeout, showBrowser = false }: ScrapeParameters, emitProgressEvent: EmitProgressEventFunction, chromePath: string, ) { diff --git a/packages/main/src/backend/import/categoryCalculationScript.ts b/packages/main/src/backend/import/categoryCalculationScript.ts index c8598fd4..7362aba6 100644 --- a/packages/main/src/backend/import/categoryCalculationScript.ts +++ b/packages/main/src/backend/import/categoryCalculationScript.ts @@ -1,9 +1,7 @@ const INTERNET = 'Internet'; const ELECTRICITY = 'Electric'; -export function getCategoryNameByTransactionDescription( - transactionDescription: string, -): string { +export function getCategoryNameByTransactionDescription(transactionDescription: string): string { if (containsStr('אינטרנט')) return INTERNET; switch (transactionDescription) { @@ -18,10 +16,6 @@ export function getCategoryNameByTransactionDescription( } function containsStr(strToSearchFor: string) { - return ( - transactionDescription - .toLowerCase() - .indexOf(strToSearchFor.toLowerCase()) !== -1 - ); + return transactionDescription.toLowerCase().indexOf(strToSearchFor.toLowerCase()) !== -1; } } diff --git a/packages/main/src/backend/import/downloadChromium.ts b/packages/main/src/backend/import/downloadChromium.ts index d645e395..51d409b1 100644 --- a/packages/main/src/backend/import/downloadChromium.ts +++ b/packages/main/src/backend/import/downloadChromium.ts @@ -1,15 +1,10 @@ import { Browser, install } from '@puppeteer/browsers'; import logger from '/@/logging/logger'; -type PuppeteerProgressCallback = ( - downloadBytes: number, - totalBytes: number, -) => void; +type PuppeteerProgressCallback = (downloadBytes: number, totalBytes: number) => void; type PercentCallback = (percent: number) => void; -const getIntegerPercent = ( - callback: PercentCallback, -): PuppeteerProgressCallback => { +const getIntegerPercent = (callback: PercentCallback): PuppeteerProgressCallback => { let prevPercent = -1; return (downloadBytes: number, totalBytes: number) => { @@ -25,10 +20,7 @@ const revision = '1364960'; // getPuppeteerConfig().chromiumRevision; see https: let downloadProm: ReturnType | null = null; -export default async function downloadChromium( - installPath: string, - onProgress?: PercentCallback, -): Promise { +export default async function downloadChromium(installPath: string, onProgress?: PercentCallback): Promise { if (downloadProm) return downloadProm; const progressCallback = onProgress && getIntegerPercent(onProgress); diff --git a/packages/main/src/backend/import/importTransactions.ts b/packages/main/src/backend/import/importTransactions.ts index fdaba3be..e0f067b5 100644 --- a/packages/main/src/backend/import/importTransactions.ts +++ b/packages/main/src/backend/import/importTransactions.ts @@ -43,9 +43,7 @@ export async function scrapeFinancialAccountsAndFetchTransactions( chromiumPath = scrapingConfig.chromiumPath; } else { console.log('Downloading chromium'); - chromiumPath = await getChrome(userDataPath, (percent) => - emitChromeDownload(eventPublisher, percent), - ); + chromiumPath = await getChrome(userDataPath, (percent) => emitChromeDownload(eventPublisher, percent)); } const limiter = new Bottleneck({ @@ -96,35 +94,25 @@ function buildImporterEvent( function emitChromeDownload(eventPublisher: EventPublisher, percent: number) { console.log(`Downloading chrome ${percent}%`); - eventPublisher.emit( - EventNames.DOWNLOAD_CHROME, - new DownalodChromeEvent(percent), - ); + eventPublisher.emit(EventNames.DOWNLOAD_CHROME, new DownalodChromeEvent(percent)); } -export async function getFinancialAccountDetails(): Promise< - FinancialAccountDetails[] -> { +export async function getFinancialAccountDetails(): Promise { const config = await getConfig(configFilePath); const eventEmitter = new BudgetTrackingEventEmitter(); const startDate = moment().subtract(30, 'days').startOf('day').toDate(); - const companyIdToTransactions = - await scrapeFinancialAccountsAndFetchTransactions( - config.scraping, - startDate, - eventEmitter, - ); + const companyIdToTransactions = await scrapeFinancialAccountsAndFetchTransactions( + config.scraping, + startDate, + eventEmitter, + ); const financialAccountDetails: { name: string; accountNumber: string }[] = []; Object.keys(companyIdToTransactions).forEach((companyId) => { - let accountNumbers = companyIdToTransactions[companyId].map( - (transaction) => transaction.accountNumber, - ); + let accountNumbers = companyIdToTransactions[companyId].map((transaction) => transaction.accountNumber); accountNumbers = _.uniq(accountNumbers); - accountNumbers.forEach((accountNumber) => - financialAccountDetails.push({ name: companyId, accountNumber }), - ); + accountNumbers.forEach((accountNumber) => financialAccountDetails.push({ name: companyId, accountNumber })); }); return financialAccountDetails; } @@ -138,19 +126,10 @@ async function fetchTransactions( timeout: number, ) { try { - await eventPublisher.emit( - EventNames.IMPORTER_START, - buildImporterEvent(account, { message: 'Importer start' }), - ); + await eventPublisher.emit(EventNames.IMPORTER_START, buildImporterEvent(account, { message: 'Importer start' })); - const emitImporterProgressEvent = async ( - eventCompanyId: string, - message: string, - ) => { - await eventPublisher.emit( - EventNames.IMPORTER_PROGRESS, - buildImporterEvent(account, { message }), - ); + const emitImporterProgressEvent = async (eventCompanyId: string, message: string) => { + await eventPublisher.emit(EventNames.IMPORTER_PROGRESS, buildImporterEvent(account, { message })); }; const companyId = account.key; const scrapeResult = await bankScraper.scrape( @@ -165,9 +144,7 @@ async function fetchTransactions( chromePath, ); if (!scrapeResult.success) { - throw new Error( - `${scrapeResult.errorType}: ${scrapeResult.errorMessage}`, - ); + throw new Error(`${scrapeResult.errorType}: ${scrapeResult.errorMessage}`); } const transactions = await postProcessTransactions(account, scrapeResult); @@ -202,29 +179,19 @@ async function postProcessTransactions( if (scrapeResult.accounts) { let transactions = scrapeResult.accounts.flatMap((transactionAccount) => { return transactionAccount.txns.map((transaction) => - enrichTransaction( - transaction, - accountToScrape.key, - transactionAccount.accountNumber, - ), + enrichTransaction(transaction, accountToScrape.key, transactionAccount.accountNumber), ); }); // Filter out pending transactions - transactions = transactions.filter( - (transaction) => transaction.status === TRANSACTION_STATUS_COMPLETED, - ); + transactions = transactions.filter((transaction) => transaction.status === TRANSACTION_STATUS_COMPLETED); transactions.sort(transactionsDateComparator); return transactions; } return []; } -function enrichTransaction( - transaction: Transaction, - companyId: string, - accountNumber: string, -): EnrichedTransaction { +function enrichTransaction(transaction: Transaction, companyId: string, accountNumber: string): EnrichedTransaction { const hash = calculateTransactionHash(transaction, companyId, accountNumber); // const category = categoryCalculation.getCategoryNameByTransactionDescription(transaction.description); const enrichedTransaction: EnrichedTransaction = { diff --git a/packages/main/src/backend/index.ts b/packages/main/src/backend/index.ts index 16c7dcb2..282bb887 100644 --- a/packages/main/src/backend/index.ts +++ b/packages/main/src/backend/index.ts @@ -13,28 +13,20 @@ export { Events, configManager, outputVendors }; export const { inputVendors } = bankScraper; -export async function scrapeAndUpdateOutputVendors( - config: Config, - optionalEventPublisher?: Events.EventPublisher, -) { - const eventPublisher = - optionalEventPublisher ?? new Events.BudgetTrackingEventEmitter(); +export async function scrapeAndUpdateOutputVendors(config: Config, optionalEventPublisher?: Events.EventPublisher) { + const eventPublisher = optionalEventPublisher ?? new Events.BudgetTrackingEventEmitter(); - const startDate = moment() - .subtract(config.scraping.numDaysBack, 'days') - .startOf('day') - .toDate(); + const startDate = moment().subtract(config.scraping.numDaysBack, 'days').startOf('day').toDate(); await eventPublisher.emit(Events.EventNames.IMPORT_PROCESS_START, { message: `Starting to scrape from ${startDate} to today`, }); - const companyIdToTransactions = - await scrapeFinancialAccountsAndFetchTransactions( - config.scraping, - startDate, - eventPublisher, - ); + const companyIdToTransactions = await scrapeFinancialAccountsAndFetchTransactions( + config.scraping, + startDate, + eventPublisher, + ); try { const executionResult = await createTransactionsInExternalVendors( config.outputVendors, diff --git a/packages/main/src/backend/transactions/dates.ts b/packages/main/src/backend/transactions/dates.ts index c91f1a80..c7c9182e 100644 --- a/packages/main/src/backend/transactions/dates.ts +++ b/packages/main/src/backend/transactions/dates.ts @@ -1,7 +1,3 @@ -export const compareObjectsByDate = ( - a: { date: string }, - b: { date: string }, -) => compareDateStrings(a.date, b.date); -const compareDateStrings = (a: string, b: string) => - compareDates(new Date(a), new Date(b)); +export const compareObjectsByDate = (a: { date: string }, b: { date: string }) => compareDateStrings(a.date, b.date); +const compareDateStrings = (a: string, b: string) => compareDates(new Date(a), new Date(b)); const compareDates = (a: Date, b: Date) => a.getTime() - b.getTime(); diff --git a/packages/main/src/backend/transactions/transactions.ts b/packages/main/src/backend/transactions/transactions.ts index 7f9312c5..21b864fa 100644 --- a/packages/main/src/backend/transactions/transactions.ts +++ b/packages/main/src/backend/transactions/transactions.ts @@ -27,38 +27,24 @@ export const calculateTransactionHash = ( companyId: string, accountNumber: string, ) => { - return unifyHash( - `${date}_${chargedAmount}_${description}_${memo}_${companyId}_${accountNumber}`, - ); + return unifyHash(`${date}_${chargedAmount}_${description}_${memo}_${companyId}_${accountNumber}`); }; -export const mergeTransactions = ( - a: EnrichedTransaction[], - b: EnrichedTransaction[], -) => { +export const mergeTransactions = (a: EnrichedTransaction[], b: EnrichedTransaction[]) => { const aObj = transactionArrayToUnifyHash(a); const bObj = transactionArrayToUnifyHash(b); const hashes = uniq(Object.keys(aObj).concat(...Object.keys(bObj))); - const mergedObj = hashes.reduce( - (merged: Record, hash: string) => { - merged[hash] = { ...aObj[hash], ...bObj[hash] }; - return merged; - }, - {}, - ); + const mergedObj = hashes.reduce((merged: Record, hash: string) => { + merged[hash] = { ...aObj[hash], ...bObj[hash] }; + return merged; + }, {}); return Object.values(mergedObj); }; -export const filterExistedHashes = ( - transactions: EnrichedTransaction[], - existingHashes: string[], -) => { +export const filterExistedHashes = (transactions: EnrichedTransaction[], existingHashes: string[]) => { const unifiedExistingHashs = existingHashes.map(unifyHash); - return transactions.filter( - ({ hash }) => !unifiedExistingHashs.includes(unifyHash(hash)), - ); + return transactions.filter(({ hash }) => !unifiedExistingHashs.includes(unifyHash(hash))); }; -export const sortByDate = (transactions: EnrichedTransaction[]) => - transactions.sort(compareObjectsByDate); +export const sortByDate = (transactions: EnrichedTransaction[]) => transactions.sort(compareObjectsByDate); diff --git a/packages/main/src/handlers/index.ts b/packages/main/src/handlers/index.ts index b4f60902..b57b3eb5 100644 --- a/packages/main/src/handlers/index.ts +++ b/packages/main/src/handlers/index.ts @@ -4,19 +4,11 @@ import { type Credentials } from '@/backend/commonTypes'; import { getConfig } from '@/backend/configManager/configManager'; import { BudgetTrackingEventEmitter } from '@/backend/eventEmitters/EventEmitter'; import electronGoogleOAuth2Connector from '@/backend/export/outputVendors/googleSheets/electronGoogleOAuth2Connector'; -import { - createClient, - validateToken, -} from '@/backend/export/outputVendors/googleSheets/googleAuth'; +import { createClient, validateToken } from '@/backend/export/outputVendors/googleSheets/googleAuth'; import { createSpreadsheet } from '@/backend/export/outputVendors/googleSheets/googleSheets'; import { getAllSpreadsheets } from '@/backend/export/outputVendors/googleSheets/googleSheetsInternalAPI'; import { getYnabAccountData } from '@/manual/setupHelpers'; -import { - dialog, - ipcMain, - type IpcMainEvent, - type IpcMainInvokeEvent, -} from 'electron'; +import { dialog, ipcMain, type IpcMainEvent, type IpcMainInvokeEvent } from 'electron'; import { discord, repository } from '../../../../package.json'; import Sentry from '../logging/sentry'; import { getConfigHandler, updateConfigHandler } from './configHandlers'; @@ -58,10 +50,8 @@ const functions: Record = { extra: Record, ) => Sentry.userReportProblem(title, body, logs, email, extra), // Google Sheets - getAllUserSpreadsheets: (_: unknown, credentials: Credentials) => - getAllSpreadsheets(createClient(credentials)), - validateToken: (_: unknown, credentials: Credentials) => - validateToken(credentials), + getAllUserSpreadsheets: (_: unknown, credentials: Credentials) => getAllSpreadsheets(createClient(credentials)), + validateToken: (_: unknown, credentials: Credentials) => validateToken(credentials), electronGoogleOAuth2Connector, createSpreadsheet: (_, spreadsheetTitle: string, credentials: Credentials) => createSpreadsheet(spreadsheetTitle, credentials), @@ -84,14 +74,8 @@ export const registerHandlers = () => { }); ipcMain.removeAllListeners('getYnabAccountData'); - ipcMain.on( - 'getYnabAccountData', - async (event, _event, ynabExporterOptions) => { - const ynabAccountData = await getYnabAccountData( - _event, - ynabExporterOptions, - ); - event.reply('getYnabAccountData', ynabAccountData); - }, - ); + ipcMain.on('getYnabAccountData', async (event, _event, ynabExporterOptions) => { + const ynabAccountData = await getYnabAccountData(_event, ynabExporterOptions); + event.reply('getYnabAccountData', ynabAccountData); + }); }; diff --git a/packages/main/src/logging/analytics.ts b/packages/main/src/logging/analytics.ts index dd3f3144..61e726f9 100644 --- a/packages/main/src/logging/analytics.ts +++ b/packages/main/src/logging/analytics.ts @@ -1,13 +1,8 @@ -import { - EventNames, - type BudgetTrackingEventEmitter, -} from '@/backend/eventEmitters/EventEmitter'; +import { EventNames, type BudgetTrackingEventEmitter } from '@/backend/eventEmitters/EventEmitter'; import Analytics from 'analytics-node'; import { machineId } from 'node-machine-id'; -const analytics = import.meta.env.VITE_SEGMENT_WRITE_KEY - ? new Analytics(import.meta.env.VITE_SEGMENT_WRITE_KEY) - : null; +const analytics = import.meta.env.VITE_SEGMENT_WRITE_KEY ? new Analytics(import.meta.env.VITE_SEGMENT_WRITE_KEY) : null; type EventProperties = Record; @@ -25,10 +20,7 @@ const EVENTS_TO_TRACK: EventNames[] = [ EventNames.GENERAL_ERROR, ]; -export async function trackPage( - pageName: string, - properties?: EventProperties, -) { +export async function trackPage(pageName: string, properties?: EventProperties) { const event = await buildEvent(properties); analytics?.page({ name: pageName, @@ -36,10 +28,7 @@ export async function trackPage( }); } -export async function trackEvent( - eventType: string, - properties?: EventProperties, -) { +export async function trackEvent(eventType: string, properties?: EventProperties) { const event = await buildEvent(properties); analytics?.track({ ...event, @@ -47,9 +36,7 @@ export async function trackEvent( }); } -export async function initAnalyticsEventHandling( - eventEmitter: BudgetTrackingEventEmitter, -) { +export async function initAnalyticsEventHandling(eventEmitter: BudgetTrackingEventEmitter) { eventEmitter.onAny((eventName, eventData) => { if (EVENTS_TO_TRACK.includes(eventName)) { trackEvent(eventName.toString(), { diff --git a/packages/main/src/logging/logger.ts b/packages/main/src/logging/logger.ts index 592d68cd..0e77b66f 100644 --- a/packages/main/src/logging/logger.ts +++ b/packages/main/src/logging/logger.ts @@ -1,9 +1,5 @@ import { App } from '@/app-globals'; -import type { - LogFunctions, - LogLevel, - MainErrorHandlerOptions, -} from 'electron-log'; +import type { LogFunctions, LogLevel, MainErrorHandlerOptions } from 'electron-log'; import log from 'electron-log/main'; import fs from 'fs'; import { EOL } from 'os'; @@ -30,16 +26,12 @@ const onError: MainErrorHandlerOptions['onError'] = ({ error }) => { log.errorHandler.startCatching({ onError }); export const getLastLines = (n: number) => { - const lines = fs - .readFileSync(log.transports.file.getFile().path) - .toString() - .split(EOL); + const lines = fs.readFileSync(log.transports.file.getFile().path).toString().split(EOL); const lastLines = lines.slice(lines.length - n); return lastLines.join(EOL); }; -export const getLogsFolder = () => - path.dirname(log.transports.file.getFile().path); +export const getLogsFolder = () => path.dirname(log.transports.file.getFile().path); logger.info(`Logs folder: ${getLogsFolder()}`); diff --git a/packages/main/src/logging/sentry.ts b/packages/main/src/logging/sentry.ts index abb3b9c1..baeb8c73 100644 --- a/packages/main/src/logging/sentry.ts +++ b/packages/main/src/logging/sentry.ts @@ -1,8 +1,4 @@ -import { - captureMessage, - init, - type ElectronMainOptions, -} from '@sentry/electron/main'; +import { captureMessage, init, type ElectronMainOptions } from '@sentry/electron/main'; const reporterConfiguration = { dsn: import.meta.env.VITE_SENTRY_DSN, diff --git a/packages/main/src/mainWindow.ts b/packages/main/src/mainWindow.ts index 5180983c..79e6c5e6 100644 --- a/packages/main/src/mainWindow.ts +++ b/packages/main/src/mainWindow.ts @@ -33,10 +33,7 @@ async function createWindow() { /** * Load the main page of the main window. */ - if ( - import.meta.env.DEV && - import.meta.env.VITE_DEV_SERVER_URL !== undefined - ) { + if (import.meta.env.DEV && import.meta.env.VITE_DEV_SERVER_URL !== undefined) { /** * Load from the Vite dev server for development. */ @@ -51,11 +48,7 @@ async function createWindow() { * @see https://github.com/nodejs/node/issues/12682 * @see https://github.com/electron/electron/issues/6869 */ - await browserWindow.loadFile( - fileURLToPath( - new URL('./../../renderer/dist/index.html', import.meta.url), - ), - ); + await browserWindow.loadFile(fileURLToPath(new URL('./../../renderer/dist/index.html', import.meta.url))); } return browserWindow; diff --git a/packages/main/src/manual/setupHelpers.ts b/packages/main/src/manual/setupHelpers.ts index c9bd315f..dc3b8ffe 100644 --- a/packages/main/src/manual/setupHelpers.ts +++ b/packages/main/src/manual/setupHelpers.ts @@ -1,20 +1,10 @@ /* eslint-disable no-console */ -import { - FETCH_YNAB_ACCOUNT_DATA_STATUS, - type YnabAccountDataType, - type YnabConfig, -} from '@/backend/commonTypes'; +import { FETCH_YNAB_ACCOUNT_DATA_STATUS, type YnabAccountDataType, type YnabConfig } from '@/backend/commonTypes'; import { getConfig } from '@/backend/configManager/configManager'; -import { - getYnabAccountDetails, - isAccessTokenValid, -} from '@/backend/export/outputVendors/ynab/ynab'; +import { getYnabAccountDetails, isAccessTokenValid } from '@/backend/export/outputVendors/ynab/ynab'; // import { getFinancialAccountDetails } from '@/backend/import/importTransactions'; -export async function getYnabAccountData( - _: unknown, - ynabOptions: YnabConfig['options'], -): Promise { +export async function getYnabAccountData(_: unknown, ynabOptions: YnabConfig['options']): Promise { const config = await getConfig(); const accessTokenValid = await isAccessTokenValid(ynabOptions.accessToken); diff --git a/packages/main/src/security-restrictions.ts b/packages/main/src/security-restrictions.ts index e590f9d9..b495ee0f 100644 --- a/packages/main/src/security-restrictions.ts +++ b/packages/main/src/security-restrictions.ts @@ -4,9 +4,7 @@ import { URL } from 'node:url'; /** * Union for all existing permissions in electron */ -type Permission = Parameters< - Exclude[0], null> ->[1]; +type Permission = Parameters[0], null>>[1]; /** * A list of origins that you allow open INSIDE the application and permissions for them. @@ -29,9 +27,7 @@ const ALLOWED_ORIGINS_AND_PERMISSIONS = new Map>( * href="https://github.com/" * > */ -const ALLOWED_EXTERNAL_ORIGINS = new Set<`https://${string}`>([ - 'https://github.com', -]); +const ALLOWED_EXTERNAL_ORIGINS = new Set<`https://${string}`>(['https://github.com']); app.on('web-contents-created', (_, contents) => { /** @@ -62,21 +58,16 @@ app.on('web-contents-created', (_, contents) => { * * @see https://www.electronjs.org/docs/latest/tutorial/security#5-handle-session-permission-requests-from-remote-content */ - contents.session.setPermissionRequestHandler( - (webContents, permission, callback) => { - const { origin } = new URL(webContents.getURL()); - - const permissionGranted = - !!ALLOWED_ORIGINS_AND_PERMISSIONS.get(origin)?.has(permission); - callback(permissionGranted); - - if (!permissionGranted && import.meta.env.DEV) { - console.warn( - `${origin} requested permission for '${permission}', but was rejected.`, - ); - } - }, - ); + contents.session.setPermissionRequestHandler((webContents, permission, callback) => { + const { origin } = new URL(webContents.getURL()); + + const permissionGranted = !!ALLOWED_ORIGINS_AND_PERMISSIONS.get(origin)?.has(permission); + callback(permissionGranted); + + if (!permissionGranted && import.meta.env.DEV) { + console.warn(`${origin} requested permission for '${permission}', but was rejected.`); + } + }); /** * Hyperlinks leading to allowed sites are opened in the default browser. @@ -113,9 +104,7 @@ app.on('web-contents-created', (_, contents) => { const { origin } = new URL(params.src); if (!ALLOWED_ORIGINS_AND_PERMISSIONS.has(origin)) { if (import.meta.env.DEV) { - console.warn( - `A webview tried to attach ${params.src}, but was blocked.`, - ); + console.warn(`A webview tried to attach ${params.src}, but was blocked.`); } event.preventDefault(); diff --git a/packages/main/tests/unit.spec.ts b/packages/main/tests/unit.spec.ts index 3f650fb3..f496b311 100644 --- a/packages/main/tests/unit.spec.ts +++ b/packages/main/tests/unit.spec.ts @@ -1,11 +1,4 @@ -import { - beforeEach, - expect, - test, - vi, - type MockedClass, - type MockedObject, -} from 'vitest'; +import { beforeEach, expect, test, vi, type MockedClass, type MockedObject } from 'vitest'; import { restoreOrCreateWindow } from '../src/mainWindow'; import { BrowserWindow } from 'electron'; @@ -17,12 +10,8 @@ vi.mock('electron', () => { // Use "as unknown as" because vi.fn() does not have static methods const bw = vi.fn() as unknown as MockedClass; bw.getAllWindows = vi.fn(() => bw.mock.instances); - bw.prototype.loadURL = vi.fn((_: string, __?: Electron.LoadURLOptions) => - Promise.resolve(), - ); - bw.prototype.loadFile = vi.fn((_: string, __?: Electron.LoadFileOptions) => - Promise.resolve(), - ); + bw.prototype.loadURL = vi.fn((_: string, __?: Electron.LoadURLOptions) => Promise.resolve()); + bw.prototype.loadFile = vi.fn((_: string, __?: Electron.LoadFileOptions) => Promise.resolve()); // Use "any" because the on function is overloaded // eslint-disable-next-line @typescript-eslint/no-explicit-any bw.prototype.on = vi.fn(); @@ -56,13 +45,9 @@ test('Should create a new window', async () => { const loadFileCalls = instance.loadFile.mock.calls.length; expect(loadURLCalls + loadFileCalls).toBe(1); if (loadURLCalls === 1) { - expect(instance.loadURL).toHaveBeenCalledWith( - expect.stringMatching(/index\.html$/), - ); + expect(instance.loadURL).toHaveBeenCalledWith(expect.stringMatching(/index\.html$/)); } else { - expect(instance.loadFile).toHaveBeenCalledWith( - expect.stringMatching(/index\.html$/), - ); + expect(instance.loadFile).toHaveBeenCalledWith(expect.stringMatching(/index\.html$/)); } }); diff --git a/packages/preload/src/commonTypes.ts b/packages/preload/src/commonTypes.ts index 44c85186..bac6c7c2 100644 --- a/packages/preload/src/commonTypes.ts +++ b/packages/preload/src/commonTypes.ts @@ -38,14 +38,8 @@ export interface AppInfo { currentVersion: string; } -export type OutputVendorConfigs = Exclude< - Config['outputVendors'][OutputVendorName], - undefined ->; -export type OutputVendorConfig = Exclude< - Config['outputVendors'][T], - undefined ->; +export type OutputVendorConfigs = Exclude; +export type OutputVendorConfig = Exclude; interface OutputVendorConfigBase { active: boolean; @@ -202,10 +196,7 @@ export interface FinancialAccountDetails { accountNumber: string; } -export type HandleScrapingEvent = ( - eventName: string, - budgetTrackingEvent?: BudgetTrackingEvent, -) => void; +export type HandleScrapingEvent = (eventName: string, budgetTrackingEvent?: BudgetTrackingEvent) => void; export enum FETCH_YNAB_ACCOUNT_DATA_STATUS { SUCCESS = 'SUCCESS', diff --git a/packages/preload/src/eventsBridge.ts b/packages/preload/src/eventsBridge.ts index a81285b7..df9771d5 100644 --- a/packages/preload/src/eventsBridge.ts +++ b/packages/preload/src/eventsBridge.ts @@ -17,9 +17,7 @@ export async function updateConfig(config: Config) { await electron.ipcRenderer.invoke('updateConfig', JSON.stringify(config)); } -export async function getYnabAccountData( - ynabOptions: YnabConfig['options'], -): Promise { +export async function getYnabAccountData(ynabOptions: YnabConfig['options']): Promise { return electron.ipcRenderer.invoke('getYnabAccountData', ynabOptions); } @@ -80,9 +78,7 @@ export async function quitAndInstall() { } // Google Sheets -export async function validateToken( - credentials: Credentials, -): Promise { +export async function validateToken(credentials: Credentials): Promise { return electron.ipcRenderer.invoke('validateToken', credentials); } export async function getAllUserSpreadsheets(credentials: Credentials) { @@ -91,13 +87,6 @@ export async function getAllUserSpreadsheets(credentials: Credentials) { export async function electronGoogleOAuth2Connector(): Promise { return electron.ipcRenderer.invoke('electronGoogleOAuth2Connector'); } -export async function createSpreadsheet( - spreadsheetId: string, - credentials: Credentials, -): Promise { - return electron.ipcRenderer.invoke( - 'createSpreadsheet', - spreadsheetId, - credentials, - ); +export async function createSpreadsheet(spreadsheetId: string, credentials: Credentials): Promise { + return electron.ipcRenderer.invoke('createSpreadsheet', spreadsheetId, credentials); } diff --git a/packages/renderer/src/accountMetadata.tsx b/packages/renderer/src/accountMetadata.tsx index 957a9655..fe7ae94a 100644 --- a/packages/renderer/src/accountMetadata.tsx +++ b/packages/renderer/src/accountMetadata.tsx @@ -19,42 +19,41 @@ const icons = { ...exporterIcons, }; -const accountIdToDisplayName: Record = - { - [CompanyTypes.MAX]: 'Max', - [CompanyTypes.AMEX]: 'אמריקן אקספרס', - [CompanyTypes.BEINLEUMI]: 'הבינלאומי', - [CompanyTypes.HAPOALIM_BE_ONLINE]: 'הפועלים בי אונליין', - [CompanyTypes.ISRACARD]: 'ישראכרט', - [CompanyTypes.LEUMI_CARD]: 'לאומי קארד', - [CompanyTypes.OTSAR_HAHAYAL]: 'אוצר החייל', - [CompanyTypes.UNION]: 'איגוד', - [CompanyTypes.LEUMI]: 'לאומי', - [CompanyTypes.MIZRAHI]: 'מזרחי', - [CompanyTypes.HAPOALIM]: 'הפועלים', - [CompanyTypes.VISACAL]: 'ויזה כאל', - [CompanyTypes.DISCOUNT]: 'דיסקונט', - [CompanyTypes.YAHAV]: 'יהב', - [CompanyTypes.BEYAHAD_BISHVILHA]: 'ביחד בשבילך', - [CompanyTypes.MASSAD]: 'מסד', - [CompanyTypes.BEHATSDAA]: 'בהצדעה', - [CompanyTypes.MERCANTILE]: 'מרכנתיל', - [OutputVendorName.CSV]: 'אקסל', - [OutputVendorName.GOOGLE_SHEETS]: 'Google Sheets', - [OutputVendorName.YNAB]: 'Ynab', - [OutputVendorName.JSON]: 'Json', - }; +const accountIdToDisplayName: Record = { + [CompanyTypes.MAX]: 'Max', + [CompanyTypes.AMEX]: 'אמריקן אקספרס', + [CompanyTypes.BEINLEUMI]: 'הבינלאומי', + [CompanyTypes.HAPOALIM_BE_ONLINE]: 'הפועלים בי אונליין', + [CompanyTypes.ISRACARD]: 'ישראכרט', + [CompanyTypes.LEUMI_CARD]: 'לאומי קארד', + [CompanyTypes.OTSAR_HAHAYAL]: 'אוצר החייל', + [CompanyTypes.UNION]: 'איגוד', + [CompanyTypes.LEUMI]: 'לאומי', + [CompanyTypes.MIZRAHI]: 'מזרחי', + [CompanyTypes.HAPOALIM]: 'הפועלים', + [CompanyTypes.VISACAL]: 'ויזה כאל', + [CompanyTypes.DISCOUNT]: 'דיסקונט', + [CompanyTypes.YAHAV]: 'יהב', + [CompanyTypes.BEYAHAD_BISHVILHA]: 'ביחד בשבילך', + [CompanyTypes.MASSAD]: 'מסד', + [CompanyTypes.BEHATSDAA]: 'בהצדעה', + [CompanyTypes.MERCANTILE]: 'מרכנתיל', + [OutputVendorName.CSV]: 'אקסל', + [OutputVendorName.GOOGLE_SHEETS]: 'Google Sheets', + [OutputVendorName.YNAB]: 'Ynab', + [OutputVendorName.JSON]: 'Json', +}; -const accountMetadata: Record< - CompanyTypes | OutputVendorName, - AccountMetadata -> = mapValues(accountIdToDisplayName, (displayName, accountId) => { - return { - companyId: accountId, - companyName: displayName, - logo: icons[accountId as CompanyTypes | OutputVendorName], - }; -}); +const accountMetadata: Record = mapValues( + accountIdToDisplayName, + (displayName, accountId) => { + return { + companyId: accountId, + companyName: displayName, + logo: icons[accountId as CompanyTypes | OutputVendorName], + }; + }, +); const CARD_SIX_DIGITS_FIELD = 'card6Digits'; const USERCODE_FIELD = 'userCode'; @@ -105,27 +104,22 @@ export const LOGIN_FIELD_MIN_LENGTH = { [NATIONAL_ID_FIELD]: 9, }; -export const importers: Account[] = Object.values(CompanyTypes).map( - (importerName) => { - const { companyId, companyName, logo } = accountMetadata[importerName]; +export const importers: Account[] = Object.values(CompanyTypes).map((importerName) => { + const { companyId, companyName, logo } = accountMetadata[importerName]; - const importer: Account = { - id: importerName, - companyId: companyId as CompanyTypes | OutputVendorName, - displayName: companyName, - logo, - type: AccountType.IMPORTER, - active: true, - logs: [], - }; - return importer; - }, -); + const importer: Account = { + id: importerName, + companyId: companyId as CompanyTypes | OutputVendorName, + displayName: companyName, + logo, + type: AccountType.IMPORTER, + active: true, + logs: [], + }; + return importer; +}); -export const exporterUIHandlers: Record< - OutputVendorName, - ExportResultMetadata -> = { +export const exporterUIHandlers: Record = { [OutputVendorName.YNAB]: { resultType: ExporterResultType.WEBSITE_URL, getResultUri(exporter: YnabConfig): string { diff --git a/packages/renderer/src/components/Body.tsx b/packages/renderer/src/components/Body.tsx index 27ff617e..dd9e8e49 100644 --- a/packages/renderer/src/components/Body.tsx +++ b/packages/renderer/src/components/Body.tsx @@ -32,9 +32,7 @@ const Body = () => { return scrape(configStore.handleScrapingEvent.bind(configStore)); }, [configStore]); - const [modalStatus, setModalStatus] = useState( - ModalStatus.HIDDEN, - ); + const [modalStatus, setModalStatus] = useState(ModalStatus.HIDDEN); const [currentAccount, setCurrentAccount] = useState(); const closeModal = () => setModalStatus(ModalStatus.HIDDEN); @@ -101,46 +99,29 @@ const Body = () => { onHide={closeModal} dialogClassName={wideModal ? styles.modalWide : ''} > - + - {modalStatus === ModalStatus.LOGS && currentAccount && ( - + {modalStatus === ModalStatus.LOGS && currentAccount && } + {modalStatus === ModalStatus.IMPORTER_SETTINGS && currentAccount && ( + + )} + {modalStatus === ModalStatus.EXPORTER_SETTINGS && currentAccount && ( + )} - {modalStatus === ModalStatus.IMPORTER_SETTINGS && - currentAccount && ( - - )} - {modalStatus === ModalStatus.EXPORTER_SETTINGS && - currentAccount && ( - - )} {modalStatus === ModalStatus.NEW_SCRAPER && ( )} - {modalStatus === ModalStatus.GENERAL_SETTINGS && ( - - )} + {modalStatus === ModalStatus.GENERAL_SETTINGS && } - { ); }; -function shouldShowWideModal( - modalStatus: ModalStatus, - currentAccount?: Account, -) { +function shouldShowWideModal(modalStatus: ModalStatus, currentAccount?: Account) { return ( modalStatus === ModalStatus.EXPORTER_SETTINGS && currentAccount && diff --git a/packages/renderer/src/components/CheckForUpdates.tsx b/packages/renderer/src/components/CheckForUpdates.tsx index c3c2611e..c76db5eb 100644 --- a/packages/renderer/src/components/CheckForUpdates.tsx +++ b/packages/renderer/src/components/CheckForUpdates.tsx @@ -1,9 +1,4 @@ -import { - checkForUpdate, - downloadUpdate, - openExternal, - quitAndInstall, -} from '#preload'; +import { checkForUpdate, downloadUpdate, openExternal, quitAndInstall } from '#preload'; import { useState } from 'react'; import { Button, Spinner } from 'react-bootstrap'; import { useAppInfoStore } from '../store'; @@ -31,11 +26,7 @@ function CheckForUpdates() { try { const info = await checkForUpdate(); setUpdateInfo(info); - setUpdateState( - info - ? UPDATE_STATES.NEW_VERSION_AVAILABLE - : UPDATE_STATES.NO_NEW_VERSION, - ); + setUpdateState(info ? UPDATE_STATES.NEW_VERSION_AVAILABLE : UPDATE_STATES.NO_NEW_VERSION); } catch (error) { console.error(error); setUpdateState(UPDATE_STATES.ERROR); @@ -54,29 +45,21 @@ function CheckForUpdates() { }; const openGithubRelease = () => { - openExternal( - `${store.appInfo?.repository}/releases/tag/v${updateInfo?.version}`, - ); + openExternal(`${store.appInfo?.repository}/releases/tag/v${updateInfo?.version}`); }; const openCompare = () => { - openExternal( - `${store.appInfo?.repository}/compare/v${store.appInfo?.currentVersion}...v${updateInfo?.version}`, - ); + openExternal(`${store.appInfo?.repository}/compare/v${store.appInfo?.currentVersion}...v${updateInfo?.version}`); }; return ( <> - {updateState === UPDATE_STATES.LOADING && ( - - )} + {updateState === UPDATE_STATES.LOADING && } {updateState === UPDATE_STATES.INIT && ( )} - {updateState === UPDATE_STATES.NO_NEW_VERSION && ( -
אין עדכונים זמינים
- )} + {updateState === UPDATE_STATES.NO_NEW_VERSION &&
אין עדכונים זמינים
} {updateState === UPDATE_STATES.NEW_VERSION_AVAILABLE && (
-
diff --git a/packages/renderer/src/components/accounts/Importers.tsx b/packages/renderer/src/components/accounts/Importers.tsx index a448122b..a0dccd42 100644 --- a/packages/renderer/src/components/accounts/Importers.tsx +++ b/packages/renderer/src/components/accounts/Importers.tsx @@ -19,12 +19,7 @@ interface ImportersProps { handleNewAccountClicked?: () => void; } -function Importers({ - accounts, - isScraping, - showModal, - handleNewAccountClicked, -}: ImportersProps) { +function Importers({ accounts, isScraping, showModal, handleNewAccountClicked }: ImportersProps) { const configStore = useConfigStore(); return ( <> @@ -33,20 +28,13 @@ function Importers({ { - configStore.openResults(account.companyId as OutputVendorName); - }, - )} + actionButtons={getActionButtons(showModal, account, isScraping, () => { + configStore.openResults(account.companyId as OutputVendorName); + })} /> ); })} - {handleNewAccountClicked ? ( - - ) : null} + {handleNewAccountClicked ? : null} ); } @@ -68,18 +56,14 @@ export function getActionButtons( clickHandler: () => showModal( account, - account.type === TypeOfAccount.IMPORTER - ? ModalStatus.IMPORTER_SETTINGS - : ModalStatus.EXPORTER_SETTINGS, + account.type === TypeOfAccount.IMPORTER ? ModalStatus.IMPORTER_SETTINGS : ModalStatus.EXPORTER_SETTINGS, ), tooltipText: 'הגדרות', }; const actionButtons: ActionButton[] = []; - const shouldLog = - account.status !== AccountStatus.PENDING && - account.status !== AccountStatus.IDLE; + const shouldLog = account.status !== AccountStatus.PENDING && account.status !== AccountStatus.IDLE; const openResultsButton = { icon: resultsIcon, diff --git a/packages/renderer/src/components/accounts/NewAccount.tsx b/packages/renderer/src/components/accounts/NewAccount.tsx index 4b0edd69..a2c243ea 100644 --- a/packages/renderer/src/components/accounts/NewAccount.tsx +++ b/packages/renderer/src/components/accounts/NewAccount.tsx @@ -8,13 +8,7 @@ interface NewAccountProps { export default function NewAccount({ onClick }: NewAccountProps) { return (
- {'חשבון + {'חשבון
חשבון חדש
diff --git a/packages/renderer/src/components/accounts/StatusIndicator.tsx b/packages/renderer/src/components/accounts/StatusIndicator.tsx index 514826b7..a88e4993 100644 --- a/packages/renderer/src/components/accounts/StatusIndicator.tsx +++ b/packages/renderer/src/components/accounts/StatusIndicator.tsx @@ -13,13 +13,7 @@ export default function StatusIndicator({ status }: StatusIndicatorProps) { case AccountStatus.PENDING: return pending; case AccountStatus.IN_PROGRESS: - return ( - - ); + return ; case AccountStatus.DONE: return success; case AccountStatus.ERROR: diff --git a/packages/renderer/src/components/accounts/Toggle.tsx b/packages/renderer/src/components/accounts/Toggle.tsx index f825a60d..e3f12438 100644 --- a/packages/renderer/src/components/accounts/Toggle.tsx +++ b/packages/renderer/src/components/accounts/Toggle.tsx @@ -9,7 +9,5 @@ interface ToggleProps { export default function Toggle({ on, onChange }: ToggleProps) { const icon = on ? toggleOn : toggleOff; const altText = on ? 'Active' : 'Not active'; - return ( - {altText} - ); + return {altText}; } diff --git a/packages/renderer/src/components/exporters/EditExporter.tsx b/packages/renderer/src/components/exporters/EditExporter.tsx index b0c750a2..bd84ac45 100644 --- a/packages/renderer/src/components/exporters/EditExporter.tsx +++ b/packages/renderer/src/components/exporters/EditExporter.tsx @@ -1,24 +1,14 @@ -import { - OutputVendorName, - type Exporter, - type YnabConfig, - type GoogleSheetsConfig, -} from '../../types'; +import { OutputVendorName, type Exporter, type YnabConfig, type GoogleSheetsConfig } from '../../types'; import EditFileExporter from './EditFileExporter'; import EditYnabExporter from './EditYnabExporter'; import EditSheetsExporter from './google-sheets/EditSheetsExporter'; export interface EditExporterProps { - handleSave: ( - exporterConfig: Exporter | YnabConfig | GoogleSheetsConfig, - ) => Promise; + handleSave: (exporterConfig: Exporter | YnabConfig | GoogleSheetsConfig) => Promise; exporter: Exporter; } -export default function EditExporter({ - handleSave, - exporter, -}: EditExporterProps) { +export default function EditExporter({ handleSave, exporter }: EditExporterProps) { const exporterTypeToEditComponent = new Map(); exporterTypeToEditComponent.set( OutputVendorName.CSV, @@ -30,17 +20,11 @@ export default function EditExporter({ ); exporterTypeToEditComponent.set( OutputVendorName.YNAB, - , + , ); exporterTypeToEditComponent.set( OutputVendorName.GOOGLE_SHEETS, - , + , ); return <>{exporterTypeToEditComponent.get(exporter.companyId)}; } diff --git a/packages/renderer/src/components/exporters/EditFileExporter.tsx b/packages/renderer/src/components/exporters/EditFileExporter.tsx index 7087a94d..53a23e03 100644 --- a/packages/renderer/src/components/exporters/EditFileExporter.tsx +++ b/packages/renderer/src/components/exporters/EditFileExporter.tsx @@ -29,9 +29,7 @@ const EditFileExporter = ({ handleSave, exporter }: EditFileExporterProps) => { }); }; - const handleChooseFile: React.ChangeEventHandler = ( - event, - ) => { + const handleChooseFile: React.ChangeEventHandler = (event) => { updateOption({ filePath: event.currentTarget.value, }); @@ -50,37 +48,20 @@ const EditFileExporter = ({ handleSave, exporter }: EditFileExporterProps) => { return (
- + לאיזה קובץ לכתוב את הטרנזאקציות? - +
diff --git a/packages/renderer/src/components/exporters/EditYnabExporter.tsx b/packages/renderer/src/components/exporters/EditYnabExporter.tsx index 4e81e103..2af61ca1 100644 --- a/packages/renderer/src/components/exporters/EditYnabExporter.tsx +++ b/packages/renderer/src/components/exporters/EditYnabExporter.tsx @@ -16,10 +16,7 @@ interface EditYnabExporterProps { const INVALID_ACCESS_TOKEN = 'INVALID_ACCESS_TOKEN'; -const EditYnabExporter = ({ - handleSave, - exporterConfig, -}: EditYnabExporterProps) => { +const EditYnabExporter = ({ handleSave, exporterConfig }: EditYnabExporterProps) => { const [ynabOptions, setYnabOptions] = useState( toJS(exporterConfig.options), ); @@ -27,23 +24,18 @@ const EditYnabExporter = ({ const store = useStore(); const isLoading = !store.ynabAccountData || store.fetchingYnabAccountData; - const isValidAccessToken = - !isLoading && store.ynabAccountData?.status !== INVALID_ACCESS_TOKEN; + const isValidAccessToken = !isLoading && store.ynabAccountData?.status !== INVALID_ACCESS_TOKEN; - const updateOptionsState = useCallback( - (optionUpdates: Partial) => { - setYnabOptions((prevYnabOptions) => ({ - ...prevYnabOptions, - ...optionUpdates, - })); - }, - [], - ); + const updateOptionsState = useCallback((optionUpdates: Partial) => { + setYnabOptions((prevYnabOptions) => ({ + ...prevYnabOptions, + ...optionUpdates, + })); + }, []); // Set default budget id if not set useEffect(() => { - const defaultBudgetId = - store.ynabAccountData?.ynabAccountData?.budgets[0]?.id; + const defaultBudgetId = store.ynabAccountData?.ynabAccountData?.budgets[0]?.id; if (!ynabOptions.budgetId && defaultBudgetId) { updateOptionsState({ budgetId: defaultBudgetId }); } @@ -65,9 +57,7 @@ const EditYnabExporter = ({ const handleOptionChangeEvent = ( propertyName: keyof YnabConfig['options'], - event: React.ChangeEvent< - HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement - >, + event: React.ChangeEvent, ) => { updateOptionsState({ [propertyName]: event.target.value }); }; @@ -91,9 +81,7 @@ const EditYnabExporter = ({ - handleOptionChangeEvent('accessToken', event) - } + onChange={(event) => handleOptionChangeEvent('accessToken', event)} /> {isValidAccessToken && ( @@ -102,39 +90,22 @@ const EditYnabExporter = ({ - handleOptionChangeEvent('budgetId', event) - } + onChange={(event) => handleOptionChangeEvent('budgetId', event)} > - {store.ynabAccountData?.ynabAccountData?.budgets.map( - (budget) => ( - - ), - )} + {store.ynabAccountData?.ynabAccountData?.budgets.map((budget) => ( + + ))} )} - {!isLoading && !isValidAccessToken && ( -
Invalid access token
- )} - {isLoading && ( - - )} + {!isLoading && !isValidAccessToken &&
Invalid access token
} + {isLoading && } {!isLoading && isValidAccessToken && ( - + )} - setActive(!active)} - label="Active" - checked={active} - /> + setActive(!active)} label="Active" checked={active} />
diff --git a/packages/renderer/src/components/exporters/YnabAccountMappingTable.tsx b/packages/renderer/src/components/exporters/YnabAccountMappingTable.tsx index f145ffb9..e65dfaec 100644 --- a/packages/renderer/src/components/exporters/YnabAccountMappingTable.tsx +++ b/packages/renderer/src/components/exporters/YnabAccountMappingTable.tsx @@ -6,16 +6,13 @@ import 'react-bootstrap-table-next/dist/react-bootstrap-table2.min.css'; import cellEditFactory, { Type } from 'react-bootstrap-table2-editor'; import { type YnabAccountDataType, type YnabConfig } from '../../types'; -type AccountNumberToYnabAccountIdMappingObject = - YnabConfig['options']['accountNumbersToYnabAccountIds']; +type AccountNumberToYnabAccountIdMappingObject = YnabConfig['options']['accountNumbersToYnabAccountIds']; interface YnabAccountMappingTableProps { accountNumberToYnabIdMapping: AccountNumberToYnabAccountIdMappingObject; ynabAccountData?: YnabAccountDataType; budgetId: string; - onUpdate: ( - accountNumberToYnabIdMapping: AccountNumberToYnabAccountIdMappingObject, - ) => void; + onUpdate: (accountNumberToYnabIdMapping: AccountNumberToYnabAccountIdMappingObject) => void; } type AccountMappingArray = { @@ -30,10 +27,9 @@ const YnabAccountMappingTable = ({ ynabAccountData, budgetId, }: YnabAccountMappingTableProps) => { - const [accountMappingArray, setAccountMappingArray] = - useState( - accountMappingObjectToArray(accountNumberToYnabIdMapping), - ); + const [accountMappingArray, setAccountMappingArray] = useState( + accountMappingObjectToArray(accountNumberToYnabIdMapping), + ); const columns = [ { @@ -55,10 +51,7 @@ const YnabAccountMappingTable = ({ type: Type.SELECT, getOptions: () => { return ynabAccountData?.ynabAccountData?.accounts - .filter( - (ynabAccount) => - ynabAccount.active && ynabAccount.budgetId === budgetId, - ) + .filter((ynabAccount) => ynabAccount.active && ynabAccount.budgetId === budgetId) .map((ynabAccount) => { return { label: ynabAccount.name, @@ -94,12 +87,7 @@ const YnabAccountMappingTable = ({ return ( <> - + @@ -110,15 +98,13 @@ const YnabAccountMappingTable = ({ function accountMappingObjectToArray( accountNumbersToYnabAccountIds: AccountNumberToYnabAccountIdMappingObject, ): AccountMappingArray { - return Object.keys(accountNumbersToYnabAccountIds).map( - (accountNumber, index) => { - return { - index, - accountNumber, - ynabAccountId: accountNumbersToYnabAccountIds[accountNumber], - }; - }, - ); + return Object.keys(accountNumbersToYnabAccountIds).map((accountNumber, index) => { + return { + index, + accountNumber, + ynabAccountId: accountNumbersToYnabAccountIds[accountNumber], + }; + }); } function accountMappingArrayToObject(accountMappingArray: AccountMappingArray) { diff --git a/packages/renderer/src/components/exporters/google-sheets/EditSheetsExporter.tsx b/packages/renderer/src/components/exporters/google-sheets/EditSheetsExporter.tsx index 2a9767bc..ee50bc0e 100644 --- a/packages/renderer/src/components/exporters/google-sheets/EditSheetsExporter.tsx +++ b/packages/renderer/src/components/exporters/google-sheets/EditSheetsExporter.tsx @@ -14,13 +14,8 @@ interface EditSheetsExporterProps { exporterConfig: GoogleSheetsConfig; } -const EditSheetsExporter: React.FC = ({ - handleSave, - exporterConfig, -}) => { - const [sheetsConfig, setSheetsConfig] = useState( - toJS(exporterConfig), - ); +const EditSheetsExporter: React.FC = ({ handleSave, exporterConfig }) => { + const [sheetsConfig, setSheetsConfig] = useState(toJS(exporterConfig)); const [loginStatus] = useTokenStatus(sheetsConfig.options.credentials); const [sendDisabled, setSendDisabled] = useState(false); @@ -43,10 +38,7 @@ const EditSheetsExporter: React.FC = ({ const handleSaveClick = async () => { setSendDisabled(true); - const newId = await createSheetIfNew( - sheetsConfig.options.spreadsheetId, - sheetsConfig.options.credentials, - ); + const newId = await createSheetIfNew(sheetsConfig.options.spreadsheetId, sheetsConfig.options.credentials); sheetsConfig.options.spreadsheetId = newId; await handleSave(sheetsConfig); }; @@ -84,20 +76,11 @@ const EditSheetsExporter: React.FC = ({ onChange={handleSheetIdChange} /> - +
-
diff --git a/packages/renderer/src/components/exporters/google-sheets/SheetsDropdown.tsx b/packages/renderer/src/components/exporters/google-sheets/SheetsDropdown.tsx index 2ee7eac9..0caf4be9 100644 --- a/packages/renderer/src/components/exporters/google-sheets/SheetsDropdown.tsx +++ b/packages/renderer/src/components/exporters/google-sheets/SheetsDropdown.tsx @@ -11,16 +11,9 @@ interface SheetsDropdownProps { onChange: (value: string) => void; } -const SheetsDropdown: React.FC = ({ - credentials, - value, - onChange, -}) => { +const SheetsDropdown: React.FC = ({ credentials, value, onChange }) => { const userSpreadsheets = useUserSpreadsheets(credentials); - const existedSpreadsheet = useMemo( - () => userSpreadsheets.find(({ id }) => id === value), - [userSpreadsheets, value], - ); + const existedSpreadsheet = useMemo(() => userSpreadsheets.find(({ id }) => id === value), [userSpreadsheets, value]); const selected = useMemo(() => { if (existedSpreadsheet) return [existedSpreadsheet]; if (!value) return []; diff --git a/packages/renderer/src/components/exporters/google-sheets/hooks.ts b/packages/renderer/src/components/exporters/google-sheets/hooks.ts index 512d8cbc..b40e6c04 100644 --- a/packages/renderer/src/components/exporters/google-sheets/hooks.ts +++ b/packages/renderer/src/components/exporters/google-sheets/hooks.ts @@ -1,8 +1,4 @@ -import { - createSpreadsheet, - getAllUserSpreadsheets, - validateToken, -} from '#preload'; +import { createSpreadsheet, getAllUserSpreadsheets, validateToken } from '#preload'; import { toJS } from 'mobx'; import { useEffect, useState } from 'react'; import { type Credentials, type Spreadsheet } from '/@/types'; @@ -44,10 +40,7 @@ export const useTokenStatus = (credentials: Credentials) => { return [status, setStatus] as const; }; -export const createSheetIfNew = async ( - spreadsheetId: string, - credentials: Credentials, -) => { +export const createSheetIfNew = async (spreadsheetId: string, credentials: Credentials) => { if (spreadsheetId.length >= 30) return spreadsheetId; const newId = await createSpreadsheet(toJS(spreadsheetId), toJS(credentials)); diff --git a/packages/renderer/src/components/topBar/LogsCanvas.tsx b/packages/renderer/src/components/topBar/LogsCanvas.tsx index 7da1953f..da1ab859 100644 --- a/packages/renderer/src/components/topBar/LogsCanvas.tsx +++ b/packages/renderer/src/components/topBar/LogsCanvas.tsx @@ -7,26 +7,15 @@ interface LogsCanvasProps { lastLines?: string; } -export default function LogsCanvas({ - show, - handleClose, - lastLines, -}: LogsCanvasProps) { +export default function LogsCanvas({ show, handleClose, lastLines }: LogsCanvasProps) { const splitted = lastLines?.split('\n'); return ( - + לוגים: - - {splitted?.map((line, index) =>
{line}
)} -
+ {splitted?.map((line, index) =>
{line}
)}
); } diff --git a/packages/renderer/src/components/topBar/NavButton.tsx b/packages/renderer/src/components/topBar/NavButton.tsx index 41c21236..5acf4890 100644 --- a/packages/renderer/src/components/topBar/NavButton.tsx +++ b/packages/renderer/src/components/topBar/NavButton.tsx @@ -8,11 +8,7 @@ interface NavButtonProps { function NavButton({ onClick, text }: NavButtonProps) { return ( - - - - setShowLogs(false)} - lastLines={lastLines} - /> + setShowLogs(false)} lastLines={lastLines} /> ); } diff --git a/packages/renderer/src/components/topBar/TopBar.tsx b/packages/renderer/src/components/topBar/TopBar.tsx index 4fd00d7c..db8cbbf5 100644 --- a/packages/renderer/src/components/topBar/TopBar.tsx +++ b/packages/renderer/src/components/topBar/TopBar.tsx @@ -15,10 +15,7 @@ function TopBar() { return ( <> - + @@ -36,17 +33,11 @@ function TopBar() { setShow(true)} text="דיווח על בעיה" /> - appInfoStore.appInfo?.discordChanel && - openExternal(appInfoStore.appInfo?.discordChanel) - } + onClick={() => appInfoStore.appInfo?.discordChanel && openExternal(appInfoStore.appInfo?.discordChanel)} text="ערוץ הדיסקורד שלנו" /> - appInfoStore.appInfo?.repository && - openExternal(appInfoStore.appInfo?.repository) - } + onClick={() => appInfoStore.appInfo?.repository && openExternal(appInfoStore.appInfo?.repository)} text="לפתוח ב-Github" /> diff --git a/packages/renderer/src/logging/logger.ts b/packages/renderer/src/logging/logger.ts index 0fc113b0..d7825bce 100644 --- a/packages/renderer/src/logging/logger.ts +++ b/packages/renderer/src/logging/logger.ts @@ -1,8 +1,4 @@ -import type { - LogFunctions, - LogLevel, - MainErrorHandlerOptions, -} from 'electron-log'; +import type { LogFunctions, LogLevel, MainErrorHandlerOptions } from 'electron-log'; import log from 'electron-log/renderer'; const logger = log.scope('renderer'); diff --git a/packages/renderer/src/store/AppInfoStore.tsx b/packages/renderer/src/store/AppInfoStore.tsx index cec88cd8..41223276 100644 --- a/packages/renderer/src/store/AppInfoStore.tsx +++ b/packages/renderer/src/store/AppInfoStore.tsx @@ -17,14 +17,8 @@ class AppInfoStore { const appInfoStore = new AppInfoStore(); const AppInfoStoreContext = createContext(appInfoStore); -export const AppInfoStoreProvider = ({ - children, -}: { - children: React.ReactNode; -}) => ( - - {children} - +export const AppInfoStoreProvider = ({ children }: { children: React.ReactNode }) => ( + {children} ); export const useInitAppInfoStore = () => { useEffect(() => { diff --git a/packages/renderer/src/store/ConfigStore.tsx b/packages/renderer/src/store/ConfigStore.tsx index 810f101b..8b47912b 100644 --- a/packages/renderer/src/store/ConfigStore.tsx +++ b/packages/renderer/src/store/ConfigStore.tsx @@ -23,9 +23,7 @@ interface AccountScrapingData { status: AccountStatus; } -const createAccountToScrapeConfigFromImporter = ( - importerConfig: Importer, -): AccountToScrapeConfig => ({ +const createAccountToScrapeConfigFromImporter = (importerConfig: Importer): AccountToScrapeConfig => ({ id: importerConfig.id, active: importerConfig.active, key: importerConfig.companyId as CompanyTypes, @@ -75,10 +73,7 @@ export class ConfigStore { chromeDownloadPercent = 0; // TODO: move this to a separate store - accountScrapingData: Map< - CompanyTypes | OutputVendorName, - AccountScrapingData - >; + accountScrapingData: Map; constructor() { this.accountScrapingData = new Map(); @@ -95,38 +90,28 @@ export class ConfigStore { get importers(): Importer[] { if (!this.config) return []; - return this.config.scraping.accountsToScrape.map( - ({ id, key, active, loginFields }) => { - return { - ...createAccountObject( - id, - key, - AccountType.IMPORTER, - !!active, - this.accountScrapingData.get(key), - ), - loginFields, - }; - }, - ); + return this.config.scraping.accountsToScrape.map(({ id, key, active, loginFields }) => { + return { + ...createAccountObject(id, key, AccountType.IMPORTER, !!active, this.accountScrapingData.get(key)), + loginFields, + }; + }); } get exporters(): Exporter[] { if (!this.config) return []; - return Object.entries(this.config.outputVendors).map( - ([exporterKey, exporter]) => { - return { - ...createAccountObject( - exporterKey, - exporterKey as OutputVendorName, - AccountType.EXPORTER, - !!exporter?.active, - this.accountScrapingData.get(exporterKey as OutputVendorName), - ), - options: exporter?.options || {}, - }; - }, - ); + return Object.entries(this.config.outputVendors).map(([exporterKey, exporter]) => { + return { + ...createAccountObject( + exporterKey, + exporterKey as OutputVendorName, + AccountType.EXPORTER, + !!exporter?.active, + this.accountScrapingData.get(exporterKey as OutputVendorName), + ), + options: exporter?.options || {}, + }; + }); } get isScraping(): boolean { @@ -166,14 +151,9 @@ export class ConfigStore { } } - handleScrapingEvent( - eventName: string, - budgetTrackingEvent?: BudgetTrackingEvent, - ) { + handleScrapingEvent(eventName: string, budgetTrackingEvent?: BudgetTrackingEvent) { if (eventName === 'DOWNLOAD_CHROME') { - this.updateChromeDownloadPercent( - (budgetTrackingEvent as DownloadChromeEvent)?.percent, - ); + this.updateChromeDownloadPercent((budgetTrackingEvent as DownloadChromeEvent)?.percent); } if (budgetTrackingEvent) { const accountId = budgetTrackingEvent.vendorId; @@ -190,8 +170,7 @@ export class ConfigStore { message: budgetTrackingEvent.message, originalEvent: budgetTrackingEvent, }); - accountScrapingData.status = - budgetTrackingEvent.accountStatus ?? AccountStatus.IDLE; + accountScrapingData.status = budgetTrackingEvent.accountStatus ?? AccountStatus.IDLE; } } } @@ -199,40 +178,31 @@ export class ConfigStore { async addImporter(importerConfig: Importer) { if (!accountMetadata[importerConfig.companyId]) { - throw new Error( - `Company id ${importerConfig.companyId} is not a valid company id`, - ); + throw new Error(`Company id ${importerConfig.companyId} is not a valid company id`); } - const accountToScrapeConfig: AccountToScrapeConfig = - createAccountToScrapeConfigFromImporter(importerConfig); + const accountToScrapeConfig: AccountToScrapeConfig = createAccountToScrapeConfigFromImporter(importerConfig); this.config.scraping.accountsToScrape.push(accountToScrapeConfig); } async updateImporter(id: string, updatedImporterConfig: Importer) { - const importerIndex = this.config.scraping.accountsToScrape.findIndex( - (importer) => importer.id === id, - ); + const importerIndex = this.config.scraping.accountsToScrape.findIndex((importer) => importer.id === id); if (importerIndex === -1) { - throw new Error( - `Cant update importer with id ${id}. No importer with that id found`, - ); + throw new Error(`Cant update importer with id ${id}. No importer with that id found`); } this.config.scraping.accountsToScrape[importerIndex] = createAccountToScrapeConfigFromImporter(updatedImporterConfig); } async deleteImporter(id: string) { - this.config.scraping.accountsToScrape = - this.config.scraping.accountsToScrape.filter( - (importer) => importer.id !== id, - ); + this.config.scraping.accountsToScrape = this.config.scraping.accountsToScrape.filter( + (importer) => importer.id !== id, + ); } async updateExporter(updatedExporterConfig: Exporter) { // @ts-expect-error the types are not complete here - this.config.outputVendors[ - updatedExporterConfig.companyId as OutputVendorName - ] = createOutputVendorConfigFromExporter(updatedExporterConfig); + this.config.outputVendors[updatedExporterConfig.companyId as OutputVendorName] = + createOutputVendorConfigFromExporter(updatedExporterConfig); } async toggleShowBrowser() { @@ -258,11 +228,7 @@ export class ConfigStore { export const configStore = new ConfigStore(); const StoreContext = createContext(configStore); -export const ConfigStoreProvider = ({ - children, -}: { - children: React.ReactNode; -}) => ( +export const ConfigStoreProvider = ({ children }: { children: React.ReactNode }) => ( {children} ); export const useConfigStore = () => useContext(StoreContext); diff --git a/packages/renderer/src/store/Store.test.tsx b/packages/renderer/src/store/Store.test.tsx index 634d31cf..1ae4f397 100644 --- a/packages/renderer/src/store/Store.test.tsx +++ b/packages/renderer/src/store/Store.test.tsx @@ -1,12 +1,5 @@ import { type ConfigStore, configStore } from './ConfigStore'; -import { - AccountType, - CompanyTypes, - type Config, - type Credentials, - type Exporter, - type Importer, -} from '../types'; +import { AccountType, CompanyTypes, type Config, type Credentials, type Exporter, type Importer } from '../types'; import { beforeEach, describe, test, expect } from 'vitest'; describe('Store', () => { @@ -29,15 +22,9 @@ describe('Store', () => { test('importers', () => { expect(store.importers).toHaveLength(3); - const discountImporter = store.importers.find( - (importer) => importer.companyId === 'discount', - ); - const visaCalImporter = store.importers.find( - (importer) => importer.companyId === 'visaCal', - ); - const maxImporter = store.importers.find( - (importer) => importer.companyId === 'max', - ); + const discountImporter = store.importers.find((importer) => importer.companyId === 'discount'); + const visaCalImporter = store.importers.find((importer) => importer.companyId === 'visaCal'); + const maxImporter = store.importers.find((importer) => importer.companyId === 'max'); expect(discountImporter).toMatchSnapshot(); expect(visaCalImporter).toMatchSnapshot(); @@ -50,12 +37,8 @@ describe('Store', () => { }); test('settings', () => { - expect(store.settings.numDaysBack).toEqual( - dummyConfig.scraping.numDaysBack, - ); - expect(store.settings.showBrowser).toEqual( - dummyConfig.scraping.showBrowser, - ); + expect(store.settings.numDaysBack).toEqual(dummyConfig.scraping.numDaysBack); + expect(store.settings.showBrowser).toEqual(dummyConfig.scraping.showBrowser); }); }); describe('actions', () => { @@ -85,15 +68,15 @@ describe('Store', () => { active: !importer.active, }; store.updateImporter(importer.id, updatedImporter); - expect( - store.importers.find((importer) => importer.id === importer.id), - ).toHaveProperty('active', updatedImporter.active); + expect(store.importers.find((importer) => importer.id === importer.id)).toHaveProperty( + 'active', + updatedImporter.active, + ); }); test('deleteImporter', () => { const importer = dummyConfig.scraping.accountsToScrape[0]; - const originalNumImporters = - dummyConfig.scraping.accountsToScrape.length; + const originalNumImporters = dummyConfig.scraping.accountsToScrape.length; expect(store.importers).toHaveLength(originalNumImporters); store.deleteImporter(importer.id); expect(store.importers).toHaveLength(originalNumImporters - 1); diff --git a/packages/renderer/src/types.tsx b/packages/renderer/src/types.tsx index a7f6022b..43b3da7c 100644 --- a/packages/renderer/src/types.tsx +++ b/packages/renderer/src/types.tsx @@ -40,14 +40,8 @@ export interface AppInfo { currentVersion: string; } -export type OutputVendorConfigs = Exclude< - Config['outputVendors'][OutputVendorName], - undefined ->; -export type OutputVendorConfig = Exclude< - Config['outputVendors'][T], - undefined ->; +export type OutputVendorConfigs = Exclude; +export type OutputVendorConfig = Exclude; export interface OutputVendorConfigBase { active: boolean; diff --git a/packages/renderer/tsconfig.json b/packages/renderer/tsconfig.json index 83f4c5d0..e809d9d5 100644 --- a/packages/renderer/tsconfig.json +++ b/packages/renderer/tsconfig.json @@ -18,11 +18,6 @@ }, "lib": ["ESNext", "dom", "dom.iterable"] }, - "include": [ - "src/**/*", - "types/**/*.d.ts", - "../../types/**/*.d.ts", - "../../.eslintrc.cjs" - ], + "include": ["src/**/*", "types/**/*.d.ts", "../../types/**/*.d.ts", "../../.eslintrc.cjs"], "exclude": ["**/*.spec.ts", "**/*.test.ts"] } diff --git a/packages/renderer/vitest-setup.js b/packages/renderer/vitest-setup.js index 049b222d..f2819c33 100644 --- a/packages/renderer/vitest-setup.js +++ b/packages/renderer/vitest-setup.js @@ -20,9 +20,7 @@ vi.mock('electron', () => { return Promise.resolve({ name: 'Test App', version: '1.0.0' }); } if (channel === 'getConfig') { - return Promise.resolve( - JSON.stringify({ config: { someKey: 'someValue' } }), - ); + return Promise.resolve(JSON.stringify({ config: { someKey: 'someValue' } })); } if (channel === 'getLogsInfo') { return Promise.resolve({ diff --git a/release.config.js b/release.config.js index 4f6225c9..f2358151 100644 --- a/release.config.js +++ b/release.config.js @@ -3,7 +3,7 @@ const releaseRules = { releaseRules: [ { tag: 'Breaking', release: 'major' }, { tag: 'Build', release: 'minor' }, - { tag: 'Chore', release: 'minor' }, + { tag: 'Chore', release: 'patch' }, { tag: 'Fix', release: 'patch' }, { tag: 'New', release: 'minor' }, { tag: 'Update', release: 'minor' }, @@ -14,8 +14,5 @@ const releaseRules = { export default { branches: ['master'], - plugins: [ - ['@semantic-release/commit-analyzer', releaseRules], - '@semantic-release/github', - ], + plugins: [['@semantic-release/commit-analyzer', releaseRules], '@semantic-release/github'], }; diff --git a/scripts/update-electron-vendors.js b/scripts/update-electron-vendors.js index e3d219f8..9aa56006 100644 --- a/scripts/update-electron-vendors.js +++ b/scripts/update-electron-vendors.js @@ -14,8 +14,5 @@ const chrome = electronRelease.v8.split('.').splice(0, 2).join(''); const browserslistrcPath = path.resolve(process.cwd(), '.browserslistrc'); -writeFileSync( - './.electron-vendors.cache.json', - JSON.stringify({ chrome, node }), -); +writeFileSync('./.electron-vendors.cache.json', JSON.stringify({ chrome, node })); writeFileSync(browserslistrcPath, `Chrome ${chrome}`, 'utf8'); diff --git a/tests/e2e.spec.ts b/tests/e2e.spec.ts index 13c887ac..906a5e88 100644 --- a/tests/e2e.spec.ts +++ b/tests/e2e.spec.ts @@ -1,9 +1,5 @@ import { type BrowserWindow } from 'electron'; -import { - _electron as electron, - type ElectronApplication, - type JSHandle, -} from 'playwright'; +import { _electron as electron, type ElectronApplication, type JSHandle } from 'playwright'; import { afterAll, beforeAll, expect, test } from 'vitest'; let electronApp: ElectronApplication; @@ -47,18 +43,12 @@ test('Main window state', async () => { expect(windowState.isCrashed, 'The app has crashed').toBeFalsy(); expect(windowState.isVisible, 'The main window was not visible').toBeTruthy(); - expect( - windowState.isDevToolsOpened, - 'The DevTools panel was open', - ).toBeFalsy(); + expect(windowState.isDevToolsOpened, 'The DevTools panel was open').toBeFalsy(); }); test('Main window web content', async () => { const page = await electronApp.firstWindow(); const element = await page.$('#app', { strict: true }); expect(element, 'Was unable to find the root element').toBeDefined(); - expect( - (await element.innerHTML()).trim(), - 'Window content was empty', - ).not.equal(''); + expect((await element.innerHTML()).trim(), 'Window content was empty').not.equal(''); });