Basic MUI implemented
This commit is contained in:
parent
5e10df93b1
commit
390620a749
16 changed files with 922 additions and 263 deletions
14
errors.txt
Normal file
14
errors.txt
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
|
||||||
|
> 124_webcalibre2@0.1.0 dev
|
||||||
|
> next dev
|
||||||
|
|
||||||
|
▲ Next.js 16.1.1 (Turbopack)
|
||||||
|
- Local: http://localhost:3000
|
||||||
|
- Network: http://192.168.1.100:3000
|
||||||
|
- Environments: .env.local, .env
|
||||||
|
|
||||||
|
✓ Starting...
|
||||||
|
✓ Ready in 810ms
|
||||||
|
GET / 500 in 7.5s (compile: 127ms, proxy.ts: 128ms, render: 7.3s)
|
||||||
|
GET / 500 in 6.9s (compile: 1654µs, proxy.ts: 2ms, render: 6.9s)
|
||||||
|
[?25h
|
||||||
672
package-lock.json
generated
672
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -9,6 +9,12 @@
|
||||||
"lint": "eslint"
|
"lint": "eslint"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@emotion/cache": "^11.14.0",
|
||||||
|
"@emotion/react": "^11.14.0",
|
||||||
|
"@emotion/styled": "^11.14.1",
|
||||||
|
"@mui/icons-material": "^7.3.6",
|
||||||
|
"@mui/material": "^7.3.6",
|
||||||
|
"@mui/material-nextjs": "^7.3.6",
|
||||||
"next": "16.1.1",
|
"next": "16.1.1",
|
||||||
"next-auth": "^5.0.0-beta.30",
|
"next-auth": "^5.0.0-beta.30",
|
||||||
"react": "19.2.3",
|
"react": "19.2.3",
|
||||||
|
|
|
||||||
|
|
@ -1,41 +1,32 @@
|
||||||
import type { Metadata } from "next";
|
import InitColorSchemeScript from '@mui/material/InitColorSchemeScript';
|
||||||
import { Geist, Geist_Mono } from "next/font/google";
|
import ThemeRegistry from '@/components/ThemeRegistry';
|
||||||
|
import Navbar from '@/components/layout/Navbar';
|
||||||
|
import { auth } from "@/auth";
|
||||||
import "./globals.css";
|
import "./globals.css";
|
||||||
import Navbar from "@/components/navbar";
|
|
||||||
import { auth } from "@/auth";
|
|
||||||
import { SessionProvider } from "next-auth/react";
|
|
||||||
|
|
||||||
const geistSans = Geist({
|
export const metadata = {
|
||||||
variable: "--font-geist-sans",
|
title: "WebCalibre 2",
|
||||||
subsets: ["latin"],
|
|
||||||
});
|
|
||||||
|
|
||||||
const geistMono = Geist_Mono({
|
|
||||||
variable: "--font-geist-mono",
|
|
||||||
subsets: ["latin"],
|
|
||||||
});
|
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
|
||||||
title: "Create Next App",
|
|
||||||
description: "Generated by create next app",
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function RootLayout({
|
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
||||||
children,
|
// Fetch the session server-side to prevent UI flickering
|
||||||
}: Readonly<{
|
|
||||||
children: React.ReactNode;
|
|
||||||
}>) {
|
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SessionProvider session={session}>
|
<html lang="en" suppressHydrationWarning>
|
||||||
<html lang="en">
|
<body>
|
||||||
<body
|
<InitColorSchemeScript attribute="class" />
|
||||||
className={`${geistSans.variable} ${geistMono.variable} antialiased`}
|
|
||||||
>
|
{/* ThemeRegistry handles the MUI Theme and Cache Provider */}
|
||||||
<Navbar />
|
<ThemeRegistry>
|
||||||
<main className="p-5">{children}</main>
|
{/* We pass the user object to the Navbar so it can show the profile pic */}
|
||||||
</body>
|
<Navbar user={session?.user} />
|
||||||
</html>
|
|
||||||
</SessionProvider>
|
<main style={{ minHeight: '100vh' }}>
|
||||||
|
{children}
|
||||||
|
</main>
|
||||||
|
</ThemeRegistry>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,20 +0,0 @@
|
||||||
import LoginButton from "@/components/LoginButton";
|
|
||||||
|
|
||||||
function Login(){
|
|
||||||
return (
|
|
||||||
<div className="flex items-center justify-center min-h-[90vh]">
|
|
||||||
<div className="p-5 border rounded-md max-w-[50%] flex gap-5 flex-col">
|
|
||||||
<h1 className="text-center font-semibold text-xl">Login</h1>
|
|
||||||
<p>
|
|
||||||
Lorem ipsum dolor sit amet consectetur adipisicing elit. Accusamus
|
|
||||||
reprehenderit aut vitae consequuntur deleniti itaque totam eaque
|
|
||||||
perspiciatis nulla magnam officiis blanditiis enim fuga perferendis
|
|
||||||
deserunt, praesentium vel. Omnis, dolorum?
|
|
||||||
</p>
|
|
||||||
<LoginButton />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Login
|
|
||||||
|
|
@ -1,66 +1,15 @@
|
||||||
import Image from "next/image";
|
// src/app/page.tsx
|
||||||
import styles from "./page.module.css";
|
import { Container, Typography, Button, Box } from '@mui/material';
|
||||||
|
|
||||||
export default function Home() {
|
export default function Home() {
|
||||||
return (
|
return (
|
||||||
<div className={styles.page}>
|
<Container maxWidth="md">
|
||||||
<main className={styles.main}>
|
<Box sx={{ my: 4, textAlign: 'center' }}>
|
||||||
<Image
|
<Typography variant="h2" component="h1" gutterBottom>
|
||||||
className={styles.logo}
|
WebCalibre 2
|
||||||
src="/next.svg"
|
</Typography>
|
||||||
alt="Next.js logo"
|
<Button variant="contained">Explore Library</Button>
|
||||||
width={100}
|
</Box>
|
||||||
height={20}
|
</Container>
|
||||||
priority
|
|
||||||
/>
|
|
||||||
<div className={styles.intro}>
|
|
||||||
<h1>To get started, edit the page.tsx file.</h1>
|
|
||||||
<p>
|
|
||||||
Looking for a starting point or more instructions? Head over to{" "}
|
|
||||||
<a
|
|
||||||
href="https://vercel.com/templates?framework=next.js&utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Templates
|
|
||||||
</a>{" "}
|
|
||||||
or the{" "}
|
|
||||||
<a
|
|
||||||
href="https://nextjs.org/learn?utm_source=create-next-app&utm_medium=appdir-template-tw&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Learning
|
|
||||||
</a>{" "}
|
|
||||||
center.
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
<div className={styles.ctas}>
|
|
||||||
<a
|
|
||||||
className={styles.primary}
|
|
||||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
<Image
|
|
||||||
className={styles.logo}
|
|
||||||
src="/vercel.svg"
|
|
||||||
alt="Vercel logomark"
|
|
||||||
width={16}
|
|
||||||
height={16}
|
|
||||||
/>
|
|
||||||
Deploy Now
|
|
||||||
</a>
|
|
||||||
<a
|
|
||||||
className={styles.secondary}
|
|
||||||
href="https://nextjs.org/docs?utm_source=create-next-app&utm_medium=appdir-template&utm_campaign=create-next-app"
|
|
||||||
target="_blank"
|
|
||||||
rel="noopener noreferrer"
|
|
||||||
>
|
|
||||||
Documentation
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
</main>
|
|
||||||
</div>
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
@ -1,24 +0,0 @@
|
||||||
"use client";
|
|
||||||
import { signIn } from "next-auth/react";
|
|
||||||
import { useState } from "react";
|
|
||||||
|
|
||||||
function LoginButton() {
|
|
||||||
const [isLoading, setIsLoading] = useState<boolean>(false);
|
|
||||||
|
|
||||||
const handleLogin = async () => {
|
|
||||||
setIsLoading(true);
|
|
||||||
await signIn("microsoft-entra-id");
|
|
||||||
setIsLoading(false);
|
|
||||||
};
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={() => handleLogin()}
|
|
||||||
className="w-full bg-blue-800 text-white my-5 p-3 rounded-md hover:opacity-80 disabled:opacity-75"
|
|
||||||
disabled={isLoading}
|
|
||||||
>
|
|
||||||
{isLoading ? "Signing in..." : "Login With Microsoft"}
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LoginButton;
|
|
||||||
|
|
@ -1,15 +0,0 @@
|
||||||
"use client";
|
|
||||||
import { signOut } from "next-auth/react";
|
|
||||||
|
|
||||||
function LogoutButton() {
|
|
||||||
return (
|
|
||||||
<button
|
|
||||||
onClick={() => signOut()}
|
|
||||||
className="w-full bg-blue-800 text-white p-3 rounded-md hover:opacity-80"
|
|
||||||
>
|
|
||||||
Logout
|
|
||||||
</button>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default LogoutButton;
|
|
||||||
17
src/components/ThemeRegistry.tsx
Normal file
17
src/components/ThemeRegistry.tsx
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import { AppRouterCacheProvider } from '@mui/material-nextjs/v15-appRouter';
|
||||||
|
import { ThemeProvider } from '@mui/material/styles';
|
||||||
|
import CssBaseline from '@mui/material/CssBaseline';
|
||||||
|
import theme from '@/lib/theme';
|
||||||
|
|
||||||
|
export default function ThemeRegistry({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<AppRouterCacheProvider options={{ enableCssLayer: true }}>
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
|
<CssBaseline />
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
</AppRouterCacheProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
22
src/components/layout/AppShell.tsx
Normal file
22
src/components/layout/AppShell.tsx
Normal file
|
|
@ -0,0 +1,22 @@
|
||||||
|
// src/components/layout/AppShell.tsx
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { AppBar, Box, Toolbar } from '@mui/material';
|
||||||
|
import Navbar from './Navbar';
|
||||||
|
|
||||||
|
const AppShell = ({ children }: { children: React.ReactNode }) => {
|
||||||
|
return (
|
||||||
|
<Box sx={{ display: 'flex', flexDirection: 'column', minHeight: '100vh' }}>
|
||||||
|
<AppBar position="fixed">
|
||||||
|
<Toolbar>
|
||||||
|
<Navbar />
|
||||||
|
</Toolbar>
|
||||||
|
</AppBar>
|
||||||
|
|
||||||
|
<Box component="main" sx={{ flexGrow: 1, p: 3, mt: 8 }}>
|
||||||
|
{children} {/* This replaces <Outlet /> */}
|
||||||
|
</Box>
|
||||||
|
</Box>
|
||||||
|
);
|
||||||
|
};
|
||||||
87
src/components/layout/Navbar.tsx
Normal file
87
src/components/layout/Navbar.tsx
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
'use client';
|
||||||
|
|
||||||
|
import React from 'react';
|
||||||
|
import { AppBar, Toolbar, Typography, Button, Box, Container, Avatar, IconButton } from '@mui/material';
|
||||||
|
import LibraryBooksIcon from '@mui/icons-material/LibraryBooks';
|
||||||
|
import MenuIcon from '@mui/icons-material/Menu';
|
||||||
|
import Link from 'next/link';
|
||||||
|
import { signIn, signOut } from "next-auth/react";
|
||||||
|
|
||||||
|
interface NavbarProps {
|
||||||
|
user?: {
|
||||||
|
name?: string | null;
|
||||||
|
email?: string | null;
|
||||||
|
image?: string | null;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Navbar({ user }: NavbarProps) {
|
||||||
|
const isLoggedIn = !!user;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<AppBar position="sticky" elevation={0} sx={{ backgroundColor: 'white', color: 'text.primary', borderBottom: '1px solid #e0e0e0' }}>
|
||||||
|
<Container maxWidth="lg">
|
||||||
|
<Toolbar disableGutters>
|
||||||
|
{/* Menu Icon for the future AppDrawer */}
|
||||||
|
<IconButton edge="start" color="inherit" aria-label="menu" sx={{ mr: 2 }}>
|
||||||
|
<MenuIcon />
|
||||||
|
</IconButton>
|
||||||
|
|
||||||
|
<Typography
|
||||||
|
variant="h6"
|
||||||
|
component={Link}
|
||||||
|
href="/"
|
||||||
|
sx={{ fontWeight: 700, color: 'primary.main', textDecoration: 'none', flexGrow: 1 }}
|
||||||
|
>
|
||||||
|
WebCalibre 2
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Box sx={{ display: 'flex', alignItems: 'center', gap: 2 }}>
|
||||||
|
{isLoggedIn ? (
|
||||||
|
<>
|
||||||
|
{/* User Info: Name and Small Email */}
|
||||||
|
<Box sx={{ textAlign: 'right', display: { xs: 'none', sm: 'block' } }}>
|
||||||
|
<Typography variant="body2" fontWeight={600} sx={{ lineHeight: 1.2 }}>
|
||||||
|
{user.name}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="caption" sx={{ color: 'text.secondary', display: 'block' }}>
|
||||||
|
{user.email}
|
||||||
|
</Typography>
|
||||||
|
</Box>
|
||||||
|
|
||||||
|
{/* Profile Picture */}
|
||||||
|
<Avatar
|
||||||
|
src={user.image || ""}
|
||||||
|
sx={{ width: 38, height: 38, border: '1px solid #eee' }}
|
||||||
|
>
|
||||||
|
{user.name?.charAt(0)}
|
||||||
|
</Avatar>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="outlined"
|
||||||
|
color="inherit"
|
||||||
|
size="small"
|
||||||
|
// We are still calling the function, just giving it a destination!
|
||||||
|
onClick={() => signOut({ callbackUrl: '/' })}
|
||||||
|
sx={{ borderRadius: '8px', textTransform: 'none' }}
|
||||||
|
>
|
||||||
|
Logout
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
// Login button triggers the Microsoft flow directly
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
disableElevation
|
||||||
|
onClick={() => signIn("microsoft-entra-id", { callbackUrl: "/dashboard" })}
|
||||||
|
sx={{ borderRadius: '8px', textTransform: 'none', px: 3 }}
|
||||||
|
>
|
||||||
|
Login
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</Box>
|
||||||
|
</Toolbar>
|
||||||
|
</Container>
|
||||||
|
</AppBar>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -1,33 +0,0 @@
|
||||||
import { auth } from "@/auth";
|
|
||||||
import Link from "next/link";
|
|
||||||
|
|
||||||
async function Navbar() {
|
|
||||||
const session = await auth();
|
|
||||||
return (
|
|
||||||
<nav className="flex items-center justify-between gap-4 px-5 py-2">
|
|
||||||
<div>
|
|
||||||
<Link href={"/"} className="font-bold">
|
|
||||||
AppLogo
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
{session?.user?.email ? (
|
|
||||||
<ul className="flex items-center gap-4 flex-row">
|
|
||||||
<li>
|
|
||||||
<Link href={"/dashboard"}>Dashboard</Link>
|
|
||||||
</li>
|
|
||||||
<li>
|
|
||||||
<Link href={"/profile"}>Profile</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
) : (
|
|
||||||
<ul className="flex items-center gap-4 flex-row">
|
|
||||||
<li>
|
|
||||||
<Link href={"/login"}>Login</Link>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
)}
|
|
||||||
</nav>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
export default Navbar;
|
|
||||||
15
src/components/providers.tsx
Normal file
15
src/components/providers.tsx
Normal file
|
|
@ -0,0 +1,15 @@
|
||||||
|
'use client';
|
||||||
|
import { SessionProvider } from "next-auth/react";
|
||||||
|
// ... other imports like ThemeProvider
|
||||||
|
|
||||||
|
export function Providers({ children }: { children: React.ReactNode }) {
|
||||||
|
return (
|
||||||
|
<SessionProvider>
|
||||||
|
<AppRouterCacheProvider>
|
||||||
|
<ThemeProvider theme={theme}>
|
||||||
|
{children}
|
||||||
|
</ThemeProvider>
|
||||||
|
</AppRouterCacheProvider>
|
||||||
|
</SessionProvider>
|
||||||
|
);
|
||||||
|
}
|
||||||
58
src/lib/theme.ts
Normal file
58
src/lib/theme.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
||||||
|
import { createTheme } from '@mui/material/styles';
|
||||||
|
import { Roboto } from 'next/font/google';
|
||||||
|
|
||||||
|
// Load the font optimized for Next.js
|
||||||
|
const roboto = Roboto({
|
||||||
|
weight: ['300', '400', '500', '700'],
|
||||||
|
subsets: ['latin'],
|
||||||
|
display: 'swap',
|
||||||
|
});
|
||||||
|
|
||||||
|
const theme = createTheme({
|
||||||
|
// 1. Color Palette (Clean & Professional for a Library App)
|
||||||
|
palette: {
|
||||||
|
mode: 'light',
|
||||||
|
primary: {
|
||||||
|
main: '#1976d2', // Professional Blue
|
||||||
|
},
|
||||||
|
secondary: {
|
||||||
|
main: '#9c27b0', // Purple for accents
|
||||||
|
},
|
||||||
|
background: {
|
||||||
|
default: '#f4f6f8', // Light grey for the app background
|
||||||
|
paper: '#ffffff',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// 2. Typography
|
||||||
|
typography: {
|
||||||
|
fontFamily: roboto.style.fontFamily,
|
||||||
|
h6: {
|
||||||
|
fontWeight: 600,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
// 3. Component Defaults
|
||||||
|
components: {
|
||||||
|
MuiButton: {
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
textTransform: 'none', // Prevents all-caps buttons
|
||||||
|
borderRadius: 8,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
MuiPaper: {
|
||||||
|
defaultProps: {
|
||||||
|
elevation: 2,
|
||||||
|
},
|
||||||
|
styleOverrides: {
|
||||||
|
root: {
|
||||||
|
borderRadius: 12,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export default theme;
|
||||||
|
|
@ -1,40 +0,0 @@
|
||||||
import { NextResponse } from "next/server";
|
|
||||||
import {auth } from "@/auth";
|
|
||||||
//export { auth as middleware } from "@/auth";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const protectedRoutes=["/dashboard","/profile"]
|
|
||||||
const authPageRoutes=["/login"]
|
|
||||||
const apiAuthPrefix ="/api/auth"
|
|
||||||
|
|
||||||
export default auth( (req) =>{
|
|
||||||
const { nextUrl } = req;
|
|
||||||
//const authData = await req.auth;
|
|
||||||
const isLoggedIn = !!req.auth;
|
|
||||||
|
|
||||||
const path = nextUrl.pathname
|
|
||||||
const isApiAuthRoute = nextUrl.pathname.startsWith(apiAuthPrefix);
|
|
||||||
const isProtectedRoute = protectedRoutes.includes(path);
|
|
||||||
const isAuthPageRoute = authPageRoutes.includes(path);
|
|
||||||
|
|
||||||
// console.log({ authData });
|
|
||||||
|
|
||||||
if(isApiAuthRoute){
|
|
||||||
return NextResponse.next();
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isProtectedRoute && !isLoggedIn){
|
|
||||||
return NextResponse.redirect(new URL("/login", req.nextUrl));
|
|
||||||
}
|
|
||||||
|
|
||||||
if(isLoggedIn && isAuthPageRoute){
|
|
||||||
return NextResponse.redirect(new URL("/dashboard",req.nextUrl))
|
|
||||||
}
|
|
||||||
|
|
||||||
return NextResponse.next();
|
|
||||||
})
|
|
||||||
|
|
||||||
export const config = {
|
|
||||||
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
|
||||||
}
|
|
||||||
30
src/proxy.ts
Normal file
30
src/proxy.ts
Normal file
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import { auth } from "@/auth";
|
||||||
|
|
||||||
|
const protectedRoutes = ["/dashboard", "/profile"];
|
||||||
|
const apiAuthPrefix = "/api/auth";
|
||||||
|
|
||||||
|
export const proxy = auth((req) => {
|
||||||
|
const { nextUrl } = req;
|
||||||
|
const isLoggedIn = !!req.auth;
|
||||||
|
|
||||||
|
const path = nextUrl.pathname;
|
||||||
|
const isApiAuthRoute = path.startsWith(apiAuthPrefix);
|
||||||
|
const isProtectedRoute = protectedRoutes.includes(path);
|
||||||
|
|
||||||
|
// 1. Allow API Auth calls (Login/Logout/Callback)
|
||||||
|
if (isApiAuthRoute) {
|
||||||
|
return NextResponse.next();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2. CHANGED: Redirect to HOME (/) instead of /login if logged out
|
||||||
|
if (isProtectedRoute && !isLoggedIn) {
|
||||||
|
return NextResponse.redirect(new URL("/", nextUrl));
|
||||||
|
}
|
||||||
|
|
||||||
|
return NextResponse.next();
|
||||||
|
});
|
||||||
|
|
||||||
|
export const config = {
|
||||||
|
matcher: ["/((?!api|_next/static|_next/image|favicon.ico).*)"],
|
||||||
|
};
|
||||||
Loading…
Reference in a new issue