diff --git a/frontend/src/components/DevModeBadge.tsx b/frontend/src/components/DevModeBadge.tsx deleted file mode 100644 index bc238c4..0000000 --- a/frontend/src/components/DevModeBadge.tsx +++ /dev/null @@ -1,150 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import useDevCompilationStatus from '../hooks/useDevCompilationStatus'; -const DevModeBadge: React.FC = () => { - const [isVisible, setIsVisible] = useState(false); - const [isCollapsed, setIsCollapsed] = useState(true); - const compilationStatus = useDevCompilationStatus(); - - const [badgeStyles, setBadgeStyles] = useState({ - position: 'fixed', - bottom: '20px', - left: '70px', - background: 'rgba(0, 0, 0, 0.85)', - color: 'white', - padding: '15px', - borderRadius: '8px', - fontFamily: 'sans-serif', - fontSize: '14px', - lineHeight: '1.5', - textAlign: 'left', - zIndex: 2147483647, - boxShadow: '0 4px 10px rgba(0, 0, 0, 0.3)', - whiteSpace: 'pre-wrap', - transition: 'width 0.3s cubic-bezier(0.25, 0.1, 0.25, 1), padding 0.3s ease-in-out, opacity 0.3s ease-in-out, background-color 0.3s ease-in-out', // Improved transition for width - opacity: 0, - pointerEvents: 'none', - width: '340px', - maxWidth: '340px', - height: 'auto', - overflow: 'hidden', - cursor: 'pointer', - }); - - const fullText = `🚧 Your app is running in development mode. -Current request is compiling and may take a few moments. - -💡 Tip: Set up a stable environment to run your app in production mode—pages will load instantly without compilation delays.`; - - const collapsedText = '🚧 DEV stage'; - - useEffect(() => { - if (compilationStatus === 'ready') { - setIsCollapsed(true); - } else { - setIsCollapsed(false); - } - - }, [compilationStatus]); - - useEffect(() => { - if (process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'dev_stage') { - setIsVisible(true); - - setBadgeStyles(prev => ({ - ...prev, - opacity: 1, - width: '120px', - maxWidth: '120px', - padding: '6px 10px', - borderRadius: '18px', - whiteSpace: 'nowrap', - fontSize: '12px', - cursor: 'pointer', - pointerEvents: 'auto', - })); - - } else { - setIsVisible(false); - setBadgeStyles(prev => ({ ...prev, opacity: 0 })); - } - }, []); - - useEffect(() => { - if (!isVisible) return; - - if (isCollapsed) { - setBadgeStyles(prev => ({ - ...prev, - width: '140px', - maxWidth: '160px', - padding: '6px 20px', - borderRadius: '18px', - whiteSpace: 'nowrap', - fontSize: '12px', - })); - } else { - setBadgeStyles(prev => ({ - ...prev, - width: '340px', - maxWidth: '340px', - padding: '15px', - borderRadius: '8px', - whiteSpace: 'pre-wrap', - fontSize: '14px', - })); - } - }, [isCollapsed, isVisible]); - - const handleToggleCollapse = (e: React.MouseEvent) => { - e.stopPropagation(); - setIsCollapsed(prev => !prev); - }; - - if (!isVisible) { - return null; - } - - return ( -
- - - {!isCollapsed && ( -
- {fullText} -
- )} - {isCollapsed && ( -
- {collapsedText} -
- )} -
- ); -}; - -export default DevModeBadge; diff --git a/frontend/src/hooks/useDevCompilationStatus.ts b/frontend/src/hooks/useDevCompilationStatus.ts deleted file mode 100644 index dd07dba..0000000 --- a/frontend/src/hooks/useDevCompilationStatus.ts +++ /dev/null @@ -1,44 +0,0 @@ -import { useState, useEffect } from 'react'; -import { useRouter } from 'next/router'; - -type CompilationStatus = 'ready' | 'compiling' | 'error' | 'initial'; - -const useDevCompilationStatus = (): CompilationStatus => { - const router = useRouter(); - const [status, setStatus] = useState('initial'); - - useEffect(() => { - if (process.env.NODE_ENV !== 'development') { - setStatus('ready'); - return; - } - - const handleRouteChangeStart = () => { - setStatus('compiling'); - }; - - const handleRouteChangeComplete = () => { - setTimeout(() => setStatus('ready'), 300); - }; - - const handleRouteChangeError = () => { - setTimeout(() => setStatus('error'), 300); - }; - - router.events.on('routeChangeStart', handleRouteChangeStart); - router.events.on('routeChangeComplete', handleRouteChangeComplete); - router.events.on('routeChangeError', handleRouteChangeError); - - setStatus('ready'); - - return () => { - router.events.off('routeChangeStart', handleRouteChangeStart); - router.events.off('routeChangeComplete', handleRouteChangeComplete); - router.events.off('routeChangeError', handleRouteChangeError); - }; - }, [router]); - - return status; -}; - -export default useDevCompilationStatus; \ No newline at end of file diff --git a/frontend/src/pages/_app.tsx b/frontend/src/pages/_app.tsx index 8632a59..e522258 100644 --- a/frontend/src/pages/_app.tsx +++ b/frontend/src/pages/_app.tsx @@ -9,28 +9,35 @@ import '../css/main.css'; import axios from 'axios'; import { baseURLApi } from '../config'; import { useRouter } from 'next/router'; -import ErrorBoundary from "../components/ErrorBoundary"; -import DevModeBadge from '../components/DevModeBadge'; +import ErrorBoundary from '../components/ErrorBoundary'; import 'intro.js/introjs.css'; import { appWithTranslation } from 'next-i18next'; import '../i18n'; import IntroGuide from '../components/IntroGuide'; -import { appSteps, loginSteps, usersSteps, rolesSteps } from '../stores/introSteps'; +import { + appSteps, + loginSteps, + usersSteps, + rolesSteps, +} from '../stores/introSteps'; // Initialize axios axios.defaults.baseURL = process.env.NEXT_PUBLIC_BACK_API - ? process.env.NEXT_PUBLIC_BACK_API - : baseURLApi; + ? process.env.NEXT_PUBLIC_BACK_API + : baseURLApi; axios.defaults.headers.common['Content-Type'] = 'application/json'; -export type NextPageWithLayout

, IP = P> = NextPage & { - getLayout?: (page: ReactElement) => ReactNode -} +export type NextPageWithLayout

, IP = P> = NextPage< + P, + IP +> & { + getLayout?: (page: ReactElement) => ReactNode; +}; type AppPropsWithLayout = AppProps & { - Component: NextPageWithLayout -} + Component: NextPageWithLayout; +}; function MyApp({ Component, pageProps }: AppPropsWithLayout) { // Use the layout defined at the page level, if available @@ -40,76 +47,82 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { const [stepName, setStepName] = React.useState(''); const [steps, setSteps] = React.useState([]); - axios.interceptors.request.use( - config => { - const token = localStorage.getItem('token'); + axios.interceptors.request.use( + (config) => { + const token = localStorage.getItem('token'); - if (token) { - config.headers.Authorization = `Bearer ${token}`; - } else { - delete config.headers.Authorization; - } + if (token) { + config.headers.Authorization = `Bearer ${token}`; + } else { + delete config.headers.Authorization; + } - return config; - }, - error => { - return Promise.reject(error); + return config; + }, + (error) => { + return Promise.reject(error); + }, + ); + + // TODO: Remove this code in future releases + React.useEffect(() => { + const allowedOrigin = (() => { + if (!document.referrer) { + return null; + } + try { + return new URL(document.referrer).origin; + } catch (error) { + console.warn( + '[postMessage] Failed to parse parent origin from referrer', + error, + ); + return null; + } + })(); + + const handleMessage = async (event: MessageEvent) => { + if (event.data === 'getLocation') { + event.source?.postMessage( + { iframeLocation: window.location.pathname }, + event.origin, + ); + return; + } + + if (event.data === 'getAuthToken') { + if (allowedOrigin && event.origin !== allowedOrigin) { + console.warn( + '[postMessage] Blocked getAuthToken from origin', + event.origin, + ); + return; } - ); + const token = localStorage.getItem('token'); + const user = localStorage.getItem('user'); + event.source?.postMessage( + { iframeAuthToken: token, iframeAuthUser: user }, + event.origin, + ); + return; + } - // TODO: Remove this code in future releases - React.useEffect(() => { - const allowedOrigin = (() => { - if (!document.referrer) { - return null; - } - try { - return new URL(document.referrer).origin; - } catch (error) { - console.warn('[postMessage] Failed to parse parent origin from referrer', error); - return null; - } - })(); + if (event.data === 'getScreenshot') { + try { + const html2canvas = (await import('html2canvas')).default; + const canvas = await html2canvas(document.body, { useCORS: true }); + const url = canvas.toDataURL('image/jpeg', 0.8); + event.source?.postMessage({ iframeScreenshot: url }, event.origin); + } catch (e) { + console.error('html2canvas failed', e); + event.source?.postMessage({ iframeScreenshot: null }, event.origin); + } + } + }; - const handleMessage = async (event: MessageEvent) => { - if (event.data === 'getLocation') { - event.source?.postMessage( - { iframeLocation: window.location.pathname }, - event.origin, - ); - return; - } - - if (event.data === 'getAuthToken') { - if (allowedOrigin && event.origin !== allowedOrigin) { - console.warn('[postMessage] Blocked getAuthToken from origin', event.origin); - return; - } - const token = localStorage.getItem('token'); - const user = localStorage.getItem('user'); - event.source?.postMessage( - { iframeAuthToken: token, iframeAuthUser: user }, - event.origin, - ); - return; - } - - if (event.data === 'getScreenshot') { - try { - const html2canvas = (await import('html2canvas')).default; - const canvas = await html2canvas(document.body, { useCORS: true }); - const url = canvas.toDataURL('image/jpeg', 0.8); - event.source?.postMessage({ iframeScreenshot: url }, event.origin); - } catch (e) { - console.error('html2canvas failed', e); - event.source?.postMessage({ iframeScreenshot: null }, event.origin); - } - } - }; - - window.addEventListener('message', handleMessage); - return () => window.removeEventListener('message', handleMessage); - }, []); + window.addEventListener('message', handleMessage); + return () => window.removeEventListener('message', handleMessage); + }, []); React.useEffect(() => { // Tour is disabled by default in generated projects. @@ -117,31 +130,37 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { const isCompleted = (stepKey: string) => { return localStorage.getItem(`completed_${stepKey}`) === 'true'; }; - if (router.pathname === '/login' && !isCompleted('loginSteps')) { + if (router.pathname === '/login' && !isCompleted('loginSteps')) { setSteps(loginSteps); setStepName('loginSteps'); - setStepsEnabled(true); - }else if (router.pathname === '/dashboard' && !isCompleted('appSteps')) { + setStepsEnabled(true); + } else if (router.pathname === '/dashboard' && !isCompleted('appSteps')) { setTimeout(() => { setSteps(appSteps); setStepName('appSteps'); - setStepsEnabled(true); + setStepsEnabled(true); }, 1000); - } else if (router.pathname === '/users/users-list' && !isCompleted('usersSteps')) { + } else if ( + router.pathname === '/users/users-list' && + !isCompleted('usersSteps') + ) { setTimeout(() => { setSteps(usersSteps); setStepName('usersSteps'); - setStepsEnabled(true); + setStepsEnabled(true); }, 1000); - } else if (router.pathname === '/roles/roles-list' && !isCompleted('rolesSteps')) { + } else if ( + router.pathname === '/roles/roles-list' && + !isCompleted('rolesSteps') + ) { setTimeout(() => { setSteps(rolesSteps); setStepName('rolesSteps'); - setStepsEnabled(true); + setStepsEnabled(true); }, 1000); } else { setSteps([]); - setStepsEnabled(false); + setStepsEnabled(false); } }, [router.pathname]); @@ -149,37 +168,39 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { setStepsEnabled(false); }; - const title = 'Coaching SaaS Workspace' - const description = "A coaching workspace for client context, session memory, action items, resources, and client portal delivery." - const url = "https://flatlogic.com/" - const image = "https://project-screens.s3.amazonaws.com/screenshots/40234/app-hero-20260609-100604.png" - const imageWidth = '1920' - const imageHeight = '960' + const title = 'Coaching SaaS Workspace'; + const description = + 'A coaching workspace for client context, session memory, action items, resources, and client portal delivery.'; + const url = 'https://flatlogic.com/'; + const image = + 'https://project-screens.s3.amazonaws.com/screenshots/40234/app-hero-20260609-100604.png'; + const imageWidth = '1920'; + const imageHeight = '960'; return ( {getLayout( <> - + - - - - - - - - + + + + + + + + - - - - - - + + + + + + - + @@ -191,11 +212,10 @@ function MyApp({ Component, pageProps }: AppPropsWithLayout) { stepsEnabled={stepsEnabled} onExit={handleExit} /> - {(process.env.NODE_ENV === 'development' || process.env.NODE_ENV === 'dev_stage') && } - + , )} - ) + ); } export default appWithTranslation(MyApp);