From 0bd60916c94e73c708d4fe6ad11732b66268f055 Mon Sep 17 00:00:00 2001 From: Ida Dittrich Date: Wed, 10 Sep 2025 13:04:16 +0200 Subject: [PATCH] updated cofig logic --- .cursorignore | 2 - .github/workflows/poweron_nyla_int.yml | 2 +- .github/workflows/poweron_nyla_main.yml | 2 +- .gitignore | 12 + README.md | Bin 6558 -> 8144 bytes .env.dev => config/.env.dev | 0 .env.int => config/.env.int | 0 .env.prod => config/.env.prod | 0 config/config.ts | 178 ++++++ config/index.ts | 12 + config/serverConfig.js | 191 +++++++ config/universalConfig.js | 101 ++++ documentation/sidebar.md | 463 --------------- index.html | 2 +- package-lock.json | 533 +++++++++++++++++- package.json | 3 +- deploy-server.js => scripts/deploy-server.js | 10 +- server.js => scripts/server.js | 7 +- src/App.tsx | 8 +- src/api.ts | 8 +- .../DashboardChat/DashboardChatAreaInput.tsx | 2 +- src/hooks/usePrompts.ts | 5 +- vite.config.ts | 37 +- 23 files changed, 1085 insertions(+), 493 deletions(-) delete mode 100644 .cursorignore rename .env.dev => config/.env.dev (100%) rename .env.int => config/.env.int (100%) rename .env.prod => config/.env.prod (100%) create mode 100644 config/config.ts create mode 100644 config/index.ts create mode 100644 config/serverConfig.js create mode 100644 config/universalConfig.js delete mode 100644 documentation/sidebar.md rename deploy-server.js => scripts/deploy-server.js (56%) rename server.js => scripts/server.js (60%) diff --git a/.cursorignore b/.cursorignore deleted file mode 100644 index b9c9db8..0000000 --- a/.cursorignore +++ /dev/null @@ -1,2 +0,0 @@ -!.env -!.env.* diff --git a/.github/workflows/poweron_nyla_int.yml b/.github/workflows/poweron_nyla_int.yml index 1dfb319..0eb8556 100644 --- a/.github/workflows/poweron_nyla_int.yml +++ b/.github/workflows/poweron_nyla_int.yml @@ -22,7 +22,7 @@ jobs: - name: Copy integration environment file run: | - cp .env.int .env + cp config/.env.int .env - name: Install dependencies run: | diff --git a/.github/workflows/poweron_nyla_main.yml b/.github/workflows/poweron_nyla_main.yml index e09cc75..5a17f29 100644 --- a/.github/workflows/poweron_nyla_main.yml +++ b/.github/workflows/poweron_nyla_main.yml @@ -22,7 +22,7 @@ jobs: - name: Copy production environment file run: | - cp .env.prod .env + cp config/.env.prod .env - name: Install dependencies run: | diff --git a/.gitignore b/.gitignore index a547bf3..f0524e8 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,15 @@ dist-ssr *.njsproj *.sln *.sw? + +# Environment files +.env +.env.local +.env.*.local + +.cursorignore + +# Keep environment template files in config/ +!config/.env.dev +!config/.env.int +!config/.env.prod \ No newline at end of file diff --git a/README.md b/README.md index 1384bda5b52a75a88ab47214065c16c55a72781d..97574fe82c852bee4f45d8ca32da13a5b2aae463 100644 GIT binary patch literal 8144 zcma)B&2Hn!5x(mw8e?qMW5|E=vzax51!P+@qgjt_Sh5$JK`^4Imc%th5@b^z?JbvF z7f6ESm{X8f$n)d{l1Io_Ro!G$re<~zvohITUG>#hf6X0roj&ONDp8l~I8d+iG%-4v zb~-yd>VNZw)7N_htJFfH>?VJ5$0xb@j(8*GPz(Ve{>z#eydxKqB5Z(uKvOZ1t=yRQcdpZdtU8vtELCYC68VbN4 zo|wWORc(Car&hhMN|Tj_Z8;wd!JBDR7y**B1;Minc(qZ-dPiK)zq#w*2$jXr&c)!e z^`kw1=$2kzT>XgibQY)U{mo7Euyx->|4r`<`OQ7L#BPhJCWuZKs0d{^HdXa1Bi*qCC`S1uw0FMxc58 zyh3I3RSO_>K&aP_ViFa67O{=|&#G-ahx0cdS5|mSD|IWiyq6qIwcU-tVFfl8z=cDz zj-zDGo5DCKigP_vsF+kp)hvqRlSz!k*)2?--s_W{qvMyq{dQvi+<%CsW^wW?`_$Yo z33Ho>7s2t1SK${#6kLh6i8!9T)X$E-AYwx0W19s1&CBDXFG-M0xJ|^XS4aBA>{MUbN)QF)rW!CA87Mc`P zWr02?fcR+{#FiAUJ*^-ysagcd6u}Q}Rc-Yu$fIBaL{$*xX;G+YG@I$1I&P55=uf6N z5x+E%Cf*mFeVjQK5+2I`(D9C8*1qyuDS4x2X|ANvoVn>hVd3814~z8)_MjbHMN?>E z&@Jy5!3@TQiO&>_yErBwQ(9-7SV5v<&T>1u+gdwy7b(1MD+6^*?1fxzOs*VmQI5g6E+^$kq2K`IuUy2Mu}xlsD-$ytt`_ zrPWYp0Y)`#j?-h^uX?IFuw(Wz1%7X%B4cqbe8RJ_YrJY6h2gsYg^pIVHqv zmO^Mt>+IDN)(eSD?mr|cBFQh+n8*JyDrb&6tu$Y&G*MJ_)bpdGBddT{I;<-+=!LYA zkfWP91a%UGr&8_|X~G~Rs>gq!ehV5k+nO+V4i-p(2uRPreRK#Hl-Mv%%c&nD8>z7k z87hI&Zota407@C`y0?VGIgjC~!%@dQUIO2FU{saLxYcAdg#t4%>opBhU_w1I(YgL_G`n75*6 zwzYY13|bP(F9(z=5O;t1qq?D*c3u%!N-($MXDP04dUY3U1Cvv*fAJ@z_=*Oai@IyJ|OM^bP&fipPoOP0r& zp~8g@@7qD4>u}b%g4Sk$FfK8s9O_TW5@~3r=)cjAa7t-0pF%>u5Fs_9dL!M~kr)2r%Yv>nQuGE_j`&1mL&1JRvc^ge zWfzEUw&ZfYaW5S3}zRCQ{fw{ox) z(@-W;*fE38VIF=-&*67~!hLwg`yfbU9NOWQ6C*a`wRLoJtwr4x-VKMN&(DJxlNf3P zd@gd_#SR_gCubIA1ynPul)1<2n293KW-r&%LBFar_Pq{*#doa^xaC0#;6`2SLDOL! z?Rs_VX=&zNi&VA9O+(#??yd>;b4?!&afw2=Omvm9L{Om<^@&ZldRxrhXt!_&6l?mH z#ev&qsF(sHXe_7fRX5kx!-K=KbBWo<-@wGwEwisX&hoognsf=esw)oD2)1XR)a EKQ=>2>i_@% literal 6558 zcmcgw+fLg+5Z&iW{RdY(v_+-)1_Tr#l@bZ6zSiVoEOJ4)G(w?2(GPBW&a9X9UhLRO zKv806?L9kl=FIHa|Nb7RL|v%|HBb|r-KZ&kPx1R+&DC4=M!rWF`=W9+!d;2#su_;I z@PD9I_@B$ng}RfgJseZD!1xNsIgWGuOEG7G>wWxBaX*i_Tzx36Pi8nya9ln6L%$9%dZEVfoL-Ib)oT*%7(QO$ zcv9G8nRtp^O%zA3u{v#CidP@7{uGD(R$c za5%v?gVjm#uP-ra%EDL>>>R5!&9<8jdaN7)t+_7Laa}0Y_`4rutCworHFTn@m##_| z$T1tSc7A-{=*9k4J4JSXl?*?Vyn0y=|Ge`R!dhqa!-|x_id(7l>2sAzBtK0(W_Ibl zz|}=nMGrOl9oacRwl>RAR&9N^)n%x@P$T7eYpMN2adi>mbd?|d?Qp8%x*b|9w=9yW zmt}84)=ihtjP)UEDR=ev8hfs#zL0Mt_>K?PTjJA}0eG1j}8zP3WE#q+~ z(zd#>T|&)0@qryq%uYYj28TXZq1O9e74_Yb>p9SU+yYUuF`@$84AoUIWTGD1XQCs~ zUxD2bRPa(!>_8>uUbdZIq59bwPr=6nblfZhxW1iX)(_c>>AmUPka>3|byyy86&|z2 z*K~Qd)^&Mg6JC{`%NT7?!?>JCC1E$-Lu7Oh+$=u2XY^;aDMe%ge3UhOavRI-c()uj>8X-|G7L)a7ZD4OHz9=yay;vDdKbtuV6r>0;&0 zVWwF<$L^q-NYT#jR4fj@J}>W3%=4p~DWk2bV+c&hD6> z8>|w|da&2Eu=CtA>&!F#={EM$$7|WYm#C2Xok@ac+9@hM-y4RNG8TPigTCKn2A@Wo zMRyY(`^c<#hT$_{Gt@BS$~H3Y1}D~sh3`Vjvw5E3%Xd`abE_=MJb^7bCovNO69@HDlJ!a`ftXk7>)| zw25P!@pJG@E$~^p3D;DOx!?3s%-(EkR$N5wk { + return import.meta.env.VITE_API_BASE_URL || 'http://localhost:8000'; +}; + +export const getApiTimeout = (): number => { + return parseInt(import.meta.env.VITE_API_TIMEOUT || '10000'); +}; + +// App Configuration +export const getAppName = (): string => { + return import.meta.env.VITE_APP_NAME || 'PowerOn'; +}; + +export const getAppVersion = (): string => { + return import.meta.env.VITE_APP_VERSION || '0.0.0'; +}; + +export const getAppEnvironment = (): string => { + return import.meta.env.VITE_APP_ENVIRONMENT || 'dev'; +}; + +// Environment Detection +export const isDevelopment = (): boolean => { + return import.meta.env.MODE === 'development' || getAppEnvironment() === 'dev'; +}; + +export const isProduction = (): boolean => { + return import.meta.env.MODE === 'production' || getAppEnvironment() === 'prod'; +}; + +export const isIntegration = (): boolean => { + return getAppEnvironment() === 'int'; +}; + +// Debug Configuration +export const isDebugMode = (): boolean => { + return import.meta.env.VITE_DEBUG === 'true'; +}; + +export const getLogLevel = (): string => { + return import.meta.env.VITE_LOG_LEVEL || 'info'; +}; + +export const isConsoleLogsEnabled = (): boolean => { + return import.meta.env.VITE_ENABLE_CONSOLE_LOGS === 'true'; +}; + +// Microsoft Authentication +export const getMicrosoftClientId = (): string | undefined => { + return import.meta.env.VITE_MICROSOFT_CLIENT_ID; +}; + +export const getMicrosoftTenantId = (): string | undefined => { + return import.meta.env.VITE_MICROSOFT_TENANT_ID; +}; + +export const getEntraClientSecret = (): string | undefined => { + return import.meta.env.VITE_ENTRA_CLIENT_SECRET; +}; + +export const getEntraAuthority = (): string | undefined => { + return import.meta.env.VITE_ENTRA_AUTHORITY; +}; + +export const getEntraRedirectPath = (): string | undefined => { + return import.meta.env.VITE_ENTRA_REDIRECT_PATH; +}; + +export const getEntraRedirectUri = (): string | undefined => { + return import.meta.env.VITE_ENTRA_REDIRECT_URI; +}; + +// Feature Flags (if needed in the future) +export const isFeatureEnabled = (feature: string): boolean => { + const envKey = `VITE_ENABLE_${feature.toUpperCase()}`; + return import.meta.env[envKey] === 'true'; +}; + +// Analytics and Monitoring +export const isAnalyticsEnabled = (): boolean => { + return import.meta.env.VITE_ENABLE_ANALYTICS === 'true'; +}; + +export const isErrorReportingEnabled = (): boolean => { + return import.meta.env.VITE_ENABLE_ERROR_REPORTING === 'true'; +}; + +export const isPerformanceMonitoringEnabled = (): boolean => { + return import.meta.env.VITE_ENABLE_PERFORMANCE_MONITORING === 'true'; +}; + +// Development Server (for dev environment) +export const getDevServerPort = (): number => { + return parseInt(import.meta.env.VITE_DEV_SERVER_PORT || '5176'); +}; + +export const getDevServerHost = (): string => { + return import.meta.env.VITE_DEV_SERVER_HOST || 'localhost'; +}; + +export const isDevServerHttps = (): boolean => { + return import.meta.env.VITE_DEV_SERVER_HTTPS === 'true'; +}; + +// Security Configuration +export const isHttpsEnabled = (): boolean => { + return import.meta.env.VITE_ENABLE_HTTPS === 'true'; +}; + +export const isCspEnabled = (): boolean => { + return import.meta.env.VITE_ENABLE_CSP === 'true'; +}; + +// Test Configuration +export const isMockDataEnabled = (): boolean => { + return import.meta.env.VITE_ENABLE_MOCK_DATA === 'true'; +}; + +export const isTestMode = (): boolean => { + return import.meta.env.VITE_ENABLE_TEST_MODE === 'true'; +}; + +// Convenience object for easy destructuring +export const config = { + // API + getApiBaseUrl, + getApiTimeout, + + // App + getAppName, + getAppVersion, + getAppEnvironment, + + // Environment + isDevelopment, + isProduction, + isIntegration, + + // Debug + isDebugMode, + getLogLevel, + isConsoleLogsEnabled, + + // Microsoft Auth + getMicrosoftClientId, + getMicrosoftTenantId, + getEntraClientSecret, + getEntraAuthority, + getEntraRedirectPath, + getEntraRedirectUri, + + // Features + isFeatureEnabled, + + // Analytics + isAnalyticsEnabled, + isErrorReportingEnabled, + isPerformanceMonitoringEnabled, + + // Dev Server + getDevServerPort, + getDevServerHost, + isDevServerHttps, + + // Security + isHttpsEnabled, + isCspEnabled, + + // Test + isMockDataEnabled, + isTestMode, +}; diff --git a/config/index.ts b/config/index.ts new file mode 100644 index 0000000..ac50c32 --- /dev/null +++ b/config/index.ts @@ -0,0 +1,12 @@ +// Export simple configuration service +export * from './config'; + +// Re-export commonly used functions +export { + getApiBaseUrl, + getAppName, + isDevelopment, + isProduction, + isDebugMode, + config +} from './config'; diff --git a/config/serverConfig.js b/config/serverConfig.js new file mode 100644 index 0000000..7dad682 --- /dev/null +++ b/config/serverConfig.js @@ -0,0 +1,191 @@ +/** + * Server Configuration Service + * Node.js compatible configuration for server files + */ + +// API Configuration +export const getApiBaseUrl = () => { + return process.env.VITE_API_BASE_URL || 'http://localhost:8000'; +}; + +export const getApiTimeout = () => { + return parseInt(process.env.VITE_API_TIMEOUT || '10000'); +}; + +// App Configuration +export const getAppName = () => { + return process.env.VITE_APP_NAME || 'PowerOn'; +}; + +export const getAppVersion = () => { + return process.env.VITE_APP_VERSION || '0.0.0'; +}; + +export const getAppEnvironment = () => { + return process.env.VITE_APP_ENVIRONMENT || 'dev'; +}; + +// Environment Detection +export const isDevelopment = () => { + return process.env.NODE_ENV === 'development' || getAppEnvironment() === 'dev'; +}; + +export const isProduction = () => { + return process.env.NODE_ENV === 'production' || getAppEnvironment() === 'prod'; +}; + +export const isIntegration = () => { + return getAppEnvironment() === 'int'; +}; + +// Debug Configuration +export const isDebugMode = () => { + return process.env.VITE_DEBUG === 'true'; +}; + +export const getLogLevel = () => { + return process.env.VITE_LOG_LEVEL || 'info'; +}; + +export const isConsoleLogsEnabled = () => { + return process.env.VITE_ENABLE_CONSOLE_LOGS === 'true'; +}; + +// Microsoft Authentication +export const getMicrosoftClientId = () => { + return process.env.VITE_MICROSOFT_CLIENT_ID; +}; + +export const getMicrosoftTenantId = () => { + return process.env.VITE_MICROSOFT_TENANT_ID; +}; + +export const getEntraClientSecret = () => { + return process.env.VITE_ENTRA_CLIENT_SECRET; +}; + +export const getEntraAuthority = () => { + return process.env.VITE_ENTRA_AUTHORITY; +}; + +export const getEntraRedirectPath = () => { + return process.env.VITE_ENTRA_REDIRECT_PATH; +}; + +export const getEntraRedirectUri = () => { + return process.env.VITE_ENTRA_REDIRECT_URI; +}; + +// Feature Flags +export const isFeatureEnabled = (feature) => { + const envKey = `VITE_ENABLE_${feature.toUpperCase()}`; + return process.env[envKey] === 'true'; +}; + +// Analytics and Monitoring +export const isAnalyticsEnabled = () => { + return process.env.VITE_ENABLE_ANALYTICS === 'true'; +}; + +export const isErrorReportingEnabled = () => { + return process.env.VITE_ENABLE_ERROR_REPORTING === 'true'; +}; + +export const isPerformanceMonitoringEnabled = () => { + return process.env.VITE_ENABLE_PERFORMANCE_MONITORING === 'true'; +}; + +// Development Server +export const getDevServerPort = () => { + return parseInt(process.env.VITE_DEV_SERVER_PORT || '5176'); +}; + +export const getDevServerHost = () => { + return process.env.VITE_DEV_SERVER_HOST || 'localhost'; +}; + +export const isDevServerHttps = () => { + return process.env.VITE_DEV_SERVER_HTTPS === 'true'; +}; + +// Security Configuration +export const isHttpsEnabled = () => { + return process.env.VITE_ENABLE_HTTPS === 'true'; +}; + +export const isCspEnabled = () => { + return process.env.VITE_ENABLE_CSP === 'true'; +}; + +// Test Configuration +export const isMockDataEnabled = () => { + return process.env.VITE_ENABLE_MOCK_DATA === 'true'; +}; + +export const isTestMode = () => { + return process.env.VITE_ENABLE_TEST_MODE === 'true'; +}; + +// Server-specific configuration +export const getServerPort = () => { + return process.env.PORT || 3000; +}; + +export const getServerHost = () => { + return process.env.HOST || '0.0.0.0'; +}; + +// Convenience object for easy destructuring +export const config = { + // API + getApiBaseUrl, + getApiTimeout, + + // App + getAppName, + getAppVersion, + getAppEnvironment, + + // Environment + isDevelopment, + isProduction, + isIntegration, + + // Debug + isDebugMode, + getLogLevel, + isConsoleLogsEnabled, + + // Microsoft Auth + getMicrosoftClientId, + getMicrosoftTenantId, + getEntraClientSecret, + getEntraAuthority, + getEntraRedirectPath, + getEntraRedirectUri, + + // Features + isFeatureEnabled, + + // Analytics + isAnalyticsEnabled, + isErrorReportingEnabled, + isPerformanceMonitoringEnabled, + + // Dev Server + getDevServerPort, + getDevServerHost, + isDevServerHttps, + + // Security + isHttpsEnabled, + isCspEnabled, + + // Test + isMockDataEnabled, + isTestMode, + + // Server + getServerPort, + getServerHost, +}; diff --git a/config/universalConfig.js b/config/universalConfig.js new file mode 100644 index 0000000..68933fa --- /dev/null +++ b/config/universalConfig.js @@ -0,0 +1,101 @@ +/** + * Universal Configuration - Works in both Node.js and Browser + */ + +// Helper to get environment variable (works in both Node.js and browser) +const getEnvVar = (key, defaultValue = undefined) => { + // Node.js environment + if (typeof process !== 'undefined' && process.env) { + return process.env[key] || defaultValue; + } + + // Browser environment (Vite) + if (typeof import !== 'undefined' && import.meta && import.meta.env) { + return import.meta.env[key] || defaultValue; + } + + return defaultValue; +}; + +// API Configuration +export const getApiBaseUrl = () => { + return getEnvVar('VITE_API_BASE_URL', 'http://localhost:8000'); +}; + +export const getApiTimeout = () => { + return parseInt(getEnvVar('VITE_API_TIMEOUT', '10000')); +}; + +// App Configuration +export const getAppName = () => { + return getEnvVar('VITE_APP_NAME', 'PowerOn'); +}; + +export const getAppVersion = () => { + return getEnvVar('VITE_APP_VERSION', '0.0.0'); +}; + +export const getAppEnvironment = () => { + return getEnvVar('VITE_APP_ENVIRONMENT', 'dev'); +}; + +// Environment Detection +export const isDevelopment = () => { + const mode = getEnvVar('NODE_ENV') || getEnvVar('MODE'); + const appEnv = getAppEnvironment(); + return mode === 'development' || appEnv === 'dev'; +}; + +export const isProduction = () => { + const mode = getEnvVar('NODE_ENV') || getEnvVar('MODE'); + const appEnv = getAppEnvironment(); + return mode === 'production' || appEnv === 'prod'; +}; + +export const isIntegration = () => { + return getAppEnvironment() === 'int'; +}; + +// Debug Configuration +export const isDebugMode = () => { + return getEnvVar('VITE_DEBUG') === 'true'; +}; + +export const getLogLevel = () => { + return getEnvVar('VITE_LOG_LEVEL', 'info'); +}; + +// Microsoft Authentication +export const getMicrosoftClientId = () => { + return getEnvVar('VITE_MICROSOFT_CLIENT_ID'); +}; + +export const getMicrosoftTenantId = () => { + return getEnvVar('VITE_MICROSOFT_TENANT_ID'); +}; + +export const getEntraAuthority = () => { + return getEnvVar('VITE_ENTRA_AUTHORITY'); +}; + +export const getEntraRedirectUri = () => { + return getEnvVar('VITE_ENTRA_REDIRECT_URI'); +}; + +// Convenience object +export const config = { + getApiBaseUrl, + getApiTimeout, + getAppName, + getAppVersion, + getAppEnvironment, + isDevelopment, + isProduction, + isIntegration, + isDebugMode, + getLogLevel, + getMicrosoftClientId, + getMicrosoftTenantId, + getEntraAuthority, + getEntraRedirectUri, +}; diff --git a/documentation/sidebar.md b/documentation/sidebar.md deleted file mode 100644 index 5be96b0..0000000 --- a/documentation/sidebar.md +++ /dev/null @@ -1,463 +0,0 @@ -# Sidebar Component Documentation - -## Architecture - -``` -src/ -├── components/Sidebar/ -│ ├── Sidebar.tsx # Main container component -│ ├── SidebarItem.tsx # Individual menu item -│ ├── SidebarSubmenu.tsx # Submenu component -│ └── SidebarUser.tsx # User info display -├── machines/ -│ └── sidebarMachine.ts # State machine definition -└── hooks/machines/ -│ └── useSidebarMachine.ts # React hook integration -└── contexts/ - └── SidebarData.tsx # Navigation data provider -``` - -## State Machine Explanation - -### Core Concepts - -**Events** - Things that can happen: -- `TOGGLE_ITEM` - User clicks a menu item -- `CLOSE_ALL` - Close all submenus -- `NAVIGATE` - User navigates to a new route - -**Context** - Data the machine remembers: -```typescript -interface SidebarContext { - openItemId: string | null; // Which submenu is open (only one allowed) - activePath: string; // Current route for highlighting -} -``` - -**States** - Possible configurations: -- `collapsed` - No submenus are open -- `expanded` - One submenu is open - -### State Transitions - -```mermaid -stateDiagram-v2 - [*] --> collapsed - collapsed --> expanded : TOGGLE_ITEM(id) - expanded --> collapsed : TOGGLE_ITEM(same_id) - expanded --> expanded : TOGGLE_ITEM(different_id) - collapsed --> collapsed : CLOSE_ALL - expanded --> collapsed : CLOSE_ALL -``` - -### Machine Definition - -```typescript -export const sidebarMachine = setup({ - types: { - context: {} as SidebarContext, - events: {} as SidebarEvent, - }, -}).createMachine({ - id: 'sidebar', - initial: 'collapsed', - context: { - openItemId: null, - activePath: '/', - }, - states: { - collapsed: { - on: { - TOGGLE_ITEM: { - target: 'expanded', - actions: assign({ - openItemId: ({ event }) => event.itemId, - }), - }, - }, - }, - expanded: { - on: { - TOGGLE_ITEM: [ - { - // Same item clicked - close it - guard: ({ context, event }) => context.openItemId === event.itemId, - target: 'collapsed', - actions: assign({ openItemId: null }), - }, - { - // Different item clicked - switch to it - target: 'expanded', - actions: assign({ - openItemId: ({ event }) => event.itemId, - }), - }, - ], - }, - }, - }, -}); -``` - -## React Hook Integration - -### useSidebarMachine Hook - -The `useSidebarMachine` hook bridges XState with React components: - -```typescript -export const useSidebarMachine = () => { - // 1. Create machine actor - const [state, send] = useActor(sidebarMachine); - - // 2. Sync with React Router - const location = useLocation(); - useEffect(() => { - send({ type: 'NAVIGATE', path: location.pathname }); - }, [location.pathname, send]); - - // 3. Return easy-to-use API - return { - // State queries - hasOpenSubmenu: sidebarState.hasOpenSubmenu, - openItemId: sidebarState.openItemId, - currentState: state.value, - - // Actions - toggleItem: (itemId: string) => send({ type: 'TOGGLE_ITEM', itemId }), - closeAll: () => send({ type: 'CLOSE_ALL' }), - isItemOpen: (itemId: string) => sidebarState.isItemOpen(itemId), - isItemActive: (itemPath?: string) => location.pathname === itemPath, - }; -}; -``` - -### Hook Benefits - -1. **Encapsulation**: State machine logic is hidden from components -2. **Type Safety**: TypeScript ensures correct usage -3. **Automatic Sync**: Route changes update the machine automatically -4. **Simple API**: Components only need simple functions - -## Data Loading (SidebarData) - -### Structure - -```typescript -interface SidebarItemType { - id: string; - name: string; - link?: string; - icon?: React.ComponentType; - submenu?: SidebarItemType[]; -} -``` - -### Implementation - -```typescript -const useSidebarData = () => { - const { t } = useLanguage(); - - return useMemo(() => [ - { - id: '1', - name: t('nav.team'), - link: '/team-bereich', - icon: MdOutlineWorkOutline, - }, - { - id: '2', - name: t('nav.dashboard'), - link: '/dashboard', - icon: LuTicket, - submenu: [ // Optional submenu - { id: '2-1', name: 'Analytics', link: '/dashboard/analytics' }, - { id: '2-2', name: 'Reports', link: '/dashboard/reports' }, - ], - }, - ], [t]); -}; -``` - -### Key Features - -- **Internationalization**: Uses language context for translations -- **Memoization**: Prevents unnecessary re-renders -- **Flexible Structure**: Supports nested submenus -- **Icon Support**: React component icons - -## Component Interactions - -### Sidebar (Main Container) - -```typescript -const Sidebar: React.FC = ({ data }) => { - const sidebarMachine = useSidebarMachine(); - - return ( -
- {/* Logo and User sections */} - -
- {data.map(item => ( - sidebarMachine.toggleItem(item.id)} - isActive={sidebarMachine.isItemActive(item.link)} - /> - ))} -
-
- ); -}; -``` - -**Responsibilities:** -- Initialize state machine -- Pass state and actions to children -- Handle layout and styling - -### SidebarItem (Individual Menu Item) - -```typescript -const SidebarItem: React.FC = ({ - item, - isOpen, - onToggle, - isActive -}) => { - const hasSubItems = item.submenu && item.submenu.length > 0; - - const toggleSubmenu = (e: React.MouseEvent) => { - if (hasSubItems) { - e.preventDefault(); - onToggle(); // Call parent's toggle function - } - }; - - return ( -
-
  • - {hasSubItems ? ( - - {item.name} - - - ) : ( - {item.name} - )} -
  • - {hasSubItems && } -
    - ); -}; -``` - -**Key Changes from Original:** -- ❌ **Removed**: `useState` for local state -- ✅ **Added**: Props from parent state machine -- ✅ **Benefit**: No independent state management - -### SidebarSubmenu (Nested Menu) - -```typescript -const SidebarSubmenu: React.FC = ({ item, isOpen }) => { - return ( - - {isOpen && ( - - {/* Submenu items */} - - )} - - ); -}; -``` - -**Responsibilities:** -- Render submenu when `isOpen` is true -- Handle animations with Framer Motion -- Manage text overflow behavior - -## Data Flow - -``` -1. User clicks menu item - ↓ -2. SidebarItem calls onToggle() - ↓ -3. onToggle sends TOGGLE_ITEM event to machine - ↓ -4. Machine transitions states and updates context - ↓ -5. Hook returns new state - ↓ -6. Sidebar re-renders with new isOpen values - ↓ -7. SidebarSubmenu shows/hides based on isOpen -``` - -## Adding New States - -### Example: Adding "Pinned" State - -1. **Update Events:** -```typescript -export type SidebarEvent = - | { type: 'TOGGLE_ITEM'; itemId: string } - | { type: 'PIN_SIDEBAR' } // New event - | { type: 'UNPIN_SIDEBAR' } // New event - | { type: 'CLOSE_ALL' } - | { type: 'NAVIGATE'; path: string }; -``` - -2. **Update Context:** -```typescript -export interface SidebarContext { - openItemId: string | null; - activePath: string; - isPinned: boolean; // New context property -} -``` - -3. **Add New States:** -```typescript -states: { - collapsed: { - on: { - PIN_SIDEBAR: 'pinnedCollapsed', - // ... existing transitions - }, - }, - expanded: { - on: { - PIN_SIDEBAR: 'pinnedExpanded', - // ... existing transitions - }, - }, - pinnedCollapsed: { // New state - on: { - UNPIN_SIDEBAR: 'collapsed', - TOGGLE_ITEM: 'pinnedExpanded', - }, - }, - pinnedExpanded: { // New state - on: { - UNPIN_SIDEBAR: 'expanded', - // ... similar to expanded - }, - }, -}, -``` - -4. **Update Hook:** -```typescript -return { - // ... existing returns - isPinned: state.matches('pinnedCollapsed') || state.matches('pinnedExpanded'), - pinSidebar: () => send({ type: 'PIN_SIDEBAR' }), - unpinSidebar: () => send({ type: 'UNPIN_SIDEBAR' }), -}; -``` - -## Best Practices - -### State Machine Design - -1. **Keep States Simple**: Each state should represent a distinct UI mode -2. **Use Guards Wisely**: For conditional transitions based on context -3. **Minimize Context**: Only store data that affects behavior -4. **Clear Event Names**: Use descriptive, action-oriented names - -### Component Architecture - -1. **Single Source of Truth**: State machine holds all navigation state -2. **Props Down**: Pass state and actions as props -3. **Events Up**: Send events to the machine, not direct state updates -4. **Separation of Concerns**: Components handle UI, machine handles logic - -### Performance - -1. **Memoize Data**: Use `useMemo` for sidebar data -2. **Avoid Deep Objects**: Keep context flat when possible -3. **Selective Subscriptions**: Only subscribe to needed state slices - -## Debugging - -### Development Tools - -1. **Debug Props**: Hook returns `_debugState` and `_debugSend` -2. **State Logging**: Log state changes in development -3. **XState DevTools**: Use `@xstate/inspect` for visual debugging - -### Common Issues - -1. **Multiple Items Open**: Check guard conditions in TOGGLE_ITEM -2. **State Not Updating**: Ensure events are sent correctly -3. **Route Sync Issues**: Verify NAVIGATE event is sent on route change - -## Testing - -### Unit Testing State Machine - -```typescript -import { sidebarMachine } from '../machines/sidebarMachine'; - -test('should open submenu when item toggled', () => { - const state = sidebarMachine.transition('collapsed', { - type: 'TOGGLE_ITEM', - itemId: 'menu-1', - }); - - expect(state.value).toBe('expanded'); - expect(state.context.openItemId).toBe('menu-1'); -}); -``` - -### Integration Testing - -```typescript -import { render, fireEvent } from '@testing-library/react'; -import { SidebarWithData } from '../components/Sidebar'; - -test('should close submenu when same item clicked twice', () => { - const { getByText } = render(); - const menuItem = getByText('Dashboard'); - - fireEvent.click(menuItem); // Open - fireEvent.click(menuItem); // Close - - expect(/* submenu is closed */).toBeTruthy(); -}); -``` - -## Migration Guide - -### From useState to State Machine - -**Before:** -```typescript -const [isOpen, setIsOpen] = useState(false); -const toggle = () => setIsOpen(!isOpen); -``` - -**After:** -```typescript -const { isItemOpen, toggleItem } = useSidebarMachine(); -// Use: isItemOpen(itemId) and toggleItem(itemId) -``` - -### Benefits of Migration - -1. **Predictable State**: No more impossible state combinations -2. **Centralized Logic**: All sidebar behavior in one place -3. **Better Testing**: State machine logic is easily testable -4. **Type Safety**: TypeScript prevents invalid transitions -5. **Debugging**: Clear state visualization and logging diff --git a/index.html b/index.html index 409813a..54bed19 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ - %VITE_APP_NAME% + <%- VITE_APP_NAME %>
    diff --git a/package-lock.json b/package-lock.json index f61e44f..cedec71 100644 --- a/package-lock.json +++ b/package-lock.json @@ -39,7 +39,8 @@ "globals": "^16.3.0", "typescript": "~5.8.3", "typescript-eslint": "^8.35.1", - "vite": "^5.4.10" + "vite": "^5.4.10", + "vite-plugin-html": "^3.2.2" } }, "node_modules/@ampproject/remapping": { @@ -1004,6 +1005,17 @@ "node": ">=6.0.0" } }, + "node_modules/@jridgewell/source-map": { + "version": "0.3.11", + "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.11.tgz", + "integrity": "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.25" + } + }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", @@ -1067,6 +1079,20 @@ "dev": true, "license": "MIT" }, + "node_modules/@rollup/pluginutils": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-4.2.1.tgz", + "integrity": "sha512-iKnFXr7NkdZAIHiIWE+BX5ULi/ucVFYWD6TbAV+rZctiRTY2PL6tsIKhoIOaoskiWAkgu+VsbXgUVDNLHf+InQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "estree-walker": "^2.0.1", + "picomatch": "^2.2.2" + }, + "engines": { + "node": ">= 8.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.45.1", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.45.1.tgz", @@ -1819,6 +1845,13 @@ "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", "license": "MIT" }, + "node_modules/async": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", + "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", + "dev": true, + "license": "MIT" + }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -1891,6 +1924,13 @@ "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "license": "MIT" }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true, + "license": "ISC" + }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", @@ -1954,6 +1994,13 @@ "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", "license": "BSD-3-Clause" }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", + "dev": true, + "license": "MIT" + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -2002,6 +2049,17 @@ "node": ">=6" } }, + "node_modules/camel-case": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.2.tgz", + "integrity": "sha512-gxGWBrTT1JuMx6R+o5PTXMmUnhnVzLQ9SNutD4YqKtI6ap897t3tKECYla6gCWEkplXnlNybEkZg9GEGxKFCgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "pascal-case": "^3.1.2", + "tslib": "^2.0.3" + } + }, "node_modules/caniuse-lite": { "version": "1.0.30001727", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", @@ -2040,6 +2098,19 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/clean-css": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/clean-css/-/clean-css-5.3.3.tgz", + "integrity": "sha512-D5J+kHaVb/wKSFcyyV75uCn8fiY4sV38XJoe4CUyGQ+mOU/fMVYUdH1hJC+CJQ5uY3EnW27SbJYS4X8BiLrAFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "source-map": "~0.6.0" + }, + "engines": { + "node": ">= 10.0" + } + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -2060,6 +2131,13 @@ "dev": true, "license": "MIT" }, + "node_modules/colorette": { + "version": "2.0.20", + "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz", + "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==", + "dev": true, + "license": "MIT" + }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", @@ -2072,6 +2150,16 @@ "node": ">= 0.8" } }, + "node_modules/commander": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-8.3.0.tgz", + "integrity": "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -2079,6 +2167,23 @@ "dev": true, "license": "MIT" }, + "node_modules/connect-history-api-fallback": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/connect-history-api-fallback/-/connect-history-api-fallback-1.6.0.tgz", + "integrity": "sha512-e54B99q/OUoH64zYYRf3HBP5z24G38h5D3qXu23JGRoigpX5Ss4r9ZnDk3g0Z8uQC2x2lPaJ+UlWBc1ZWBWdLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/consola": { + "version": "2.15.3", + "resolved": "https://registry.npmjs.org/consola/-/consola-2.15.3.tgz", + "integrity": "sha512-9vAdYbHj6x2fLKC4+oPH0kFzY/orMZyG2Aj+kNylHxKGJ/Ed4dpNyAQYwJOdqO4zdM7XpVHmyejQDcQHrnuXbw==", + "dev": true, + "license": "MIT" + }, "node_modules/content-disposition": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", @@ -2137,6 +2242,36 @@ "node": ">= 8" } }, + "node_modules/css-select": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-4.3.0.tgz", + "integrity": "sha512-wPpOYtnsVontu2mODhA19JrqWxNsfdatRKd64kmpRbQgh1KtItko5sTnEpPdpSaJszTOhEMlF/RPz28qj4HqhQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.0.1", + "domhandler": "^4.3.1", + "domutils": "^2.8.0", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, "node_modules/csstype": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.3.tgz", @@ -2197,6 +2332,76 @@ "npm": "1.2.8000 || >= 1.4.16" } }, + "node_modules/dom-serializer": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-1.4.1.tgz", + "integrity": "sha512-VHwB3KfrcOOkelEG2ZOfxqLZdfkil8PtJi4P8N2MMXucZq2yLp75ClViUlOVwyoHEDjYU433Aq+5zWP61+RGag==", + "dev": true, + "license": "MIT", + "dependencies": { + "domelementtype": "^2.0.1", + "domhandler": "^4.2.0", + "entities": "^2.0.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "license": "BSD-2-Clause" + }, + "node_modules/domhandler": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-4.3.1.tgz", + "integrity": "sha512-GrwoxYN+uWlzO8uhUXRl0P+kHE4GtVPfYzVLcUxPL7KNdHKj66vvlhiweIHqYYXWlw+T8iLMp42Lm67ghw4WMQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "domelementtype": "^2.2.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-2.8.0.tgz", + "integrity": "sha512-w96Cjofp72M5IIhpjgobBimYEfoPjx1Vx0BSX9P30WBdZW2WIKU0T1Bd0kz2eNZ9ikjKgHbEyKx8BB6H1L3h3A==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "dom-serializer": "^1.0.1", + "domelementtype": "^2.2.0", + "domhandler": "^4.2.0" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dot-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/dot-case/-/dot-case-3.0.4.tgz", + "integrity": "sha512-Kv5nKlh6yRrdrGvxeJ2e5y2eRUpkUosIW4A2AS38zwSz27zu7ufDwQPi5Jhs3XAlGNetl3bmnGhQsMtkKJnj3w==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/dotenv": { "version": "16.6.1", "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", @@ -2209,6 +2414,16 @@ "url": "https://dotenvx.com" } }, + "node_modules/dotenv-expand": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/dotenv-expand/-/dotenv-expand-8.0.3.tgz", + "integrity": "sha512-SErOMvge0ZUyWd5B0NXMQlDkN+8r+HhVUsxgOO7IoPDOdDRD2JjExpN6y3KnFR66jsJMwSn1pqIivhU5rcJiNg==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + } + }, "node_modules/dunder-proto": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", @@ -2238,6 +2453,22 @@ "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", "license": "MIT" }, + "node_modules/ejs": { + "version": "3.1.10", + "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", + "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "jake": "^10.8.5" + }, + "bin": { + "ejs": "bin/cli.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.191", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.191.tgz", @@ -2254,6 +2485,16 @@ "node": ">= 0.8" } }, + "node_modules/entities": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-2.2.0.tgz", + "integrity": "sha512-p92if5Nz619I0w+akJrLZH0MX0Pb5DX39XOwQTtXSdQQOaYH03S1uIQp4mhOZtAXrxq4ViO67YTiLBo2638o9A==", + "dev": true, + "license": "BSD-2-Clause", + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, "node_modules/es-define-property": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", @@ -2535,6 +2776,13 @@ "node": ">=4.0" } }, + "node_modules/estree-walker": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz", + "integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w==", + "dev": true, + "license": "MIT" + }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", @@ -2701,6 +2949,39 @@ "node": ">= 12" } }, + "node_modules/filelist": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", + "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "minimatch": "^5.0.1" + } + }, + "node_modules/filelist/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/filelist/node_modules/minimatch": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -2872,6 +3153,21 @@ "integrity": "sha512-3XY9e1pP0CVEUCdj5BmfIZxRBTSDycnbqhIOGec9QYtmVH2fbLpj86CFWkrNOkt/Fvty4KZG5lTglL9j/gJ87w==", "license": "ISC" }, + "node_modules/fs-extra": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-10.1.0.tgz", + "integrity": "sha512-oRXApq54ETRj4eMiFzGnHWGy+zo5raudjuxN0b8H7s/RU2oW0Wvsx9O0ACRN/kRq9E8Vu/ReskGB5o3ji+FzHQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", @@ -2981,6 +3277,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true, + "license": "ISC" + }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", @@ -3037,6 +3340,38 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "dev": true, + "license": "MIT", + "bin": { + "he": "bin/he" + } + }, + "node_modules/html-minifier-terser": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/html-minifier-terser/-/html-minifier-terser-6.1.0.tgz", + "integrity": "sha512-YXxSlJBZTP7RS3tWnQw74ooKa6L9b9i9QYXY21eUEvhZ3u9XLfv6OnFsQq6RxkhHygsaUMvYsZRV5rU/OVNZxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "camel-case": "^4.1.2", + "clean-css": "^5.2.2", + "commander": "^8.3.0", + "he": "^1.2.0", + "param-case": "^3.0.4", + "relateurl": "^0.2.7", + "terser": "^5.10.0" + }, + "bin": { + "html-minifier-terser": "cli.js" + }, + "engines": { + "node": ">=12" + } + }, "node_modules/http-errors": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", @@ -3157,6 +3492,24 @@ "dev": true, "license": "ISC" }, + "node_modules/jake": { + "version": "10.9.4", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.4.tgz", + "integrity": "sha512-wpHYzhxiVQL+IV05BLE2Xn34zW1S223hvjtqk0+gsPrwd/8JNLXJgZZM/iPFsYc1xyphF+6M6EvdE5E9MBGkDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.6", + "filelist": "^1.0.4", + "picocolors": "^1.1.1" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/js-cookie": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", @@ -3232,6 +3585,19 @@ "node": ">=6" } }, + "node_modules/jsonfile": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -3397,6 +3763,16 @@ "loose-envify": "cli.js" } }, + "node_modules/lower-case": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/lower-case/-/lower-case-2.0.2.tgz", + "integrity": "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg==", + "dev": true, + "license": "MIT", + "dependencies": { + "tslib": "^2.0.3" + } + }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", @@ -3595,6 +3971,28 @@ "node": ">= 0.6" } }, + "node_modules/no-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/no-case/-/no-case-3.0.4.tgz", + "integrity": "sha512-fgAN3jGAh+RoxUGZHTSOLJIqUc2wmoBwGR4tbpNAKmmovFoWq0OdRkb0VkldReO2a2iBT/OEulG9XSUc10r3zg==", + "dev": true, + "license": "MIT", + "dependencies": { + "lower-case": "^2.0.2", + "tslib": "^2.0.3" + } + }, + "node_modules/node-html-parser": { + "version": "5.4.2", + "resolved": "https://registry.npmjs.org/node-html-parser/-/node-html-parser-5.4.2.tgz", + "integrity": "sha512-RaBPP3+51hPne/OolXxcz89iYvQvKOydaqoePpOgXcrOKZhjVIzmpKZz+Hd/RBO2/zN2q6CNJhQzucVz+u3Jyw==", + "dev": true, + "license": "MIT", + "dependencies": { + "css-select": "^4.2.1", + "he": "1.2.0" + } + }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", @@ -3602,6 +4000,19 @@ "dev": true, "license": "MIT" }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -3685,6 +4096,17 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/param-case": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/param-case/-/param-case-3.0.4.tgz", + "integrity": "sha512-RXlj7zCYokReqWpOPH9oYivUzLYZ5vAPIfEmCTNViosC78F8F0H9y7T7gG2M39ymgutxF5gcFEsyZQSph9Bp3A==", + "dev": true, + "license": "MIT", + "dependencies": { + "dot-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -3707,6 +4129,17 @@ "node": ">= 0.8" } }, + "node_modules/pascal-case": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pascal-case/-/pascal-case-3.1.2.tgz", + "integrity": "sha512-uWlGT3YSnK9x3BQJaOdcZwrnV6hPpd8jFH1/ucpiLRPh/2zCVJKS19E4GvYHvaCcACn3foXZ0cLB9Wrx1KGe5g==", + "dev": true, + "license": "MIT", + "dependencies": { + "no-case": "^3.0.4", + "tslib": "^2.0.3" + } + }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", @@ -3733,6 +4166,13 @@ "integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==", "license": "MIT" }, + "node_modules/pathe": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/pathe/-/pathe-0.2.0.tgz", + "integrity": "sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==", + "dev": true, + "license": "MIT" + }, "node_modules/pg": { "version": "8.16.3", "resolved": "https://registry.npmjs.org/pg/-/pg-8.16.3.tgz", @@ -4130,6 +4570,16 @@ "node": ">=18" } }, + "node_modules/relateurl": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/relateurl/-/relateurl-0.2.7.tgz", + "integrity": "sha512-G08Dxvm4iDN3MLM0EsP62EDV9IuhXPR6blNz6Utcp7zyV3tr4HVNINt6MpaRWbxoOHT3Q7YN2P+jaHX8vUbgog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -4427,6 +4877,16 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", @@ -4437,6 +4897,17 @@ "node": ">=0.10.0" } }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, "node_modules/split2": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz", @@ -4481,6 +4952,32 @@ "node": ">=8" } }, + "node_modules/terser": { + "version": "5.44.0", + "resolved": "https://registry.npmjs.org/terser/-/terser-5.44.0.tgz", + "integrity": "sha512-nIVck8DK+GM/0Frwd+nIhZ84pR/BX7rmXMfYwyg+Sri5oGVE99/E3KvXqpC2xHFxyqXyGHTKBSioxxplrO4I4w==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "@jridgewell/source-map": "^0.3.3", + "acorn": "^8.15.0", + "commander": "^2.20.0", + "source-map-support": "~0.5.20" + }, + "bin": { + "terser": "bin/terser" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/terser/node_modules/commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", + "dev": true, + "license": "MIT" + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -4586,6 +5083,16 @@ "typescript": ">=4.8.4 <5.9.0" } }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.0.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", @@ -4737,6 +5244,30 @@ } } }, + "node_modules/vite-plugin-html": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/vite-plugin-html/-/vite-plugin-html-3.2.2.tgz", + "integrity": "sha512-vb9C9kcdzcIo/Oc3CLZVS03dL5pDlOFuhGlZYDCJ840BhWl/0nGeZWf3Qy7NlOayscY4Cm/QRgULCQkEZige5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "@rollup/pluginutils": "^4.2.0", + "colorette": "^2.0.16", + "connect-history-api-fallback": "^1.6.0", + "consola": "^2.15.3", + "dotenv": "^16.0.0", + "dotenv-expand": "^8.0.2", + "ejs": "^3.1.6", + "fast-glob": "^3.2.11", + "fs-extra": "^10.0.1", + "html-minifier-terser": "^6.1.0", + "node-html-parser": "^5.3.3", + "pathe": "^0.2.0" + }, + "peerDependencies": { + "vite": ">=2.0.0" + } + }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", diff --git a/package.json b/package.json index 73c7e7e..195f7db 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "globals": "^16.3.0", "typescript": "~5.8.3", "typescript-eslint": "^8.35.1", - "vite": "^5.4.10" + "vite": "^5.4.10", + "vite-plugin-html": "^3.2.2" } } diff --git a/deploy-server.js b/scripts/deploy-server.js similarity index 56% rename from deploy-server.js rename to scripts/deploy-server.js index dd43008..375c1dd 100644 --- a/deploy-server.js +++ b/scripts/deploy-server.js @@ -1,6 +1,7 @@ import express from 'express'; import path from 'path'; import { fileURLToPath } from 'url'; +import { getAppName, getAppVersion, getAppEnvironment, isDebugMode } from '../config/serverConfig.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -20,7 +21,10 @@ const port = process.env.PORT || 8080; // Listen on all interfaces (important for Azure) app.listen(port, '0.0.0.0', () => { - console.log(`Server running on port ${port}`); - console.log(`Environment: ${process.env.NODE_ENV || 'development'}`); - console.log(`Directory: ${__dirname}`); + console.log(`🚀 ${getAppName()} Deploy Server running on port ${port}`); + console.log(`📦 Version: ${getAppVersion()}`); + console.log(`🌍 Environment: ${getAppEnvironment()}`); + console.log(`🐛 Debug Mode: ${isDebugMode() ? 'Enabled' : 'Disabled'}`); + console.log(`📁 Serving from: ${__dirname}`); + console.log(`🔧 Node Environment: ${process.env.NODE_ENV || 'development'}`); }); \ No newline at end of file diff --git a/server.js b/scripts/server.js similarity index 60% rename from server.js rename to scripts/server.js index 4943d52..7a2a66e 100644 --- a/server.js +++ b/scripts/server.js @@ -1,6 +1,7 @@ import express from 'express'; import path from 'path'; import { fileURLToPath } from 'url'; +import { getAppName, getAppVersion, getAppEnvironment, isDebugMode } from '../config/serverConfig.js'; const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); @@ -20,5 +21,9 @@ const port = process.env.PORT || 3000; // Listen on all interfaces (important for Azure) app.listen(port, '0.0.0.0', () => { - console.log(`Server running on port ${port}`); + console.log(`🚀 ${getAppName()} Server running on port ${port}`); + console.log(`📦 Version: ${getAppVersion()}`); + console.log(`🌍 Environment: ${getAppEnvironment()}`); + console.log(`🐛 Debug Mode: ${isDebugMode() ? 'Enabled' : 'Disabled'}`); + console.log(`📁 Serving from: ${path.join(__dirname, 'dist')}`); }); \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 2e65d5d..23ac220 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -17,9 +17,11 @@ import './assets/styles/light.css'; function App() { // Load saved theme preference and set app name on app mount useEffect(() => { - // Set app name globally - const appName = import.meta.env.VITE_APP_NAME || "PowerOn"; - document.title = appName; + // Set app name globally using configuration + import('../config/config').then(({ getAppName }) => { + const appName = getAppName(); + document.title = appName; + }); // Load saved theme preference const savedTheme = localStorage.getItem('theme'); diff --git a/src/api.ts b/src/api.ts index 26e5dac..33fe9e1 100644 --- a/src/api.ts +++ b/src/api.ts @@ -18,8 +18,10 @@ const resolveHostnameToIP = async (hostname: string): Promise => } }; +import { getApiBaseUrl } from '../config/config'; + const api = axios.create({ - baseURL: import.meta.env.VITE_API_BASE_URL, + baseURL: getApiBaseUrl(), withCredentials: true }); @@ -27,7 +29,7 @@ const api = axios.create({ api.interceptors.request.use( async (config) => { // Log backend information - const backendUrl = config.baseURL || import.meta.env.VITE_API_BASE_URL; + const backendUrl = config.baseURL || getApiBaseUrl(); console.log(`🌐 Communicating with backend: ${backendUrl}`); // Try to resolve and log the IP address @@ -43,7 +45,7 @@ api.interceptors.request.use( // Log environment info console.log(`🏗️ Environment: ${import.meta.env.MODE}`); - console.log(`⚙️ API Base URL from env: ${import.meta.env.VITE_API_BASE_URL}`); + console.log(`⚙️ API Base URL: ${getApiBaseUrl()}`); } catch (error) { console.warn('Could not parse backend URL:', error); } diff --git a/src/components/Dashboard/DashboardChat/DashboardChatAreaInput.tsx b/src/components/Dashboard/DashboardChat/DashboardChatAreaInput.tsx index 3f9b969..70c6c1a 100644 --- a/src/components/Dashboard/DashboardChat/DashboardChatAreaInput.tsx +++ b/src/components/Dashboard/DashboardChat/DashboardChatAreaInput.tsx @@ -257,7 +257,7 @@ const InputArea: React.FC = ({ className={styles.prompt_dropdown} > - {prompts.map(prompt => ( + {prompts && Array.isArray(prompts) && prompts.map(prompt => ( diff --git a/src/hooks/usePrompts.ts b/src/hooks/usePrompts.ts index 9b0a2a1..0eee636 100644 --- a/src/hooks/usePrompts.ts +++ b/src/hooks/usePrompts.ts @@ -23,9 +23,12 @@ export function usePrompts() { method: 'get' }); - setPrompts(data || []); + // Ensure we always set an array + setPrompts(Array.isArray(data) ? data : []); } catch (error) { // Error is already handled by useApiRequest + // Ensure we still have an empty array on error + setPrompts([]); } }; diff --git a/vite.config.ts b/vite.config.ts index 90f7a4f..0d4b929 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -1,15 +1,30 @@ -import { defineConfig } from 'vite'; +import { defineConfig, loadEnv } from 'vite'; import react from '@vitejs/plugin-react'; +import { createHtmlPlugin } from 'vite-plugin-html'; -export default defineConfig({ - plugins: [react()], - envPrefix: 'VITE_', - server: { - https: false, - }, - css: { - modules: { - scopeBehaviour: 'local', // Default behavior for CSS modules +export default defineConfig(({ mode }) => { + // Load env file based on mode + const env = loadEnv(mode, process.cwd(), ''); + + return { + plugins: [ + react(), + createHtmlPlugin({ + inject: { + data: { + VITE_APP_NAME: env.VITE_APP_NAME || 'PowerOn', + }, + }, + }), + ], + envPrefix: 'VITE_', + server: { + https: false, }, - }, + css: { + modules: { + scopeBehaviour: 'local', // Default behavior for CSS modules + }, + }, + }; });