diff --git a/app/frontend/.gitignore b/app/frontend/.gitignore new file mode 100644 index 0000000..245259b --- /dev/null +++ b/app/frontend/.gitignore @@ -0,0 +1,2 @@ +.vercel +.env* diff --git a/app/frontend/src/app/App.smoke.test.tsx b/app/frontend/src/app/App.smoke.test.tsx index 2af983d..8bb097e 100644 --- a/app/frontend/src/app/App.smoke.test.tsx +++ b/app/frontend/src/app/App.smoke.test.tsx @@ -32,7 +32,7 @@ describe('App smoke routes', () => { expect(await screen.findByRole('heading', { name: /^projects\s*_?$/i })).toBeInTheDocument(); expect(screen.getByText(/recent deployments/i)).toBeInTheDocument(); - expect(screen.getAllByText(/core-services/i).length).toBeGreaterThan(0); + expect(screen.getAllByText(/core platform/i).length).toBeGreaterThan(0); }); it('renders builds page in demo mode', async () => { diff --git a/app/frontend/src/app/App.tsx b/app/frontend/src/app/App.tsx index ef3444c..81fe82e 100644 --- a/app/frontend/src/app/App.tsx +++ b/app/frontend/src/app/App.tsx @@ -23,14 +23,10 @@ import { } from '@/features/secondary/pages'; import { SignInPage, SignUpPage } from '@/features/auth/pages'; import { useAuthSession } from '@/lib/use-auth-session'; +import { useDemoMode } from '@/lib/demo-mode'; import { getCurrentUserProfile } from '@/lib/api-client'; import { ErrorBoundary, LoadingState } from '@/shared/components'; -function useDemoMode() { - const location = useLocation(); - return new URLSearchParams(location.search).get('demo') === '1'; -} - function AuthRequired() { const location = useLocation(); const isDemoMode = useDemoMode(); diff --git a/app/frontend/src/app/layout/PlatformShell.tsx b/app/frontend/src/app/layout/PlatformShell.tsx index 241ef02..64961e3 100644 --- a/app/frontend/src/app/layout/PlatformShell.tsx +++ b/app/frontend/src/app/layout/PlatformShell.tsx @@ -37,6 +37,7 @@ import { pullUpgradeImage, } from '@/lib/api-client'; import { signOutAuthSession } from '@/lib/auth-client'; +import { isDemoSearch } from '@/lib/demo-mode'; import { useAuthSession } from '@/lib/use-auth-session'; import { useToast } from '@/shared/components'; @@ -103,8 +104,18 @@ export function PlatformShell() { const navigate = useNavigate(); const queryClient = useQueryClient(); const { showToast } = useToast(); - const isDemoMode = new URLSearchParams(location.search).get('demo') === '1'; + const isDemoMode = isDemoSearch(location.search); const href = (target: string) => (isDemoMode ? `${target}?demo=1` : target); + // Auth/admin-scoped pages have no demo fixtures — keep the demo nav honest. + const visibleNavSections = isDemoMode + ? navSections + .map((section) => + section.title === 'Workspace' + ? { ...section, items: section.items.filter((item) => item.href === '/docs') } + : section, + ) + .filter((section) => section.items.length > 0) + : navSections; const [theme, setTheme] = useState(() => getInitialTheme()); const [sidebarExpanded, setSidebarExpanded] = useState(() => getInitialSidebar()); const [notificationsOpen, setNotificationsOpen] = useState(false); @@ -118,7 +129,7 @@ export function PlatformShell() { staleTime: 60_000, retry: false, }); - const isAdmin = isDemoMode || Boolean(profileQuery.data?.isAdmin); + const isAdmin = !isDemoMode && Boolean(profileQuery.data?.isAdmin); const upgradeQuery = useQuery({ queryKey: ['upgrade-status'], queryFn: getUpgradeStatus, @@ -174,8 +185,8 @@ export function PlatformShell() { const isActiveRoute = (itemHref: string) => location.pathname === itemHref || location.pathname.startsWith(`${itemHref}/`); - const userName = profileQuery.data?.name ?? sessionQuery.data?.user.name ?? 'Account'; - const userEmail = profileQuery.data?.email ?? sessionQuery.data?.user.email ?? 'Local session'; + const userName = isDemoMode ? 'Demo workspace' : profileQuery.data?.name ?? sessionQuery.data?.user.name ?? 'Account'; + const userEmail = isDemoMode ? 'Sample data · read-only' : profileQuery.data?.email ?? sessionQuery.data?.user.email ?? 'Local session'; const initials = userName .split(' ') .map((part) => part[0]) @@ -220,7 +231,7 @@ export function PlatformShell() {