From 3cf56b335c7ef8c9120115f3092cd94f9349647f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fran=20Jurmanovi=C4=87?= Date: Sat, 21 Mar 2026 22:42:22 +0100 Subject: [PATCH] fix: add missing logout button --- src/app/dashboard/membership/page.tsx | 16 +-- src/components/ui/Button.tsx | 10 +- src/components/ui/LogoutButton.tsx | 5 +- src/components/ui/Sidebar.tsx | 167 +++++++++++++++----------- src/lib/api/server/base.ts | 4 +- 5 files changed, 118 insertions(+), 84 deletions(-) diff --git a/src/app/dashboard/membership/page.tsx b/src/app/dashboard/membership/page.tsx index 7e07f02..dfaf8c8 100755 --- a/src/app/dashboard/membership/page.tsx +++ b/src/app/dashboard/membership/page.tsx @@ -4,24 +4,26 @@ import type { UserListParams } from '@/lib/api/server/membership'; import { UserManagementTable } from '@/components/membership/UserManagementTable'; interface MembershipPageProps { - searchParams: { + searchParams: Promise<{ username?: string; role_name?: string; sort_by?: string; sort_desc?: string; page?: string; - }; + }>; } export default async function MembershipPage({ searchParams }: MembershipPageProps) { const session = await requireAuth(); + const resolvedParams = await searchParams; + const params: UserListParams = { - username: searchParams.username, - role_name: searchParams.role_name, - sort_by: searchParams.sort_by || 'username', - sort_desc: searchParams.sort_desc === 'true', - page: parseInt(searchParams.page || '1'), + username: resolvedParams.username, + role_name: resolvedParams.role_name, + sort_by: resolvedParams.sort_by || 'username', + sort_desc: resolvedParams.sort_desc === 'true', + page: parseInt(resolvedParams.page || '1'), limit: 20 }; diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx index f6fefb6..3621381 100644 --- a/src/components/ui/Button.tsx +++ b/src/components/ui/Button.tsx @@ -2,7 +2,7 @@ import { cn } from '@/lib/utils'; import { ButtonHTMLAttributes } from 'react'; type ButtonVariant = 'primary' | 'ghost' | 'danger-outline' | 'danger-solid'; -type ButtonSize = 'sm' | 'md'; +type ButtonSize = 'sm' | 'md' | 'logout'; interface ButtonProps extends ButtonHTMLAttributes { variant?: ButtonVariant; @@ -12,13 +12,15 @@ interface ButtonProps extends ButtonHTMLAttributes { const variantClasses: Record = { primary: 'bg-btn-green border border-btn-green-hover text-white hover:bg-btn-green-hover', ghost: 'bg-transparent border border-border text-secondary hover:bg-overlay hover:text-primary', - 'danger-outline': 'bg-transparent border border-border text-red hover:border-red/40 hover:bg-red-bg', - 'danger-solid': 'bg-overlay border border-red/20 text-red hover:bg-red-bg', + 'danger-outline': + 'bg-transparent border border-border text-red hover:border-red/40 hover:bg-red-bg', + 'danger-solid': 'bg-overlay border border-red/20 text-red hover:bg-red-bg' }; const sizeClasses: Record = { sm: 'px-3 py-1.5 text-xs', md: 'px-4 py-2 text-sm', + logout: 'px-2 py-2 cursor-pointer hover:bg-red' }; export function Button({ @@ -34,7 +36,7 @@ export function Button({ {...props} disabled={disabled} className={cn( - 'inline-flex items-center gap-1.5 justify-center rounded-md font-medium transition-colors focus:outline-none focus:ring-2 focus:ring-blue focus:ring-offset-1 focus:ring-offset-base', + 'focus:ring-blue focus:ring-offset-base inline-flex items-center justify-center gap-1.5 rounded-md font-medium transition-colors focus:ring-2 focus:ring-offset-1 focus:outline-none', 'disabled:cursor-not-allowed disabled:opacity-40', variantClasses[variant], sizeClasses[size], diff --git a/src/components/ui/LogoutButton.tsx b/src/components/ui/LogoutButton.tsx index 519a17b..0c2c963 100755 --- a/src/components/ui/LogoutButton.tsx +++ b/src/components/ui/LogoutButton.tsx @@ -8,10 +8,10 @@ export default function LogoutButton() { const [, formAction] = useActionState(logoutAction, null); return (
-
); diff --git a/src/components/ui/Sidebar.tsx b/src/components/ui/Sidebar.tsx index b933440..d651e68 100644 --- a/src/components/ui/Sidebar.tsx +++ b/src/components/ui/Sidebar.tsx @@ -3,87 +3,120 @@ import Link from 'next/link'; import { usePathname } from 'next/navigation'; import { cn } from '@/lib/utils'; +import LogoutButton from './LogoutButton'; interface SidebarProps { - showUsers: boolean; // true if user has membership.view permission + showUsers: boolean; // true if user has membership.view permission } function ServerIcon({ active }: { active: boolean }) { - return ( - - - - - - - - - ); + return ( + + + + + + + + + ); } function UsersIcon({ active }: { active: boolean }) { - return ( - - - - - - ); + return ( + + + + + + ); } export function Sidebar({ showUsers }: SidebarProps) { - const pathname = usePathname(); - const isServers = pathname === '/dashboard' || pathname.startsWith('/dashboard/server'); - const isUsers = pathname.startsWith('/dashboard/membership'); + const pathname = usePathname(); + const isServers = pathname === '/dashboard' || pathname.startsWith('/dashboard/server'); + const isUsers = pathname.startsWith('/dashboard/membership'); - const navItems = [ - { href: '/dashboard', label: 'Servers', active: isServers, icon: }, - ...(showUsers ? [{ href: '/dashboard/membership', label: 'Users', active: isUsers, icon: }] : []), - ]; + const navItems = [ + { + href: '/dashboard', + label: 'Servers', + active: isServers, + icon: + }, + ...(showUsers + ? [ + { + href: '/dashboard/membership', + label: 'Users', + active: isUsers, + icon: + } + ] + : []) + ]; - return ( - <> - - - - ); + + + ); } diff --git a/src/lib/api/server/base.ts b/src/lib/api/server/base.ts index 19bb40e..2baa181 100755 --- a/src/lib/api/server/base.ts +++ b/src/lib/api/server/base.ts @@ -30,9 +30,7 @@ export async function fetchServerAPI( if (response.status == 401) { redirect('/login?expired=true'); } - throw new Error( - `API Error: ${response.statusText} - ${method} - ${BASE_URL}${endpoint} - ${token}` - ); + throw new Error(`API Error: ${response.statusText} - ${method} - ${BASE_URL}${endpoint}`); } if (response.headers.get('Content-Type')?.includes('application/json')) {