feat: Dark Mode + Zertifikat-Verwaltung im Superadmin

- Dark Mode: ThemeProvider (next-themes), ThemeToggle in Navbar (Hell/Dunkel/System)
- Zertifikat-Tab (superadmin only): aktuelles Zertifikat anzeigen, Upload (cert+key),
  Self-Signed ausstellen, Let's Encrypt/ACME
- Backend: /api/admin/cert/* Endpunkte (info, upload, self-signed, acme), nginx reload
- HTTPS bereits live auf Server (self-signed RSA-4096, Port 443)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
sysops
2026-03-20 00:14:43 +01:00
parent 0e62b10bd4
commit 9e71af104f
8 changed files with 708 additions and 3 deletions
+5 -1
View File
@@ -2,6 +2,7 @@
import Link from "next/link";
import { UserNav } from "@/components/UserNav";
import { ThemeToggle } from "@/components/theme-toggle";
interface NavbarProps {
username: string;
@@ -49,7 +50,10 @@ export function Navbar({ username, role }: NavbarProps) {
</Link>
)}
</div>
<UserNav username={username} role={role} />
<div className="flex items-center gap-2">
<ThemeToggle />
<UserNav username={username} role={role} />
</div>
</div>
</nav>
);
+16
View File
@@ -0,0 +1,16 @@
"use client"
import { ThemeProvider as NextThemesProvider } from "next-themes"
export function ThemeProvider({ children }: { children: React.ReactNode }) {
return (
<NextThemesProvider
attribute="class"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>
{children}
</NextThemesProvider>
)
}
+38
View File
@@ -0,0 +1,38 @@
"use client"
import { useTheme } from "next-themes"
import { Moon, Sun } from "lucide-react"
import { Button } from "@/components/ui/button"
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu"
export function ThemeToggle() {
const { setTheme } = useTheme()
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" size="icon" aria-label="Design wechseln">
<Sun className="h-4 w-4 rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
<Moon className="absolute h-4 w-4 rotate-90 scale-0 transition-all dark:rotate-0 dark:scale-100" />
<span className="sr-only">Design wechseln</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem onClick={() => setTheme("light")}>
Hell
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("dark")}>
Dunkel
</DropdownMenuItem>
<DropdownMenuItem onClick={() => setTheme("system")}>
System
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}