refactor frontend forms for updated HeroUI inputs
Some checks failed
Deploy Discord Bot / deploy (push) Has been cancelled

This commit is contained in:
Pepe44DEV
2026-07-01 15:21:59 +02:00
parent e2d8002bf2
commit da72f49255
15 changed files with 373 additions and 262 deletions

View File

@@ -79,10 +79,10 @@ export function Header() {
}
function useHeaderData() {
const { guildInfo, guilds, currentGuildId } = useApp();
const { guildInfo, guilds, currentGuildId, statusMessage } = useApp();
const selectedGuild = guilds.find((g) => g.id === currentGuildId);
return {
guildName: guildInfo?.name || selectedGuild?.name || 'Dashboard',
statusMessage: null,
statusMessage,
};
}

View File

@@ -1,4 +1,4 @@
import { Card, CardHeader, CardContent } from '@heroui/react';
import { Card, CardHeader, CardContent, CardTitle, CardDescription } from '@heroui/react';
import type { ReactNode } from 'react';
type Props = {
@@ -12,9 +12,9 @@ export function SectionCard({ title, subtitle, children, action }: Props) {
return (
<Card className="border border-default-100 bg-gradient-to-b from-default-50/40 to-background">
<CardHeader className="flex items-start justify-between gap-4 px-6 pt-6 pb-0">
<div className="min-w-0">
<h2 className="text-xl font-bold tracking-tight">{title}</h2>
{subtitle && <p className="mt-1 text-small text-default-500">{subtitle}</p>}
<div className="min-w-0 flex flex-col gap-1">
<CardTitle>{title}</CardTitle>
{subtitle && <CardDescription>{subtitle}</CardDescription>}
</div>
{action && <div className="shrink-0">{action}</div>}
</CardHeader>

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Switch, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Switch, Separator, TextField, Label } from '@heroui/react';
import { Shield, Filter, Link, Ban, AlertTriangle, Save } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -14,37 +14,41 @@ export function Automod() {
<h3 className="text-base font-semibold">Filter konfigurieren</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Switch isSelected={settings.automodEnabled !== false} onValueChange={(v) => setSettings((s) => ({ ...s, automodEnabled: v }))}>
<Switch isSelected={settings.automodEnabled !== false} onChange={(v) => setSettings((s) => ({ ...s, automodEnabled: v }))}>
<div className="flex items-center gap-2">
<Shield size={16} /> Automod aktiv
</div>
</Switch>
<div className="grid grid-cols-2 gap-3">
<Switch isSelected={settings.automodConfig?.badWordFilter ?? false} onValueChange={(v) => setSettings((s) => ({ ...s, automodConfig: { ...(s.automodConfig || {}), badWordFilter: v } }))}>
<Switch isSelected={settings.automodConfig?.badWordFilter ?? false} onChange={(v) => setSettings((s) => ({ ...s, automodConfig: { ...(s.automodConfig || {}), badWordFilter: v } }))}>
<div className="flex items-center gap-2"><Ban size={14} /> Bad-Word-Filter</div>
</Switch>
<Switch isSelected={settings.automodConfig?.linkFilter ?? false} onValueChange={(v) => setSettings((s) => ({ ...s, automodConfig: { ...(s.automodConfig || {}), linkFilter: v } }))}>
<Switch isSelected={settings.automodConfig?.linkFilter ?? false} onChange={(v) => setSettings((s) => ({ ...s, automodConfig: { ...(s.automodConfig || {}), linkFilter: v } }))}>
<div className="flex items-center gap-2"><Link size={14} /> Link-Filter</div>
</Switch>
<Switch isSelected={settings.automodConfig?.spamFilter ?? false} onValueChange={(v) => setSettings((s) => ({ ...s, automodConfig: { ...(s.automodConfig || {}), spamFilter: v } }))}>
<Switch isSelected={settings.automodConfig?.spamFilter ?? false} onChange={(v) => setSettings((s) => ({ ...s, automodConfig: { ...(s.automodConfig || {}), spamFilter: v } }))}>
<div className="flex items-center gap-2"><AlertTriangle size={14} /> Spam-Filter</div>
</Switch>
</div>
<TextField>
<Label>Log Channel ID</Label>
<Input
label="Log Channel ID"
placeholder="Channel ID f<>r Logs"
placeholder="Channel ID f<>r Logs"
value={settings.automodConfig?.logChannelId || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, automodConfig: { ...(s.automodConfig || {}), logChannelId: v } }))}
onChange={(e) => setSettings((s) => ({ ...s, automodConfig: { ...(s.automodConfig || {}), logChannelId: e.target.value } }))}
/>
</TextField>
<TextField>
<Label>Whitelist Links (Komma-getrennt)</Label>
<TextArea
label="Whitelist Links (Komma-getrennt)"
value={(settings.automodConfig?.linkWhitelist || []).join(', ')}
onValueChange={(v) => setSettings((s) => ({ ...s, automodConfig: { ...(s.automodConfig || {}), linkWhitelist: v.split(',').map((x) => x.trim()).filter(Boolean) } }))}
onChange={(e) => setSettings((s) => ({ ...s, automodConfig: { ...(s.automodConfig || {}), linkWhitelist: e.target.value.split(',').map((x) => x.trim()).filter(Boolean) } }))}
placeholder="trusted-domain.com, another-safe.site"
/>
</TextField>
<Separator />
@@ -64,13 +68,13 @@ export function Automod() {
<p className="text-default-500">Die Automod-Einstellungen werden nach dem Speichern sofort aktiv.</p>
</div>
<div className="rounded-xl border border-default-100 bg-default-50/30 px-4 py-3 text-small">
<p className="text-default-500">Bad-Word-Filter entfernt Nachrichten mit unerw<EFBFBD>nschten Begriffen.</p>
<p className="text-default-500">Bad-Word-Filter entfernt Nachrichten mit unerw<EFBFBD>nschten Begriffen.</p>
</div>
<div className="rounded-xl border border-default-100 bg-default-50/30 px-4 py-3 text-small">
<p className="text-default-500">Link-Filter blockiert bekannte sch<EFBFBD>dliche Domains und nicht-whitelistete Links.</p>
<p className="text-default-500">Link-Filter blockiert bekannte sch<EFBFBD>dliche Domains und nicht-whitelistete Links.</p>
</div>
<div className="rounded-xl border border-default-100 bg-default-50/30 px-4 py-3 text-small">
<p className="text-default-500">Spam-Filter erkennt und unterdr<EFBFBD>ckt Mehrfachnachrichten in kurzer Zeit.</p>
<p className="text-default-500">Spam-Filter erkennt und unterdr<EFBFBD>ckt Mehrfachnachrichten in kurzer Zeit.</p>
</div>
</CardContent>
</Card>

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Switch, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Switch, Separator, TextField, Label } from '@heroui/react';
import { CalendarDays, Save, Cake, Clock } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -7,40 +7,46 @@ export function Birthday() {
const { birthday, setBirthday, saveBirthday } = useApp();
return (
<SectionCard title="Birthday" subtitle="Geburtstags-Feature und gespeicherte Eintr<74>ge">
<SectionCard title="Birthday" subtitle="Geburtstags-Feature und gespeicherte Eintr<74>ge">
<div className="grid gap-5 xl:grid-cols-2">
<Card className="border border-default-100 bg-default-50/20">
<CardHeader className="px-5 pt-5 pb-0">
<h3 className="text-base font-semibold">Konfiguration</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Switch isSelected={birthday.config?.enabled !== false} onValueChange={(v) => setBirthday((s) => ({ ...s, config: { ...s.config, enabled: v } }))}>
<Switch isSelected={birthday.config?.enabled !== false} onChange={(v) => setBirthday((s) => ({ ...s, config: { ...s.config, enabled: v } }))}>
<div className="flex items-center gap-2"><Cake size={16} /> Birthday aktiv</div>
</Switch>
<TextField>
<Label>Channel ID</Label>
<Input
label="Channel ID"
placeholder="Channel f<>r Geburtstagsnachrichten"
placeholder="Channel f<>r Geburtstagsnachrichten"
value={birthday.config?.channelId || ''}
onValueChange={(v) => setBirthday((s) => ({ ...s, config: { ...s.config, channelId: v } }))}
onChange={(e) => setBirthday((s) => ({ ...s, config: { ...s.config, channelId: e.target.value } }))}
/>
</TextField>
<TextField>
<Label>Sendezeit (Stunde)</Label>
<Input
label="Sendezeit (Stunde)"
type="number"
min="0"
max="23"
value={String(birthday.config?.sendHour ?? 9)}
onValueChange={(v) => setBirthday((s) => ({ ...s, config: { ...s.config, sendHour: Number(v || 0) } }))}
onChange={(e) => setBirthday((s) => ({ ...s, config: { ...s.config, sendHour: Number(e.target.value || 0) } }))}
startContent={<Clock size={16} className="text-default-400" />}
/>
</TextField>
<TextField>
<Label>Template</Label>
<TextArea
label="Template"
placeholder="Alles Gute zum Geburtstag, {user}!"
value={birthday.config?.messageTemplate || ''}
onValueChange={(v) => setBirthday((s) => ({ ...s, config: { ...s.config, messageTemplate: v } }))}
onChange={(e) => setBirthday((s) => ({ ...s, config: { ...s.config, messageTemplate: e.target.value } }))}
/>
</TextField>
<Separator />
@@ -66,7 +72,7 @@ export function Birthday() {
)) : (
<div className="flex flex-col items-center gap-2 py-4 text-center text-tiny text-default-400">
<CalendarDays size={20} />
Keine Eintr<EFBFBD>ge
Keine Eintr<EFBFBD>ge
</div>
)}
</CardContent>

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, Button, Chip, Switch, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, Button, Chip, Switch, Separator, TextField, Label } from '@heroui/react';
import { AudioLines, Save, Mic, Users } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -14,30 +14,36 @@ export function DynamicVoice() {
<h3 className="text-base font-semibold">Konfiguration</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Switch isSelected={settings.dynamicVoiceEnabled !== false} onValueChange={(v) => setSettings((s) => ({ ...s, dynamicVoiceEnabled: v }))}>
<Switch isSelected={settings.dynamicVoiceEnabled !== false} onChange={(v) => setSettings((s) => ({ ...s, dynamicVoiceEnabled: v }))}>
<div className="flex items-center gap-2"><AudioLines size={16} /> Dynamic Voice aktiv</div>
</Switch>
<TextField>
<Label>Lobby Channel ID</Label>
<Input
label="Lobby Channel ID"
placeholder="Channel ID der Lobby"
value={settings.dynamicVoiceConfig?.lobbyChannelId || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, dynamicVoiceConfig: { ...(s.dynamicVoiceConfig || {}), lobbyChannelId: v } }))}
onChange={(e) => setSettings((s) => ({ ...s, dynamicVoiceConfig: { ...(s.dynamicVoiceConfig || {}), lobbyChannelId: e.target.value } }))}
/>
</TextField>
<TextField>
<Label>Kategorie ID</Label>
<Input
label="Kategorie ID"
placeholder="Kategorie f<>r neue Channels"
placeholder="Kategorie f<EFBFBD>r neue Channels"
value={settings.dynamicVoiceConfig?.categoryId || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, dynamicVoiceConfig: { ...(s.dynamicVoiceConfig || {}), categoryId: v } }))}
onChange={(e) => setSettings((s) => ({ ...s, dynamicVoiceConfig: { ...(s.dynamicVoiceConfig || {}), categoryId: e.target.value } }))}
/>
</TextField>
<TextField>
<Label>Template</Label>
<Input
label="Template"
placeholder="Channel-Name Template"
value={settings.dynamicVoiceConfig?.template || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, dynamicVoiceConfig: { ...(s.dynamicVoiceConfig || {}), template: v } }))}
onChange={(e) => setSettings((s) => ({ ...s, dynamicVoiceConfig: { ...(s.dynamicVoiceConfig || {}), template: e.target.value } }))}
/>
</TextField>
<Separator />
@@ -59,7 +65,7 @@ export function DynamicVoice() {
</div>
<div className="flex items-center gap-3 rounded-xl border border-default-100 bg-default-50/30 px-4 py-3 text-small">
<Users size={16} className="text-success-400" />
<span className="text-default-500">Channel-Owner k<EFBFBD>nnen Limits und Berechtigungen verwalten</span>
<span className="text-default-500">Channel-Owner k<EFBFBD>nnen Limits und Berechtigungen verwalten</span>
</div>
</CardContent>
</Card>

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Separator, TextField, Label } from '@heroui/react';
import { CalendarDays, Trash2, Plus, Clock } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -22,7 +22,7 @@ export function Events() {
<span className="font-semibold text-small truncate">{event.title}</span>
</div>
<Button color="danger" size="sm" variant="flat" startContent={<Trash2 size={14} />} onPress={() => deleteEvent(event.id)}>
L<EFBFBD>schen
L<EFBFBD>schen
</Button>
</div>
<p className="text-small text-default-400">{event.description || 'Keine Beschreibung'}</p>
@@ -46,34 +46,42 @@ export function Events() {
<h3 className="text-base font-semibold">Neues Event</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<TextField>
<Label>Titel</Label>
<Input
label="Titel"
placeholder="Event Name"
value={eventDraft.title}
onValueChange={(v) => setEventDraft((s) => ({ ...s, title: v }))}
onChange={(e) => setEventDraft((s) => ({ ...s, title: e.target.value }))}
/>
</TextField>
<TextField>
<Label>Beschreibung</Label>
<TextArea
label="Beschreibung"
placeholder="Event Beschreibung"
value={eventDraft.description}
onValueChange={(v) => setEventDraft((s) => ({ ...s, description: v }))}
onChange={(e) => setEventDraft((s) => ({ ...s, description: e.target.value }))}
/>
</TextField>
<TextField>
<Label>Channel ID</Label>
<Input
label="Channel ID"
placeholder="Channel f<>r Erinnerungen"
placeholder="Channel f<>r Erinnerungen"
value={eventDraft.channelId}
onValueChange={(v) => setEventDraft((s) => ({ ...s, channelId: v }))}
onChange={(e) => setEventDraft((s) => ({ ...s, channelId: e.target.value }))}
/>
</TextField>
<TextField>
<Label>Start (ISO)</Label>
<Input
label="Start (ISO)"
type="datetime-local"
placeholder="2024-12-24T18:00"
value={eventDraft.startsAt}
onValueChange={(v) => setEventDraft((s) => ({ ...s, startsAt: v }))}
onChange={(e) => setEventDraft((s) => ({ ...s, startsAt: e.target.value }))}
/>
</TextField>
<Button color="primary" startContent={<Plus size={16} />} onPress={saveEvent}>
Event speichern

View File

@@ -28,7 +28,7 @@ export function ModulesPage() {
<div className="text-small text-default-400 truncate">{module.description}</div>
)}
</div>
<Switch isSelected={module.enabled} onValueChange={(v) => toggleModule(module.key, v)} />
<Switch isSelected={module.enabled} onChange={(v) => toggleModule(module.key, v)} />
</CardContent>
</Card>
))}
@@ -52,7 +52,7 @@ export function ModulesPage() {
<div className="text-small text-default-400 truncate">{module.description}</div>
)}
</div>
<Switch isSelected={module.enabled} onValueChange={(v) => toggleModule(module.key, v)} />
<Switch isSelected={module.enabled} onChange={(v) => toggleModule(module.key, v)} />
</CardContent>
</Card>
))}
@@ -63,7 +63,7 @@ export function ModulesPage() {
{modules.length === 0 && (
<div className="flex flex-col items-center gap-2 py-8 text-center text-small text-default-400">
<Puzzle size={24} />
Keine Module verf<EFBFBD>gbar
Keine Module verf<EFBFBD>gbar
</div>
)}
</div>

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Separator, TextField, Label } from '@heroui/react';
import { Tag, Save, Hash, List } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -22,7 +22,7 @@ export function ReactionRoles() {
<div className="font-semibold text-small truncate">{set.title || 'Reaction Role'}</div>
<div className="text-tiny text-default-400 truncate">Channel: {set.channelId || '-'}</div>
</div>
<Chip size="sm" variant="flat">{(set.entries?.length || 0)} Eintr<EFBFBD>ge</Chip>
<Chip size="sm" variant="flat">{(set.entries?.length || 0)} Eintr<EFBFBD>ge</Chip>
</CardContent>
</Card>
)) : (
@@ -39,27 +39,31 @@ export function ReactionRoles() {
<h3 className="text-base font-semibold">Neues Set</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<TextField>
<Label>Titel</Label>
<Input
label="Titel"
placeholder="Rollenauswahl"
value={reactionDraft.title}
onValueChange={(v) => setReactionDraft((s) => ({ ...s, title: v }))}
onChange={(e) => setReactionDraft((s) => ({ ...s, title: e.target.value }))}
/>
</TextField>
<TextField>
<Label>Channel ID</Label>
<Input
label="Channel ID"
placeholder="Channel f<>r die Nachricht"
placeholder="Channel f<>r die Nachricht"
value={reactionDraft.channelId}
onValueChange={(v) => setReactionDraft((s) => ({ ...s, channelId: v }))}
onChange={(e) => setReactionDraft((s) => ({ ...s, channelId: e.target.value }))}
/>
</TextField>
<div>
<label className="block text-small font-medium mb-1">Eintr<EFBFBD>ge</label>
<label className="block text-small font-medium mb-1">Eintr<EFBFBD>ge</label>
<TextArea
placeholder="Emoji | Role ID | Label&#10;:emoji: | 123456789 | Rolle 1&#10;:wave: | 987654321 | Rolle 2"
minRows={6}
value={reactionDraft.entries}
onValueChange={(v) => setReactionDraft((s) => ({ ...s, entries: v }))}
onChange={(e) => setReactionDraft((s) => ({ ...s, entries: e.target.value }))}
/>
<p className="mt-1 text-tiny text-default-400">
Pro Zeile: Emoji | Role ID | Label (optional) | Beschreibung (optional)

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Tabs, Tab, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Tabs, Tab, Separator, TextField, Label } from '@heroui/react';
import { ClipboardList, Pencil, Trash2, Send, Plus, FileText } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -12,10 +12,10 @@ export function Register() {
} = useApp();
return (
<SectionCard title="Registrierungsformulare" subtitle="Bewerbungs-Formulare und eingegangene Antr<74>ge verwalten.">
<SectionCard title="Registrierungsformulare" subtitle="Bewerbungs-Formulare und eingegangene Antr<74>ge verwalten.">
<Tabs aria-label="Register Tabs" color="primary" selectedKey={registerTab} variant="bordered" onSelectionChange={(key) => setRegisterTab(String(key))}>
<Tab key="forms" title="Formulare" />
<Tab key="apps" title="Antr<EFBFBD>ge" />
<Tab key="forms">Formulare</Tab>
<Tab key="apps">Anträge</Tab>
</Tabs>
{registerTab === 'forms' && (
@@ -76,11 +76,26 @@ export function Register() {
<h3 className="text-base font-semibold">{editingFormId ? 'Formular bearbeiten' : 'Neues Formular'}</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Input label="Name" value={formDraft.name} onValueChange={(v) => setFormDraft((s) => ({ ...s, name: v }))} />
<Input label="Beschreibung" value={formDraft.description} onValueChange={(v) => setFormDraft((s) => ({ ...s, description: v }))} />
<Input label="Review Channel ID" value={formDraft.reviewChannelId} onValueChange={(v) => setFormDraft((s) => ({ ...s, reviewChannelId: v }))} />
<Input label="Benachrichtigungs-Rollen (Komma-getrennt)" value={formDraft.notifyRoleIds} onValueChange={(v) => setFormDraft((s) => ({ ...s, notifyRoleIds: v }))} />
<TextArea label="Felder (label|type|required|options)" minRows={6} value={formDraft.fields} onValueChange={(v) => setFormDraft((s) => ({ ...s, fields: v }))} />
<TextField>
<Label>Name</Label>
<Input value={formDraft.name} onChange={(e) => setFormDraft((s) => ({ ...s, name: e.target.value }))} />
</TextField>
<TextField>
<Label>Beschreibung</Label>
<Input value={formDraft.description} onChange={(e) => setFormDraft((s) => ({ ...s, description: e.target.value }))} />
</TextField>
<TextField>
<Label>Review Channel ID</Label>
<Input value={formDraft.reviewChannelId} onChange={(e) => setFormDraft((s) => ({ ...s, reviewChannelId: e.target.value }))} />
</TextField>
<TextField>
<Label>Benachrichtigungs-Rollen (Komma-getrennt)</Label>
<Input value={formDraft.notifyRoleIds} onChange={(e) => setFormDraft((s) => ({ ...s, notifyRoleIds: e.target.value }))} />
</TextField>
<TextField>
<Label>Felder (label|type|required|options)</Label>
<TextArea minRows={6} value={formDraft.fields} onChange={(e) => setFormDraft((s) => ({ ...s, fields: e.target.value }))} />
</TextField>
<p className="text-tiny text-default-400">
Pro Zeile: label | type (text/paragraph/select/multi) | required | option1,option2
</p>
@@ -99,7 +114,7 @@ export function Register() {
{registerTab === 'apps' && (
<div className="mt-5">
<h3 className="mb-3 text-base font-semibold">Eingegangene Antr<EFBFBD>ge ({registerApps.length})</h3>
<h3 className="mb-3 text-base font-semibold">Eingegangene Antr<EFBFBD>ge ({registerApps.length})</h3>
<div className="space-y-3">
{registerApps.length ? registerApps.map((app) => (
<Card key={app.id} className="border border-default-100 bg-default-50/20">
@@ -126,7 +141,7 @@ export function Register() {
)) : (
<div className="flex flex-col items-center gap-2 py-8 text-center text-small text-default-400">
<ClipboardList size={24} />
Keine Antr<EFBFBD>ge
Keine Antr<EFBFBD>ge
</div>
)}
</div>

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, Button, Chip, Switch, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, Button, Chip, Switch, Separator, TextField, Label } from '@heroui/react';
import { Activity, Save, Trash2, Plus, BarChart3 } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -16,23 +16,27 @@ export function ServerStats() {
<h3 className="text-base font-semibold">Konfiguration</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Switch isSelected={statsDraft?.enabled === true} onValueChange={(v) => setStatsDraft((s) => ({ ...(s || {}), enabled: v }))}>
<Switch isSelected={statsDraft?.enabled === true} onChange={(v) => setStatsDraft((s) => ({ ...(s || {}), enabled: v }))}>
<div className="flex items-center gap-2"><BarChart3 size={16} /> Server Stats aktiv</div>
</Switch>
<TextField>
<Label>Kategorie-Name</Label>
<Input
label="Kategorie-Name"
placeholder="?? Server Stats"
value={statsDraft?.categoryName || ''}
onValueChange={(v) => setStatsDraft((s) => ({ ...(s || {}), categoryName: v }))}
onChange={(e) => setStatsDraft((s) => ({ ...(s || {}), categoryName: e.target.value }))}
/>
</TextField>
<TextField>
<Label>Refresh (Minuten)</Label>
<Input
label="Refresh (Minuten)"
type="number"
value={String(statsDraft?.refreshMinutes || 10)}
onValueChange={(v) => setStatsDraft((s) => ({ ...(s || {}), refreshMinutes: Number(v || 10) }))}
onChange={(e) => setStatsDraft((s) => ({ ...(s || {}), refreshMinutes: Number(e.target.value || 10) }))}
/>
</TextField>
<Button color="primary" startContent={<Save size={16} />} onPress={saveServerStats}>
Server Stats speichern
@@ -66,9 +70,9 @@ export function ServerStats() {
<Separator />
<div>
<h4 className="text-small font-semibold mb-2">Item hinzuf<EFBFBD>gen</h4>
<h4 className="text-small font-semibold mb-2">Item hinzuf<EFBFBD>gen</h4>
<div className="flex flex-col gap-2">
<Input placeholder="Label" value={statsItemDraft.label} onValueChange={(v) => setStatsItemDraft((s) => ({ ...s, label: v }))} />
<Input placeholder="Label" value={statsItemDraft.label} onChange={(e) => setStatsItemDraft((s) => ({ ...s, label: e.target.value }))} />
<select
className="w-full rounded-xl border border-default-200 bg-default-50 px-3 py-2 text-sm outline-none"
value={statsItemDraft.type}
@@ -82,7 +86,7 @@ export function ServerStats() {
<option value="custom">Custom</option>
</select>
<Button size="sm" color="primary" startContent={<Plus size={14} />} onPress={addStatsItem}>
Hinzuf<EFBFBD>gen
Hinzuf<EFBFBD>gen
</Button>
</div>
</div>

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, Button, Switch, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, Button, Switch, Separator, TextField, Label } from '@heroui/react';
import { Settings, Save, Logs, Bell, Shield, Edit3, Trash2 } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -14,26 +14,32 @@ export function SettingsPage() {
<h3 className="text-base font-semibold">Allgemein</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<TextField>
<Label>Welcome Channel ID</Label>
<Input
label="Welcome Channel ID"
placeholder="Channel ID"
value={settings.welcomeChannelId || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, welcomeChannelId: v }))}
onChange={(e) => setSettings((s) => ({ ...s, welcomeChannelId: e.target.value }))}
/>
</TextField>
<TextField>
<Label>Log Channel ID</Label>
<Input
label="Log Channel ID"
placeholder="Channel ID"
value={settings.logChannelId || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, logChannelId: v }))}
onChange={(e) => setSettings((s) => ({ ...s, logChannelId: e.target.value }))}
/>
</TextField>
<TextField>
<Label>Support Role ID</Label>
<Input
label="Support Role ID"
placeholder="Role ID"
value={settings.supportRoleId || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, supportRoleId: v }))}
onChange={(e) => setSettings((s) => ({ ...s, supportRoleId: e.target.value }))}
/>
</TextField>
<Separator />
@@ -48,23 +54,23 @@ export function SettingsPage() {
<h3 className="text-base font-semibold">Logging Kategorien</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Switch isSelected={settings.loggingConfig?.categories?.joinLeave !== false} onValueChange={(v) => setSettings((s) => ({ ...s, loggingConfig: { ...(s.loggingConfig || {}), categories: { ...(s.loggingConfig?.categories || {}), joinLeave: v } } }))}>
<Switch isSelected={settings.loggingConfig?.categories?.joinLeave !== false} onChange={(v) => setSettings((s) => ({ ...s, loggingConfig: { ...(s.loggingConfig || {}), categories: { ...(s.loggingConfig?.categories || {}), joinLeave: v } } }))}>
<div className="flex items-center gap-2"><Logs size={14} /> Join / Leave loggen</div>
</Switch>
<Switch isSelected={settings.loggingConfig?.categories?.messageEdit !== false} onValueChange={(v) => setSettings((s) => ({ ...s, loggingConfig: { ...(s.loggingConfig || {}), categories: { ...(s.loggingConfig?.categories || {}), messageEdit: v } } }))}>
<Switch isSelected={settings.loggingConfig?.categories?.messageEdit !== false} onChange={(v) => setSettings((s) => ({ ...s, loggingConfig: { ...(s.loggingConfig || {}), categories: { ...(s.loggingConfig?.categories || {}), messageEdit: v } } }))}>
<div className="flex items-center gap-2"><Edit3 size={14} /> Message Edit loggen</div>
</Switch>
<Switch isSelected={settings.loggingConfig?.categories?.messageDelete !== false} onValueChange={(v) => setSettings((s) => ({ ...s, loggingConfig: { ...(s.loggingConfig || {}), categories: { ...(s.loggingConfig?.categories || {}), messageDelete: v } } }))}>
<Switch isSelected={settings.loggingConfig?.categories?.messageDelete !== false} onChange={(v) => setSettings((s) => ({ ...s, loggingConfig: { ...(s.loggingConfig || {}), categories: { ...(s.loggingConfig?.categories || {}), messageDelete: v } } }))}>
<div className="flex items-center gap-2"><Trash2 size={14} /> Message Delete loggen</div>
</Switch>
<Switch isSelected={settings.loggingConfig?.categories?.automodActions !== false} onValueChange={(v) => setSettings((s) => ({ ...s, loggingConfig: { ...(s.loggingConfig || {}), categories: { ...(s.loggingConfig?.categories || {}), automodActions: v } } }))}>
<Switch isSelected={settings.loggingConfig?.categories?.automodActions !== false} onChange={(v) => setSettings((s) => ({ ...s, loggingConfig: { ...(s.loggingConfig || {}), categories: { ...(s.loggingConfig?.categories || {}), automodActions: v } } }))}>
<div className="flex items-center gap-2"><Shield size={14} /> Automod Actions loggen</div>
</Switch>
<Switch isSelected={settings.loggingConfig?.categories?.ticketActions !== false} onValueChange={(v) => setSettings((s) => ({ ...s, loggingConfig: { ...(s.loggingConfig || {}), categories: { ...(s.loggingConfig?.categories || {}), ticketActions: v } } }))}>
<Switch isSelected={settings.loggingConfig?.categories?.ticketActions !== false} onChange={(v) => setSettings((s) => ({ ...s, loggingConfig: { ...(s.loggingConfig || {}), categories: { ...(s.loggingConfig?.categories || {}), ticketActions: v } } }))}>
<div className="flex items-center gap-2"><Bell size={14} /> Ticket Actions loggen</div>
</Switch>

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, Button, Chip, Switch, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, Button, Chip, Switch, Separator, TextField, Label } from '@heroui/react';
import { RadioTower, Save, Trash2, Plus, Activity as ActivityIcon } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -17,23 +17,27 @@ export function Statuspage() {
<h3 className="text-base font-semibold">Konfiguration</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Switch isSelected={statusDraft?.enabled !== false} onValueChange={(v) => setStatusDraft((s) => ({ ...(s || {}), enabled: v }))}>
<Switch isSelected={statusDraft?.enabled !== false} onChange={(v) => setStatusDraft((s) => ({ ...(s || {}), enabled: v }))}>
<div className="flex items-center gap-2"><RadioTower size={16} /> Statuspage aktiv</div>
</Switch>
<TextField>
<Label>Channel ID</Label>
<Input
label="Channel ID"
placeholder="Channel f<>r Status-Updates"
placeholder="Channel f<>r Status-Updates"
value={statusDraft?.channelId || ''}
onValueChange={(v) => setStatusDraft((s) => ({ ...(s || {}), channelId: v }))}
onChange={(e) => setStatusDraft((s) => ({ ...(s || {}), channelId: e.target.value }))}
/>
</TextField>
<TextField>
<Label>Intervall (ms)</Label>
<Input
label="Intervall (ms)"
type="number"
value={String(statusDraft?.intervalMs || 60000)}
onValueChange={(v) => setStatusDraft((s) => ({ ...(s || {}), intervalMs: Number(v || 60000) }))}
onChange={(e) => setStatusDraft((s) => ({ ...(s || {}), intervalMs: Number(e.target.value || 60000) }))}
/>
</TextField>
<Button color="primary" startContent={<Save size={16} />} onPress={saveStatuspage}>
Statuspage speichern
@@ -76,12 +80,12 @@ export function Statuspage() {
<Separator />
<div>
<h4 className="text-small font-semibold mb-2">Service hinzuf<EFBFBD>gen</h4>
<h4 className="text-small font-semibold mb-2">Service hinzuf<EFBFBD>gen</h4>
<div className="flex flex-col gap-2">
<Input placeholder="Name" value={statusServiceDraft.name} onValueChange={(v) => setStatusServiceDraft((s) => ({ ...s, name: v }))} />
<Input placeholder="URL (optional)" value={statusServiceDraft.url} onValueChange={(v) => setStatusServiceDraft((s) => ({ ...s, url: v }))} />
<Input placeholder="Name" value={statusServiceDraft.name} onChange={(e) => setStatusServiceDraft((s) => ({ ...s, name: e.target.value }))} />
<Input placeholder="URL (optional)" value={statusServiceDraft.url} onChange={(e) => setStatusServiceDraft((s) => ({ ...s, url: e.target.value }))} />
<Button size="sm" color="primary" startContent={<Plus size={14} />} onPress={addStatusService}>
Hinzuf<EFBFBD>gen
Hinzuf<EFBFBD>gen
</Button>
</div>
</div>

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Switch, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Switch, Separator, TextField, Label } from '@heroui/react';
import { LogIn, UserRound, Eye, Save, Send, RefreshCw } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -7,7 +7,7 @@ export function SupportLogin() {
const { supportLogin, setSupportLogin, saveSupportLogin } = useApp();
return (
<SectionCard title="Support Login" subtitle="Login-Panel f<>r Supporter konfigurieren">
<SectionCard title="Support Login" subtitle="Login-Panel f<>r Supporter konfigurieren">
<div className="grid gap-5 xl:grid-cols-[1fr_400px]">
<Card className="border border-default-100 bg-default-50/20">
<CardHeader className="px-5 pt-5 pb-0">
@@ -16,41 +16,51 @@ export function SupportLogin() {
<CardContent className="flex flex-col gap-4 p-5">
<Switch
isSelected={supportLogin?.config?.autoRefresh !== false}
onValueChange={(v) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, autoRefresh: v } } : s)}
onChange={(v) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, autoRefresh: v } } : s)}
>
Auto-Refresh aktiv
</Switch>
<TextField>
<Label>Panel Channel ID</Label>
<Input
label="Panel Channel ID"
placeholder="Channel ID eingeben"
value={supportLogin?.config?.panelChannelId || ''}
onValueChange={(v) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, panelChannelId: v } } : s)}
onChange={(e) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, panelChannelId: e.target.value } } : s)}
/>
</TextField>
<TextField>
<Label>Titel</Label>
<Input
label="Titel"
value={supportLogin?.config?.title || 'Support Login'}
onValueChange={(v) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, title: v } } : s)}
onChange={(e) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, title: e.target.value } } : s)}
/>
</TextField>
<TextField>
<Label>Beschreibung</Label>
<TextArea
label="Beschreibung"
value={supportLogin?.config?.description || ''}
onValueChange={(v) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, description: v } } : s)}
onChange={(e) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, description: e.target.value } } : s)}
/>
</TextField>
<TextField>
<Label>Login Button Label</Label>
<Input
label="Login Button Label"
value={supportLogin?.config?.loginLabel || 'Ich bin jetzt im Support'}
onValueChange={(v) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, loginLabel: v } } : s)}
onChange={(e) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, loginLabel: e.target.value } } : s)}
/>
</TextField>
<TextField>
<Label>Logout Button Label</Label>
<Input
label="Logout Button Label"
value={supportLogin?.config?.logoutLabel || 'Ich bin nicht mehr im Support'}
onValueChange={(v) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, logoutLabel: v } } : s)}
onChange={(e) => setSupportLogin((s) => s ? { ...s, config: { ...s.config, logoutLabel: e.target.value } } : s)}
/>
</TextField>
<Separator />

View File

@@ -1,5 +1,5 @@
import { useMemo } from 'react';
import { Card, CardContent, CardHeader, Chip, Button, Tabs, Tab, Input, TextArea, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Chip, Button, Tabs, Tab, Input, TextArea, Separator, TextField, Label } from '@heroui/react';
import { Ticket, Clock, UserRound, CheckCircle, MessageSquare, FileText, Pencil, Trash2, ChevronRight } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { formatDate } from '../utils/formatters';
@@ -22,11 +22,11 @@ export function Tickets() {
return (
<SectionCard title="Ticketsystem" subtitle="Ticket-Übersicht, Pipeline, SLA, Automationen und Knowledge Base.">
<Tabs aria-label="Ticket Tabs" color="primary" selectedKey={ticketTab} variant="bordered" onSelectionChange={(key) => setTicketTab(String(key))}>
<Tab key="overview" title="Übersicht" />
<Tab key="pipeline" title="Pipeline" />
<Tab key="sla" title="SLA" />
<Tab key="automations" title="Automationen" />
<Tab key="kb" title="Knowledge Base" />
<Tab key="overview">Übersicht</Tab>
<Tab key="pipeline">Pipeline</Tab>
<Tab key="sla">SLA</Tab>
<Tab key="automations">Automationen</Tab>
<Tab key="kb">Knowledge Base</Tab>
</Tabs>
{ticketTab === 'overview' && (
@@ -256,9 +256,18 @@ export function Tickets() {
<h3 className="text-base font-semibold">Automation bearbeiten</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Input label="Name" value={automationEditDraft.name} onValueChange={(v) => setAutomationEditDraft((s) => ({ ...s, name: v }))} />
<Input label="Kategorie / Zustand" value={automationEditDraft.conditionValue} onValueChange={(v) => setAutomationEditDraft((s) => ({ ...s, conditionValue: v }))} />
<TextArea label="Aktion / Nachricht" value={automationEditDraft.actionValue} onValueChange={(v) => setAutomationEditDraft((s) => ({ ...s, actionValue: v }))} />
<TextField>
<Label>Name</Label>
<Input value={automationEditDraft.name} onChange={(e) => setAutomationEditDraft((s) => ({ ...s, name: e.target.value }))} />
</TextField>
<TextField>
<Label>Kategorie / Zustand</Label>
<Input value={automationEditDraft.conditionValue} onChange={(e) => setAutomationEditDraft((s) => ({ ...s, conditionValue: e.target.value }))} />
</TextField>
<TextField>
<Label>Aktion / Nachricht</Label>
<TextArea value={automationEditDraft.actionValue} onChange={(e) => setAutomationEditDraft((s) => ({ ...s, actionValue: e.target.value }))} />
</TextField>
<div className="flex gap-2">
<Button color="primary" onPress={() => updateAutomation(automationEditDraft.id)}>Aktualisieren</Button>
<Button variant="flat" onPress={() => setAutomationEditDraft(null)}>Abbrechen</Button>
@@ -271,9 +280,18 @@ export function Tickets() {
<h3 className="text-base font-semibold">Neue Automation</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Input label="Name" value={automationDraft.name} onValueChange={(v) => setAutomationDraft((s) => ({ ...s, name: v }))} />
<Input label="Kategorie / Zustand" value={automationDraft.conditionValue} onValueChange={(v) => setAutomationDraft((s) => ({ ...s, conditionValue: v }))} />
<TextArea label="Aktion / Nachricht" value={automationDraft.actionValue} onValueChange={(v) => setAutomationDraft((s) => ({ ...s, actionValue: v }))} />
<TextField>
<Label>Name</Label>
<Input value={automationDraft.name} onChange={(e) => setAutomationDraft((s) => ({ ...s, name: e.target.value }))} />
</TextField>
<TextField>
<Label>Kategorie / Zustand</Label>
<Input value={automationDraft.conditionValue} onChange={(e) => setAutomationDraft((s) => ({ ...s, conditionValue: e.target.value }))} />
</TextField>
<TextField>
<Label>Aktion / Nachricht</Label>
<TextArea value={automationDraft.actionValue} onChange={(e) => setAutomationDraft((s) => ({ ...s, actionValue: e.target.value }))} />
</TextField>
<Button color="primary" onPress={saveAutomation}>Automation speichern</Button>
</CardContent>
</Card>
@@ -315,9 +333,18 @@ export function Tickets() {
<h3 className="text-base font-semibold">Artikel bearbeiten</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Input label="Titel" value={kbEditDraft.title} onValueChange={(v) => setKbEditDraft((s) => ({ ...s, title: v }))} />
<Input label="Keywords" value={kbEditDraft.keywords} onValueChange={(v) => setKbEditDraft((s) => ({ ...s, keywords: v }))} />
<TextArea label="Inhalt" minRows={5} value={kbEditDraft.content} onValueChange={(v) => setKbEditDraft((s) => ({ ...s, content: v }))} />
<TextField>
<Label>Titel</Label>
<Input value={kbEditDraft.title} onChange={(e) => setKbEditDraft((s) => ({ ...s, title: e.target.value }))} />
</TextField>
<TextField>
<Label>Keywords</Label>
<Input value={kbEditDraft.keywords} onChange={(e) => setKbEditDraft((s) => ({ ...s, keywords: e.target.value }))} />
</TextField>
<TextField>
<Label>Inhalt</Label>
<TextArea minRows={5} value={kbEditDraft.content} onChange={(e) => setKbEditDraft((s) => ({ ...s, content: e.target.value }))} />
</TextField>
<div className="flex gap-2">
<Button color="primary" onPress={() => updateKbArticle(kbEditDraft.id)}>Aktualisieren</Button>
<Button variant="flat" onPress={() => setKbEditDraft(null)}>Abbrechen</Button>
@@ -330,9 +357,18 @@ export function Tickets() {
<h3 className="text-base font-semibold">Neuer KB-Artikel</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Input label="Titel" value={kbDraft.title} onValueChange={(v) => setKbDraft((s) => ({ ...s, title: v }))} />
<Input label="Keywords" value={kbDraft.keywords} onValueChange={(v) => setKbDraft((s) => ({ ...s, keywords: v }))} />
<TextArea label="Inhalt" minRows={5} value={kbDraft.content} onValueChange={(v) => setKbDraft((s) => ({ ...s, content: v }))} />
<TextField>
<Label>Titel</Label>
<Input value={kbDraft.title} onChange={(e) => setKbDraft((s) => ({ ...s, title: e.target.value }))} />
</TextField>
<TextField>
<Label>Keywords</Label>
<Input value={kbDraft.keywords} onChange={(e) => setKbDraft((s) => ({ ...s, keywords: e.target.value }))} />
</TextField>
<TextField>
<Label>Inhalt</Label>
<TextArea minRows={5} value={kbDraft.content} onChange={(e) => setKbDraft((s) => ({ ...s, content: e.target.value }))} />
</TextField>
<Button color="primary" onPress={saveKbArticle}>Artikel speichern</Button>
</CardContent>
</Card>

View File

@@ -1,4 +1,4 @@
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Switch, Separator } from '@heroui/react';
import { Card, CardContent, CardHeader, Input, TextArea, Button, Chip, Switch, Separator, TextField, Label } from '@heroui/react';
import { Sparkles, Save, Eye } from 'lucide-react';
import { useApp } from '../context/AppContext';
import { SectionCard } from '../components/shared/SectionCard';
@@ -14,37 +14,45 @@ export function Welcome() {
<h3 className="text-base font-semibold">Welcome konfigurieren</h3>
</CardHeader>
<CardContent className="flex flex-col gap-4 p-5">
<Switch isSelected={settings.welcomeConfig?.enabled !== false} onValueChange={(v) => setSettings((s) => ({ ...s, welcomeConfig: { ...(s.welcomeConfig || {}), enabled: v } }))}>
<Switch isSelected={settings.welcomeConfig?.enabled !== false} onChange={(v) => setSettings((s) => ({ ...s, welcomeConfig: { ...(s.welcomeConfig || {}), enabled: v } }))}>
<div className="flex items-center gap-2"><Sparkles size={16} /> Welcome aktiv</div>
</Switch>
<TextField>
<Label>Channel ID</Label>
<Input
label="Channel ID"
placeholder="Channel ID f<>r Willkommensnachrichten"
placeholder="Channel ID f<>r Willkommensnachrichten"
value={settings.welcomeConfig?.channelId || settings.welcomeChannelId || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, welcomeConfig: { ...(s.welcomeConfig || {}), channelId: v } }))}
onChange={(e) => setSettings((s) => ({ ...s, welcomeConfig: { ...(s.welcomeConfig || {}), channelId: e.target.value } }))}
/>
</TextField>
<TextField>
<Label>Titel</Label>
<Input
label="Titel"
placeholder="Willkommen {user}!"
value={settings.welcomeConfig?.embedTitle || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, welcomeConfig: { ...(s.welcomeConfig || {}), embedTitle: v } }))}
onChange={(e) => setSettings((s) => ({ ...s, welcomeConfig: { ...(s.welcomeConfig || {}), embedTitle: e.target.value } }))}
/>
</TextField>
<TextField>
<Label>Beschreibung</Label>
<TextArea
label="Beschreibung"
placeholder="Beschreibung des Embeds"
value={settings.welcomeConfig?.embedDescription || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, welcomeConfig: { ...(s.welcomeConfig || {}), embedDescription: v } }))}
onChange={(e) => setSettings((s) => ({ ...s, welcomeConfig: { ...(s.welcomeConfig || {}), embedDescription: e.target.value } }))}
/>
</TextField>
<TextField>
<Label>Footer</Label>
<Input
label="Footer"
placeholder={new Date().getFullYear().toString()}
value={settings.welcomeConfig?.embedFooter || ''}
onValueChange={(v) => setSettings((s) => ({ ...s, welcomeConfig: { ...(s.welcomeConfig || {}), embedFooter: v } }))}
onChange={(e) => setSettings((s) => ({ ...s, welcomeConfig: { ...(s.welcomeConfig || {}), embedFooter: e.target.value } }))}
/>
</TextField>
<Separator />
@@ -75,7 +83,7 @@ export function Welcome() {
</div>
</div>
<p className="mt-3 text-tiny text-default-400">
Nutze {'{user}'} f<EFBFBD>r den Benutzernamen und {'{server}'} f<EFBFBD>r den Servernamen.
Nutze {'{user}'} f<EFBFBD>r den Benutzernamen und {'{server}'} f<EFBFBD>r den Servernamen.
</p>
</CardContent>
</Card>