mirror of
https://github.com/Dvorinka/Containr.git
synced 2026-10-29 00:01:22 +00:00
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:
1 parent
84b102a2c6
commit
5b0a8e0759
20 files changed
+873
-525
No files matched your search
@@ -14,6 +14,8 @@ import {
|
||||
import { listProjects, listTemplates } from '@/lib/api-client';
|
||||
import { DocsBrowser } from '@/features/docs/DocsBrowser';
|
||||
import { useAuthSession } from '@/lib/use-auth-session';
|
||||
import { useDemoMode } from '@/lib/demo-mode';
|
||||
import { demoProjects, demoTemplates } from '@/lib/demo-data';
|
||||
|
||||
const features = [
|
||||
{ icon: Rocket, title: 'Git-push deploys', text: 'Connect a repository and ship. Builds, rollouts, and rollbacks are handled for you.' },
|
||||
@@ -25,14 +27,16 @@ const features = [
|
||||
];
|
||||
|
||||
export function LandingPage() {
|
||||
const sessionQuery = useAuthSession();
|
||||
const signedIn = Boolean(sessionQuery.data);
|
||||
const isDemoMode = useDemoMode();
|
||||
const sessionQuery = useAuthSession({ enabled: !isDemoMode });
|
||||
const signedIn = !isDemoMode && Boolean(sessionQuery.data);
|
||||
const demo = isDemoMode ? '?demo=1' : '';
|
||||
|
||||
const projectsQuery = useQuery({ queryKey: ['landing-projects'], queryFn: () => listProjects(), staleTime: 60_000 });
|
||||
const templatesQuery = useQuery({ queryKey: ['landing-templates'], queryFn: () => listTemplates(), staleTime: 60_000 });
|
||||
const projectsQuery = useQuery({ queryKey: ['landing-projects'], queryFn: () => listProjects(), staleTime: 60_000, enabled: !isDemoMode });
|
||||
const templatesQuery = useQuery({ queryKey: ['landing-templates'], queryFn: () => listTemplates(), staleTime: 60_000, enabled: !isDemoMode });
|
||||
|
||||
const projects = projectsQuery.data ?? [];
|
||||
const templates = (templatesQuery.data ?? []).slice(0, 12);
|
||||
const projects = isDemoMode ? demoProjects : (projectsQuery.data ?? []);
|
||||
const templates = (isDemoMode ? demoTemplates : (templatesQuery.data ?? [])).slice(0, 12);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-[var(--bg-void)] text-[var(--text-secondary)]">
|
||||
@@ -51,7 +55,7 @@ export function LandingPage() {
|
||||
<a href="#templates" className="rounded-[var(--radius-md)] px-3 py-1.5 text-[var(--text-secondary)] hover:bg-[var(--surface-muted)] hover:text-[var(--text-primary)] max-sm:hidden">Templates</a>
|
||||
<a href="#docs" className="rounded-[var(--radius-md)] px-3 py-1.5 text-[var(--text-secondary)] hover:bg-[var(--surface-muted)] hover:text-[var(--text-primary)] max-sm:hidden">Docs</a>
|
||||
<Link
|
||||
to="/projects"
|
||||
to={`/projects${demo}`}
|
||||
className="rounded-[var(--radius-md)] px-3 py-1.5 font-medium text-[var(--text-secondary)] hover:bg-[var(--surface-muted)] hover:text-[var(--text-primary)]"
|
||||
>
|
||||
Browse
|
||||
@@ -65,10 +69,10 @@ export function LandingPage() {
|
||||
</Link>
|
||||
) : (
|
||||
<Link
|
||||
to="/auth/sign-in"
|
||||
to={isDemoMode ? `/projects${demo}` : '/auth/sign-in'}
|
||||
className="ml-1 inline-flex items-center gap-1.5 rounded-[var(--radius-md)] bg-[var(--accent-primary)] px-3.5 py-1.5 font-semibold text-[var(--accent-on)]"
|
||||
>
|
||||
Sign in <ArrowRight size={13} />
|
||||
{isDemoMode ? 'Live demo' : 'Sign in'} <ArrowRight size={13} />
|
||||
</Link>
|
||||
)}
|
||||
</nav>
|
||||
@@ -91,10 +95,10 @@ export function LandingPage() {
|
||||
</p>
|
||||
<div className="mt-8 flex items-center justify-center gap-3">
|
||||
<Link
|
||||
to="/projects"
|
||||
to={`/projects${demo}`}
|
||||
className="inline-flex items-center gap-2 rounded-[var(--radius-md)] bg-[var(--accent-primary)] px-5 py-2.5 text-sm font-semibold text-[var(--accent-on)] transition-transform hover:scale-[1.02]"
|
||||
>
|
||||
Browse projects <ArrowRight size={15} />
|
||||
{isDemoMode ? 'Explore the live demo' : 'Browse projects'} <ArrowRight size={15} />
|
||||
</Link>
|
||||
<a
|
||||
href="#docs"
|
||||
@@ -152,7 +156,7 @@ export function LandingPage() {
|
||||
</p>
|
||||
</div>
|
||||
<Link
|
||||
to="/templates"
|
||||
to={`/templates${demo}`}
|
||||
className="inline-flex items-center gap-1.5 text-[13px] font-medium text-[var(--accent-primary)] hover:underline"
|
||||
>
|
||||
Browse all <ArrowRight size={13} />
|
||||
@@ -162,7 +166,7 @@ export function LandingPage() {
|
||||
{templates.map((template) => (
|
||||
<Link
|
||||
key={template.id}
|
||||
to="/templates"
|
||||
to={`/templates?template=${template.id}${isDemoMode ? '&demo=1' : ''}`}
|
||||
className="group rounded-[var(--radius-lg)] border border-[var(--border-subtle)] bg-[var(--surface-card)] p-4 transition-colors hover:border-[var(--border-strong)]"
|
||||
>
|
||||
<div className="flex items-center gap-2.5">
|
||||
@@ -212,9 +216,9 @@ export function LandingPage() {
|
||||
Containr — self-hosted container platform
|
||||
</span>
|
||||
<div className="flex items-center gap-4">
|
||||
<Link to="/projects" className="hover:text-[var(--text-secondary)]">Projects</Link>
|
||||
<Link to="/templates" className="hover:text-[var(--text-secondary)]">Templates</Link>
|
||||
<Link to="/admin" className="hover:text-[var(--text-secondary)]">Admin</Link>
|
||||
<Link to={`/projects${demo}`} className="hover:text-[var(--text-secondary)]">Projects</Link>
|
||||
<Link to={`/templates${demo}`} className="hover:text-[var(--text-secondary)]">Templates</Link>
|
||||
{!isDemoMode ? <Link to="/admin" className="hover:text-[var(--text-secondary)]">Admin</Link> : null}
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
Reference in new issue
Block a user