Implement Owner Panel features and maintenance mode handling

- Introduced the Owner Panel with new routes and UI components for managing guilds, user blacklists, feature flags, and bot presence.
- Added maintenance mode functionality to prevent non-owner users from executing commands during maintenance periods.
- Enhanced localization with new keys for Owner Panel features in both English and German.
- Updated job processing to include a presence refresh job, ensuring the bot's status is regularly updated.
- Improved environment configuration to support owner user IDs for access control.
This commit is contained in:
smueller
2026-07-22 16:10:30 +02:00
parent bcbfa07697
commit 8a8aefb4fb
42 changed files with 2385 additions and 50 deletions

View File

@@ -9,17 +9,25 @@ interface DashboardShellProps {
currentGuild: DashboardGuild;
otherGuilds: DashboardGuild[];
user: SessionUser;
showOwnerLink?: boolean;
children: ReactNode;
}
export function DashboardShell({ guildId, currentGuild, otherGuilds, user, children }: DashboardShellProps) {
export function DashboardShell({
guildId,
currentGuild,
otherGuilds,
user,
showOwnerLink = false,
children
}: DashboardShellProps) {
return (
<div className="flex min-h-screen">
<Sidebar guildId={guildId} />
<div className="flex flex-1 flex-col">
<header className="flex h-14 items-center justify-between border-b border-border px-6">
<ServerSwitcher currentGuild={currentGuild} otherGuilds={otherGuilds} />
<UserMenu user={user} />
<UserMenu user={user} showOwnerLink={showOwnerLink} />
</header>
<main className="flex-1 px-6 py-8">
<div className="mx-auto w-full max-w-[1200px]">{children}</div>

View File

@@ -1,7 +1,7 @@
'use client';
import type { SessionUser } from '@nexumi/shared';
import { LogOut, Moon, Sun } from 'lucide-react';
import { LogOut, Moon, Shield, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import { useRouter } from 'next/navigation';
import { useState } from 'react';
@@ -26,7 +26,7 @@ function initials(user: SessionUser): string {
return name.slice(0, 2).toUpperCase();
}
export function UserMenu({ user }: { user: SessionUser }) {
export function UserMenu({ user, showOwnerLink = false }: { user: SessionUser; showOwnerLink?: boolean }) {
const t = useTranslations();
const { theme, setTheme } = useTheme();
const router = useRouter();
@@ -58,6 +58,19 @@ export function UserMenu({ user }: { user: SessionUser }) {
<DropdownMenuContent align="end" className="w-56">
<DropdownMenuLabel className="truncate">{user.globalName ?? user.username}</DropdownMenuLabel>
<DropdownMenuSeparator />
{showOwnerLink ? (
<>
<DropdownMenuItem
onClick={() => {
router.push('/owner');
}}
>
<Shield className="size-4" />
{t('userMenu.ownerPanel')}
</DropdownMenuItem>
<DropdownMenuSeparator />
</>
) : null}
<DropdownMenuItem onClick={() => setTheme(theme === 'dark' ? 'light' : 'dark')}>
{theme === 'dark' ? <Sun className="size-4" /> : <Moon className="size-4" />}
{theme === 'dark' ? t('userMenu.themeLight') : t('userMenu.themeDark')}