Settings Page & Bug Fixes

This commit is contained in:
headlessdev
2025-04-12 20:01:43 +02:00
parent cd8a801fff
commit 4a0681b162
10 changed files with 505 additions and 7 deletions

View File

@@ -32,6 +32,8 @@ export async function POST(request: NextRequest) {
}
});
const servers_all = await prisma.server.findMany()
const applicationsWithServers = applications.map(app => ({
...app,
server: servers.find(s => s.id === app.serverId)?.name || 'No server'
@@ -42,7 +44,7 @@ export async function POST(request: NextRequest) {
return NextResponse.json({
applications: applicationsWithServers,
servers,
servers: servers_all,
maxPage
});
} catch (error: any) {

View File

@@ -75,7 +75,7 @@ export default function Dashboard() {
const [loading, setLoading] = useState(true);
useEffect(() => {
const savedLayout = Cookies.get('layoutPreference');
const savedLayout = Cookies.get('layoutPreference-app');
const layout_bool = savedLayout === 'grid'
setIsGridLayout(layout_bool);
setItemsPerPage(layout_bool ? 15 : 5)
@@ -84,7 +84,7 @@ export default function Dashboard() {
const toggleLayout = () => {
const newLayout = !isGridLayout;
setIsGridLayout(newLayout);
Cookies.set('layoutPreference', newLayout ? 'grid' : 'standard', {
Cookies.set('layoutPreference-app', newLayout ? 'grid' : 'standard', {
expires: 365,
path: '/',
sameSite: 'strict'

View File

@@ -76,14 +76,14 @@ export default function Dashboard() {
const [loading, setLoading] = useState(true);
useEffect(() => {
const savedLayout = Cookies.get('layoutPreference');
const savedLayout = Cookies.get('layoutPreference-servers');
setIsGridLayout(savedLayout === 'grid');
}, []);
const toggleLayout = () => {
const newLayout = !isGridLayout;
setIsGridLayout(newLayout);
Cookies.set('layoutPreference', newLayout ? 'grid' : 'standard', {
Cookies.set('layoutPreference-servers', newLayout ? 'grid' : 'standard', {
expires: 365,
path: '/',
sameSite: 'strict'

View File

@@ -0,0 +1,78 @@
import { AppSidebar } from "@/components/app-sidebar"
import {
Breadcrumb,
BreadcrumbItem,
BreadcrumbList,
BreadcrumbPage,
BreadcrumbSeparator,
} from "@/components/ui/breadcrumb"
import { Separator } from "@/components/ui/separator"
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar"
import { Card, CardHeader } from "@/components/ui/card"
import { useTheme } from "next-themes"
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select"
export default function Settings() {
const { theme, setTheme } = useTheme()
return (
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem className="hidden md:block">
<BreadcrumbPage>/</BreadcrumbPage>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>Dashboard</BreadcrumbPage>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>Settings</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
<div className="pl-4 pr-4">
<span className="text-2xl font-semibold">Settings</span>
<div className="pt-4">
<Card className="w-full mb-4 relative">
<CardHeader>
<span className="text-xl font-bold">Theme</span>
<Select value={theme} onValueChange={setTheme}>
<SelectTrigger className="w-full [&_svg]:hidden">
<SelectValue>
{theme?.charAt(0).toUpperCase() + theme?.slice(1)}
</SelectValue>
</SelectTrigger>
<SelectContent>
<SelectItem value="light">Light</SelectItem>
<SelectItem value="dark">Dark</SelectItem>
<SelectItem value="system">System</SelectItem>
</SelectContent>
</Select>
</CardHeader>
</Card>
</div>
</div>
</SidebarInset>
</SidebarProvider>
)
}

View File

@@ -0,0 +1,59 @@
"use client";
import { useEffect, useState } from "react";
import Cookies from "js-cookie";
import { useRouter } from "next/navigation";
import Settings from "./Settings"
import axios from "axios";
export default function DashboardPage() {
const router = useRouter();
const [isAuthChecked, setIsAuthChecked] = useState(false);
const [isValid, setIsValid] = useState(false);
useEffect(() => {
const token = Cookies.get("token");
if (!token) {
router.push("/");
} else {
const checkToken = async () => {
try {
const response = await axios.post("/api/auth/validate", {
token: token,
});
if (response.status === 200) {
setIsValid(true);
}
} catch (error: any) {
Cookies.remove("token");
router.push("/");
}
}
checkToken();
}
setIsAuthChecked(true);
}, [router]);
if (!isAuthChecked) {
return (
<div className="flex items-center justify-center h-screen">
<div className='inline-block' role='status' aria-label='loading'>
<svg className='w-6 h-6 stroke-white animate-spin ' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
<g clip-path='url(#clip0_9023_61563)'>
<path d='M14.6437 2.05426C11.9803 1.2966 9.01686 1.64245 6.50315 3.25548C1.85499 6.23817 0.504864 12.4242 3.48756 17.0724C6.47025 21.7205 12.6563 23.0706 17.3044 20.088C20.4971 18.0393 22.1338 14.4793 21.8792 10.9444' stroke='stroke-current' stroke-width='1.4' stroke-linecap='round' class='my-path'></path>
</g>
<defs>
<clipPath id='clip0_9023_61563'>
<rect width='24' height='24' fill='white'></rect>
</clipPath>
</defs>
</svg>
<span className='sr-only'>Loading...</span>
</div>
</div>
)
}
return isValid ? <Settings /> : null;
}

View File

@@ -30,7 +30,7 @@ export default function RootLayout({
>
<ThemeProvider
attribute="class"
defaultTheme="dark"
defaultTheme="system"
enableSystem
disableTransitionOnChange
>