Frontend: fix HeroUI Switch usage across the whole app (Content/Control/ Thumb composition was missing, so no toggle ever rendered visibly), replace raw channel/role ID text fields with proper name-based dropdowns (ChannelSelect/RoleSelect) backed by the existing /guild/resources endpoint, redesign Automod's filters as inline toggle rows, turn the Ticket Pipeline tab into a drag-and-drop Kanban board, narrow the sidebar further and rework its collapse toggle and profile footer, and add a Discord-style message preview for Welcome/Support Login. Backend: fix the ~50 TypeScript build errors blocking `npm run build` (SlashCommandBuilder typing, discord.js v14 channel-union guards, Prisma JSON null handling, logger.warn signature, and related type narrowing) so the project compiles cleanly again. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
138 lines
5.8 KiB
TypeScript
138 lines
5.8 KiB
TypeScript
import { Card, CardContent, CardDescription, CardHeader, CardTitle, Input, TextArea, Button, Chip, Separator, TextField, Label } from '@heroui/react';
|
|
import { UserRound, Save, Send } from 'lucide-react';
|
|
import { useApp } from '../context/AppContext';
|
|
import { SectionCard } from '../components/shared/SectionCard';
|
|
import { AppAvatar } from '../components/shared/AppAvatar';
|
|
import { DiscordPreview, DiscordButton } from '../components/shared/DiscordPreview';
|
|
import { ChannelSelect } from '../components/shared/ChannelSelect';
|
|
import { AppSwitch } from '../components/shared/AppSwitch';
|
|
import { useGuildResources } from '../hooks/useGuildResources';
|
|
|
|
export function SupportLogin() {
|
|
const { supportLogin, setSupportLogin, saveSupportLogin, currentGuildId } = useApp();
|
|
const { channels } = useGuildResources(currentGuildId);
|
|
|
|
return (
|
|
<SectionCard title="Support Login" subtitle="Login-Panel fuer Supporter konfigurieren">
|
|
<div className="grid gap-5 xl:grid-cols-[1fr_400px]">
|
|
<Card>
|
|
<CardHeader>
|
|
<div>
|
|
<CardTitle>Panel-Konfiguration</CardTitle>
|
|
<CardDescription>Texte und Zielkanal mit normalen HeroUI-Feldern pflegen.</CardDescription>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="flex flex-col gap-4">
|
|
<AppSwitch
|
|
isSelected={supportLogin?.config?.autoRefresh !== false}
|
|
onChange={(v) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, autoRefresh: v } } : s)}
|
|
label="Auto-Refresh aktiv"
|
|
/>
|
|
|
|
<TextField>
|
|
<Label>Panel Channel</Label>
|
|
<ChannelSelect
|
|
options={channels}
|
|
value={supportLogin?.config?.panelChannelId}
|
|
onChange={(id) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, panelChannelId: id } } : s)}
|
|
placeholder="Channel für das Panel wählen"
|
|
/>
|
|
</TextField>
|
|
|
|
<TextField>
|
|
<Label>Titel</Label>
|
|
<Input
|
|
value={supportLogin?.config?.title || 'Support Login'}
|
|
onChange={(e) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, title: e.target.value } } : s)}
|
|
/>
|
|
</TextField>
|
|
|
|
<TextField>
|
|
<Label>Beschreibung</Label>
|
|
<TextArea
|
|
value={supportLogin?.config?.description || ''}
|
|
onChange={(e) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, description: e.target.value } } : s)}
|
|
/>
|
|
</TextField>
|
|
|
|
<TextField>
|
|
<Label>Login Button Label</Label>
|
|
<Input
|
|
value={supportLogin?.config?.loginLabel || 'Ich bin jetzt im Support'}
|
|
onChange={(e) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, loginLabel: e.target.value } } : s)}
|
|
/>
|
|
</TextField>
|
|
|
|
<TextField>
|
|
<Label>Logout Button Label</Label>
|
|
<Input
|
|
value={supportLogin?.config?.logoutLabel || 'Ich bin nicht mehr im Support'}
|
|
onChange={(e) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, logoutLabel: e.target.value } } : s)}
|
|
/>
|
|
</TextField>
|
|
|
|
<Separator />
|
|
|
|
<div className="flex gap-2">
|
|
<Button variant="primary" onPress={saveSupportLogin}>
|
|
<Save size={16} /> Speichern & Panel senden
|
|
</Button>
|
|
<Button variant="ghost">
|
|
<Send size={16} /> Panel manuell senden
|
|
</Button>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<div className="space-y-4">
|
|
<Card>
|
|
<CardHeader>
|
|
<div>
|
|
<CardTitle>Live Vorschau</CardTitle>
|
|
<CardDescription>So sieht das Panel auf Discord aus.</CardDescription>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
<DiscordPreview
|
|
title={supportLogin?.config?.title || 'Support Login'}
|
|
description={supportLogin?.config?.description || 'Melde dich als Support an/ab.'}
|
|
>
|
|
<DiscordButton variant="primary">{supportLogin?.config?.loginLabel || 'Ich bin jetzt im Support'}</DiscordButton>
|
|
<DiscordButton variant="secondary">{supportLogin?.config?.logoutLabel || 'Ich bin nicht mehr im Support'}</DiscordButton>
|
|
</DiscordPreview>
|
|
</CardContent>
|
|
</Card>
|
|
|
|
<Card>
|
|
<CardHeader>
|
|
<div>
|
|
<CardTitle>Aktive Supporter</CardTitle>
|
|
<CardDescription>Aktueller Status aus der Guild.</CardDescription>
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent className="flex flex-col gap-2">
|
|
{supportLogin?.status?.active?.length ? supportLogin.status.active.map((s, i) => (
|
|
<Card key={i} className="bg-surface-tertiary">
|
|
<CardContent className="flex items-center gap-3">
|
|
<AppAvatar name={s.username} size="sm" className="size-6" />
|
|
<span className="font-medium">{s.username || s.userId}</span>
|
|
<Chip size="sm" variant="soft" color="success" className="ml-auto">Online</Chip>
|
|
</CardContent>
|
|
</Card>
|
|
)) : (
|
|
<div className="flex flex-col items-center gap-2 py-4 text-center text-xs text-muted">
|
|
<UserRound size={20} />
|
|
Keine aktiven Supporter
|
|
</div>
|
|
)}
|
|
<p className="mt-2 text-xs text-muted">
|
|
Support Role ID: {supportLogin?.supportRoleId || 'Nicht gesetzt'}
|
|
</p>
|
|
</CardContent>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
</SectionCard>
|
|
);
|
|
}
|