diff --git a/.github/workflows/frontend.yml b/.github/workflows/frontend.yml index 8f638a0..87ee73d 100644 --- a/.github/workflows/frontend.yml +++ b/.github/workflows/frontend.yml @@ -29,8 +29,11 @@ jobs: - name: Install dependencies run: cd Frontend && npm install --force # This should install all dependencies, regardless of the workspace + + - name: Write shared .env file + run: echo "${{ github.ref == 'refs/heads/deployment' && secrets.SHARED_PROD_ENV_FILE || secrets.SHARED_DEV_ENV_FILE }}" > Frontend/shared/.env - - name: Write .env file + - name: Write Frontend .env file run: echo "${{ github.ref == 'refs/heads/deployment' && secrets.FRONTEND_PROD_ENV_FILE || secrets.FRONTEND_DEV_ENV_FILE }}" > Frontend/speedcart-react/.env - name: Build front end diff --git a/Frontend/package-lock.json b/Frontend/package-lock.json index 35d30e4..2726d02 100644 --- a/Frontend/package-lock.json +++ b/Frontend/package-lock.json @@ -37,6 +37,7 @@ "process": "^0.11.10", "react": "18.2.0", "react-dom": "^18.2.0", + "react-error-boundary": "^6.0.0", "react-icons": "^5.2.1", "react-intersection-observer": "^9.5.3", "react-native": "0.74.5", @@ -12341,6 +12342,11 @@ "node": ">=8" } }, + "node_modules/globrex": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/globrex/-/globrex-0.1.2.tgz", + "integrity": "sha512-uHJgbwAMwNFf5mLst7IWLNg14x1CkeqglJb/K3doi4dw6q2IvAAmM/Y81kevy83wP+Sst+nutFTYOGg3d1lsxg==" + }, "node_modules/gopd": { "version": "1.0.1", "license": "MIT", @@ -17962,6 +17968,17 @@ "react": "^18.2.0" } }, + "node_modules/react-error-boundary": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/react-error-boundary/-/react-error-boundary-6.0.0.tgz", + "integrity": "sha512-gdlJjD7NWr0IfkPlaREN2d9uUZUlksrfOx7SX62VRerwXbMY6ftGCIZua1VG1aXFNOimhISsTq+Owp725b9SiA==", + "dependencies": { + "@babel/runtime": "^7.12.5" + }, + "peerDependencies": { + "react": ">=16.13.1" + } + }, "node_modules/react-icons": { "version": "5.3.0", "license": "MIT", @@ -20301,6 +20318,57 @@ "vite": "^2.9.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0" } }, + "node_modules/vite-tsconfig-paths": { + "version": "5.1.4", + "resolved": "https://registry.npmjs.org/vite-tsconfig-paths/-/vite-tsconfig-paths-5.1.4.tgz", + "integrity": "sha512-cYj0LRuLV2c2sMqhqhGpaO3LretdtMn/BVX4cPLanIZuwwrkVl+lK84E/miEXkCHWXuq65rhNN4rXsBcOB3S4w==", + "dependencies": { + "debug": "^4.1.1", + "globrex": "^0.1.2", + "tsconfck": "^3.0.3" + }, + "peerDependencies": { + "vite": "*" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + } + }, + "node_modules/vite-tsconfig-paths/node_modules/tsconfck": { + "version": "3.1.6", + "resolved": "https://registry.npmjs.org/tsconfck/-/tsconfck-3.1.6.tgz", + "integrity": "sha512-ks6Vjr/jEw0P1gmOVwutM3B7fWxoWBL2KRDb1JfqGVawBmO5UsvmWOQFGHBPl5yxYz4eERr19E6L7NMv+Fej4w==", + "bin": { + "tsconfck": "bin/tsconfck.js" + }, + "engines": { + "node": "^18 || >=20" + }, + "peerDependencies": { + "typescript": "^5.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/vite-tsconfig-paths/node_modules/typescript": { + "version": "5.9.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz", + "integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==", + "optional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, "node_modules/vite/node_modules/fdir": { "version": "6.4.4", "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", @@ -20982,7 +21050,8 @@ "speedcart-react": { "version": "0.8.5", "dependencies": { - "vite": "^6.3.3" + "vite": "^6.3.3", + "vite-tsconfig-paths": "^5.1.4" } }, "speedcart-react-native": { diff --git a/Frontend/package.json b/Frontend/package.json index d37ba6c..6f50873 100644 --- a/Frontend/package.json +++ b/Frontend/package.json @@ -1,5 +1,6 @@ { "name": "my-monorepo", + "type": "module", "private": true, "workspaces": [ "shared", @@ -35,6 +36,7 @@ "process": "^0.11.10", "react": "18.2.0", "react-dom": "^18.2.0", + "react-error-boundary": "^6.0.0", "react-icons": "^5.2.1", "react-intersection-observer": "^9.5.3", "react-native": "0.74.5", diff --git a/Frontend/shared/.env b/Frontend/shared/.env deleted file mode 100644 index 5494f0f..0000000 --- a/Frontend/shared/.env +++ /dev/null @@ -1,2 +0,0 @@ -API_DOMAIN=api.speedcartapp.com -TESTING_MODE=false \ No newline at end of file diff --git a/Frontend/shared/.gitignore b/Frontend/shared/.gitignore index c228cbe..6032708 100644 --- a/Frontend/shared/.gitignore +++ b/Frontend/shared/.gitignore @@ -1,3 +1,4 @@ node_modules/ dist/ -src/constants/config.json \ No newline at end of file +src/constants/config.json +.env \ No newline at end of file diff --git a/Frontend/shared/generate-config.js b/Frontend/shared/generate-config.js index 5f521ee..b83c6e3 100644 --- a/Frontend/shared/generate-config.js +++ b/Frontend/shared/generate-config.js @@ -6,11 +6,15 @@ const dotenv = require('dotenv'); dotenv.config(); const config = { - API_DOMAIN: process.env.API_DOMAIN, - API_PORT: process.env.API_PORT, - TESTING_MODE: process.env.TESTING_MODE, + API_DOMAIN: process.env.API_DOMAIN || 'localhost', + API_PORT: process.env.API_PORT || '', // ensure it always exists + TESTING_MODE: process.env.TESTING_MODE || 'false', }; // Write the config to a file in the shared directory -fs.writeFileSync(path.resolve(__dirname, './src/constants/config.json'), JSON.stringify(config, null, 2)); +fs.writeFileSync( + path.resolve(__dirname, './src/constants/config.json'), + JSON.stringify(config, null, 2) +); + console.log('Config file generated successfully.'); diff --git a/Frontend/shared/src/constants/BaseUrl.ts b/Frontend/shared/src/constants/BaseUrl.ts index d73b5d6..3f64565 100644 --- a/Frontend/shared/src/constants/BaseUrl.ts +++ b/Frontend/shared/src/constants/BaseUrl.ts @@ -2,10 +2,13 @@ import config from './config.json'; const API_DOMAIN = config.API_DOMAIN; +const API_PORT = config.API_PORT; // Protocol depends on if we're using dev or production const API_PROTOCOL = API_DOMAIN === 'localhost' ? 'http' : 'https'; // This can be reused for all backend interactions -export const BASE_URL: string = `${API_PROTOCOL}://${API_DOMAIN}`; +export const BASE_URL: string = API_PORT + ? `${API_PROTOCOL}://${API_DOMAIN}:${API_PORT}` + : `${API_PROTOCOL}://${API_DOMAIN}`; export const TESTING_MODE: boolean = config.TESTING_MODE === 'true'; \ No newline at end of file diff --git a/Frontend/speedcart-react/package.json b/Frontend/speedcart-react/package.json index 3ff017f..42f7427 100644 --- a/Frontend/speedcart-react/package.json +++ b/Frontend/speedcart-react/package.json @@ -26,6 +26,7 @@ ] }, "dependencies": { - "vite": "^6.3.3" + "vite": "^6.3.3", + "vite-tsconfig-paths": "^5.1.4" } } diff --git a/Frontend/speedcart-react/src/App.tsx b/Frontend/speedcart-react/src/App.tsx index 74dc80b..8af3281 100644 --- a/Frontend/speedcart-react/src/App.tsx +++ b/Frontend/speedcart-react/src/App.tsx @@ -1,6 +1,9 @@ // App.js import React, { useEffect, useState } from 'react'; import { BrowserRouter as Router, Route, Routes } from 'react-router-dom'; +import { ErrorBoundary } from 'react-error-boundary'; + +import PageLayout from '@components/PageLayout'; import Navigation from '@components/Navigation'; import Footer from '@components/Footer'; import Modal from '@components/Modal'; @@ -8,15 +11,25 @@ import SitePolicies from '@components/SitePolicies'; import { AppRoute } from '@constants/routes'; -import Home from '@pages/Home'; -import ShoppingListShare from '@pages/ShoppingListShare'; -import Login from '@pages/Login'; -import Dashboard from '@pages/Dashboard'; -import NewShoppingListWithProvider from '@pages/NewShoppingListWithProvider'; -import ShoppingListDetailWithProvider from '@pages/ShoppingListDetailWithProvider'; +const Home = React.lazy(() => import('@pages/Home')); +const ShoppingListShare = React.lazy(() => import('@pages/ShoppingListShare')); +const Login = React.lazy(() => import('@pages/Login')); +const Dashboard = React.lazy(() => import('@pages/Dashboard')); +const NewShoppingListWithProvider = React.lazy(() => import('@pages/NewShoppingListWithProvider')); +const ShoppingListDetailWithProvider = React.lazy(() => import('@pages/ShoppingListDetailWithProvider')); import './App.css'; +function ErrorFallback({ error, resetErrorBoundary }: { error: Error; resetErrorBoundary: () => void }) { + return ( +
Something went wrong:
+{error.message}
+
+