This commit is contained in:
headlesdev 2025-05-17 19:33:52 +02:00
parent 9b4d2e9735
commit ec8c5ffc03
5 changed files with 201 additions and 4 deletions

View File

@ -30,7 +30,7 @@ export async function POST(request: NextRequest) {
return NextResponse.json({ error: "User not found" }, { status: 404 });
}
return NextResponse.json({ message: "Valid" }, { status: 200 });
return NextResponse.json({ message: "Valid", username: user.username, name: user.name }, { status: 200 });
} catch (error: any) {
return NextResponse.json({ error: "Internal Server Error" }, { status: 500 });

View File

@ -1,9 +1,20 @@
"use client";
import Sidebar from "@/components/Sidebar";
export default function DashboardPage() {
interface DashboardPageProps {
username: string;
name: string;
}
export default function DashboardPage({ username, name }: DashboardPageProps) {
return (
<div>
<h1>Dashboard</h1>
<Sidebar username={username} fullName={name}>
<main>
<h1>Dashboard</h1>
</main>
</Sidebar>
</div>
);
}

View File

@ -11,6 +11,8 @@ import Cookies from "js-cookie";
export default function Dashboard() {
const router = useRouter();
const [loading, setLoading] = useState(true);
const [username, setUsername] = useState("");
const [name, setName] = useState("");
useEffect(() => {
const init = async () => {
@ -23,6 +25,8 @@ export default function Dashboard() {
Cookies.remove("token");
router.push("/");
} else {
setUsername(response.data.username);
setName(response.data.name);
setLoading(false);
}
};
@ -32,6 +36,6 @@ export default function Dashboard() {
if (loading) {
return <Loading />;
} else {
return <DashboardPage />;
return <DashboardPage username={username} name={name} />;
}
}

182
components/Sidebar.tsx Normal file
View File

@ -0,0 +1,182 @@
"use client"
import type React from "react"
import { useState } from "react"
import Link from "next/link"
import {
Home,
Globe,
Server,
Layout,
Clock,
PenToolIcon as Tool,
Settings,
LogOut,
ChevronDown,
Menu,
} from "lucide-react"
import packageJson from "@/package.json"
import Image from "next/image"
import Cookies from "js-cookie"
import { useRouter } from "next/navigation"
interface SidebarProps {
children: React.ReactNode
username: string
fullName: string
}
export default function Sidebar({ children, username, fullName }: SidebarProps) {
const router = useRouter()
const [isProfileOpen, setIsProfileOpen] = useState(false)
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
const initials = useState(() => {
const nameToUse = fullName || username
return nameToUse
.split(" ")
.filter((part) => part.length > 0)
.map((part) => part[0])
.join("")
.toUpperCase()
.substring(0, 2)
})[0]
const logout = () => {
Cookies.remove("token")
router.push("/")
}
return (
<div className="flex flex-col md:flex-row min-h-screen w-full bg-base-100">
{/* Mobile menu button */}
<div className="md:hidden flex items-center p-4 bg-base-200 border-b">
<button onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)} className="btn btn-square btn-ghost">
<Menu size={24} />
</button>
<span className="ml-2 text-xl font-bold">CoreControl</span>
</div>
{/* Sidebar */}
<aside
className={`
bg-base-200 text-base-content md:flex md:flex-col
${isMobileMenuOpen ? "block" : "hidden"} md:block
w-full md:w-72 md:h-screen md:sticky md:top-0 md:left-0
relative
`}
>
{/* Top section with logo and title */}
<div className="hidden md:block">
<div className="p-4">
<div className="flex items-center gap-3">
<Image src="/logo.png" alt="Logo" width={60} height={60} className="rounded-lg" />
<div>
<h2 className="text-xl font-bold">CoreControl</h2>
<p className="text-sm opacity-70">v{packageJson.version}</p>
</div>
</div>
</div>
</div>
{/* Navigation menu - scrollable if needed */}
<div className="overflow-y-auto" style={{ height: "calc(100vh - 160px)" }}>
<ul className="menu menu-md p-4 w-full">
<li className="w-full">
<Link href="/dashboard" className="flex items-center w-full justify-start px-3 py-2">
<Home size={18} />
<span>Home</span>
</Link>
</li>
<li className="menu-title mt-4">
<span>Resources</span>
</li>
<li className="w-full">
<Link href="/dashboard/sites" className="flex items-center w-full justify-start px-3 py-2">
<Globe size={18} />
<span>Sites</span>
</Link>
</li>
<li className="w-full">
<Link href="/dashboard/servers" className="flex items-center w-full justify-start px-3 py-2">
<Server size={18} />
<span>Servers</span>
</Link>
</li>
<li className="w-full">
<Link href="/dashboard/applications" className="flex items-center w-full justify-start px-3 py-2">
<Layout size={18} />
<span>Applications</span>
</Link>
</li>
<li className="w-full">
<Link href="/dashboard/uptime" className="flex items-center w-full justify-start px-3 py-2">
<Clock size={18} />
<span>Uptime Pages</span>
</Link>
</li>
<li className="menu-title mt-4">
<span>System</span>
</li>
<li className="w-full">
<Link href="/dashboard/maintenance" className="flex items-center w-full justify-start px-3 py-2">
<Tool size={18} />
<span>Maintenance</span>
</Link>
</li>
<li className="w-full">
<Link href="/dashboard/settings" className="flex items-center w-full justify-start px-3 py-2">
<Settings size={18} />
<span>Settings</span>
</Link>
</li>
</ul>
</div>
{/* User profile fixed at bottom */}
<div className="p-4 border-t border-base-300 absolute bottom-0 left-0 w-full bg-base-200">
<div className="relative">
<button
onClick={() => setIsProfileOpen(!isProfileOpen)}
className="flex items-center gap-3 w-full p-2 rounded-lg hover:bg-base-300 transition-colors"
>
<div className="flex items-center justify-center w-10 h-10 rounded-full bg-primary text-primary-content">
<span className="inline-flex items-center justify-center" style={{ lineHeight: 1 }}>
{initials}
</span>
</div>
<div className="flex-1 text-left">
<p className="font-medium">{username}</p>
<p className="text-sm opacity-70">{fullName}</p>
</div>
<ChevronDown size={16} className={`transition-transform ${isProfileOpen ? "rotate-180" : ""}`} />
</button>
{isProfileOpen && (
<div className="absolute bottom-full mb-2 left-0 w-full bg-base-100 rounded-lg shadow-lg border border-base-300 overflow-hidden">
<ul className="menu p-0 w-full">
<li className="w-full">
<button
onClick={() => logout()}
className="flex items-center text-error w-full rounded-none px-4 py-3 justify-start"
>
<LogOut size={16} />
<span>Logout</span>
</button>
</li>
</ul>
</div>
)}
</div>
</div>
</aside>
{/* Main content */}
<main className="flex-1 p-4 md:p-6 overflow-auto">{children}</main>
</div>
)
}

BIN
public/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB