feat: lab layout

This commit is contained in:
Oleg Proskurin 2025-11-30 01:53:45 +07:00
parent f247191ead
commit 3579c8e4cf
23 changed files with 146 additions and 31 deletions

View File

@ -0,0 +1,34 @@
import Image from 'next/image';
export default function LabLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<>
<header className="relative z-10 border-b border-white/10 backdrop-blur-sm">
<nav className="max-w-7xl mx-auto px-6 py-3 flex justify-between items-center h-16">
<div className="h-full flex items-center">
<Image
src="/banatie-logo-horisontal.png"
alt="Banatie Logo"
width={150}
height={40}
priority
className="h-full w-auto object-contain"
/>
</div>
<a
href="#waitlist"
className="text-sm text-gray-300 hover:text-white transition-colors"
>
Join Beta
</a>
</nav>
</header>
{children}
</>
);
}

View File

@ -0,0 +1,37 @@
import Image from 'next/image';
import { Footer } from '@/components/shared/Footer';
export default function MainLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<>
<header className="relative z-10 border-b border-white/10 backdrop-blur-sm">
<nav className="max-w-7xl mx-auto px-6 py-3 flex justify-between items-center h-16">
<div className="h-full flex items-center">
<Image
src="/banatie-logo-horisontal.png"
alt="Banatie Logo"
width={150}
height={40}
priority
className="h-full w-auto object-contain"
/>
</div>
<a
href="#waitlist"
className="text-sm text-gray-300 hover:text-white transition-colors"
>
Join Beta
</a>
</nav>
</header>
{children}
<Footer />
</>
);
}

View File

@ -1,7 +1,5 @@
import type { Metadata } from 'next'; import type { Metadata } from 'next';
import { Inter } from 'next/font/google'; import { Inter } from 'next/font/google';
import Image from 'next/image';
import { Footer } from '@/components/shared/Footer';
import './globals.css'; import './globals.css';
const inter = Inter({ const inter = Inter({
@ -71,38 +69,12 @@ export default function RootLayout({
</head> </head>
<body className={`${inter.variable} antialiased`}> <body className={`${inter.variable} antialiased`}>
<div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950"> <div className="min-h-screen bg-gradient-to-br from-slate-950 via-slate-900 to-slate-950">
{/* Animated gradient background */}
<div className="fixed inset-0 overflow-hidden pointer-events-none"> <div className="fixed inset-0 overflow-hidden pointer-events-none">
<div className="absolute top-1/4 -left-1/4 w-96 h-96 bg-purple-600/10 rounded-full blur-3xl animate-pulse"></div> <div className="absolute top-1/4 -left-1/4 w-96 h-96 bg-purple-600/10 rounded-full blur-3xl animate-pulse"></div>
<div className="absolute bottom-1/4 -right-1/4 w-96 h-96 bg-cyan-600/10 rounded-full blur-3xl animate-pulse delay-700"></div> <div className="absolute bottom-1/4 -right-1/4 w-96 h-96 bg-cyan-600/10 rounded-full blur-3xl animate-pulse delay-700"></div>
</div> </div>
{/* Header */}
<header className="relative z-10 border-b border-white/10 backdrop-blur-sm">
<nav className="max-w-7xl mx-auto px-6 py-3 flex justify-between items-center h-16">
<div className="h-full flex items-center">
<Image
src="/banatie-logo-horisontal.png"
alt="Banatie Logo"
width={150}
height={40}
priority
className="h-full w-auto object-contain"
/>
</div>
<a
href="#waitlist"
className="text-sm text-gray-300 hover:text-white transition-colors"
>
Join Beta
</a>
</nav>
</header>
{/* Page content */}
{children} {children}
<Footer />
</div> </div>
</body> </body>
</html> </html>

View File

@ -0,0 +1,62 @@
'use client';
/**
* Lab Footer Component
*
* Simple 1-line footer for lab section with contextual navigation links.
* Displays copyright on left and contextual docs/API links on right.
*/
import { usePathname } from 'next/navigation';
import Link from 'next/link';
type LinkMapEntry = {
docs: string;
api: string;
};
type LinkMap = {
[key: string]: LinkMapEntry;
};
const linkMap: LinkMap = {
'/lab/generate': { docs: '/docs', api: '/docs/api/text-to-image' },
'/lab/images': { docs: '/docs', api: '/docs/api/images' },
'/lab/live': { docs: '/docs', api: '/docs/api/text-to-image' },
'/lab/upload': { docs: '/docs', api: '/docs/api/upload' },
};
export const LabFooter = () => {
const pathname = usePathname();
const links = linkMap[pathname] || { docs: '/docs', api: '/docs/api' };
return (
<footer
className="border-t border-white/10 bg-slate-950/50 backdrop-blur-sm"
role="contentinfo"
>
<div className="flex flex-col md:flex-row md:items-center md:justify-between gap-4 md:gap-0 px-6 py-4 md:h-14">
{/* Left: Copyright */}
<p className="text-sm text-gray-500 order-2 md:order-1">
© 2025 Banatie. Built for builders who create.
</p>
{/* Right: Contextual Links */}
<nav aria-label="Footer navigation" className="flex items-center gap-6 order-1 md:order-2">
<Link
href={links.docs}
className="text-sm text-gray-500 hover:text-white transition-colors min-h-[44px] flex items-center"
>
Documentation
</Link>
<Link
href={links.api}
className="text-sm text-gray-500 hover:text-white transition-colors min-h-[44px] flex items-center"
>
API Reference
</Link>
</nav>
</div>
</footer>
);
};

View File

@ -7,10 +7,14 @@
* Uses ThreeColumnLayout for consistent column structure across the app. * Uses ThreeColumnLayout for consistent column structure across the app.
* *
* Structure: * Structure:
* - Left: LabSidebar (w-64, hidden lg:block) * - Left: LabSidebar (w-64, hidden lg:block, scrollable)
* - Center: Page content (flex-1) * - Center: Scrollable content area + fixed LabFooter at bottom
* - Right: Reserved for future use (TOC, preview panels, etc.) * - Right: Reserved for future use (TOC, preview panels, etc.)
* *
* The center column uses a fixed height container with:
* - Scrollable content area (flex-1 overflow-y-auto)
* - Sticky footer always visible at bottom
*
* This layout is rendered inside PageProvider context which provides: * This layout is rendered inside PageProvider context which provides:
* - SubsectionNav at the top * - SubsectionNav at the top
* - ApiKeyWidget in the right slot * - ApiKeyWidget in the right slot
@ -20,6 +24,7 @@
import { ReactNode } from 'react'; import { ReactNode } from 'react';
import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout'; import { ThreeColumnLayout } from '@/components/layout/ThreeColumnLayout';
import { LabSidebar } from './LabSidebar'; import { LabSidebar } from './LabSidebar';
import { LabFooter } from './LabFooter';
type LabLayoutProps = { type LabLayoutProps = {
children: ReactNode; children: ReactNode;
@ -33,7 +38,12 @@ export const LabLayout = ({ children }: LabLayoutProps) => {
<LabSidebar /> <LabSidebar />
</div> </div>
} }
center={children} center={
<div className="flex flex-col h-[calc(100vh-3rem)]">
<div className="flex-1 overflow-y-auto">{children}</div>
<LabFooter />
</div>
}
/> />
); );
}; };