From ed2ea23e87eedca147abdf35e38695ef520fc5ee Mon Sep 17 00:00:00 2001 From: TheOnlyMace <0815cracky@gmail.com> Date: Wed, 22 Jul 2026 21:23:57 +0200 Subject: [PATCH] Refactor channel and role selection in various forms to use Discord components - Replaced standard input fields with DiscordChannelSelect and DiscordRoleSelect components in the BirthdaysForm, FeedsManager, GiveawaysManager, LevelingForm, LoggingForm, SchedulerManager, SelfRolesManager, StatsForm, SuggestionsConfigForm, TagsManager, TempVoiceForm, TicketConfigForm, and StarboardForm for improved user experience and consistency. - Updated state management to accommodate new component structures, enhancing data handling for channel and role selections across the application. --- .../app/dashboard/[guildId]/commands/page.tsx | 8 +- .../src/components/modules/birthdays-form.tsx | 20 ++- .../components/modules/commands-manager.tsx | 20 +-- .../src/components/modules/feeds-manager.tsx | 13 +- .../components/modules/giveaways-manager.tsx | 13 +- .../src/components/modules/leveling-form.tsx | 56 ++---- .../src/components/modules/logging-form.tsx | 61 +++---- .../components/modules/scheduler-manager.tsx | 13 +- .../components/modules/selfroles-manager.tsx | 16 +- .../src/components/modules/starboard-form.tsx | 64 +++---- .../src/components/modules/stats-form.tsx | 31 +++- .../modules/suggestions-config-form.tsx | 32 ++-- .../src/components/modules/tags-manager.tsx | 63 ++++--- .../src/components/modules/tempvoice-form.tsx | 21 ++- .../modules/ticket-categories-manager.tsx | 36 ++-- .../components/modules/ticket-config-form.tsx | 22 ++- .../components/modules/verification-form.tsx | 20 ++- .../src/components/modules/welcome-form.tsx | 62 +++---- .../components/settings/access-rules-form.tsx | 24 +-- .../components/ui/discord-channel-select.tsx | 161 ++++++++++++++++++ .../src/components/ui/discord-role-select.tsx | 144 ++++++++++++++++ apps/webui/src/hooks/use-guild-channels.ts | 56 ++++++ apps/webui/src/hooks/use-guild-roles.ts | 56 ++++++ apps/webui/src/lib/discord-guild-resources.ts | 31 +++- apps/webui/src/messages/de.json | 3 +- apps/webui/src/messages/en.json | 3 +- docs/PHASE-TRACKING.md | 22 +++ 27 files changed, 749 insertions(+), 322 deletions(-) create mode 100644 apps/webui/src/components/ui/discord-channel-select.tsx create mode 100644 apps/webui/src/components/ui/discord-role-select.tsx create mode 100644 apps/webui/src/hooks/use-guild-channels.ts create mode 100644 apps/webui/src/hooks/use-guild-roles.ts diff --git a/apps/webui/src/app/dashboard/[guildId]/commands/page.tsx b/apps/webui/src/app/dashboard/[guildId]/commands/page.tsx index 01ac972..68d7f21 100644 --- a/apps/webui/src/app/dashboard/[guildId]/commands/page.tsx +++ b/apps/webui/src/app/dashboard/[guildId]/commands/page.tsx @@ -1,6 +1,6 @@ import { Suspense } from 'react'; import { CommandsManager } from '@/components/modules/commands-manager'; -import { listGuildChannelsForDashboard, listGuildRolesForDashboard } from '@/lib/discord-guild-resources'; +import { listGuildChannelsForDashboard } from '@/lib/discord-guild-resources'; import { getLocale, t } from '@/lib/i18n'; import { getCommandGlobalRules } from '@/lib/module-configs/command-global-rules'; import { listCommandOverridesDashboard } from '@/lib/module-configs/commands'; @@ -11,12 +11,11 @@ interface CommandsPageProps { export default async function CommandsPage({ params }: CommandsPageProps) { const { guildId } = await params; - const [locale, commands, globalRules, channels, roles] = await Promise.all([ + const [locale, commands, globalRules, channels] = await Promise.all([ getLocale(), listCommandOverridesDashboard(guildId), getCommandGlobalRules(guildId), - listGuildChannelsForDashboard(guildId).catch(() => []), - listGuildRolesForDashboard(guildId).catch(() => []) + listGuildChannelsForDashboard(guildId).catch(() => []) ]); return ( @@ -31,7 +30,6 @@ export default async function CommandsPage({ params }: CommandsPageProps) { initialCommands={commands} initialGlobalRules={globalRules} channels={channels} - roles={roles} /> diff --git a/apps/webui/src/components/modules/birthdays-form.tsx b/apps/webui/src/components/modules/birthdays-form.tsx index c1f3dde..cccadb4 100644 --- a/apps/webui/src/components/modules/birthdays-form.tsx +++ b/apps/webui/src/components/modules/birthdays-form.tsx @@ -5,6 +5,8 @@ import { useId } from 'react'; import { FieldAnchor } from '@/components/layout/field-anchor'; import { useTranslations } from '@/components/locale-provider'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { DiscordChannelSelect } from '@/components/ui/discord-channel-select'; +import { DiscordRoleSelect } from '@/components/ui/discord-role-select'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Switch } from '@/components/ui/switch'; @@ -36,8 +38,6 @@ interface BirthdaysFormProps { export function BirthdaysForm({ guildId, initialValue }: BirthdaysFormProps) { const t = useTranslations(); - const channelId = useId(); - const roleId = useId(); const timezoneId = useId(); return ( @@ -58,14 +58,22 @@ export function BirthdaysForm({ guildId, initialValue }: BirthdaysFormProps) {
- - setValue((prev) => ({ ...prev, channelId: event.target.value.trim() }))} placeholder="123456789012345678" /> + + setValue((prev) => ({ ...prev, channelId }))} + />
- - setValue((prev) => ({ ...prev, roleId: event.target.value.trim() }))} placeholder="123456789012345678" /> + + setValue((prev) => ({ ...prev, roleId }))} + />
diff --git a/apps/webui/src/components/modules/commands-manager.tsx b/apps/webui/src/components/modules/commands-manager.tsx index b9f4866..8d45084 100644 --- a/apps/webui/src/components/modules/commands-manager.tsx +++ b/apps/webui/src/components/modules/commands-manager.tsx @@ -3,8 +3,7 @@ import type { CommandGlobalRules, CommandOverrideDashboard, - DiscordChannelOption, - DiscordRoleOption + DiscordChannelOption } from '@nexumi/shared'; import { RotateCcw } from 'lucide-react'; import { useSearchParams } from 'next/navigation'; @@ -14,6 +13,7 @@ import { useTranslations } from '@/components/locale-provider'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; import { DiscordResourceMultiSelect } from '@/components/ui/discord-resource-multi-select'; +import { DiscordRoleMultiSelect } from '@/components/ui/discord-role-select'; import { Input } from '@/components/ui/input'; import { Switch } from '@/components/ui/switch'; @@ -26,15 +26,13 @@ interface CommandsManagerProps { initialCommands: CommandOverrideDashboard[]; initialGlobalRules: CommandGlobalRules; channels: DiscordChannelOption[]; - roles: DiscordRoleOption[]; } export function CommandsManager({ guildId, initialCommands, initialGlobalRules, - channels, - roles + channels }: CommandsManagerProps) { const t = useTranslations(); const searchParams = useSearchParams(); @@ -48,10 +46,6 @@ export function CommandsManager({ () => channels.map((channel) => ({ id: channel.id, name: channel.name, prefix: '#' })), [channels] ); - const roleOptions = useMemo( - () => roles.map((role) => ({ id: role.id, name: role.name, prefix: '@' })), - [roles] - ); useEffect(() => { const q = searchParams.get('q'); @@ -250,8 +244,8 @@ export function CommandsManager({

{t('modulePages.commands.allowedRoleIds')}

- patchLocal(entry.commandName, { allowedRoleIds })} placeholder={t('modulePages.commands.roleSearchPlaceholder')} @@ -260,8 +254,8 @@ export function CommandsManager({

{t('modulePages.commands.deniedRoleIds')}

- patchLocal(entry.commandName, { deniedRoleIds })} placeholder={t('modulePages.commands.roleSearchPlaceholder')} diff --git a/apps/webui/src/components/modules/feeds-manager.tsx b/apps/webui/src/components/modules/feeds-manager.tsx index b4a55bd..a8ba0d0 100644 --- a/apps/webui/src/components/modules/feeds-manager.tsx +++ b/apps/webui/src/components/modules/feeds-manager.tsx @@ -8,6 +8,8 @@ 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 { DiscordChannelSelect } from '@/components/ui/discord-channel-select'; +import { DiscordRoleSelect } from '@/components/ui/discord-role-select'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; @@ -182,17 +184,18 @@ export function FeedsManager({ guildId, initialFeeds }: FeedsManagerProps) {
- setDraft((prev) => ({ ...prev, channelId: event.target.value }))} - placeholder="123456789012345678" + onChange={(channelId) => setDraft((prev) => ({ ...prev, channelId }))} />
- setDraft((prev) => ({ ...prev, rolePingId: event.target.value }))} + onChange={(rolePingId) => setDraft((prev) => ({ ...prev, rolePingId }))} placeholder={t('common.optional')} />
diff --git a/apps/webui/src/components/modules/giveaways-manager.tsx b/apps/webui/src/components/modules/giveaways-manager.tsx index 788418b..2d285b5 100644 --- a/apps/webui/src/components/modules/giveaways-manager.tsx +++ b/apps/webui/src/components/modules/giveaways-manager.tsx @@ -9,6 +9,8 @@ import { useTranslations } from '@/components/locale-provider'; import { Badge } from '@/components/ui/badge'; import { Button } from '@/components/ui/button'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { DiscordChannelSelect } from '@/components/ui/discord-channel-select'; +import { DiscordRoleSelect } from '@/components/ui/discord-role-select'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; @@ -127,10 +129,10 @@ export function GiveawaysManager({ guildId, initialGiveaways }: GiveawaysManager
- setDraft((prev) => ({ ...prev, channelId: event.target.value }))} - placeholder="123456789012345678" + onChange={(channelId) => setDraft((prev) => ({ ...prev, channelId }))} />
@@ -156,9 +158,10 @@ export function GiveawaysManager({ guildId, initialGiveaways }: GiveawaysManager
- setDraft((prev) => ({ ...prev, requiredRoleId: event.target.value }))} + onChange={(requiredRoleId) => setDraft((prev) => ({ ...prev, requiredRoleId }))} placeholder={t('common.optional')} />
diff --git a/apps/webui/src/components/modules/leveling-form.tsx b/apps/webui/src/components/modules/leveling-form.tsx index e537e3c..3aab9a2 100644 --- a/apps/webui/src/components/modules/leveling-form.tsx +++ b/apps/webui/src/components/modules/leveling-form.tsx @@ -1,10 +1,11 @@ 'use client'; import type { LevelingConfigDashboard } from '@nexumi/shared'; -import { useId } from 'react'; import { FieldAnchor } from '@/components/layout/field-anchor'; import { useTranslations } from '@/components/locale-provider'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; +import { DiscordChannelMultiSelect, DiscordChannelSelect } from '@/components/ui/discord-channel-select'; +import { DiscordRoleMultiSelect } from '@/components/ui/discord-role-select'; import { Input } from '@/components/ui/input'; import { Label } from '@/components/ui/label'; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'; @@ -14,24 +15,11 @@ import type { SettingsSaveResult } from '@/components/settings/settings-form'; import { SettingsForm } from '@/components/settings/settings-form'; interface LocalLevelingValue - extends Omit { - noXpChannelIdsText: string; - noXpRoleIdsText: string; + extends Omit { roleMultipliersText: string; channelMultipliersText: string; } -function toCsv(ids: string[]): string { - return ids.join(', '); -} - -function fromCsv(text: string): string[] { - return text - .split(',') - .map((entry) => entry.trim()) - .filter((entry) => entry.length > 0); -} - function toMultiplierText(map: Record): string { return Object.entries(map) .map(([id, multiplier]) => `${id}:${multiplier}`) @@ -58,11 +46,9 @@ function fromMultiplierText(text: string): { value: Record; erro } function toLocal(config: LevelingConfigDashboard): LocalLevelingValue { - const { noXpChannelIds, noXpRoleIds, roleMultipliers, channelMultipliers, ...rest } = config; + const { roleMultipliers, channelMultipliers, ...rest } = config; return { ...rest, - noXpChannelIdsText: toCsv(noXpChannelIds), - noXpRoleIdsText: toCsv(noXpRoleIds), roleMultipliersText: toMultiplierText(roleMultipliers), channelMultipliersText: toMultiplierText(channelMultipliers) }; @@ -78,12 +64,10 @@ async function saveLeveling(guildId: string, value: LocalLevelingValue): Promise return { ok: false, error: channelMultipliers.error }; } - const { noXpChannelIdsText, noXpRoleIdsText, roleMultipliersText: _roleMultipliersText, channelMultipliersText: _channelMultipliersText, ...rest } = value; + const { roleMultipliersText: _roleMultipliersText, channelMultipliersText: _channelMultipliersText, ...rest } = value; const payload: LevelingConfigDashboard = { ...rest, - noXpChannelIds: fromCsv(noXpChannelIdsText), - noXpRoleIds: fromCsv(noXpRoleIdsText), roleMultipliers: roleMultipliers.value, channelMultipliers: channelMultipliers.value }; @@ -111,8 +95,6 @@ interface LevelingFormProps { export function LevelingForm({ guildId, initialValue }: LevelingFormProps) { const t = useTranslations(); - const noXpChannelsId = useId(); - const noXpRolesId = useId(); return ( @@ -192,23 +174,21 @@ export function LevelingForm({ guildId, initialValue }: LevelingFormProps) {
- -