Skip to content

Commit 3225ff0

Browse files
committed
feat: initialize Next.js frontend with app router, TailwindCSS and authentication layout
1 parent 06db935 commit 3225ff0

10 files changed

Lines changed: 142 additions & 167 deletions

File tree

frontend-nextjs/next.config.ts

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,22 @@
11
import type { NextConfig } from "next";
22

33
const nextConfig: NextConfig = {
4-
/* config options here */
4+
experimental: {
5+
serverActions: {
6+
bodySizeLimit: '2mb',
7+
},
8+
},
9+
images: {
10+
remotePatterns: [
11+
{
12+
protocol: 'http',
13+
hostname: 'localhost',
14+
port: '8000',
15+
pathname: '/static/**',
16+
},
17+
],
18+
},
19+
520
};
621

722
export default nextConfig;

frontend-nextjs/package-lock.json

Lines changed: 0 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

frontend-nextjs/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
"react-dom": "19.1.0",
2828
"react-error-boundary": "^6.0.0",
2929
"react-hook-form": "^7.60.0",
30-
"react-icons": "^5.5.0",
30+
3131
"tailwind-merge": "^3.3.1",
3232
"tailwindcss-animate": "^1.0.7",
3333
"zod": "^4.0.5"

frontend-nextjs/src/app/globals.css

Lines changed: 2 additions & 96 deletions
Original file line numberDiff line numberDiff line change
@@ -1,112 +1,18 @@
11
@tailwind base;
2-
3-
@custom-variant dark (&:is(.dark *));
42
@tailwind components;
53
@tailwind utilities;
64

7-
@layer base {
8-
:root {
9-
--background: 0 0% 100%;
10-
--foreground: 222.2 84% 4.9%;
11-
--card: 0 0% 100%;
12-
--card-foreground: 222.2 84% 4.9%;
13-
--popover: 0 0% 100%;
14-
--popover-foreground: 222.2 84% 4.9%;
15-
--primary: 222.2 47.4% 11.2%;
16-
--primary-foreground: 210 40% 98%;
17-
--secondary: 210 40% 96%;
18-
--secondary-foreground: 222.2 47.4% 11.2%;
19-
--muted: 210 40% 96%;
20-
--muted-foreground: 215.4 16.3% 46.9%;
21-
--accent: 210 40% 96%;
22-
--accent-foreground: 222.2 47.4% 11.2%;
23-
--destructive: 0 84.2% 60.2%;
24-
--destructive-foreground: 210 40% 98%;
25-
--border: 214.3 31.8% 91.4%;
26-
--input: 214.3 31.8% 91.4%;
27-
--ring: 222.2 84% 4.9%;
28-
--radius: 0.5rem;
29-
--chart-1: 12 76% 61%;
30-
--chart-2: 173 58% 39%;
31-
--chart-3: 197 37% 24%;
32-
--chart-4: 43 74% 66%;
33-
--chart-5: 27 87% 67%;
34-
}
35-
36-
.dark {
37-
--background: 222.2 84% 4.9%;
38-
--foreground: 210 40% 98%;
39-
--card: 222.2 84% 4.9%;
40-
--card-foreground: 210 40% 98%;
41-
--popover: 222.2 84% 4.9%;
42-
--popover-foreground: 210 40% 98%;
43-
--primary: 210 40% 98%;
44-
--primary-foreground: 222.2 47.4% 11.2%;
45-
--secondary: 217.2 32.6% 17.5%;
46-
--secondary-foreground: 210 40% 98%;
47-
--muted: 217.2 32.6% 17.5%;
48-
--muted-foreground: 215 20.2% 65.1%;
49-
--accent: 217.2 32.6% 17.5%;
50-
--accent-foreground: 210 40% 98%;
51-
--destructive: 0 62.8% 30.6%;
52-
--destructive-foreground: 210 40% 98%;
53-
--border: 217.2 32.6% 17.5%;
54-
--input: 217.2 32.6% 17.5%;
55-
--ring: 212.7 26.8% 83.9%;
56-
--chart-1: 220 70% 50%;
57-
--chart-2: 160 60% 45%;
58-
--chart-3: 30 80% 55%;
59-
--chart-4: 280 65% 60%;
60-
--chart-5: 340 75% 55%;
61-
}
62-
}
63-
645
@layer base {
656
* {
667
@apply border-border;
678
}
689
body {
6910
@apply bg-background text-foreground;
11+
font-feature-settings: "rlig" 1, "calt" 1;
7012
}
7113
}
7214

73-
@theme inline {
74-
--radius-sm: calc(var(--radius) - 4px);
75-
--radius-md: calc(var(--radius) - 2px);
76-
--radius-lg: var(--radius);
77-
--radius-xl: calc(var(--radius) + 4px);
78-
--color-background: var(--background);
79-
--color-foreground: var(--foreground);
80-
--color-card: var(--card);
81-
--color-card-foreground: var(--card-foreground);
82-
--color-popover: var(--popover);
83-
--color-popover-foreground: var(--popover-foreground);
84-
--color-primary: var(--primary);
85-
--color-primary-foreground: var(--primary-foreground);
86-
--color-secondary: var(--secondary);
87-
--color-secondary-foreground: var(--secondary-foreground);
88-
--color-muted: var(--muted);
89-
--color-muted-foreground: var(--muted-foreground);
90-
--color-accent: var(--accent);
91-
--color-accent-foreground: var(--accent-foreground);
92-
--color-destructive: var(--destructive);
93-
--color-border: var(--border);
94-
--color-input: var(--input);
95-
--color-ring: var(--ring);
96-
--color-chart-1: var(--chart-1);
97-
--color-chart-2: var(--chart-2);
98-
--color-chart-3: var(--chart-3);
99-
--color-chart-4: var(--chart-4);
100-
--color-chart-5: var(--chart-5);
101-
--color-sidebar: var(--sidebar);
102-
--color-sidebar-foreground: var(--sidebar-foreground);
103-
--color-sidebar-primary: var(--sidebar-primary);
104-
--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
105-
--color-sidebar-accent: var(--sidebar-accent);
106-
--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
107-
--color-sidebar-border: var(--sidebar-border);
108-
--color-sidebar-ring: var(--sidebar-ring);
109-
}
15+
11016

11117
:root {
11218
--radius: 0.625rem;

frontend-nextjs/src/app/layout.tsx

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import type { Metadata } from "next";
1+
import type { Metadata, Viewport } from "next";
22
import { Geist, Geist_Mono } from "next/font/google";
33
import "./globals.css";
44
import { Providers } from "@/lib/providers";
@@ -17,17 +17,28 @@ const geistMono = Geist_Mono({
1717
export const metadata: Metadata = {
1818
title: "FastAPI Full Stack Template",
1919
description: "A modern full-stack application with FastAPI backend and Next.js frontend",
20+
icons: {
21+
icon: "/favicon.ico",
22+
},
2023
};
2124

25+
export const viewport: Viewport = {
26+
themeColor: [
27+
{ media: '(prefers-color-scheme: light)', color: 'white' },
28+
{ media: '(prefers-color-scheme: dark)', color: 'black' },
29+
],
30+
}
31+
2232
export default function RootLayout({
2333
children,
2434
}: Readonly<{
2535
children: React.ReactNode;
2636
}>) {
2737
return (
28-
<html lang="en" suppressHydrationWarning>
38+
<html lang="en" suppressHydrationWarning className="h-full">
2939
<body
30-
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
40+
className={`${geistSans.variable} ${geistMono.variable} font-sans antialiased min-h-screen bg-background text-foreground`}
41+
suppressHydrationWarning
3142
>
3243
<Providers>
3344
{children}

frontend-nextjs/src/app/page.tsx

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AppLayout } from "@/components/layout/AppLayout"
22
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
3-
import { FiBox, FiUsers, FiSearch, FiActivity } from "react-icons/fi"
3+
import { Package, Users, Search, Activity } from "lucide-react"
44

55
export default function Home() {
66
return (
@@ -19,7 +19,7 @@ export default function Home() {
1919
<CardTitle className="text-sm font-medium">
2020
Items
2121
</CardTitle>
22-
<FiBox className="h-4 w-4 text-muted-foreground" />
22+
<Package className="h-4 w-4 text-muted-foreground" />
2323
</CardHeader>
2424
<CardContent>
2525
<div className="text-2xl font-bold">--</div>
@@ -34,7 +34,7 @@ export default function Home() {
3434
<CardTitle className="text-sm font-medium">
3535
Users
3636
</CardTitle>
37-
<FiUsers className="h-4 w-4 text-muted-foreground" />
37+
<Users className="h-4 w-4 text-muted-foreground" />
3838
</CardHeader>
3939
<CardContent>
4040
<div className="text-2xl font-bold">--</div>
@@ -49,7 +49,7 @@ export default function Home() {
4949
<CardTitle className="text-sm font-medium">
5050
ColPali Search
5151
</CardTitle>
52-
<FiSearch className="h-4 w-4 text-muted-foreground" />
52+
<Search className="h-4 w-4 text-muted-foreground" />
5353
</CardHeader>
5454
<CardContent>
5555
<div className="text-2xl font-bold">Active</div>
@@ -64,7 +64,7 @@ export default function Home() {
6464
<CardTitle className="text-sm font-medium">
6565
System Status
6666
</CardTitle>
67-
<FiActivity className="h-4 w-4 text-muted-foreground" />
67+
<Activity className="h-4 w-4 text-muted-foreground" />
6868
</CardHeader>
6969
<CardContent>
7070
<div className="text-2xl font-bold">Online</div>

frontend-nextjs/src/components/layout/AppLayout.tsx

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,35 +3,62 @@
33
import { useAuth } from "@/hooks/useAuth"
44
import { Sidebar } from "./Sidebar"
55
import { useRouter } from "next/navigation"
6-
import { useEffect } from "react"
6+
import { useEffect, useState } from "react"
7+
import { Loader2 } from "lucide-react"
78

89
interface AppLayoutProps {
910
children: React.ReactNode
1011
}
1112

1213
export function AppLayout({ children }: AppLayoutProps) {
14+
const [mounted, setMounted] = useState(false)
1315
const { isAuthenticated, isLoading } = useAuth()
1416
const router = useRouter()
1517

1618
useEffect(() => {
17-
if (!isLoading && !isAuthenticated) {
19+
setMounted(true)
20+
}, [])
21+
22+
useEffect(() => {
23+
if (mounted && !isLoading && !isAuthenticated) {
1824
router.push("/login")
1925
}
20-
}, [isAuthenticated, isLoading, router])
26+
}, [mounted, isAuthenticated, isLoading, router])
2127

28+
// Don't render anything on server or before hydration
29+
if (!mounted) {
30+
return (
31+
<div className="flex h-screen items-center justify-center bg-background">
32+
<div className="text-center">
33+
<Loader2 className="h-8 w-8 animate-spin mx-auto mb-4 text-primary" />
34+
<p className="text-sm text-muted-foreground">Loading...</p>
35+
</div>
36+
</div>
37+
)
38+
}
39+
40+
// Show loading state during authentication check
2241
if (isLoading) {
2342
return (
24-
<div className="flex h-screen items-center justify-center">
43+
<div className="flex h-screen items-center justify-center bg-background">
2544
<div className="text-center">
26-
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-primary mx-auto mb-4"></div>
27-
<p className="text-muted-foreground">Loading...</p>
45+
<Loader2 className="h-8 w-8 animate-spin mx-auto mb-4 text-primary" />
46+
<p className="text-sm text-muted-foreground">Loading...</p>
2847
</div>
2948
</div>
3049
)
3150
}
3251

52+
// Show loading state if not authenticated (while redirect is happening)
3353
if (!isAuthenticated) {
34-
return null // Will redirect to login
54+
return (
55+
<div className="flex h-screen items-center justify-center bg-background">
56+
<div className="text-center">
57+
<Loader2 className="h-8 w-8 animate-spin mx-auto mb-4 text-primary" />
58+
<p className="text-sm text-muted-foreground">Redirecting...</p>
59+
</div>
60+
</div>
61+
)
3562
}
3663

3764
return (
@@ -44,4 +71,4 @@ export function AppLayout({ children }: AppLayoutProps) {
4471
</main>
4572
</div>
4673
)
47-
}
74+
}

frontend-nextjs/src/components/layout/Sidebar.tsx

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,21 @@ import { usePathname } from "next/navigation"
55
import { cn } from "@/lib/utils"
66
import { Button } from "@/components/ui/button"
77
import {
8-
FiHome,
9-
FiBox,
10-
FiUsers,
11-
FiSettings,
12-
FiSearch,
13-
FiLogOut
14-
} from "react-icons/fi"
8+
Home,
9+
Package,
10+
Users,
11+
Settings,
12+
Search,
13+
LogOut
14+
} from "lucide-react"
1515
import { useAuth } from "@/hooks/useAuth"
1616

1717
const navigation = [
18-
{ name: "Dashboard", href: "/", icon: FiHome },
19-
{ name: "Items", href: "/items", icon: FiBox },
20-
{ name: "ColPali Search", href: "/colpali", icon: FiSearch },
21-
{ name: "Admin", href: "/admin", icon: FiUsers, adminOnly: true },
22-
{ name: "Settings", href: "/settings", icon: FiSettings },
18+
{ name: "Dashboard", href: "/", icon: Home },
19+
{ name: "Items", href: "/items", icon: Package },
20+
{ name: "ColPali Search", href: "/colpali", icon: Search },
21+
{ name: "Admin", href: "/admin", icon: Users, adminOnly: true },
22+
{ name: "Settings", href: "/settings", icon: Settings },
2323
]
2424

2525
export function Sidebar() {
@@ -79,7 +79,7 @@ export function Sidebar() {
7979
onClick={logout}
8080
className="h-8 w-8 p-0"
8181
>
82-
<FiLogOut className="h-4 w-4" />
82+
<LogOut className="h-4 w-4" />
8383
</Button>
8484
</div>
8585
</div>

0 commit comments

Comments
 (0)