diff --git a/.eslintrc.cjs b/.eslintrc.cjs index afff23f8..9e8a4ca5 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -16,7 +16,7 @@ module.exports = { parserOptions: { ecmaVersion: 12, sourceType: 'module', - project: './tsconfig.test.json', + project: './tsconfig.json', }, plugins: ['@typescript-eslint'], ignorePatterns: ['node_modules/**', '**/dist/**'], diff --git a/package.json b/package.json index 02b6d5f3..a00cb25e 100644 --- a/package.json +++ b/package.json @@ -42,6 +42,7 @@ "@types/lodash": "^4.17.4", "@types/node": "20.14.1", "@types/react": "^18.3.3", + "@types/react-bootstrap-table-next": "^4.0.26", "@types/react-dom": "^18.3.0", "@types/uuid": "^10.0.0", "@typescript-eslint/eslint-plugin": "7.12.0", @@ -96,7 +97,7 @@ "react-dom": "^18.3.1", "svelte": "^4.2.19", "vite-plugin-node-polyfills": "^0.22.0", - "web-vitals": "^4.2.0", + "web-vitals": "^4.2.3", "ynab": "^1.19.0" } } diff --git a/packages/renderer/src/accountMetadata.tsx b/packages/renderer/src/accountMetadata.tsx index 1979899a..957a9655 100644 --- a/packages/renderer/src/accountMetadata.tsx +++ b/packages/renderer/src/accountMetadata.tsx @@ -52,7 +52,7 @@ const accountMetadata: Record< return { companyId: accountId, companyName: displayName, - logo: icons[accountId], + logo: icons[accountId as CompanyTypes | OutputVendorName], }; }); @@ -111,7 +111,7 @@ export const importers: Account[] = Object.values(CompanyTypes).map( const importer: Account = { id: importerName, - companyId, + companyId: companyId as CompanyTypes | OutputVendorName, displayName: companyName, logo, type: AccountType.IMPORTER, diff --git a/packages/renderer/src/components/Body.tsx b/packages/renderer/src/components/Body.tsx index dba11709..e316d29e 100644 --- a/packages/renderer/src/components/Body.tsx +++ b/packages/renderer/src/components/Body.tsx @@ -7,9 +7,10 @@ import { useConfigStore } from '../store/ConfigStore'; import { ModalStatus, OutputVendorName, + YnabConfig, type Account, type Exporter, - type Importer, + type Importer, GoogleSheetsConfig, } from '../types'; import styles from './Body.module.css'; import CheckForUpdates from './CheckForUpdates'; @@ -84,9 +85,6 @@ const Body = () => { {configStore.config?.outputVendors && ( { currentAccount && ( )} {modalStatus === ModalStatus.EXPORTER_SETTINGS && currentAccount && ( Promise} + exporter={currentAccount as Exporter} /> )} {modalStatus === ModalStatus.NEW_SCRAPER && ( @@ -147,7 +144,7 @@ const Body = () => { showModal(null, ModalStatus.GENERAL_SETTINGS)} + onClick={() => showModal({} as Account, ModalStatus.GENERAL_SETTINGS)} className={styles.pointer} /> diff --git a/packages/renderer/src/components/GeneralSettings.tsx b/packages/renderer/src/components/GeneralSettings.tsx index 46cf72ff..6a5bf899 100644 --- a/packages/renderer/src/components/GeneralSettings.tsx +++ b/packages/renderer/src/components/GeneralSettings.tsx @@ -34,7 +34,7 @@ function GeneralSettings() { className={styles.input} defaultValue={configStore.config?.scraping.numDaysBack} onBlur={(event) => - configStore.setNumDaysBack(event.target.value) + configStore.setNumDaysBack(Number(event.target.value)) } autoFocus /> @@ -45,7 +45,7 @@ function GeneralSettings() { className={styles.input} defaultValue={configStore.config?.scraping.maxConcurrency} onBlur={(event) => - configStore.setMaxConcurrency(event.target.value) + configStore.setMaxConcurrency(Number(event.target.value)) } /> @@ -60,7 +60,7 @@ function GeneralSettings() { /> - כמה זמן לחכות לשליפה? (millisec) + כמה זמן לחכות לשליפה? (millisecond) (); - const handleChooseImporter = (importer: Importer) => - setImporterToCreate({ ...importer, id: uuidv4() }); + const handleChooseImporter = (importer: AccountType) => + setImporterToCreate({ ...importer, id: uuidv4(), loginFields: {} }); return (
{importerToCreate ? ( diff --git a/packages/renderer/src/components/accounts/EditImporter.tsx b/packages/renderer/src/components/accounts/EditImporter.tsx index dcaa87fb..2b26c0c8 100644 --- a/packages/renderer/src/components/accounts/EditImporter.tsx +++ b/packages/renderer/src/components/accounts/EditImporter.tsx @@ -32,19 +32,19 @@ export default function EditImporter({ }); }; - const checkFieldValidity = (loginFieldName: string, value): boolean => { + const checkFieldValidity = (loginFieldName: keyof typeof LOGIN_FIELD_MIN_LENGTH, value: string): boolean => { return value.length >= LOGIN_FIELD_MIN_LENGTH[loginFieldName]; }; - const checkFieldsValidity = (fieldsToCheck) => { + const checkFieldsValidity = (fieldsToCheck: Record) => { setValidated( Object.entries(fieldsToCheck).every(([key, value]) => - checkFieldValidity(key, value), + checkFieldValidity(key as keyof typeof LOGIN_FIELD_MIN_LENGTH, value), ), ); }; - const onLoginFieldChanged = (loginFieldName: string, loginFieldValue) => { + const onLoginFieldChanged = (loginFieldName: string, loginFieldValue: string) => { setLoginFields((prevLoginFields) => { const nextLoginFields = { ...prevLoginFields, @@ -74,15 +74,17 @@ export default function EditImporter({ />
- {IMPORTERS_LOGIN_FIELDS[importer.companyId].map( - (loginField, index) => ( + {IMPORTERS_LOGIN_FIELDS[ + importer.companyId as keyof typeof IMPORTERS_LOGIN_FIELDS + ].map( + (loginField: string, index: number) => ( diff --git a/packages/renderer/src/components/accounts/Importers.tsx b/packages/renderer/src/components/accounts/Importers.tsx index b2e6e241..f938f76e 100644 --- a/packages/renderer/src/components/accounts/Importers.tsx +++ b/packages/renderer/src/components/accounts/Importers.tsx @@ -4,6 +4,7 @@ import resultsIcon from '../../assets/results.svg'; import { AccountStatus, ModalStatus, + OutputVendorName, AccountType as TypeOfAccount, type Account as AccountType, } from '../../types'; @@ -37,7 +38,7 @@ function Importers({ account, isScraping, () => { - configStore.openResults(account.companyId); + configStore.openResults(account.companyId as OutputVendorName); }, )} /> diff --git a/packages/renderer/src/components/exporters/EditExporter.tsx b/packages/renderer/src/components/exporters/EditExporter.tsx index 85c6ef3c..6d93c0f6 100644 --- a/packages/renderer/src/components/exporters/EditExporter.tsx +++ b/packages/renderer/src/components/exporters/EditExporter.tsx @@ -1,10 +1,10 @@ -import { OutputVendorName, type Exporter, type YnabConfig } 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'; interface EditExporterProps { - handleSave: (exporterConfig: Exporter | YnabConfig) => Promise; + handleSave: (exporterConfig: Exporter | YnabConfig | GoogleSheetsConfig) => Promise; exporter: Exporter; } @@ -23,11 +23,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 cc997056..a194944e 100644 --- a/packages/renderer/src/components/exporters/EditFileExporter.tsx +++ b/packages/renderer/src/components/exporters/EditFileExporter.tsx @@ -2,7 +2,7 @@ import { showSaveDialog } from '#preload'; import { observer } from 'mobx-react-lite'; import React, { useState } from 'react'; import { Button, Card, Form, Image } from 'react-bootstrap'; -import { type Exporter } from '/@/types'; +import type { CsvConfig, Exporter, JsonConfig } from '/@/types'; import styles from './EditFileExporter.module.css'; interface EditFileExporterProps { @@ -63,7 +63,7 @@ const EditFileExporter = ({ handleSave, exporter }: EditFileExporterProps) => { לאיזה קובץ לכתוב את הטרנזאקציות? diff --git a/packages/renderer/src/components/exporters/EditYnabExporter.tsx b/packages/renderer/src/components/exporters/EditYnabExporter.tsx index 3e8261da..6dd27470 100644 --- a/packages/renderer/src/components/exporters/EditYnabExporter.tsx +++ b/packages/renderer/src/components/exporters/EditYnabExporter.tsx @@ -30,6 +30,16 @@ const EditYnabExporter = ({ const isValidAccessToken = !isLoading && store.ynabAccountData?.status !== INVALID_ACCESS_TOKEN; + const updateOptionsState = useCallback( + (optionUpdates: Partial) => { + setYnabOptions((prevYnabOptions) => ({ + ...prevYnabOptions, + ...optionUpdates, + })); + }, + [], + ); + // Set default budget id if not set useEffect(() => { const defaultBudgetId = @@ -45,16 +55,6 @@ const EditYnabExporter = ({ } }, [ynabOptions, store]); - const updateOptionsState = useCallback( - (optionUpdates: Partial) => { - setYnabOptions((prevYnabOptions) => ({ - ...prevYnabOptions, - ...optionUpdates, - })); - }, - [], - ); - const handleSaveClick = async () => { await handleSave({ ...exporterConfig, @@ -65,7 +65,7 @@ const EditYnabExporter = ({ const handleOptionChangeEvent = ( propertyName: keyof YnabConfig['options'], - event: React.ChangeEvent, + event: React.ChangeEvent, ) => { updateOptionsState({ [propertyName]: event.target.value }); }; @@ -83,7 +83,6 @@ const EditYnabExporter = ({ { mappingObject[accountNumber] = ynabAccountId; }); diff --git a/packages/renderer/src/components/exporters/google-sheets/EditSheetsExporter.tsx b/packages/renderer/src/components/exporters/google-sheets/EditSheetsExporter.tsx index b684469c..dc8647ae 100644 --- a/packages/renderer/src/components/exporters/google-sheets/EditSheetsExporter.tsx +++ b/packages/renderer/src/components/exporters/google-sheets/EditSheetsExporter.tsx @@ -7,6 +7,7 @@ import styles from '../EditFileExporter.module.css'; import LoginButton from './LoginButton'; import SheetsDropdown from './SheetsDropdown'; import { Status, createSheetIfNew, useTokenStatus } from './hooks'; +import type { Auth } from 'googleapis'; interface EditSheetsExporterProps { handleSave: (exporterConfig: GoogleSheetsConfig) => Promise; @@ -59,7 +60,7 @@ const EditSheetsExporter: React.FC = ({ } if (loginStatus === Status.LOGIN) { - const updateCredentials = (credentials: Credentials) => { + const updateCredentials = (credentials: Auth.Credentials) => { setSheetsConfig((prev) => ({ ...prev, options: { @@ -76,7 +77,6 @@ const EditSheetsExporter: React.FC = ({ = ({ onCredentialsChange }) => { } catch (ex) { console.error(ex); setLoading(false); - onCredentialsChange(null); + onCredentialsChange({}); } }; diff --git a/packages/renderer/src/components/exporters/google-sheets/SheetsDropdown.tsx b/packages/renderer/src/components/exporters/google-sheets/SheetsDropdown.tsx index ef1394d2..c51c7cb9 100644 --- a/packages/renderer/src/components/exporters/google-sheets/SheetsDropdown.tsx +++ b/packages/renderer/src/components/exporters/google-sheets/SheetsDropdown.tsx @@ -3,12 +3,14 @@ import { Form } from 'react-bootstrap'; import { Typeahead } from 'react-bootstrap-typeahead'; import { useUserSpreadsheets } from './hooks'; import { type Credentials } from '/@/types'; +import { type Option } from 'react-bootstrap-typeahead/types/types'; interface SheetsDropdownProps { credentials: Credentials; value: string; onChange: (value: string) => void; } + const SheetsDropdown: React.FC = ({ credentials, value, @@ -26,10 +28,11 @@ const SheetsDropdown: React.FC = ({ }, [existedSpreadsheet, value]); const onSelectionChange = useCallback( - (selections) => { - if (selections[0]?.customOption) onChange(selections[0]?.name); - else if (selections.length) onChange(selections[0]?.id); - else onChange(null); + (selections: Option[]) => { + const selectedOption = selections[0] as { id: string; name: string; customOption?: boolean }; + if (selectedOption?.customOption) onChange(selectedOption.name); + else if (selections.length) onChange(selectedOption.id); + else onChange(''); }, [onChange], ); @@ -51,4 +54,4 @@ const SheetsDropdown: React.FC = ({ ); }; -export default SheetsDropdown; +export default SheetsDropdown; \ No newline at end of file diff --git a/packages/renderer/src/components/topBar/ReportProblemModal.tsx b/packages/renderer/src/components/topBar/ReportProblemModal.tsx index 55e47fad..b59aed29 100644 --- a/packages/renderer/src/components/topBar/ReportProblemModal.tsx +++ b/packages/renderer/src/components/topBar/ReportProblemModal.tsx @@ -23,6 +23,7 @@ interface ReportProblemModalProps { } interface ValidationError { + [key: string]: string | undefined; title?: string; email?: string; } @@ -56,7 +57,7 @@ function ReportProblemModal({ show, onClose }: ReportProblemModalProps) { const setField = (field: string, value: string) => { setForm((prevForm) => ({ ...prevForm, [field]: value })); - if (errors[field]) setErrors({ ...errors, [field]: null }); + if (errors[field]) setErrors({ ...errors, [field]: undefined }); }; const validateForm = (validateEmail = true) => { @@ -73,7 +74,7 @@ function ReportProblemModal({ show, onClose }: ReportProblemModalProps) { return newErrors; }; - const openGithub = (e) => { + const openGithub = (e: React.MouseEvent) => { e.preventDefault(); const formErrors = validateForm(false); @@ -114,19 +115,19 @@ ${details}` const sysInfo = ` ## System Info - - Source Version: \`${appInfoStore.appInfo.sourceCommitShort || 'unknown'}\` + - Source Version: \`${appInfoStore.appInfo?.sourceCommitShort ?? 'unknown'}\` - OS: \`${os.platform()}${os.arch()}\` - OS Version: \`${os.release()}\` `; return `${ - `${appInfoStore.appInfo.repository}/issues/new?` + + `${appInfoStore.appInfo?.repository}/issues/new?` + `title=${encodeURIComponent(title)}` + '&body=' }${encodeURIComponent(formattedDetails + formattedLog + sysInfo)}`; }; - const sendReport = async (e) => { + const sendReport = async (e: React.MouseEvent) => { e.preventDefault(); const formErrors = validateForm(true); @@ -233,7 +234,6 @@ ${details}` /> setShow(true)} text="דיווח על בעיה" /> openExternal(appInfoStore.appInfo?.discordChanel)} + onClick={() => openExternal(appInfoStore.appInfo?.discordChanel ?? '')} text="ערוץ הדיסקורד שלנו" /> openExternal(appInfoStore.appInfo?.repository)} + onClick={() => openExternal(appInfoStore.appInfo?.repository ?? '')} text="לפתוח ב-Github" /> @@ -51,4 +51,4 @@ function TopBar() { ); } -export default TopBar; +export default TopBar; \ No newline at end of file diff --git a/packages/renderer/src/react-bootstrap-table2-editor.d.ts b/packages/renderer/src/react-bootstrap-table2-editor.d.ts new file mode 100644 index 00000000..a0d4d2c7 --- /dev/null +++ b/packages/renderer/src/react-bootstrap-table2-editor.d.ts @@ -0,0 +1 @@ +declare module 'react-bootstrap-table2-editor'; \ No newline at end of file diff --git a/packages/renderer/src/reportWebVitals.ts b/packages/renderer/src/reportWebVitals.ts index 7fc2a19b..f8eec312 100644 --- a/packages/renderer/src/reportWebVitals.ts +++ b/packages/renderer/src/reportWebVitals.ts @@ -1,13 +1,13 @@ -import { type ReportHandler } from 'web-vitals'; +import { type MetricType } from 'web-vitals'; -const reportWebVitals = (onPerfEntry?: ReportHandler) => { - if (onPerfEntry && onPerfEntry instanceof Function) { - import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { - getCLS(onPerfEntry); - getFID(onPerfEntry); - getFCP(onPerfEntry); - getLCP(onPerfEntry); - getTTFB(onPerfEntry); +const reportWebVitals = (onPerfEntry?: (metric: MetricType) => void) => { + if (onPerfEntry) { + import('web-vitals').then(({ onCLS, onINP, onFCP, onLCP, onTTFB }) => { + onCLS(onPerfEntry); + onINP(onPerfEntry); + onFCP(onPerfEntry); + onLCP(onPerfEntry); + onTTFB(onPerfEntry); }); } }; diff --git a/packages/renderer/src/store/AppInfoStore.tsx b/packages/renderer/src/store/AppInfoStore.tsx index f86d97ea..cec88cd8 100644 --- a/packages/renderer/src/store/AppInfoStore.tsx +++ b/packages/renderer/src/store/AppInfoStore.tsx @@ -4,7 +4,7 @@ import { createContext, useContext, useEffect } from 'react'; import { type AppInfo } from '../types'; class AppInfoStore { - appInfo: AppInfo; + appInfo: AppInfo | null = null; constructor() { makeAutoObservable(this); diff --git a/packages/renderer/src/store/ConfigStore.tsx b/packages/renderer/src/store/ConfigStore.tsx index 572cbd04..9995ffbf 100644 --- a/packages/renderer/src/store/ConfigStore.tsx +++ b/packages/renderer/src/store/ConfigStore.tsx @@ -15,7 +15,7 @@ import { type Importer, type Log, type OutputVendorName, - type DownalodChromeEvent, + type DownloadChromeEvent, } from '../types'; interface AccountScrapingData { @@ -70,7 +70,7 @@ const saveConfigIntoFile = (config?: Config) => { }; export class ConfigStore { - config: Config; + config: Config = {} as Config; chromeDownloadPercent = 0; @@ -172,7 +172,7 @@ export class ConfigStore { ) { if (eventName === 'DOWNLOAD_CHROME') { this.updateChromeDownloadPercent( - (budgetTrackingEvent as DownalodChromeEvent)?.percent, + (budgetTrackingEvent as DownloadChromeEvent)?.percent, ); } if (budgetTrackingEvent) { @@ -229,6 +229,8 @@ export class ConfigStore { } async updateExporter(updatedExporterConfig: Exporter) { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-expect-error this.config.outputVendors[ updatedExporterConfig.companyId as OutputVendorName ] = createOutputVendorConfigFromExporter(updatedExporterConfig); diff --git a/packages/renderer/src/types.tsx b/packages/renderer/src/types.tsx index a1406117..a7f6022b 100644 --- a/packages/renderer/src/types.tsx +++ b/packages/renderer/src/types.tsx @@ -149,7 +149,7 @@ export class BudgetTrackingEvent { } } -export class DownalodChromeEvent extends BudgetTrackingEvent { +export class DownloadChromeEvent extends BudgetTrackingEvent { percent: number; constructor(percent: number) { diff --git a/yarn.lock b/yarn.lock index 4826cb11..811b8fb3 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1636,6 +1636,13 @@ resolved "https://registry.yarnpkg.com/@types/range-parser/-/range-parser-1.2.7.tgz#50ae4353eaaddc04044279812f52c8c65857dbcb" integrity sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ== +"@types/react-bootstrap-table-next@^4.0.26": + version "4.0.26" + resolved "https://registry.yarnpkg.com/@types/react-bootstrap-table-next/-/react-bootstrap-table-next-4.0.26.tgz#06d0a290f05ae3952b6afa08d5e15d305aae8443" + integrity sha512-k9QlhxVthvEyOD6YSozJoEQVZyDEQbITfvxXEub00yw17iOAWE2g7RvnXGkXgsDshVb+vj+Z4nb0yz/FL7xsag== + dependencies: + "@types/react" "*" + "@types/react-dom@^18.3.0": version "18.3.0" resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-18.3.0.tgz#0cbc818755d87066ab6ca74fbedb2547d74a82b0" @@ -6921,10 +6928,10 @@ warning@^4.0.0, warning@^4.0.1, warning@^4.0.2, warning@^4.0.3: dependencies: loose-envify "^1.0.0" -web-vitals@^4.2.0: - version "4.2.0" - resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.0.tgz#008949ab79717a68ccaaa3c4371cbc7bbbd78a92" - integrity sha512-ohj72kbtVWCpKYMxcbJ+xaOBV3En76hW47j52dG+tEGG36LZQgfFw5yHl9xyjmosy3XUMn8d/GBUAy4YPM839w== +web-vitals@^4.2.3: + version "4.2.3" + resolved "https://registry.yarnpkg.com/web-vitals/-/web-vitals-4.2.3.tgz#270c4baecfbc6ec6fc15da1989e465e5f9b94fb7" + integrity sha512-/CFAm1mNxSmOj6i0Co+iGFJ58OS4NRGVP+AWS/l509uIK5a1bSoIVaHz/ZumpHTfHSZBpgrJ+wjfpAOrTHok5Q== webidl-conversions@^3.0.0: version "3.0.1"