fix: add missing logout button
This commit is contained in:
@@ -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<HTMLButtonElement> {
|
||||
variant?: ButtonVariant;
|
||||
@@ -12,13 +12,15 @@ interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {
|
||||
const variantClasses: Record<ButtonVariant, string> = {
|
||||
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<ButtonSize, string> = {
|
||||
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],
|
||||
|
||||
@@ -8,10 +8,10 @@ export default function LogoutButton() {
|
||||
const [, formAction] = useActionState(logoutAction, null);
|
||||
return (
|
||||
<form action={formAction}>
|
||||
<Button type="submit" variant="ghost" size="sm" className="flex items-center gap-1">
|
||||
<Button type="submit" title="Logout" variant="ghost" size="logout">
|
||||
<svg
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
className="h-4 w-4"
|
||||
className="h-5 w-5"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
stroke="currentColor"
|
||||
@@ -23,7 +23,6 @@ export default function LogoutButton() {
|
||||
d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"
|
||||
/>
|
||||
</svg>
|
||||
<span className="hidden sm:inline">Logout</span>
|
||||
</Button>
|
||||
</form>
|
||||
);
|
||||
|
||||
@@ -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 (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} className={cn('h-5 w-5', active ? 'text-blue' : 'text-subtle')}>
|
||||
<rect x="2" y="3" width="20" height="5" rx="1" />
|
||||
<rect x="2" y="10" width="20" height="5" rx="1" />
|
||||
<rect x="2" y="17" width="20" height="5" rx="1" />
|
||||
<circle cx="6" cy="5.5" r="1" fill="currentColor" />
|
||||
<circle cx="6" cy="12.5" r="1" fill="currentColor" />
|
||||
<circle cx="6" cy="19.5" r="1" fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
className={cn('h-5 w-5', active ? 'text-blue' : 'text-subtle')}
|
||||
>
|
||||
<rect x="2" y="3" width="20" height="5" rx="1" />
|
||||
<rect x="2" y="10" width="20" height="5" rx="1" />
|
||||
<rect x="2" y="17" width="20" height="5" rx="1" />
|
||||
<circle cx="6" cy="5.5" r="1" fill="currentColor" />
|
||||
<circle cx="6" cy="12.5" r="1" fill="currentColor" />
|
||||
<circle cx="6" cy="19.5" r="1" fill="currentColor" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
function UsersIcon({ active }: { active: boolean }) {
|
||||
return (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={2} className={cn('h-5 w-5', active ? 'text-blue' : 'text-subtle')}>
|
||||
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75" />
|
||||
</svg>
|
||||
);
|
||||
return (
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="currentColor"
|
||||
strokeWidth={2}
|
||||
className={cn('h-5 w-5', active ? 'text-blue' : 'text-subtle')}
|
||||
>
|
||||
<path d="M17 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" />
|
||||
<circle cx="9" cy="7" r="4" />
|
||||
<path d="M23 21v-2a4 4 0 00-3-3.87M16 3.13a4 4 0 010 7.75" />
|
||||
</svg>
|
||||
);
|
||||
}
|
||||
|
||||
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: <ServerIcon active={isServers} /> },
|
||||
...(showUsers ? [{ href: '/dashboard/membership', label: 'Users', active: isUsers, icon: <UsersIcon active={isUsers} /> }] : []),
|
||||
];
|
||||
const navItems = [
|
||||
{
|
||||
href: '/dashboard',
|
||||
label: 'Servers',
|
||||
active: isServers,
|
||||
icon: <ServerIcon active={isServers} />
|
||||
},
|
||||
...(showUsers
|
||||
? [
|
||||
{
|
||||
href: '/dashboard/membership',
|
||||
label: 'Users',
|
||||
active: isUsers,
|
||||
icon: <UsersIcon active={isUsers} />
|
||||
}
|
||||
]
|
||||
: [])
|
||||
];
|
||||
|
||||
return (
|
||||
<>
|
||||
<aside className="fixed inset-y-0 left-0 z-40 hidden w-14 flex-col items-center border-r border-border-muted bg-sidebar py-3 md:flex">
|
||||
<div className="mb-4 flex h-8 w-8 items-center justify-center rounded-lg bg-blue">
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="#0d1117" strokeWidth={2.5} className="h-4 w-4">
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
|
||||
</svg>
|
||||
</div>
|
||||
return (
|
||||
<>
|
||||
<aside className="border-border-muted bg-sidebar fixed inset-y-0 left-0 z-40 hidden w-14 flex-col items-center border-r py-3 md:flex">
|
||||
<div className="bg-blue mb-4 flex h-8 w-8 items-center justify-center rounded-lg">
|
||||
<svg
|
||||
viewBox="0 0 24 24"
|
||||
fill="none"
|
||||
stroke="#0d1117"
|
||||
strokeWidth={2.5}
|
||||
className="h-4 w-4"
|
||||
>
|
||||
<path d="M12 2L2 7l10 5 10-5-10-5zM2 17l10 5 10-5M2 12l10 5 10-5" />
|
||||
</svg>
|
||||
</div>
|
||||
|
||||
<nav className="flex flex-1 flex-col items-center gap-1">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
title={item.label}
|
||||
className={cn(
|
||||
'flex h-9 w-9 items-center justify-center rounded-lg transition-colors',
|
||||
item.active ? 'bg-blue-bg' : 'hover:bg-overlay'
|
||||
)}
|
||||
>
|
||||
{item.icon}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
<nav className="flex flex-1 flex-col items-center gap-1">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
title={item.label}
|
||||
className={cn(
|
||||
'flex h-9 w-9 items-center justify-center rounded-lg transition-colors',
|
||||
item.active ? 'bg-blue-bg' : 'hover:bg-overlay'
|
||||
)}
|
||||
>
|
||||
{item.icon}
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
|
||||
<div className="mt-auto h-7 w-7 rounded-full border-2 border-border bg-overlay" />
|
||||
</aside>
|
||||
<LogoutButton />
|
||||
</aside>
|
||||
|
||||
<nav className="fixed inset-x-0 bottom-0 z-40 flex h-14 items-center justify-around border-t border-border-muted bg-sidebar md:hidden">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
'flex flex-col items-center justify-center gap-0.5 px-4 py-2 text-xs transition-colors',
|
||||
item.active ? 'text-blue' : 'text-subtle'
|
||||
)}
|
||||
>
|
||||
{item.icon}
|
||||
<span>{item.label}</span>
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</>
|
||||
);
|
||||
<nav className="border-border-muted bg-sidebar fixed inset-x-0 bottom-0 z-40 flex h-14 items-center justify-around border-t md:hidden">
|
||||
{navItems.map((item) => (
|
||||
<Link
|
||||
key={item.href}
|
||||
href={item.href}
|
||||
className={cn(
|
||||
'flex flex-col items-center justify-center gap-0.5 px-4 py-2 text-xs transition-colors',
|
||||
item.active ? 'text-blue' : 'text-subtle'
|
||||
)}
|
||||
>
|
||||
{item.icon}
|
||||
<span>{item.label}</span>
|
||||
</Link>
|
||||
))}
|
||||
</nav>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user