From 14e7c64eab98eb033446cfb8f662b388746fec18 Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Fri, 21 Mar 2025 11:28:30 -0700 Subject: [PATCH] Add watcher status and additional typing. --- .prettierrc.js | 17 +++ eslint.config.mjs | 4 +- src/main/decoder/decode-stl.ts | 4 +- src/main/decoder/decode-veh.interface.ts | 2 +- src/main/decoder/decoder.ts | 101 ++++++++++++------ src/main/graphql/queries.ts | 81 +++++++++++--- src/main/ipc/ipcMainConfig.ts | 6 +- src/main/ipc/ipcMainHandler.settings.ts | 4 +- src/main/watcher/watcher.ts | 38 +++++-- .../components/Settings/Settings.Watcher.tsx | 38 ++++++- src/renderer/src/redux/app.slice.ts | 42 +++++--- src/renderer/src/redux/reduxHooks.ts | 5 +- src/renderer/src/util/ipcRendererHandler.ts | 33 ++++++ src/util/ipcTypes.json | 5 + src/util/translations/en-US/renderer.json | 9 +- translations.babel | 70 ++++++++++++ tsconfig.json | 5 +- tsconfig.node.json | 1 + tsconfig.web.json | 1 + 19 files changed, 385 insertions(+), 81 deletions(-) create mode 100644 .prettierrc.js diff --git a/.prettierrc.js b/.prettierrc.js new file mode 100644 index 0000000..391da91 --- /dev/null +++ b/.prettierrc.js @@ -0,0 +1,17 @@ +export default { + printWidth: 120, + useTabs: false, + tabWidth: 2, + trailingComma: "none", + semi: true, + singleQuote: false, + bracketSpacing: true, + arrowParens: "always", + jsxSingleQuote: false, + bracketSameLine: false, + endOfLine: "lf" + // importOrder: ["^@core/(.*)$", "^@server/(.*)$", "^@ui/(.*)$", "^[./]"], + // importOrderSeparation: true, + // importOrderSortSpecifiers: true +}; + diff --git a/eslint.config.mjs b/eslint.config.mjs index 4beaad2..f4490e8 100644 --- a/eslint.config.mjs +++ b/eslint.config.mjs @@ -1,5 +1,5 @@ -import tseslint from "@electron-toolkit/eslint-config-ts"; import eslintConfigPrettier from "@electron-toolkit/eslint-config-prettier"; +import tseslint from "@electron-toolkit/eslint-config-ts"; import eslintPluginReact from "eslint-plugin-react"; import eslintPluginReactHooks from "eslint-plugin-react-hooks"; import eslintPluginReactRefresh from "eslint-plugin-react-refresh"; @@ -27,5 +27,5 @@ export default tseslint.config( ...eslintPluginReactRefresh.configs.vite.rules, }, }, - eslintConfigPrettier + eslintConfigPrettier, ); diff --git a/src/main/decoder/decode-stl.ts b/src/main/decoder/decode-stl.ts index e535d73..2c322a0 100644 --- a/src/main/decoder/decode-stl.ts +++ b/src/main/decoder/decode-stl.ts @@ -6,7 +6,7 @@ import errorTypeCheck from "../../util/errorTypeCheck"; import { DecodedStl, DecodedStlLine } from "./decode-stl.interface"; const DecodeStl = async ( - extensionlessFilePath: string + extensionlessFilePath: string, ): Promise => { let dbf: DBFFile | null = null; try { @@ -47,7 +47,7 @@ const DecodeStl = async ( "TTL_TYPAMT", "TTL_HRS", "TTL_AMT", - ]) + ]), ); //Apply line by line adjustments. diff --git a/src/main/decoder/decode-veh.interface.ts b/src/main/decoder/decode-veh.interface.ts index a56f70b..75bcb9e 100644 --- a/src/main/decoder/decode-veh.interface.ts +++ b/src/main/decoder/decode-veh.interface.ts @@ -15,7 +15,7 @@ export interface DecodedVeh { impact2?: string; }; // Complete vehicle data object - vehicle: { data: VehicleRecordInterface }; + vehicle?: { data: VehicleRecordInterface }; } export interface VehicleRecordInterface { diff --git a/src/main/decoder/decoder.ts b/src/main/decoder/decoder.ts index e03fbeb..9b51407 100644 --- a/src/main/decoder/decoder.ts +++ b/src/main/decoder/decoder.ts @@ -1,10 +1,22 @@ +import { UUID } from "crypto"; import log from "electron-log/main"; +import fs from "fs"; import path from "path"; import errorTypeCheck from "../../util/errorTypeCheck"; +import client from "../graphql/graphql-client"; +import { + QUERY_JOB_BY_CLM_NO_TYPED, + QUERY_VEHICLE_BY_VIN_TYPED, + QueryJobByClmNoResult, + VehicleQueryResult, +} from "../graphql/queries"; +import store from "../store/store"; import DecodeAD1 from "./decode-ad1"; import { DecodedAd1 } from "./decode-ad1.interface"; import DecodeAD2 from "./decode-ad2"; import { DecodedAD2 } from "./decode-ad2.interface"; +import DecodeEnv from "./decode-env"; +import { DecodedEnv } from "./decode-env.interface"; import DecodeLin from "./decode-lin"; import { DecodedLin } from "./decode-lin.interface"; import DecodePfh from "./decode-pfh"; @@ -25,17 +37,12 @@ import DecodeTtl from "./decode-ttl"; import { DecodedTtl } from "./decode-ttl.interface"; import DecodeVeh from "./decode-veh"; import { DecodedVeh } from "./decode-veh.interface"; -import { DecodedEnv } from "./decode-env.interface"; -import DecodeEnv from "./decode-env"; -import fs from "fs"; -import store from "../store/store"; -import client from "../graphql/graphql-client"; async function ImportJob(filepath: string): Promise { const parsedFilePath = path.parse(filepath); const extensionlessFilePath = path.join( parsedFilePath.dir, - parsedFilePath.name + parsedFilePath.name, ); log.debug("Importing Job", extensionlessFilePath); @@ -56,7 +63,7 @@ async function ImportJob(filepath: string): Promise { const ttl: DecodedTtl = await DecodeTtl(extensionlessFilePath); const pfp: DecodedPfp = await DecodePfp(extensionlessFilePath); - const jobObject = { + const jobObject: RawJobDataObject = { ...env, ...ad1, ...ad2, @@ -96,17 +103,7 @@ async function ImportJob(filepath: string): Promise { //Build the request object //Insert it - const newAvailableJob = { - // newJob.uploaded_by = Auth.authlink.User.Email; - // newJob.bodyshopid = AppMetaData.ActiveShopId; - // newJob.cieca_id = item.Job.ciecaid; - // newJob.est_data = item.Job; - // newJob.ownr_name = item.Job.ownr_fn?.Value + " " + item.Job.ownr_ln?.Value + " " + item.Job.ownr_co_nm?.Value; - // newJob.ins_co_nm = item.Job.ins_co_nm?.Value; - // newJob.vehicle_info = item.Job.vehicle.data.v_model_yr?.Value + " " + item.Job.vehicle.data.v_make_desc?.Value + " " + item.Job.vehicle.data.v_model_desc?.Value; - // newJob.clm_no = item.Job.clm_no?.Value; - // newJob.clm_amt = item.Job.clm_total?.Value; - // newJob.source_system = item.Job.source_system?.Value; + const newAvailableJob: AvailableJobSchema = { uploaded_by: store.get("app.user.email"), bodyshopid: store.get("app.bodyshop.id"), cieca_id: jobObject.ciecaid, @@ -117,30 +114,68 @@ async function ImportJob(filepath: string): Promise { clm_no: jobObject.clm_no, clm_amt: jobObject.clm_total, // source_system: jobObject.source_system, //TODO: Add back source system if needed. + issupplement: false, + jobid: null, }; - const existingVehicleId: uuid = await client.query () - // var vehuuid = await Utils.Queries.VehicleQueries.GetVehicleUuidByVin(item?.Job?.vehicle?.data?.v_vin?.Value ?? ""); - // if (!string.IsNullOrEmpty(vehuuid)) - // { - // newJob.est_data.vehicle = null; - // newJob.est_data.vehicleid = vehuuid; - // } + const existingVehicleRecord: VehicleQueryResult = await client.request( + QUERY_VEHICLE_BY_VIN_TYPED, + { + vin: jobObject.v_vin, + }, + ); + if (existingVehicleRecord.vehicles.length > 0) { + delete newAvailableJob.est_data.vehicle; + newAvailableJob.est_data.vehicleid = existingVehicleRecord.vehicles[0].id; + } - // string jobId = await Utils.Queries.JobsQueries.CheckSupplementByClaimNo(item.Job.clm_no?.Value ?? ""); + console.log(newAvailableJob); - // if (!string.IsNullOrEmpty(jobId)) - // { - // newJob.issupplement = true; - // newJob.jobid = jobId; - // }; - - //Check if the vehicle exists, if it does, use that UUID, if not, keep it to insert it. + const existingJobRecord: QueryJobByClmNoResult = await client.request( + QUERY_JOB_BY_CLM_NO_TYPED, + { clm_no: jobObject.clm_no }, + ); + if (existingJobRecord.jobs.length > 0) { + newAvailableJob.issupplement = true; + newAvailableJob.jobid = existingJobRecord.jobs[0].id; + } } catch (error) { log.error("Error encountered while decoding job. ", errorTypeCheck(error)); } } export default ImportJob; + +export interface RawJobDataObject + extends DecodedEnv, + DecodedAd1, + DecodedAD2, + DecodedVeh, + DecodedLin, + DecodedPfh, + DecodedPfl, + DecodedPft, + DecodedPfm, + DecodedPfo, + DecodedStl, + DecodedTtl, + DecodedPfp { + vehicleid?: UUID; +} + +export interface AvailableJobSchema { + uploaded_by: string; + bodyshopid: UUID; + cieca_id?: string; + est_data: RawJobDataObject; + ownr_name: string; + ins_co_nm?: string; + vehicle_info: string; + clm_no?: string; + clm_amt: number; + source_system?: string | null; + issupplement: boolean; + jobid: UUID | null; +} diff --git a/src/main/graphql/queries.ts b/src/main/graphql/queries.ts index 135cafb..383980c 100644 --- a/src/main/graphql/queries.ts +++ b/src/main/graphql/queries.ts @@ -1,6 +1,7 @@ +import { UUID } from "crypto"; import { parse, TypedQueryDocumentNode } from "graphql"; import { gql } from "graphql-request"; - +import { AvailableJobSchema } from "../decoder/decoder"; // Define types for the query result and variables export interface ActiveBodyshopQueryResult { bodyshops: Array<{ @@ -9,10 +10,8 @@ export interface ActiveBodyshopQueryResult { region_config: string; }>; } - // No variables needed for this query interface ActiveBodyshopQueryVariables {} - // Transform the string query into a TypedQueryDocumentNode export const QUERY_ACTIVE_BODYSHOP_TYPED: TypedQueryDocumentNode< ActiveBodyshopQueryResult, @@ -25,7 +24,10 @@ export const QUERY_ACTIVE_BODYSHOP_TYPED: TypedQueryDocumentNode< region_config } } -`); +`) as TypedQueryDocumentNode< + ActiveBodyshopQueryResult, + ActiveBodyshopQueryVariables +>; export interface MasterdataQueryResult { masterdata: Array<{ @@ -33,11 +35,9 @@ export interface MasterdataQueryResult { key: string; }>; } - interface MasterdataQueryVariables { key: string; } - export const QUERY_MASTERDATA_TYPED: TypedQueryDocumentNode< MasterdataQueryResult, MasterdataQueryVariables @@ -48,19 +48,16 @@ export const QUERY_MASTERDATA_TYPED: TypedQueryDocumentNode< key } } -`); +`) as TypedQueryDocumentNode; export interface VehicleQueryResult { - masterdata: Array<{ - value: string; - key: string; + vehicles: Array<{ + id: UUID; }>; } - interface VehicleQueryVariables { vin: string; } - export const QUERY_VEHICLE_BY_VIN_TYPED: TypedQueryDocumentNode< VehicleQueryResult, VehicleQueryVariables @@ -70,4 +67,62 @@ export const QUERY_VEHICLE_BY_VIN_TYPED: TypedQueryDocumentNode< id } } -`); +`) as TypedQueryDocumentNode; + +export interface QueryJobByClmNoResult { + jobs: Array<{ + id: UUID; + }>; +} +export interface QueryJobByClmNoVariables { + clm_no: string; +} +export const QUERY_JOB_BY_CLM_NO_TYPED: TypedQueryDocumentNode< + QueryJobByClmNoResult, + QueryJobByClmNoVariables +> = parse(gql` + query QUERY_JOB_BY_CLM_NO($clm_no: String!) { + jobs(where: { clm_no: { _eq: $clm_no } }) { + id + } + } +`) as TypedQueryDocumentNode; + +export interface InsertAvailableJobResult { + returning: Array<{ + id: UUID; + }>; +} +export interface InsertAvailableJobVariables { + jobInput: Array; +} +export const INSERT_AVAILABLE_JOB_TYPED: TypedQueryDocumentNode< + InsertAvailableJobResult, + InsertAvailableJobVariables +> = parse(gql` + mutation INSERT_AVAILABLE_JOB($jobInput: [available_jobs_insert_input!]!) { + insert_available_jobs( + objects: $jobInput + on_conflict: { + constraint: available_jobs_clm_no_bodyshopid_key + update_columns: [ + clm_amt + cieca_id + est_data + issupplement + ownr_name + source_system + supplement_number + vehicle_info + ] + } + ) { + returning { + id + } + } + } +`) as TypedQueryDocumentNode< + InsertAvailableJobResult, + InsertAvailableJobVariables +>; diff --git a/src/main/ipc/ipcMainConfig.ts b/src/main/ipc/ipcMainConfig.ts index 0ce7bbf..2b840ea 100644 --- a/src/main/ipc/ipcMainConfig.ts +++ b/src/main/ipc/ipcMainConfig.ts @@ -3,7 +3,7 @@ import log from "electron-log/main"; import path from "path"; import ipcTypes from "../../util/ipcTypes.json"; import ImportJob from "../decoder/decoder"; -import { StartWatcher } from "../watcher/watcher"; +import { StartWatcher, StopWatcher } from "../watcher/watcher"; import { SettingsWatchedFilePathsAdd, SettingsWatchedFilePathsGet, @@ -83,4 +83,8 @@ ipcMain.on(ipcTypes.toMain.watcher.start, () => { StartWatcher(); }); +ipcMain.on(ipcTypes.toMain.watcher.stop, () => { + StopWatcher(); +}); + logIpcMessages(); diff --git a/src/main/ipc/ipcMainHandler.settings.ts b/src/main/ipc/ipcMainHandler.settings.ts index 690ea76..c2a5bc9 100644 --- a/src/main/ipc/ipcMainHandler.settings.ts +++ b/src/main/ipc/ipcMainHandler.settings.ts @@ -2,6 +2,7 @@ import { BrowserWindow, dialog, IpcMainInvokeEvent } from "electron"; import log from "electron-log/main"; import _ from "lodash"; import Store from "../store/store"; +import { addWatcherPath, removeWatcherPath, watcher } from "../watcher/watcher"; const SettingsWatchedFilePathsAdd = async (): Promise => { const mainWindow = BrowserWindow.getAllWindows()[0]; //TODO: Filter to only main window once a proper key has been set. @@ -18,6 +19,7 @@ const SettingsWatchedFilePathsAdd = async (): Promise => { "settings.filepaths", _.union(result.filePaths, Store.get("settings.filepaths")) ); + addWatcherPath(result.filePaths); } return Store.get("settings.filepaths"); @@ -30,7 +32,7 @@ const SettingsWatchedFilePathsRemove = async ( "settings.filepaths", _.without(Store.get("settings.filepaths"), path) ); - + removeWatcherPath(path); return Store.get("settings.filepaths"); }; diff --git a/src/main/watcher/watcher.ts b/src/main/watcher/watcher.ts index 3237133..53330d1 100644 --- a/src/main/watcher/watcher.ts +++ b/src/main/watcher/watcher.ts @@ -1,10 +1,11 @@ import chokidar, { FSWatcher } from "chokidar"; -import { Notification } from "electron"; +import { BrowserWindow, Notification } from "electron"; import log from "electron-log/main"; import path from "path"; import errorTypeCheck from "../../util/errorTypeCheck"; -import store from "../store/store"; +import ipcTypes from "../../util/ipcTypes.json"; import ImportJob from "../decoder/decoder"; +import store from "../store/store"; let watcher: FSWatcher; @@ -67,6 +68,10 @@ async function StartWatcher(): Promise { // }) .on("error", function (error) { log.error("Error in Watcher", errorTypeCheck(error)); + // mainWindow.webContents.send( + // ipcTypes.toRenderer.watcher.error, + // errorTypeCheck(error) + // ); }) .on("ready", onWatcherReady) .on("raw", function (event, path, details) { @@ -77,24 +82,37 @@ async function StartWatcher(): Promise { return true; } +function removeWatcherPath(path: string): void { + watcher.unwatch(path); + log.debug(`Stopped watching path: ${path}`); +} + +function addWatcherPath(path: string | string[]): void { + watcher.add(path); + log.debug(`Started watching path: ${path}`); +} + function onWatcherReady(): void { - log.info("Watcher ready!"); - // const b = BrowserWindow.getAllWindows()[0]; - // b.webContents.send(ipcTypes.default.fileWatcher.toRenderer.startSuccess); + const mainWindow = BrowserWindow.getAllWindows()[0]; //TODO: Filter to only main window once a proper key has been set. + new Notification({ title: "Watcher Started", body: "Newly exported estimates will be automatically uploaded.", }).show(); log.info("Confirmed watched paths:", watcher.getWatched()); + mainWindow.webContents.send(ipcTypes.toRenderer.watcher.started); } async function StopWatcher(): Promise { + const mainWindow = BrowserWindow.getAllWindows()[0]; //TODO: Filter to only main window once a proper key has been set. + if (watcher) { await watcher.close(); log.info("Watcher stopped."); + mainWindow.webContents.send(ipcTypes.toRenderer.watcher.stopped); new Notification({ - title: "RPS Watcher Stopped", + title: "Watcher Stopped", body: "Estimates will not be automatically uploaded.", }).show(); return true; @@ -107,4 +125,10 @@ async function HandleNewFile(path): Promise { log.log("Received a new file", path); } -export { StartWatcher, StopWatcher, watcher }; +export { + StartWatcher, + StopWatcher, + watcher, + removeWatcherPath, + addWatcherPath, +}; diff --git a/src/renderer/src/components/Settings/Settings.Watcher.tsx b/src/renderer/src/components/Settings/Settings.Watcher.tsx index 1ee62de..55be840 100644 --- a/src/renderer/src/components/Settings/Settings.Watcher.tsx +++ b/src/renderer/src/components/Settings/Settings.Watcher.tsx @@ -1,16 +1,50 @@ -import { Button } from "antd"; +import { + CheckCircleOutlined, + ExclamationCircleOutlined, +} from "@ant-design/icons"; +import { + selectWatcherError, + selectWatcherStatus, +} from "@renderer/redux/app.slice"; +import { useAppDispatch, useAppSelector } from "@renderer/redux/reduxHooks"; +import { Button, Space } from "antd"; import { useTranslation } from "react-i18next"; import ipcTypes from "../../../../util/ipcTypes.json"; const SettingsWatcher: React.FC = () => { const { t } = useTranslation(); + const isWatcherStarted = useAppSelector(selectWatcherStatus); + const watcherError = useAppSelector(selectWatcherError); + const dispatch = useAppDispatch(); const handleStart = (): void => { window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.start); }; + const handleStop = (): void => { + window.electron.ipcRenderer.send(ipcTypes.toMain.watcher.stop); + }; + return ( - + <> + + + {isWatcherStarted} + {watcherError} + {isWatcherStarted ? ( + + + {t("settings.labels.started")} + + ) : ( + + + {t("settings.labels.stopped")} + + )} + ); }; export default SettingsWatcher; diff --git a/src/renderer/src/redux/app.slice.ts b/src/renderer/src/redux/app.slice.ts index b4b6cde..9b3cf18 100644 --- a/src/renderer/src/redux/app.slice.ts +++ b/src/renderer/src/redux/app.slice.ts @@ -1,46 +1,58 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; +import log from "electron-log/renderer"; import type { RootState } from "./redux-store"; - -// Define a type for the slice state interface AppState { value: number; + watcher: { + started: boolean; + error: string | null; + }; } // Define the initial state using that type const initialState: AppState = { value: 0, + watcher: { + started: false, + error: null, + }, }; export const appSlice = createSlice({ - name: "counter", + name: "app", // `createSlice` will infer the state type from the `initialState` argument initialState, reducers: { - increment: (state) => { - state.value += 1; + watcherStarted: (state) => { + state.watcher.started = true; }, - decrement: (state) => { - state.value -= 1; + watcherStopped: (state) => { + state.watcher.started = false; }, - // Use the PayloadAction type to declare the contents of `action.payload` - incrementByAmount: (state, action: PayloadAction) => { - state.value += action.payload; + watcherError: (state, action: PayloadAction) => { + state.watcher.error = action.payload; + state.watcher.started = false; + log.error("[Redux] AppSlice: Watcher Error", action.payload); }, }, }); -export const { increment, decrement, incrementByAmount } = appSlice.actions; +export const { watcherError, watcherStarted, watcherStopped } = + appSlice.actions; // Other code such as selectors can use the imported `RootState` type -export const selectCount = (state: RootState): number => state.app.value; +export const selectWatcherStatus = (state: RootState): boolean => + state.app.watcher.started; + +export const selectWatcherError = (state: RootState): string | null => + state.app.watcher.error; //Async Functions - Thunks // Define a thunk that dispatches those action creators const fetchUsers = () => async (dispatch) => { - dispatch(increment()); + //dispatch(watcherStarted()); //Some sort of async action. - - dispatch(incrementByAmount(100)); + // dispatch(incrementByAmount(100)); }; export default appSlice.reducer; diff --git a/src/renderer/src/redux/reduxHooks.ts b/src/renderer/src/redux/reduxHooks.ts index 6732d78..6834500 100644 --- a/src/renderer/src/redux/reduxHooks.ts +++ b/src/renderer/src/redux/reduxHooks.ts @@ -1,9 +1,10 @@ import type { TypedUseSelectorHook } from "react-redux"; import { useDispatch, useSelector, useStore } from "react-redux"; import type { AppDispatch, AppStore, RootState } from "./redux-store"; - +import store from "./redux-store"; //Use these custom hooks to access the Redux store from your component with type safety. -export const useAppDispatch: () => AppDispatch = useDispatch; +export type AppDispatch = typeof store.dispatch; +export const useAppDispatch = useDispatch.withTypes(); // Ex export const useAppSelector: TypedUseSelectorHook = useSelector; export const useAppStore: () => AppStore = useStore; diff --git a/src/renderer/src/util/ipcRendererHandler.ts b/src/renderer/src/util/ipcRendererHandler.ts index 718a87f..84695d9 100644 --- a/src/renderer/src/util/ipcRendererHandler.ts +++ b/src/renderer/src/util/ipcRendererHandler.ts @@ -1,8 +1,15 @@ //Set up all of the IPC handlers. +import { + watcherError, + watcherStarted, + watcherStopped, +} from "@renderer/redux/app.slice"; +import store from "@renderer/redux/redux-store"; import ipcTypes from "../../../util/ipcTypes.json"; import { auth } from "./firebase"; const ipcRenderer = window.electron.ipcRenderer; +const dispatch = store.dispatch; ipcRenderer.on( ipcTypes.toRenderer.test, @@ -19,3 +26,29 @@ ipcRenderer.on( ipcRenderer.send(ipcTypes.toMain.user.getTokenResponse, token); } ); + +ipcRenderer.on( + ipcTypes.toRenderer.watcher.started, + (event: Electron.IpcRendererEvent, arg) => { + console.log("Watcher has started"); + console.log(arg); + dispatch(watcherStarted()); + } +); + +ipcRenderer.on( + ipcTypes.toRenderer.watcher.stopped, + (event: Electron.IpcRendererEvent, arg) => { + console.log("Watcher has stopped"); + console.log(arg); + dispatch(watcherStopped()); + } +); +ipcRenderer.on( + ipcTypes.toRenderer.watcher.error, + (event: Electron.IpcRendererEvent, error: string) => { + console.log("Watcher has encountered an error"); + console.log(error); + dispatch(watcherError(error)); + } +); diff --git a/src/util/ipcTypes.json b/src/util/ipcTypes.json index 586803e..5137cb7 100644 --- a/src/util/ipcTypes.json +++ b/src/util/ipcTypes.json @@ -22,6 +22,11 @@ }, "toRenderer": { "test": "toRenderer_test", + "watcher": { + "started": "toRenderer_watcher_started", + "stopped": "toRenderer_watcher_stopped", + "error": "toRenderer_watcher_error" + }, "user": { "getToken": "toRenderer_user_getToken" } diff --git a/src/util/translations/en-US/renderer.json b/src/util/translations/en-US/renderer.json index 202bfbe..0d8881d 100644 --- a/src/util/translations/en-US/renderer.json +++ b/src/util/translations/en-US/renderer.json @@ -6,7 +6,14 @@ }, "settings": { "actions": { - "addpath": "Add path" + "addpath": "Add path", + "startwatcher": "Start Watcher", + "stopwatcher": "Stop Watcher\n" + }, + "labels": { + "started": "Started", + "stopped": "Stopped", + "watcherstatus": "Watcher Status" } } } diff --git a/translations.babel b/translations.babel index 40aaf62..e49a64c 100644 --- a/translations.babel +++ b/translations.babel @@ -95,6 +95,76 @@ + + startwatcher + false + + + + + + en-US + false + + + + + stopwatcher + false + + + + + + en-US + false + + + + + + + labels + + + started + false + + + + + + en-US + false + + + + + stopped + false + + + + + + en-US + false + + + + + watcherstatus + false + + + + + + en-US + false + + + diff --git a/tsconfig.json b/tsconfig.json index 31bac6e..155ebaa 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,4 +1,7 @@ { "files": [], - "references": [{ "path": "./tsconfig.node.json" }, { "path": "./tsconfig.web.json" }] + "references": [ + { "path": "./tsconfig.node.json" }, + { "path": "./tsconfig.web.json" } + ] } diff --git a/tsconfig.node.json b/tsconfig.node.json index 5748494..1e92029 100644 --- a/tsconfig.node.json +++ b/tsconfig.node.json @@ -9,6 +9,7 @@ "tests/index.spec.ts" ], "compilerOptions": { + "resolveJsonModule": true, "composite": true, "types": ["electron-vite/node"] } diff --git a/tsconfig.web.json b/tsconfig.web.json index 6120f9e..d3e4739 100644 --- a/tsconfig.web.json +++ b/tsconfig.web.json @@ -8,6 +8,7 @@ "src/util/**/*" ], "compilerOptions": { + "resolveJsonModule": true, "composite": true, "jsx": "react-jsx", "baseUrl": ".",