feat(app): unified demo mode + public-demo build for Vercel

- Central demo-mode helper (lib/demo-mode.ts): ?demo=1 query flag plus
  VITE_PUBLIC_DEMO build flag that makes every route demo for the
  public Vercel showcase.
- Single demo fixture source (lib/demo-data.ts): projects, services,
  builds, databases, cron, HA, security, host monitoring, templates.
  Fixes cross-page ID drift that broke project navigation.
- Demo support on Projects, HA, Security, Usage, Templates, Landing,
  Service detail; DemoRestricted gate on People/Settings/Audit/Admin.
- Demo-aware PlatformShell: nav filtering, env:demo, demo account menu,
  ?demo=1 preserved through internal links.
- TemplatesPage: selection moved into ?template= URL param for deep
  links; template fixtures shared via demo-data.
- requestJson returns {} for unparseable 200 bodies (SPA fallback) so
  envelope unwraps degrade instead of dereferencing null.
- Recharts ResponsiveContainer initialDimension kills -1 warnings.
- vercel.json: vite framework + SPA rewrites for direct deep links.

Generated with [Devin](https://devin.ai)

Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
Tomas DvorakandDevin committed 2026-09-23 17:38:02 +02:00
1 parent 84b102a2c6
commit 5b0a8e0759
20 files changed
+873 -525

No files matched your search

@@ -1,6 +1,6 @@
import { useEffect, useMemo, useRef, useState } from 'react';
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
import { useNavigate, useParams, useSearchParams } from 'react-router-dom';
import { useNavigate, useParams } from 'react-router-dom';
import {
createDeployment,
deleteService,
@@ -36,6 +36,7 @@ import {
type CronJobEntity,
} from '@/lib/api-client';
import { getDemoProjectById, getDemoServiceById, getDemoCronJobsByService } from '@/lib/demo-data';
import { useDemoMode } from '@/lib/demo-mode';
import { useAuthSession } from '@/lib/use-auth-session';
import { getCurrentUserProfile } from '@/lib/api-client';
import { parseDotenv, validateVariableRows, type VariableDraft } from '../variable-utils';
@@ -120,10 +121,9 @@ function StatusBadge({ status }: { status: string }) {
export function ServiceDetailPage() {
const { projectId = '', serviceId = '' } = useParams<{ projectId: string; serviceId: string }>();
const [searchParams] = useSearchParams();
const navigate = useNavigate();
const queryClient = useQueryClient();
const isDemoMode = searchParams.get('demo') === '1';
const isDemoMode = useDemoMode();
const sessionQuery = useAuthSession({ enabled: !isDemoMode });
const signedIn = isDemoMode || Boolean(sessionQuery.data);
const profileQuery = useQuery({