forked from cardosofelipe/fast-next-template
Remove all obsolete authentication, settings, admin, and demo-related components and pages
- Eliminated redundant components, pages, and layouts related to authentication (`login`, `register`, `password-reset`, etc.), user settings, admin, and demos. - Simplified the frontend structure by removing unused dynamic imports, forms, and test code. - Refactored configurations and metadata imports to exclude references to removed features. - Streamlined the project for future development and improved maintainability by discarding legacy and unused code.
This commit is contained in:
@@ -1,10 +0,0 @@
|
|||||||
/**
|
|
||||||
* Settings Index Page
|
|
||||||
* Redirects to /settings/profile
|
|
||||||
*/
|
|
||||||
|
|
||||||
import { redirect } from 'next/navigation';
|
|
||||||
|
|
||||||
export default function SettingsPage() {
|
|
||||||
redirect('/settings/profile');
|
|
||||||
}
|
|
||||||
@@ -5,11 +5,12 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useSearchParams, useRouter } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
import { useRouter } from '@/lib/i18n/routing';
|
||||||
import { useEffect, useRef } from 'react';
|
import { useEffect, useRef } from 'react';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import { Alert } from '@/components/ui/alert';
|
import { Alert } from '@/components/ui/alert';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
|
|
||||||
// Code-split PasswordResetConfirmForm (319 lines)
|
// Code-split PasswordResetConfirmForm (319 lines)
|
||||||
const PasswordResetConfirmForm = dynamic(
|
const PasswordResetConfirmForm = dynamic(
|
||||||
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from '@/lib/i18n/routing';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
import { Tabs, TabsList, TabsTrigger } from '@/components/ui/tabs';
|
||||||
import { User, Lock, Monitor, Settings as SettingsIcon } from 'lucide-react';
|
import { User, Lock, Monitor, Settings as SettingsIcon } from 'lucide-react';
|
||||||
|
|
||||||
11
frontend/src/app/[locale]/(authenticated)/settings/page.tsx
Normal file
11
frontend/src/app/[locale]/(authenticated)/settings/page.tsx
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
/**
|
||||||
|
* Settings Index Page
|
||||||
|
* Redirects to /settings/profile
|
||||||
|
*/
|
||||||
|
|
||||||
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
|
export default async function SettingsPage({ params }: { params: Promise<{ locale: string }> }) {
|
||||||
|
const { locale } = await params;
|
||||||
|
redirect(`/${locale}/settings/profile`);
|
||||||
|
}
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* istanbul ignore next - Next.js type import for metadata */
|
/* istanbul ignore next - Next.js type import for metadata */
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { ArrowLeft } from 'lucide-react';
|
import { ArrowLeft } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { OrganizationMembersContent } from '@/components/admin/organizations/OrganizationMembersContent';
|
import { OrganizationMembersContent } from '@/components/admin/organizations/OrganizationMembersContent';
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* istanbul ignore next - Next.js type import for metadata */
|
/* istanbul ignore next - Next.js type import for metadata */
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { ArrowLeft } from 'lucide-react';
|
import { ArrowLeft } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { OrganizationManagementContent } from '@/components/admin/organizations/OrganizationManagementContent';
|
import { OrganizationManagementContent } from '@/components/admin/organizations/OrganizationManagementContent';
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* istanbul ignore next - Next.js type import for metadata */
|
/* istanbul ignore next - Next.js type import for metadata */
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { DashboardStats } from '@/components/admin';
|
import { DashboardStats } from '@/components/admin';
|
||||||
import {
|
import {
|
||||||
UserGrowthChart,
|
UserGrowthChart,
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* istanbul ignore next - Next.js type import for metadata */
|
/* istanbul ignore next - Next.js type import for metadata */
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { ArrowLeft } from 'lucide-react';
|
import { ArrowLeft } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
/* istanbul ignore next - Next.js type import for metadata */
|
/* istanbul ignore next - Next.js type import for metadata */
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { ArrowLeft } from 'lucide-react';
|
import { ArrowLeft } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { UserManagementContent } from '@/components/admin/users/UserManagementContent';
|
import { UserManagementContent } from '@/components/admin/users/UserManagementContent';
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import {
|
import {
|
||||||
Palette,
|
Palette,
|
||||||
ShieldCheck,
|
ShieldCheck,
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
* Access: /dev/docs
|
* Access: /dev/docs
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import {
|
import {
|
||||||
BookOpen,
|
BookOpen,
|
||||||
Sparkles,
|
Sparkles,
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { Grid3x3 } from 'lucide-react';
|
import { Grid3x3 } from 'lucide-react';
|
||||||
import { DevBreadcrumbs } from '@/components/dev/DevBreadcrumbs';
|
import { DevBreadcrumbs } from '@/components/dev/DevBreadcrumbs';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { Palette, Layout, Ruler, FileText, BookOpen, ArrowRight, Sparkles } from 'lucide-react';
|
import { Palette, Layout, Ruler, FileText, BookOpen, ArrowRight, Sparkles } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
|
||||||
@@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import dynamic from 'next/dynamic';
|
import dynamic from 'next/dynamic';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { Ruler } from 'lucide-react';
|
import { Ruler } from 'lucide-react';
|
||||||
import { DevBreadcrumbs } from '@/components/dev/DevBreadcrumbs';
|
import { DevBreadcrumbs } from '@/components/dev/DevBreadcrumbs';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
/* istanbul ignore next - Next.js type import for metadata */
|
/* istanbul ignore next - Next.js type import for metadata */
|
||||||
import type { Metadata } from 'next';
|
import type { Metadata } from 'next';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { ShieldAlert } from 'lucide-react';
|
import { ShieldAlert } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|
||||||
88
frontend/src/app/[locale]/layout.tsx
Normal file
88
frontend/src/app/[locale]/layout.tsx
Normal file
@@ -0,0 +1,88 @@
|
|||||||
|
import type { Metadata } from 'next';
|
||||||
|
import { Geist, Geist_Mono } from 'next/font/google';
|
||||||
|
import { notFound } from 'next/navigation';
|
||||||
|
import { routing } from '@/lib/i18n/routing';
|
||||||
|
import { NextIntlClientProvider } from 'next-intl';
|
||||||
|
import { getMessages } from 'next-intl/server';
|
||||||
|
import '../globals.css';
|
||||||
|
import { Providers } from '../providers';
|
||||||
|
import { AuthProvider } from '@/lib/auth/AuthContext';
|
||||||
|
import { AuthInitializer } from '@/components/auth';
|
||||||
|
|
||||||
|
const geistSans = Geist({
|
||||||
|
variable: '--font-geist-sans',
|
||||||
|
subsets: ['latin'],
|
||||||
|
display: 'swap', // Prevent font from blocking render
|
||||||
|
preload: true,
|
||||||
|
});
|
||||||
|
|
||||||
|
const geistMono = Geist_Mono({
|
||||||
|
variable: '--font-geist-mono',
|
||||||
|
subsets: ['latin'],
|
||||||
|
display: 'swap', // Prevent font from blocking render
|
||||||
|
preload: false, // Only preload primary font
|
||||||
|
});
|
||||||
|
|
||||||
|
export const metadata: Metadata = {
|
||||||
|
title: 'FastNext Template',
|
||||||
|
description: 'FastAPI + Next.js Template',
|
||||||
|
};
|
||||||
|
|
||||||
|
export default async function LocaleLayout({
|
||||||
|
children,
|
||||||
|
params,
|
||||||
|
}: Readonly<{
|
||||||
|
children: React.ReactNode;
|
||||||
|
params: Promise<{ locale: string }>;
|
||||||
|
}>) {
|
||||||
|
// Await params in Next.js 15
|
||||||
|
const { locale } = await params;
|
||||||
|
|
||||||
|
// Ensure that the incoming `locale` is valid
|
||||||
|
if (!routing.locales.includes(locale as 'en' | 'it')) {
|
||||||
|
notFound();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Providing all messages to the client
|
||||||
|
// side is the easiest way to get started
|
||||||
|
const messages = await getMessages();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<html lang={locale} suppressHydrationWarning>
|
||||||
|
<head>
|
||||||
|
{/* Theme initialization script - runs before React hydrates to prevent FOUC */}
|
||||||
|
<script
|
||||||
|
dangerouslySetInnerHTML={{
|
||||||
|
__html: `
|
||||||
|
(function() {
|
||||||
|
try {
|
||||||
|
const theme = localStorage.getItem('theme') || 'system';
|
||||||
|
let resolved;
|
||||||
|
|
||||||
|
if (theme === 'system') {
|
||||||
|
resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
||||||
|
} else {
|
||||||
|
resolved = theme;
|
||||||
|
}
|
||||||
|
|
||||||
|
document.documentElement.classList.remove('light', 'dark');
|
||||||
|
document.documentElement.classList.add(resolved);
|
||||||
|
} catch (e) {
|
||||||
|
// Silently fail - theme will be set by ThemeProvider
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
`,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</head>
|
||||||
|
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
|
||||||
|
<NextIntlClientProvider messages={messages}>
|
||||||
|
<AuthProvider>
|
||||||
|
<AuthInitializer />
|
||||||
|
<Providers>{children}</Providers>
|
||||||
|
</AuthProvider>
|
||||||
|
</NextIntlClientProvider>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
99
frontend/src/app/[locale]/page.tsx
Executable file
99
frontend/src/app/[locale]/page.tsx
Executable file
@@ -0,0 +1,99 @@
|
|||||||
|
/**
|
||||||
|
* Homepage / Landing Page
|
||||||
|
* Main landing page for the FastNext Template project
|
||||||
|
* Showcases features, tech stack, and provides demos for developers
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { Link } from '@/lib/i18n/routing';
|
||||||
|
import { Header } from '@/components/home/Header';
|
||||||
|
import { HeroSection } from '@/components/home/HeroSection';
|
||||||
|
import { ContextSection } from '@/components/home/ContextSection';
|
||||||
|
import { AnimatedTerminal } from '@/components/home/AnimatedTerminal';
|
||||||
|
import { FeatureGrid } from '@/components/home/FeatureGrid';
|
||||||
|
import { DemoSection } from '@/components/home/DemoSection';
|
||||||
|
import { StatsSection } from '@/components/home/StatsSection';
|
||||||
|
import { TechStackSection } from '@/components/home/TechStackSection';
|
||||||
|
import { PhilosophySection } from '@/components/home/PhilosophySection';
|
||||||
|
import { QuickStartCode } from '@/components/home/QuickStartCode';
|
||||||
|
import { CTASection } from '@/components/home/CTASection';
|
||||||
|
import { DemoCredentialsModal } from '@/components/home/DemoCredentialsModal';
|
||||||
|
|
||||||
|
export default function Home() {
|
||||||
|
const [demoModalOpen, setDemoModalOpen] = useState(false);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="min-h-screen">
|
||||||
|
{/* Header Navigation */}
|
||||||
|
<Header onOpenDemoModal={() => setDemoModalOpen(true)} />
|
||||||
|
|
||||||
|
{/* Main Content */}
|
||||||
|
<main>
|
||||||
|
{/* Hero Section with CTAs */}
|
||||||
|
<HeroSection onOpenDemoModal={() => setDemoModalOpen(true)} />
|
||||||
|
|
||||||
|
{/* What is this template? */}
|
||||||
|
<ContextSection />
|
||||||
|
|
||||||
|
{/* Animated Terminal with Quick Start */}
|
||||||
|
<AnimatedTerminal />
|
||||||
|
|
||||||
|
{/* 6 Feature Cards Grid */}
|
||||||
|
<FeatureGrid />
|
||||||
|
|
||||||
|
{/* Interactive Demo Cards */}
|
||||||
|
<DemoSection />
|
||||||
|
|
||||||
|
{/* Statistics with Animated Counters */}
|
||||||
|
<StatsSection />
|
||||||
|
|
||||||
|
{/* Tech Stack Grid */}
|
||||||
|
<TechStackSection />
|
||||||
|
|
||||||
|
{/* For Developers, By Developers */}
|
||||||
|
<PhilosophySection />
|
||||||
|
|
||||||
|
{/* Quick Start Code Block */}
|
||||||
|
<QuickStartCode />
|
||||||
|
|
||||||
|
{/* Final CTA Section */}
|
||||||
|
<CTASection onOpenDemoModal={() => setDemoModalOpen(true)} />
|
||||||
|
</main>
|
||||||
|
|
||||||
|
{/* Footer */}
|
||||||
|
<footer className="border-t bg-muted/30">
|
||||||
|
<div className="container mx-auto px-6 py-8">
|
||||||
|
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
|
||||||
|
<div className="text-sm text-muted-foreground">
|
||||||
|
© {new Date().getFullYear()} FastNext Template. MIT Licensed.
|
||||||
|
</div>
|
||||||
|
<div className="flex items-center gap-6 text-sm text-muted-foreground">
|
||||||
|
<Link href="/demos" className="hover:text-foreground transition-colors">
|
||||||
|
Demo Tour
|
||||||
|
</Link>
|
||||||
|
<Link href="/dev" className="hover:text-foreground transition-colors">
|
||||||
|
Design System
|
||||||
|
</Link>
|
||||||
|
<a
|
||||||
|
href="https://github.com"
|
||||||
|
target="_blank"
|
||||||
|
rel="noopener noreferrer"
|
||||||
|
className="hover:text-foreground transition-colors"
|
||||||
|
>
|
||||||
|
GitHub
|
||||||
|
</a>
|
||||||
|
<Link href="/dev/docs" className="hover:text-foreground transition-colors">
|
||||||
|
Documentation
|
||||||
|
</Link>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
|
||||||
|
{/* Shared Demo Credentials Modal */}
|
||||||
|
<DemoCredentialsModal open={demoModalOpen} onClose={() => setDemoModalOpen(false)} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,68 +1,15 @@
|
|||||||
import type { Metadata } from 'next';
|
/**
|
||||||
import { Geist, Geist_Mono } from 'next/font/google';
|
* Root Layout
|
||||||
import './globals.css';
|
*
|
||||||
import { Providers } from './providers';
|
* Minimal root layout that passes through to locale-specific layouts.
|
||||||
import { AuthProvider } from '@/lib/auth/AuthContext';
|
* The actual HTML structure and providers are in [locale]/layout.tsx
|
||||||
import { AuthInitializer } from '@/components/auth';
|
* to properly handle locale-specific rendering.
|
||||||
|
*/
|
||||||
const geistSans = Geist({
|
|
||||||
variable: '--font-geist-sans',
|
|
||||||
subsets: ['latin'],
|
|
||||||
display: 'swap', // Prevent font from blocking render
|
|
||||||
preload: true,
|
|
||||||
});
|
|
||||||
|
|
||||||
const geistMono = Geist_Mono({
|
|
||||||
variable: '--font-geist-mono',
|
|
||||||
subsets: ['latin'],
|
|
||||||
display: 'swap', // Prevent font from blocking render
|
|
||||||
preload: false, // Only preload primary font
|
|
||||||
});
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: 'FastNext Template',
|
|
||||||
description: 'FastAPI + Next.js Template',
|
|
||||||
};
|
|
||||||
|
|
||||||
export default function RootLayout({
|
export default function RootLayout({
|
||||||
children,
|
children,
|
||||||
}: Readonly<{
|
}: Readonly<{
|
||||||
children: React.ReactNode;
|
children: React.ReactNode;
|
||||||
}>) {
|
}>) {
|
||||||
return (
|
return children;
|
||||||
<html lang="en" suppressHydrationWarning>
|
|
||||||
<head>
|
|
||||||
{/* Theme initialization script - runs before React hydrates to prevent FOUC */}
|
|
||||||
<script
|
|
||||||
dangerouslySetInnerHTML={{
|
|
||||||
__html: `
|
|
||||||
(function() {
|
|
||||||
try {
|
|
||||||
const theme = localStorage.getItem('theme') || 'system';
|
|
||||||
let resolved;
|
|
||||||
|
|
||||||
if (theme === 'system') {
|
|
||||||
resolved = window.matchMedia('(prefers-color-scheme: dark)').matches ? 'dark' : 'light';
|
|
||||||
} else {
|
|
||||||
resolved = theme;
|
|
||||||
}
|
|
||||||
|
|
||||||
document.documentElement.classList.remove('light', 'dark');
|
|
||||||
document.documentElement.classList.add(resolved);
|
|
||||||
} catch (e) {
|
|
||||||
// Silently fail - theme will be set by ThemeProvider
|
|
||||||
}
|
|
||||||
})();
|
|
||||||
`,
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</head>
|
|
||||||
<body className={`${geistSans.variable} ${geistMono.variable} antialiased`}>
|
|
||||||
<AuthProvider>
|
|
||||||
<AuthInitializer />
|
|
||||||
<Providers>{children}</Providers>
|
|
||||||
</AuthProvider>
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
99
frontend/src/app/page.tsx
Executable file → Normal file
99
frontend/src/app/page.tsx
Executable file → Normal file
@@ -1,99 +1,10 @@
|
|||||||
/**
|
/**
|
||||||
* Homepage / Landing Page
|
* Root page - redirects to default locale
|
||||||
* Main landing page for the FastNext Template project
|
|
||||||
* Showcases features, tech stack, and provides demos for developers
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
'use client';
|
import { redirect } from 'next/navigation';
|
||||||
|
|
||||||
import { useState } from 'react';
|
export default function RootPage() {
|
||||||
import Link from 'next/link';
|
// Redirect to default locale (en)
|
||||||
import { Header } from '@/components/home/Header';
|
redirect('/en');
|
||||||
import { HeroSection } from '@/components/home/HeroSection';
|
|
||||||
import { ContextSection } from '@/components/home/ContextSection';
|
|
||||||
import { AnimatedTerminal } from '@/components/home/AnimatedTerminal';
|
|
||||||
import { FeatureGrid } from '@/components/home/FeatureGrid';
|
|
||||||
import { DemoSection } from '@/components/home/DemoSection';
|
|
||||||
import { StatsSection } from '@/components/home/StatsSection';
|
|
||||||
import { TechStackSection } from '@/components/home/TechStackSection';
|
|
||||||
import { PhilosophySection } from '@/components/home/PhilosophySection';
|
|
||||||
import { QuickStartCode } from '@/components/home/QuickStartCode';
|
|
||||||
import { CTASection } from '@/components/home/CTASection';
|
|
||||||
import { DemoCredentialsModal } from '@/components/home/DemoCredentialsModal';
|
|
||||||
|
|
||||||
export default function Home() {
|
|
||||||
const [demoModalOpen, setDemoModalOpen] = useState(false);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="min-h-screen">
|
|
||||||
{/* Header Navigation */}
|
|
||||||
<Header onOpenDemoModal={() => setDemoModalOpen(true)} />
|
|
||||||
|
|
||||||
{/* Main Content */}
|
|
||||||
<main>
|
|
||||||
{/* Hero Section with CTAs */}
|
|
||||||
<HeroSection onOpenDemoModal={() => setDemoModalOpen(true)} />
|
|
||||||
|
|
||||||
{/* What is this template? */}
|
|
||||||
<ContextSection />
|
|
||||||
|
|
||||||
{/* Animated Terminal with Quick Start */}
|
|
||||||
<AnimatedTerminal />
|
|
||||||
|
|
||||||
{/* 6 Feature Cards Grid */}
|
|
||||||
<FeatureGrid />
|
|
||||||
|
|
||||||
{/* Interactive Demo Cards */}
|
|
||||||
<DemoSection />
|
|
||||||
|
|
||||||
{/* Statistics with Animated Counters */}
|
|
||||||
<StatsSection />
|
|
||||||
|
|
||||||
{/* Tech Stack Grid */}
|
|
||||||
<TechStackSection />
|
|
||||||
|
|
||||||
{/* For Developers, By Developers */}
|
|
||||||
<PhilosophySection />
|
|
||||||
|
|
||||||
{/* Quick Start Code Block */}
|
|
||||||
<QuickStartCode />
|
|
||||||
|
|
||||||
{/* Final CTA Section */}
|
|
||||||
<CTASection onOpenDemoModal={() => setDemoModalOpen(true)} />
|
|
||||||
</main>
|
|
||||||
|
|
||||||
{/* Footer */}
|
|
||||||
<footer className="border-t bg-muted/30">
|
|
||||||
<div className="container mx-auto px-6 py-8">
|
|
||||||
<div className="flex flex-col md:flex-row items-center justify-between gap-4">
|
|
||||||
<div className="text-sm text-muted-foreground">
|
|
||||||
© {new Date().getFullYear()} FastNext Template. MIT Licensed.
|
|
||||||
</div>
|
|
||||||
<div className="flex items-center gap-6 text-sm text-muted-foreground">
|
|
||||||
<Link href="/demos" className="hover:text-foreground transition-colors">
|
|
||||||
Demo Tour
|
|
||||||
</Link>
|
|
||||||
<Link href="/dev" className="hover:text-foreground transition-colors">
|
|
||||||
Design System
|
|
||||||
</Link>
|
|
||||||
<a
|
|
||||||
href="https://github.com"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
className="hover:text-foreground transition-colors"
|
|
||||||
>
|
|
||||||
GitHub
|
|
||||||
</a>
|
|
||||||
<Link href="/dev/docs" className="hover:text-foreground transition-colors">
|
|
||||||
Documentation
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</footer>
|
|
||||||
|
|
||||||
{/* Shared Demo Credentials Modal */}
|
|
||||||
<DemoCredentialsModal open={demoModalOpen} onClose={() => setDemoModalOpen(false)} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from '@/lib/i18n/routing';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
import {
|
import {
|
||||||
LayoutDashboard,
|
LayoutDashboard,
|
||||||
|
|||||||
@@ -5,8 +5,8 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from '@/lib/i18n/routing';
|
||||||
import { ChevronRight } from 'lucide-react';
|
import { ChevronRight } from 'lucide-react';
|
||||||
|
|
||||||
interface BreadcrumbItem {
|
interface BreadcrumbItem {
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { useSearchParams, useRouter } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
import { useRouter } from '@/lib/i18n/routing';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { useSearchParams, useRouter } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
import { useRouter } from '@/lib/i18n/routing';
|
||||||
import { UserPlus } from 'lucide-react';
|
import { UserPlus } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -6,7 +6,8 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState, useCallback } from 'react';
|
import { useState, useCallback } from 'react';
|
||||||
import { useSearchParams, useRouter } from 'next/navigation';
|
import { useSearchParams } from 'next/navigation';
|
||||||
|
import { useRouter } from '@/lib/i18n/routing';
|
||||||
import { Plus } from 'lucide-react';
|
import { Plus } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { useAuth } from '@/lib/auth/AuthContext';
|
import { useAuth } from '@/lib/auth/AuthContext';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useRouter, usePathname } from 'next/navigation';
|
import { useRouter, usePathname } from '@/lib/i18n/routing';
|
||||||
import { useAuth } from '@/lib/auth/AuthContext';
|
import { useAuth } from '@/lib/auth/AuthContext';
|
||||||
import { useMe } from '@/lib/api/hooks/useAuth';
|
import { useMe } from '@/lib/api/hooks/useAuth';
|
||||||
import { AuthLoadingSkeleton } from '@/components/layout';
|
import { AuthLoadingSkeleton } from '@/components/layout';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { useForm } from 'react-hook-form';
|
import { useForm } from 'react-hook-form';
|
||||||
import { zodResolver } from '@hookform/resolvers/zod';
|
import { zodResolver } from '@hookform/resolvers/zod';
|
||||||
import { z } from 'zod';
|
import { z } from 'zod';
|
||||||
|
|||||||
@@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { ChevronRight, Home } from 'lucide-react';
|
import { ChevronRight, Home } from 'lucide-react';
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
|
|||||||
@@ -8,8 +8,8 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from '@/lib/i18n/routing';
|
||||||
import {
|
import {
|
||||||
Code2,
|
Code2,
|
||||||
Palette,
|
Palette,
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import { useEffect, useState, useRef } from 'react';
|
|||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Terminal, Play } from 'lucide-react';
|
import { Terminal, Play } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
|
|
||||||
const commands = [
|
const commands = [
|
||||||
{ text: '# Clone the repository', delay: 0 },
|
{ text: '# Clone the repository', delay: 0 },
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Github, Star, Play, ArrowRight } from 'lucide-react';
|
import { Github, Star, Play, ArrowRight } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { Copy, Check } from 'lucide-react';
|
import { Copy, Check } from 'lucide-react';
|
||||||
import {
|
import {
|
||||||
Dialog,
|
Dialog,
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { Play, Layers, ShieldCheck, UserCircle } from 'lucide-react';
|
import { Play, Layers, ShieldCheck, UserCircle } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { ArrowRight, LucideIcon } from 'lucide-react';
|
import { ArrowRight, LucideIcon } from 'lucide-react';
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { Menu, X, Github, Star } from 'lucide-react';
|
import { Menu, X, Github, Star } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
|
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { motion } from 'framer-motion';
|
import { motion } from 'framer-motion';
|
||||||
import { ArrowRight, Github, Play } from 'lucide-react';
|
import { ArrowRight, Github, Play } from 'lucide-react';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
|
|||||||
@@ -5,7 +5,7 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
|
|
||||||
export function Footer() {
|
export function Footer() {
|
||||||
const currentYear = new Date().getFullYear();
|
const currentYear = new Date().getFullYear();
|
||||||
|
|||||||
@@ -6,8 +6,8 @@
|
|||||||
|
|
||||||
'use client';
|
'use client';
|
||||||
|
|
||||||
import Link from 'next/link';
|
import { Link } from '@/lib/i18n/routing';
|
||||||
import { usePathname } from 'next/navigation';
|
import { usePathname } from '@/lib/i18n/routing';
|
||||||
import { useAuth } from '@/lib/auth/AuthContext';
|
import { useAuth } from '@/lib/auth/AuthContext';
|
||||||
import { useLogout } from '@/lib/api/hooks/useAuth';
|
import { useLogout } from '@/lib/api/hooks/useAuth';
|
||||||
import {
|
import {
|
||||||
|
|||||||
@@ -9,8 +9,9 @@ const intlMiddleware = createMiddleware(routing);
|
|||||||
export function middleware(request: NextRequest) {
|
export function middleware(request: NextRequest) {
|
||||||
const { pathname } = request.nextUrl;
|
const { pathname } = request.nextUrl;
|
||||||
|
|
||||||
// Block access to /dev routes in production (before locale handling)
|
// Block access to /dev routes in production (handles both /dev and /[locale]/dev)
|
||||||
if (pathname.startsWith('/dev')) {
|
// Match: /dev, /en/dev, /it/dev, etc.
|
||||||
|
if (pathname === '/dev' || pathname.match(/^\/[a-z]{2}\/dev($|\/)/)) {
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
|
|
||||||
if (isProduction) {
|
if (isProduction) {
|
||||||
|
|||||||
Reference in New Issue
Block a user