Skip to content
Merged
Show file tree
Hide file tree
Changes from 10 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
1d8d529
feat: shadcn/ui theme setup with dark mode toggle
Mar 1, 2026
eec474f
feat: migrate dashboard to shadcn components (Switch, Input, Label, F…
Mar 1, 2026
a2f4719
feat: add RoleSelector and ChannelSelector components with auto-fill
Mar 1, 2026
77f734f
fix: apply Biome formatting (single quotes + semicolons) to shadcn UI…
Mar 1, 2026
6ba21bf
fix: add accessible labels and connect htmlFor/id pairs for screen re…
Mar 1, 2026
e73a841
fix: form context null guards, PopoverTitle h2, stale roles clear, re…
Mar 1, 2026
e6141b2
fix: move DialogHeader inside DialogContent and fix invalid Tailwind …
Mar 1, 2026
c1cc229
fix(channel-selector): clear stale channels on guild change
Mar 1, 2026
9c4166b
fix(channel-selector): show removable chips for unknown selected IDs
Mar 1, 2026
2eaed47
fix(role-selector): show removable chips for unknown selected role IDs
Mar 1, 2026
90ea779
fix(channel-selector): guard setChannels/setError against stale requests
BillChirico Mar 1, 2026
5e0eb28
fix(form): import LabelPrimitive as value not type
BillChirico Mar 1, 2026
bdb08b1
fix(dialog): add type="button" to footer close button
BillChirico Mar 1, 2026
389d176
fix(providers): wire Toaster theme to resolvedTheme
BillChirico Mar 1, 2026
a166a4f
fix(role-selector): guard setRoles/setError against stale requests
BillChirico Mar 1, 2026
d0afd59
fix: resolve merge conflicts with main
BillChirico Mar 1, 2026
fb97c1b
fix(form): protect FormControl a11y attrs from consumer override
Mar 1, 2026
a73e1b1
fix(form): protect FormLabel htmlFor from consumer override
Mar 1, 2026
458e281
fix(dialog): remove data-slot from non-rendered Radix primitives
Mar 1, 2026
a02c3d7
docs(providers): update JSDoc to reflect resolved theme usage
Mar 1, 2026
c6ffe2e
test(setup): add window.matchMedia polyfill for next-themes in jsdom
Mar 1, 2026
17ab20b
fix(deps): regenerate pnpm lockfile to sync with package.json changes
Mar 1, 2026
e1fa5e4
fix: add missing next-themes dependency to web package.json
Mar 1, 2026
4b6bc14
fix(deps): add react-hook-form dependency to web/package.json
Mar 1, 2026
00001c9
fix: use type-only import for LabelPrimitive in form.tsx
Mar 1, 2026
bddcb85
fix(lint): auto-fix all Biome errors (import type, organize imports, …
Mar 1, 2026
a8742e0
fix(tests): guard matchMedia polyfill with existence check and config…
Mar 1, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 35 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-separator": "^1.1.8",
"@radix-ui/react-slot": "^1.2.4",
"radix-ui": "^1.4.3",
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"cmdk": "^1.1.1",
"diff": "^8.0.3",
"lucide-react": "^0.525.0",
"next": "^16.1.6",
"next-auth": "^4.24.13",
"next-themes": "^0.4.6",
"radix-ui": "^1.4.3",
"react": "^19.2.4",
"react-dom": "^19.2.4",
"recharts": "^3.5.0",
Expand Down
2 changes: 1 addition & 1 deletion web/src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ export const metadata: Metadata = {

export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en" className="dark" suppressHydrationWarning>
<html lang="en" suppressHydrationWarning>
<body className={inter.className}>
<Providers>{children}</Providers>
</body>
Expand Down
21 changes: 14 additions & 7 deletions web/src/components/dashboard/config-sections/AiSection.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
'use client';

import { SystemPromptEditor } from '@/components/dashboard/system-prompt-editor';
import { ToggleSwitch } from '@/components/dashboard/toggle-switch';
import { Card, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import type { GuildConfig } from '@/lib/config-utils';
import { SYSTEM_PROMPT_MAX_LENGTH } from '@/types/config';

Expand All @@ -28,12 +29,18 @@ export function AiSection({
<CardTitle className="text-base">AI Chat</CardTitle>
<CardDescription>Configure the AI assistant behavior.</CardDescription>
</div>
<ToggleSwitch
checked={draftConfig.ai?.enabled ?? false}
onChange={onEnabledChange}
disabled={saving}
label="AI Chat"
/>
<div className="flex items-center gap-2">
<Switch
id="ai-toggle"
checked={draftConfig.ai?.enabled ?? false}
onCheckedChange={onEnabledChange}
disabled={saving}
aria-label="Toggle AI Chat"
/>
<Label htmlFor="ai-toggle" className="sr-only">
AI Chat
</Label>
</div>
</div>
</CardHeader>
</Card>
Expand Down
56 changes: 32 additions & 24 deletions web/src/components/dashboard/config-sections/ModerationSection.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
'use client';

import { ToggleSwitch } from '@/components/dashboard/toggle-switch';
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card';
import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';
import { Switch } from '@/components/ui/switch';
import type { GuildConfig } from '@/lib/config-utils';

const inputClasses =
'w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50';

interface ModerationSectionProps {
draftConfig: GuildConfig;
saving: boolean;
Expand Down Expand Up @@ -36,56 +35,65 @@ export function ModerationSection({
Configure moderation, escalation, and logging settings.
</CardDescription>
</div>
<ToggleSwitch
<Switch
checked={draftConfig.moderation?.enabled ?? false}
onChange={onEnabledChange}
onCheckedChange={onEnabledChange}
disabled={saving}
label="Moderation"
aria-label="Toggle Moderation"
/>
</div>
</CardHeader>
<CardContent className="space-y-4">
<label className="space-y-2">
<span className="text-sm font-medium">Alert Channel ID</span>
<input
<div className="space-y-2">
<Label htmlFor="alert-channel">Alert Channel ID</Label>
<Input
id="alert-channel"
type="text"
value={draftConfig.moderation?.alertChannelId ?? ''}
onChange={(e) => onFieldChange('alertChannelId', e.target.value)}
disabled={saving}
className={inputClasses}
placeholder="Channel ID for moderation alerts"
/>
</label>
</div>
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Auto-delete flagged messages</span>
<ToggleSwitch
<Label htmlFor="auto-delete" className="text-sm font-medium">
Auto-delete flagged messages
</Label>
<Switch
id="auto-delete"
checked={draftConfig.moderation?.autoDelete ?? false}
onChange={(v) => onFieldChange('autoDelete', v)}
onCheckedChange={(v) => onFieldChange('autoDelete', v)}
disabled={saving}
label="Auto Delete"
aria-label="Toggle auto-delete"
/>
</div>
<fieldset className="space-y-2">
<legend className="text-sm font-medium">DM Notifications</legend>
{(['warn', 'timeout', 'kick', 'ban'] as const).map((action) => (
<div key={action} className="flex items-center justify-between">
<span className="text-sm capitalize text-muted-foreground">{action}</span>
<ToggleSwitch
<Label htmlFor={`dm-${action}`} className="text-sm capitalize text-muted-foreground">
{action}
</Label>
<Switch
id={`dm-${action}`}
checked={draftConfig.moderation?.dmNotifications?.[action] ?? false}
onChange={(v) => onDmNotificationChange(action, v)}
onCheckedChange={(v) => onDmNotificationChange(action, v)}
disabled={saving}
label={`DM on ${action}`}
aria-label={`DM on ${action}`}
/>
</div>
))}
</fieldset>
<div className="flex items-center justify-between">
<span className="text-sm font-medium">Escalation Enabled</span>
<ToggleSwitch
<Label htmlFor="escalation" className="text-sm font-medium">
Escalation Enabled
</Label>
<Switch
id="escalation"
checked={draftConfig.moderation?.escalation?.enabled ?? false}
onChange={(v) => onEscalationChange(v)}
onCheckedChange={(v) => onEscalationChange(v)}
disabled={saving}
label="Escalation"
aria-label="Toggle escalation"
/>
</div>
</CardContent>
Expand Down
19 changes: 12 additions & 7 deletions web/src/components/dashboard/config-sections/NumberField.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
const inputClasses =
'w-full rounded-md border bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50';
'use client';

import { useId } from 'react';

import { Input } from '@/components/ui/input';
import { Label } from '@/components/ui/label';

interface NumberFieldProps {
label: string;
Expand All @@ -11,10 +15,12 @@ interface NumberFieldProps {
}

export function NumberField({ label, value, onChange, disabled, min, step }: NumberFieldProps) {
const id = useId();
return (
<label className="space-y-2">
<span className="text-sm font-medium">{label}</span>
<input
<div className="space-y-2">
<Label htmlFor={id}>{label}</Label>
<Input
id={id}
type="number"
step={step}
min={min}
Expand All @@ -27,8 +33,7 @@ export function NumberField({ label, value, onChange, disabled, min, step }: Num
onChange(num);
}}
disabled={disabled}
className={inputClasses}
/>
</label>
</div>
);
}
Loading