Add new features and enhancements to the WebUI and dashboard components
- Updated package.json to include new dependencies: `@radix-ui/react-dialog` and `cmdk`. - Enhanced global styles in globals.css for improved visual depth in dark mode. - Refactored dashboard page components to improve layout and accessibility, including new icons and responsive design adjustments. - Implemented suspense handling in commands page for better loading states. - Improved sidebar navigation with new icons and mobile responsiveness. - Added FieldAnchor components across various forms for better accessibility and structure. - Enhanced commands manager with search parameter handling for improved user experience. - Updated multiple module forms to include FieldAnchor for consistent layout and accessibility.
This commit is contained in:
@@ -2,7 +2,8 @@
|
||||
|
||||
import type { CommandOverrideDashboard } from '@nexumi/shared';
|
||||
import { RotateCcw } from 'lucide-react';
|
||||
import { useMemo, useState } from 'react';
|
||||
import { useSearchParams } from 'next/navigation';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
import { useTranslations } from '@/components/locale-provider';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -32,9 +33,17 @@ interface CommandsManagerProps {
|
||||
|
||||
export function CommandsManager({ guildId, initialCommands }: CommandsManagerProps) {
|
||||
const t = useTranslations();
|
||||
const searchParams = useSearchParams();
|
||||
const [commands, setCommands] = useState<LocalOverride[]>(initialCommands);
|
||||
const [search, setSearch] = useState('');
|
||||
|
||||
useEffect(() => {
|
||||
const q = searchParams.get('q');
|
||||
if (q) {
|
||||
setSearch(q);
|
||||
}
|
||||
}, [searchParams]);
|
||||
|
||||
const filtered = useMemo(() => {
|
||||
const query = search.trim().toLowerCase();
|
||||
if (!query) {
|
||||
@@ -64,7 +73,9 @@ export function CommandsManager({ guildId, initialCommands }: CommandsManagerPro
|
||||
cooldownSeconds: entry.cooldownSeconds
|
||||
})
|
||||
});
|
||||
const body = (await response.json().catch(() => null)) as (CommandOverrideDashboard & { error?: string }) | null;
|
||||
const body = (await response.json().catch(() => null)) as
|
||||
| (CommandOverrideDashboard & { error?: string })
|
||||
| null;
|
||||
if (!response.ok || !body) {
|
||||
toast.error(body?.error ?? t('common.saveError'));
|
||||
return;
|
||||
@@ -81,7 +92,9 @@ export function CommandsManager({ guildId, initialCommands }: CommandsManagerPro
|
||||
async function handleReset(entry: LocalOverride) {
|
||||
patchLocal(entry.commandName, { saving: true });
|
||||
try {
|
||||
const response = await fetch(`/api/guilds/${guildId}/commands/${entry.commandName}`, { method: 'DELETE' });
|
||||
const response = await fetch(`/api/guilds/${guildId}/commands/${entry.commandName}`, {
|
||||
method: 'DELETE'
|
||||
});
|
||||
const body = (await response.json().catch(() => null)) as CommandOverrideDashboard | null;
|
||||
if (!response.ok || !body) {
|
||||
toast.error(t('common.saveError'));
|
||||
@@ -147,7 +160,9 @@ export function CommandsManager({ guildId, initialCommands }: CommandsManagerPro
|
||||
<p className="text-xs text-muted-foreground">{t('modulePages.commands.allowedRoleIds')}</p>
|
||||
<Input
|
||||
value={toCsv(entry.allowedRoleIds)}
|
||||
onChange={(event) => patchLocal(entry.commandName, { allowedRoleIds: fromCsv(event.target.value) })}
|
||||
onChange={(event) =>
|
||||
patchLocal(entry.commandName, { allowedRoleIds: fromCsv(event.target.value) })
|
||||
}
|
||||
placeholder={t('modulePages.commands.idsPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
@@ -155,7 +170,9 @@ export function CommandsManager({ guildId, initialCommands }: CommandsManagerPro
|
||||
<p className="text-xs text-muted-foreground">{t('modulePages.commands.deniedRoleIds')}</p>
|
||||
<Input
|
||||
value={toCsv(entry.deniedRoleIds)}
|
||||
onChange={(event) => patchLocal(entry.commandName, { deniedRoleIds: fromCsv(event.target.value) })}
|
||||
onChange={(event) =>
|
||||
patchLocal(entry.commandName, { deniedRoleIds: fromCsv(event.target.value) })
|
||||
}
|
||||
placeholder={t('modulePages.commands.idsPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
@@ -164,7 +181,9 @@ export function CommandsManager({ guildId, initialCommands }: CommandsManagerPro
|
||||
<Input
|
||||
value={toCsv(entry.allowedChannelIds)}
|
||||
onChange={(event) =>
|
||||
patchLocal(entry.commandName, { allowedChannelIds: fromCsv(event.target.value) })
|
||||
patchLocal(entry.commandName, {
|
||||
allowedChannelIds: fromCsv(event.target.value)
|
||||
})
|
||||
}
|
||||
placeholder={t('modulePages.commands.idsPlaceholder')}
|
||||
/>
|
||||
@@ -174,18 +193,32 @@ export function CommandsManager({ guildId, initialCommands }: CommandsManagerPro
|
||||
<Input
|
||||
value={toCsv(entry.deniedChannelIds)}
|
||||
onChange={(event) =>
|
||||
patchLocal(entry.commandName, { deniedChannelIds: fromCsv(event.target.value) })
|
||||
patchLocal(entry.commandName, {
|
||||
deniedChannelIds: fromCsv(event.target.value)
|
||||
})
|
||||
}
|
||||
placeholder={t('modulePages.commands.idsPlaceholder')}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button type="button" variant="ghost" size="sm" disabled={entry.saving} onClick={() => handleReset(entry)}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
disabled={entry.saving}
|
||||
onClick={() => handleReset(entry)}
|
||||
>
|
||||
<RotateCcw className="size-4" />
|
||||
{t('modulePages.commands.reset')}
|
||||
</Button>
|
||||
<Button type="button" variant="outline" size="sm" disabled={entry.saving} onClick={() => handleSave(entry)}>
|
||||
<Button
|
||||
type="button"
|
||||
variant="outline"
|
||||
size="sm"
|
||||
disabled={entry.saving}
|
||||
onClick={() => handleSave(entry)}
|
||||
>
|
||||
{entry.saving ? t('common.saving') : t('common.save')}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user