refactor(ui): move category creation from ChannelSidebar to ServerSettingsMenu
All checks were successful
Deploy Waveform to VPS / deploy (push) Successful in 3m24s

This commit is contained in:
Chneemann 2026-09-17 04:41:40 +02:00
parent c6af4e1565
commit 4622dac9bf
No known key found for this signature in database
2 changed files with 88 additions and 33 deletions

View file

@ -1,57 +1,119 @@
/**
* @file components/layout/ServerSettingsMenu.tsx
* @description Header button triggering the server overview/settings modal.
* @description Single settings dropdown menu providing server management actions (Settings, Create Category, Create Channel).
*/
"use client";
import { useState } from "react";
import { Settings } from "lucide-react";
import { useState, useRef, useEffect } from "react";
import { Settings, FolderPlus, PlusCircle, Sliders } from "lucide-react";
import { EditServerModal } from "@/components/modals/EditServerModal";
import { CreateCategoryModal } from "@/components/modals/CreateCategoryModal";
import { CreateChannelModal } from "@/components/modals/CreateChannelModal";
/**
* Props for the ServerSettingsMenu component.
*
* @interface ServerSettingsMenuProps
* @property {string} serverId - The unique identifier of the server.
* @property {string} serverName - The display name of the server.
*/
/** Props for the ServerSettingsMenu component. */
interface ServerSettingsMenuProps {
serverId: string;
serverName: string;
}
/**
* Renders a settings button that opens the edit server modal.
*
* @param {ServerSettingsMenuProps} props - The component props.
* @param {string} props.serverId - The unique identifier of the server.
* @param {string} props.serverName - The display name of the server.
* @returns {JSX.Element} The rendered server settings trigger and modal component.
*/
/** Renders a server management dropdown menu with modal triggers for channel, category, and server settings. */
export function ServerSettingsMenu({
serverId,
serverName,
}: ServerSettingsMenuProps) {
const [isModalOpen, setIsModalOpen] = useState(false);
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [isEditServerOpen, setIsEditServerOpen] = useState(false);
const [isCreateCategoryOpen, setIsCreateCategoryOpen] = useState(false);
const [isCreateChannelOpen, setIsCreateChannelOpen] = useState(false);
const menuRef = useRef<HTMLDivElement>(null);
// Close the menu when clicked outside of it
useEffect(() => {
function handleClickOutside(event: MouseEvent) {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
setIsMenuOpen(false);
}
}
if (isMenuOpen) {
document.addEventListener("mousedown", handleClickOutside);
}
return () => document.removeEventListener("mousedown", handleClickOutside);
}, [isMenuOpen]);
return (
<>
<div className="relative" ref={menuRef}>
{/* Button */}
<button
type="button"
onClick={() => setIsModalOpen(true)}
title="Server Settings"
onClick={() => setIsMenuOpen((prev) => !prev)}
title="Server-Optionen"
className="p-1.5 rounded-md text-muted hover:text-white hover:bg-surface transition-colors cursor-pointer"
>
<Settings className="w-5 h-5" />
</button>
{/* Dropdown Menü */}
{isMenuOpen && (
<div className="absolute right-0 mt-2 w-52 bg-surface border border-background rounded-lg shadow-lg py-1 z-50 animate-in fade-in zoom-in-95 duration-100">
<button
type="button"
onClick={() => {
setIsMenuOpen(false);
setIsCreateChannelOpen(true);
}}
className="w-full flex items-center gap-2 px-3 py-2 text-sm text-muted hover:text-white hover:bg-background transition-colors text-left cursor-pointer"
>
<PlusCircle className="w-4 h-4" />
<span>Kanal erstellen</span>
</button>
<button
type="button"
onClick={() => {
setIsMenuOpen(false);
setIsCreateCategoryOpen(true);
}}
className="w-full flex items-center gap-2 px-3 py-2 text-sm text-muted hover:text-white hover:bg-background transition-colors text-left cursor-pointer"
>
<FolderPlus className="w-4 h-4" />
<span>Kategorie erstellen</span>
</button>
<div className="my-1 border-t border-background" />
<button
type="button"
onClick={() => {
setIsMenuOpen(false);
setIsEditServerOpen(true);
}}
className="w-full flex items-center gap-2 px-3 py-2 text-sm text-muted hover:text-white hover:bg-background transition-colors text-left cursor-pointer"
>
<Sliders className="w-4 h-4" />
<span>Servereinstellungen</span>
</button>
</div>
)}
{/* Modals */}
<EditServerModal
isOpen={isModalOpen}
isOpen={isEditServerOpen}
serverId={serverId}
initialName={serverName}
onClose={() => setIsModalOpen(false)}
onClose={() => setIsEditServerOpen(false)}
/>
</>
<CreateCategoryModal
isOpen={isCreateCategoryOpen}
onClose={() => setIsCreateCategoryOpen(false)}
serverId={serverId}
/>
<CreateChannelModal
isOpen={isCreateChannelOpen}
onClose={() => setIsCreateChannelOpen(false)}
serverId={serverId}
/>
</div>
);
}

View file

@ -14,7 +14,7 @@ import { CreateCategoryModal } from "@/components/modals/CreateCategoryModal";
import { EditChannelModal } from "@/components/modals/EditChannelModal";
import { EditCategoryModal } from "@/components/modals/EditCategoryModal";
import { CategorySection } from "./CategorySection";
import { FolderPlus, PanelLeftClose } from "lucide-react";
import { PanelLeftClose } from "lucide-react";
import type { Category, Channel } from "@/db/schema";
/** Renders the channel sidebar navigation for the active server. */
@ -55,13 +55,6 @@ export function ChannelSidebar() {
<div className="h-14 border-b border-background flex items-center justify-between px-4 font-bold text-white shadow-sm shrink-0">
<span className="truncate">{activeServer.name}</span>
<div className="flex items-center gap-1">
<button
type="button"
onClick={() => setIsCreateCategoryOpen(true)}
className="p-1.5 rounded-md text-muted hover:text-white hover:bg-surface transition-colors cursor-pointer shrink-0"
>
<FolderPlus className="w-4 h-4" />
</button>
<button
type="button"
onClick={toggleNav}