'use client'; import type { SelfRoleBehavior, SelfRoleEntry, SelfRoleMode, SelfRolePanelDashboard } from '@nexumi/shared'; import { Plus, Trash2 } from 'lucide-react'; import { useState } from 'react'; import { toast } from 'sonner'; import { FieldAnchor } from '@/components/layout/field-anchor'; import { useTranslations } from '@/components/locale-provider'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; import { Textarea } from '@/components/ui/textarea'; const MODES: SelfRoleMode[] = ['BUTTONS', 'DROPDOWN', 'REACTIONS']; const BEHAVIORS: SelfRoleBehavior[] = ['NORMAL', 'UNIQUE', 'VERIFY', 'TEMPORARY']; function rolesToText(roles: SelfRoleEntry[]): string { return roles.map((role) => [role.roleId, role.label, role.emoji ?? '', role.description ?? ''].join(' | ')).join('\n'); } function textToRoles(text: string): SelfRoleEntry[] { return text .split('\n') .map((line) => line.trim()) .filter((line) => line.length > 0) .map((line) => { const [roleId, label, emoji, description] = line.split('|').map((part) => part.trim()); return { roleId: roleId ?? '', label: label || roleId || '', emoji: emoji || undefined, description: description || undefined }; }) .filter((role) => role.roleId.length > 0); } interface LocalPanel extends SelfRolePanelDashboard { clientKey: string; rolesText: string; saving?: boolean; } const EMPTY_DRAFT = { channelId: '', title: '', description: '', mode: 'BUTTONS' as SelfRoleMode, behavior: 'NORMAL' as SelfRoleBehavior, rolesText: '' }; interface SelfRolesManagerProps { guildId: string; initialPanels: SelfRolePanelDashboard[]; } export function SelfRolesManager({ guildId, initialPanels }: SelfRolesManagerProps) { const t = useTranslations(); const [panels, setPanels] = useState( initialPanels.map((panel, index) => ({ ...panel, clientKey: panel.id ?? `existing-${index}`, rolesText: rolesToText(panel.roles) })) ); const [draft, setDraft] = useState(EMPTY_DRAFT); const [creating, setCreating] = useState(false); async function handleCreate() { const roles = textToRoles(draft.rolesText); if (!draft.channelId.trim() || !draft.title.trim() || roles.length === 0) { toast.error(t('modulePages.selfroles.formIncomplete')); return; } setCreating(true); try { const response = await fetch(`/api/guilds/${guildId}/selfroles`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channelId: draft.channelId.trim(), title: draft.title.trim(), description: draft.description.trim() || null, mode: draft.mode, behavior: draft.behavior, roles }) }); const body = (await response.json().catch(() => null)) as (SelfRolePanelDashboard & { error?: string }) | null; if (!response.ok || !body) { toast.error(body?.error ?? t('common.saveError')); return; } setPanels((prev) => [ { ...body, clientKey: body.id ?? `new-${Date.now()}`, rolesText: rolesToText(body.roles) }, ...prev ]); setDraft(EMPTY_DRAFT); toast.success(t('common.saveSuccess')); } catch { toast.error(t('common.saveError')); } finally { setCreating(false); } } async function handleSave(panel: LocalPanel) { setPanels((prev) => prev.map((entry) => (entry.clientKey === panel.clientKey ? { ...entry, saving: true } : entry))); try { const response = await fetch(`/api/guilds/${guildId}/selfroles/${panel.id}`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ channelId: panel.channelId, title: panel.title, description: panel.description, mode: panel.mode, behavior: panel.behavior, roles: textToRoles(panel.rolesText) }) }); const body = (await response.json().catch(() => null)) as (SelfRolePanelDashboard & { error?: string }) | null; if (!response.ok || !body) { toast.error(body?.error ?? t('common.saveError')); return; } toast.success(t('common.saveSuccess')); } catch { toast.error(t('common.saveError')); } finally { setPanels((prev) => prev.map((entry) => (entry.clientKey === panel.clientKey ? { ...entry, saving: false } : entry))); } } async function handleDelete(panel: LocalPanel) { if (!window.confirm(t('modulePages.selfroles.confirmDelete'))) { return; } try { const response = await fetch(`/api/guilds/${guildId}/selfroles/${panel.id}`, { method: 'DELETE' }); if (!response.ok) { toast.error(t('common.saveError')); return; } setPanels((prev) => prev.filter((entry) => entry.clientKey !== panel.clientKey)); toast.success(t('common.saveSuccess')); } catch { toast.error(t('common.saveError')); } } return (
{t('modulePages.selfroles.createTitle')} {t('modulePages.selfroles.createDescription')}
setDraft((prev) => ({ ...prev, channelId: event.target.value }))} placeholder="123456789012345678" />
setDraft((prev) => ({ ...prev, title: event.target.value }))} />
setDraft((prev) => ({ ...prev, description: event.target.value }))} />