From da43ade088528d99964ed0417db0b25d02545e4f Mon Sep 17 00:00:00 2001 From: Patrick Fic Date: Wed, 2 Apr 2025 15:23:34 -0700 Subject: [PATCH] Add isTest to settings and ability to redirect to test env. Add rome/imex specific builds. --- .env.imex | 4 ++ .env.local | 4 +- .env.rome | 4 ++ README.md | 44 +++++-------- ...n-builder.yml => electron-builder.imex.yml | 0 electron-builder.rome.yml | 62 +++++++++++++++++++ package.json | 7 ++- src/env.d.ts | 2 + src/main/graphql/graphql-client.ts | 5 +- src/main/index.ts | 43 ++++++++++++- src/main/store/store.ts | 1 + src/preload/index.ts | 9 ++- .../NavigationHeader/Navigationheader.tsx | 36 +++++------ .../src/components/Settings/Settings.tsx | 1 + src/renderer/src/util/firebase.ts | 6 +- 15 files changed, 170 insertions(+), 58 deletions(-) create mode 100644 .env.imex create mode 100644 .env.rome rename electron-builder.yml => electron-builder.imex.yml (100%) create mode 100644 electron-builder.rome.yml diff --git a/.env.imex b/.env.imex new file mode 100644 index 0000000..a772856 --- /dev/null +++ b/.env.imex @@ -0,0 +1,4 @@ +VITE_FIREBASE_CONFIG={"apiKey":"AIzaSyDSezy-jGJreo7ulgpLdlpOwAOrgcaEkhU","authDomain":"imex-prod.firebaseapp.com","databaseURL":"https://imex-prod.firebaseio.com","projectId":"imex-prod","storageBucket":"imex-prod.appspot.com","messagingSenderId":"253497221485","appId":"1:253497221485:web:3c81c483b94db84b227a64","measurementId":"G-NTWBKG2L0M"} +VITE_GRAPHQL_ENDPOINT=https://db.imex.online/v1/graphql +VITE_FIREBASE_CONFIG_TEST={ "apiKey":"AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c", "authDomain":"imex-test.firebaseapp.com", "projectId":"imex-test", "storageBucket":"imex-test.appspot.com", "messagingSenderId":"991923618608", "appId":"1:991923618608:web:633437569cdad78299bef5", "measurementId":"G-TW0XLZEH18"} +VITE_GRAPHQL_ENDPOINT_TEST=https://db.test.bodyshop.app/v1/graphql \ No newline at end of file diff --git a/.env.local b/.env.local index b44ada4..704063a 100644 --- a/.env.local +++ b/.env.local @@ -1,2 +1,4 @@ VITE_FIREBASE_CONFIG={"apiKey":"AIzaSyDPLT8GiDHDR1R4nI66Qi0BY1aYviDPioc","authDomain":"imex-dev.firebaseapp.com","databaseURL":"https://imex-dev.firebaseio.com","projectId":"imex-dev","storageBucket":"imex-dev.appspot.com","messagingSenderId":"759548147434","appId":"1:759548147434:web:e8239868a48ceb36700993","measurementId":"G-K5XRBVVB4S"} -VITE_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql \ No newline at end of file +VITE_GRAPHQL_ENDPOINT=https://db.dev.imex.online/v1/graphql +VITE_FIREBASE_CONFIG_TEST={ "apiKey":"AIzaSyBw7_GTy7GtQyfkIRPVrWHEGKfcqeyXw0c", "authDomain":"imex-test.firebaseapp.com", "projectId":"imex-test", "storageBucket":"imex-test.appspot.com", "messagingSenderId":"991923618608", "appId":"1:991923618608:web:633437569cdad78299bef5", "measurementId":"G-TW0XLZEH18"} +VITE_GRAPHQL_ENDPOINT_TEST=https://db.test.bodyshop.app/v1/graphql \ No newline at end of file diff --git a/.env.rome b/.env.rome new file mode 100644 index 0000000..b0871ce --- /dev/null +++ b/.env.rome @@ -0,0 +1,4 @@ +VITE_FIREBASE_CONFIG={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"} +VITE_GRAPHQL_ENDPOINT=https://db.romeonline.io/v1/graphql +VITE_FIREBASE_CONFIG_TEST={ "apiKey": "AIzaSyAuLQR9SV5LsVxjU8wh9hvFLdhcAHU6cxE", "authDomain": "rome-prod-1.firebaseapp.com", "projectId": "rome-prod-1", "storageBucket": "rome-prod-1.appspot.com", "messagingSenderId": "147786367145", "appId": "1:147786367145:web:9d4cba68071c3f29a8a9b8", "measurementId": "G-G8Z9DRHTZS"} +VITE_GRAPHQL_ENDPOINT_TEST=https://db.test.romeonline.io/v1/graphql \ No newline at end of file diff --git a/README.md b/README.md index 1028732..602d47b 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,18 @@ -# bodyshop-desktop +# Shop Partner +An electron app that is replacing the existing Bodyshop Partner that was a C#/WPF Application. -An Electron application with React and TypeScript +The purpose of this application is to: +* Parse EMS files, and upload them to the IO back end. +* Receive requests for EMS file parsing + +The following functionality will be coming: +* Interact with QuickBooks desktop (Windows Only) +* Paint scale integrations +* Parts Price Changes for CCC -## Recommended IDE Setup +Toggling between the Production and Test servers can be done by pressing `CTRL/CMD + SHIFT + T`, and then going to the application menu, and enabling test. The application will restart automatically. -- [VSCode](https://code.visualstudio.com/) + [ESLint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint) + [Prettier](https://marketplace.visualstudio.com/items?itemName=esbenp.prettier-vscode) +## Dev and Build Notes +Unlike the main app, the dev mode will only connect to ImEX Online test data. -## Project Setup - -### Install - -```bash -$ npm install -``` - -### Development - -```bash -$ npm run dev -``` - -### Build - -```bash -# For windows -$ npm run build:win - -# For macOS -$ npm run build:mac - -# For Linux -$ npm run build:linux -``` +Building the app will require specifying the company to build for. Those details are captured in their respective ENV and YAML files. diff --git a/electron-builder.yml b/electron-builder.imex.yml similarity index 100% rename from electron-builder.yml rename to electron-builder.imex.yml diff --git a/electron-builder.rome.yml b/electron-builder.rome.yml new file mode 100644 index 0000000..bb392e3 --- /dev/null +++ b/electron-builder.rome.yml @@ -0,0 +1,62 @@ +appId: com.convenientbrands.bodyshop-desktop +copyright: Convenient Brands, LLC. +productName: Rome Shop Partner +generateUpdatesFilesForAllChannels: true + +directories: + buildResources: build +files: + - "!**/.vscode/*" + - "!src/*" + - "!electron.vite.config.{js,ts,mjs,cjs}" + - "!{.eslintignore,.eslintrc.cjs,.prettierignore,.prettierrc.yaml,dev-app-update.yml,CHANGELOG.md,README.md}" + - "!{.env,.env.*,.npmrc,pnpm-lock.yaml}" + - "!{tsconfig.json,tsconfig.node.json,tsconfig.web.json}" +asarUnpack: + - resources/** +win: + executableName: ShopPartner + icon: resources/diamond.png + azureSignOptions: + endpoint: https://eus.codesigning.azure.net + certificateProfileName: ImEXRPS + codeSigningAccountName: ImEX + +nsis: + artifactName: ${name}-${version}-setup.${ext} + shortcutName: ${productName} + uninstallDisplayName: ${productName} + createDesktopShortcut: always +mac: + entitlementsInherit: build/entitlements.mac.plist + category: public.app-category.business + extendInfo: + - NSCameraUsageDescription: Application requests access to the device's camera. + - NSMicrophoneUsageDescription: Application requests access to the device's microphone. + - NSDocumentsFolderUsageDescription: Application requests access to the user's Documents folder. + - NSDownloadsFolderUsageDescription: Application requests access to the user's Downloads folder. + target: + - target: default + arch: + - arm64 + - target: default + arch: + - x64 +dmg: + artifactName: ${name}-${version}-${arch}.${ext} +linux: + target: + - AppImage + - snap + - deb + maintainer: electronjs.org + category: Utility +appImage: + artifactName: ${name}-${version}.${ext} +npmRebuild: false +publish: + provider: s3 + bucket: imex-partner + region: ca-central-1 +electronDownload: + mirror: https://npmmirror.com/mirrors/electron/ diff --git a/package.json b/package.json index 2763f3e..116dd76 100644 --- a/package.json +++ b/package.json @@ -13,12 +13,13 @@ "typecheck": "npm run typecheck:node && npm run typecheck:web", "start": "electron-vite preview", "dev": "electron-vite dev", - "build": "electron-vite build", + "build:mac:imex": "electron-vite build --mode imex && electron-builder --config electron-builder.imex.yml", + "build:mac:rome": "electron-vite build --mode rome && electron-builder --config electron-builder.rome.yml", "postinstall": "electron-builder install-app-deps", "build:unpack": "npm run build && electron-builder --dir", "build:win": "npm run build && electron-builder --win", - "build:mac": "electron-vite build && electron-builder --mac", - "build:linux": "electron-vite build && electron-builder --linux" + "build:mac": "npm run build && electron-builder --mac", + "build:linux": "npm run build && electron-builder --linux" }, "dependencies": { "@apollo/client": "^3.13.5", diff --git a/src/env.d.ts b/src/env.d.ts index 33cf21d..c2e6c8b 100644 --- a/src/env.d.ts +++ b/src/env.d.ts @@ -3,6 +3,8 @@ export interface ImportMetaEnv { readonly VITE_FIREBASE_CONFIG: string; readonly VITE_GRAPHQL_ENDPOINT: string; + readonly VITE_FIREBASE_CONFIG_TEST: string; + readonly VITE_GRAPHQL_ENDPOINT_TEST: string; } export interface ImportMeta { diff --git a/src/main/graphql/graphql-client.ts b/src/main/graphql/graphql-client.ts index a1d7d08..d1dad22 100644 --- a/src/main/graphql/graphql-client.ts +++ b/src/main/graphql/graphql-client.ts @@ -3,6 +3,7 @@ import log from "electron-log/main"; import { GraphQLClient, RequestMiddleware } from "graphql-request"; import errorTypeCheck from "../../util/errorTypeCheck.js"; import ipcTypes from "../../util/ipcTypes.json"; +import store from "../store/store.js"; const requestMiddleware: RequestMiddleware = async (request) => { const token = await getTokenFromRenderer(); @@ -20,7 +21,9 @@ const requestMiddleware: RequestMiddleware = async (request) => { }; const client: GraphQLClient = new GraphQLClient( - import.meta.env.VITE_GRAPHQL_ENDPOINT, + store.get("app.isTest") || false + ? import.meta.env.VITE_GRAPHQL_ENDPOINT_TEST + : import.meta.env.VITE_GRAPHQL_ENDPOINT, { requestMiddleware, }, diff --git a/src/main/index.ts b/src/main/index.ts index 6b43085..175428b 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -1,6 +1,14 @@ import { electronApp, is, optimizer } from "@electron-toolkit/utils"; import Sentry from "@sentry/electron/main"; -import { app, BrowserWindow, Menu, nativeImage, shell, Tray } from "electron"; +import { + app, + BrowserWindow, + globalShortcut, + Menu, + nativeImage, + shell, + Tray, +} from "electron"; import log from "electron-log/main"; import { autoUpdater } from "electron-updater"; import path, { join } from "path"; @@ -138,6 +146,20 @@ function createWindow(): void { } }, }, + { + label: "Connect to Test", + checked: store.get("app.isTest") as boolean, + visible: false, + type: "checkbox", + id: "toggleTest", + click: (): void => { + const currentSetting = store.get("app.isTest") as boolean; + store.set("app.isTest", !currentSetting); + log.info("Setting isTest to: ", !currentSetting); + app.relaunch(); // Relaunch the app + app.exit(0); // Exit the current instance + }, + }, ], }, // { role: 'windowMenu' } @@ -219,6 +241,23 @@ function createWindow(): void { const menu: Electron.Menu = Menu.buildFromTemplate(template); Menu.setApplicationMenu(menu); + // Register a global shortcut to show the hidden item + globalShortcut.register("CommandOrControl+Shift+T", () => { + console.log("Shortcut pressed! Revealing hidden item."); + + // Update the menu to make the hidden item visible + // Find the menu item dynamically by its id + const fileMenu = template.find((item) => item.label === "Application"); + const hiddenItem = fileMenu?.submenu?.find( + (item) => item.id === "toggleTest", + ); + if (hiddenItem) { + hiddenItem.visible = true; // Update the visibility dynamically + const menu: Electron.Menu = Menu.buildFromTemplate(template); + Menu.setApplicationMenu(menu); + } + }); + // Store window properties for later const storeWindowState = (): void => { const [width, height] = mainWindow.getSize(); @@ -388,7 +427,7 @@ app.on("before-quit", () => { openAtLogin: !currentSetting, }); } - + globalShortcut.unregisterAll(); isAppQuitting = true; }); diff --git a/src/main/store/store.ts b/src/main/store/store.ts index 0f99b46..fa5d343 100644 --- a/src/main/store/store.ts +++ b/src/main/store/store.ts @@ -20,6 +20,7 @@ const store = new Store({ y: undefined, }, user: null, + isTest: false, bodyshop: {}, masterdata: { opcodes: null, diff --git a/src/preload/index.ts b/src/preload/index.ts index 3f7d1ec..4485593 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -1,9 +1,16 @@ import { contextBridge } from "electron"; import { electronAPI } from "@electron-toolkit/preload"; import "electron-log/preload"; +import store from "../main/store/store"; // Custom APIs for renderer -const api = {}; +interface Api { + isTest: () => boolean; +} + +const api: Api = { + isTest: (): boolean => store.get("app.isTest") || false, +}; // Use `contextBridge` APIs to expose Electron APIs to // renderer only if context isolation is enabled, otherwise diff --git a/src/renderer/src/components/NavigationHeader/Navigationheader.tsx b/src/renderer/src/components/NavigationHeader/Navigationheader.tsx index ddea3de..31ecc8c 100644 --- a/src/renderer/src/components/NavigationHeader/Navigationheader.tsx +++ b/src/renderer/src/components/NavigationHeader/Navigationheader.tsx @@ -1,5 +1,6 @@ +import { is } from "@electron-toolkit/utils"; import { auth } from "@renderer/util/firebase"; -import { Layout, Menu } from "antd"; +import { Badge, Layout, Menu } from "antd"; import { MenuItemType } from "antd/es/menu/interface"; import { signOut } from "firebase/auth"; import { useTranslation } from "react-i18next"; @@ -18,25 +19,22 @@ const NavigationHeader: React.FC = () => { }, }, ]; + const isTest = window.api.isTest(); return ( - // - - - - // + + + + + ); }; diff --git a/src/renderer/src/components/Settings/Settings.tsx b/src/renderer/src/components/Settings/Settings.tsx index 059bbe1..92e8404 100644 --- a/src/renderer/src/components/Settings/Settings.tsx +++ b/src/renderer/src/components/Settings/Settings.tsx @@ -4,6 +4,7 @@ import SettingsWatcher from "./Settings.Watcher"; import Welcome from "../Welcome/Welcome"; const Settings: React.FC = () => { + console.log("is test?", window.api.isTest()); return ( diff --git a/src/renderer/src/util/firebase.ts b/src/renderer/src/util/firebase.ts index b506ba9..6d85c3b 100644 --- a/src/renderer/src/util/firebase.ts +++ b/src/renderer/src/util/firebase.ts @@ -2,7 +2,11 @@ import { initializeApp } from "firebase/app"; import { getAuth } from "firebase/auth"; // TODO: Replace the following with your app's Firebase project configuration -const firebaseConfig = JSON.parse(import.meta.env.VITE_FIREBASE_CONFIG); +const firebaseConfig = JSON.parse( + window.api.isTest() + ? import.meta.env.VITE_FIREBASE_CONFIG_TEST + : import.meta.env.VITE_FIREBASE_CONFIG, +); const app = initializeApp(firebaseConfig); export const auth = getAuth();