Skip to content

feat: implement RoleSelector and ChannelSelector in config forms#175

Merged
BillChirico merged 38 commits intomainfrom
feat/selector-implementation-v2
Mar 1, 2026
Merged

feat: implement RoleSelector and ChannelSelector in config forms#175
BillChirico merged 38 commits intomainfrom
feat/selector-implementation-v2

Conversation

@BillChirico
Copy link
Collaborator

Summary

Implement RoleSelector and ChannelSelector components across dashboard configuration forms for better UX when selecting Discord channels and roles.

Changes

Channel Selectors (8 locations)

  • config-editor.tsx:

    • welcome.rulesChannel — Rules channel selection
    • welcome.introChannel — Introduction channel selection
    • starboard.channelId — Starboard channel selection
    • challenges.channelId — Daily challenges channel
    • github.feed.channelId — GitHub feed channel
    • reputation.announceChannelId — Reputation announcements
  • ModerationSection.tsx:

    • moderation.alertChannelId — Moderation alerts channel
  • TriageSection.tsx:

    • triage.moderationLogChannel — Triage moderation log channel

Role Selectors (2 locations)

  • config-editor.tsx:
    • permissions.adminRoleId — Admin role selection
    • permissions.moderatorRoleId — Moderator role selection

Features

  • Auto-fetches channels/roles from Discord API when opened
  • Search/filter functionality
  • Visual indicators (channel type icons, role colors)
  • Single selection mode (maxSelections={1})
  • Text channel filtering where appropriate
  • Graceful fallback when no guild is selected

Technical Details

  • Uses useGuildSelection() hook for guild context
  • Integrates with existing form state management
  • TypeScript compatible with proper type conversions
  • Follows shadcn/ui patterns with Command + Popover

Pip Build and others added 28 commits March 1, 2026 00:06
…move unused theme var

- FormFieldContext/FormItemContext: initialize to null, move null checks
  before context is accessed so useFormField() throws early if used outside
  its providers
- PopoverTitle: render <h2> instead of <div> for semantic correctness
- role-selector: clear stale roles (setRoles([])) before fetching new guild's
  role list to prevent stale IDs from being applied
- theme-toggle: remove unused 'theme' destructure from useTheme()
…variant in command.tsx

- DialogHeader (with sr-only title/description) must be a descendant of
  DialogContent for Radix Dialog accessibility (title/description bound to
  the dialog role via aria-labelledby/aria-describedby)
- **:data-[slot=command-input-wrapper]:h-12 was an invalid Tailwind variant;
  corrected to *:data-[slot=command-input-wrapper]:h-12
Ensures state updates only apply when the current AbortController matches
the one that initiated the fetch — consistent with the existing setLoading
guard in the finally block. Addresses Greptile review comment.
next-themes is imported in theme-provider.tsx, providers.tsx, and
theme-toggle.tsx but was missing from web/package.json dependencies,
causing module resolution failures at build time.

Fixes reviewer comments on missing next-themes dep.
form.tsx imports Controller, FormProvider, useFormContext, useFormState
from react-hook-form but the package was missing from web/package.json,
causing CI lockfile validation failures.
Switch to  since LabelPrimitive is only referenced
in type positions (React.ComponentProps<typeof LabelPrimitive.Root>).
This avoids an unnecessary runtime import/bundle side effect with
isolatedModules.

Addresses copilot review suggestion.
…urable flag

Addresses copilot review thread: Object.defineProperty unconditionally
redefining window.matchMedia can throw if a future jsdom version provides
a non-configurable implementation. Add typeof guard and configurable: true
to make the setup more robust.

Resolves: PRRT_kwDORICdSM5xZb6X
Copilot AI review requested due to automatic review settings March 1, 2026 17:33
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 1, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Refactors dashboard config editing to use structured per-option role menus and RoleSelector/ChannelSelector components, adds a Zustand store for caching Discord entities, adds abort-guarding for async fetches, introduces small UI/doc updates, and adds next-themes, react-hook-form, and zustand dependencies.

Changes

Cohort / File(s) Summary
Dependencies & Package
web/package.json
Added next-themes, react-hook-form, and zustand; minor formatting change to tailwind-merge.
Dashboard Config Editor & Sections
web/src/components/dashboard/config-editor.tsx, web/src/components/dashboard/config-sections/ModerationSection.tsx, web/src/components/dashboard/config-sections/TriageSection.tsx
Replaced raw-text role/menu inputs with per-option UI and stable ids; switched Admin/Moderator role and alert/moderation log channel fields to RoleSelector/ChannelSelector; added option add/remove, max 25 enforcement, and draftConfig-based validation/patching.
Discord Entities Store
web/src/stores/discord-entities.ts
New Zustand store useDiscordEntitiesStore exporting DiscordChannel/DiscordRole shapes and get/set/clear methods for channels/roles cached by guild.
Selectors & Async Safety
web/src/components/ui/channel-selector.tsx, web/src/components/ui/role-selector.tsx
Guarded state/error updates with abort-controller checks to prevent stale updates during fetches; minor DOM adjustments and added JSDoc.
UI Components & Docs
web/src/components/ui/command.tsx, web/src/components/ui/dialog.tsx, web/src/components/ui/form.tsx, web/src/components/layout/header.tsx, web/src/components/providers.tsx
Added JSDoc blocks, adjusted some imports/type-only imports, merged aria-describedby in FormControl, added ThemedToaster docblock, and minor import/reorder or attribute tweaks.
Types
web/src/types/config.ts
Added optional id?: string to WelcomeRoleOption to support per-option identification.

Possibly related PRs

  • PR #170: Modifies the same RoleSelector/ChannelSelector and provider/toaster/theme pieces that this change documents and extends.
  • PR #83: Implements and alters the dashboard config editor and related components that this PR refactors (role/menu editing flow).
  • PR #149: Also updates the welcome role-menu handling and types; directly related to the per-option WelcomeRoleOption changes.

Suggested reviewers

  • claude
🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 70.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title clearly and concisely describes the main implementation work: adding RoleSelector and ChannelSelector components to configuration forms.
Description check ✅ Passed The PR description provides relevant context, listing specific locations where selectors are implemented and detailing features, technical approach, and integration points.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/selector-implementation-v2

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/components/dashboard/config-editor.tsx`:
- Around line 183-189: The normalization of data.welcome.roleMenu.options
assumes each option is an object and directly accesses opt.id, which can throw
on null/primitive items; update the map in the data.welcome.roleMenu.options
normalization to defensively handle non-object entries by coalescing each opt
into an object (e.g., if typeof opt !== 'object' || opt === null then replace
with a new object) and always assign id: opt.id || crypto.randomUUID(); also
preserve any known fields when opt is an object so existing properties aren’t
lost.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05978bc and b8d09b3.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • web/package.json
  • web/src/components/dashboard/config-editor.tsx
  • web/src/stores/discord-entities.ts
  • web/src/types/config.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Greptile Review
  • GitHub Check: Agent
🧰 Additional context used
📓 Path-based instructions (2)
web/src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with type safety. Share contracts between dashboard UI and API responses via web/src/types/analytics.ts and similar type definition files

Files:

  • web/src/types/config.ts
  • web/src/stores/discord-entities.ts
web/src/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Component files should integrate with Zustand stores for state management (e.g., discord-entities store for caching Discord channels and roles per guild)

Files:

  • web/src/components/dashboard/config-editor.tsx
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T06:03:34.399Z
Learning: Applies to web/src/components/**/*.tsx : Component files should integrate with Zustand stores for state management (e.g., discord-entities store for caching Discord channels and roles per guild)
📚 Learning: 2026-03-01T06:03:34.399Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T06:03:34.399Z
Learning: Applies to web/src/components/**/*.tsx : Component files should integrate with Zustand stores for state management (e.g., discord-entities store for caching Discord channels and roles per guild)

Applied to files:

  • web/src/stores/discord-entities.ts
  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-01T06:03:34.399Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T06:03:34.399Z
Learning: Applies to src/modules/*.js : Per-request modules (AI, spam, moderation) should call `getConfig(guildId)` on every invocation for automatic config changes. Stateful resources should use `onConfigChange` listeners for reactive updates

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
🧬 Code graph analysis (1)
web/src/components/dashboard/config-editor.tsx (1)
web/src/components/ui/role-selector.tsx (1)
  • RoleSelector (52-292)
🔇 Additional comments (6)
web/src/types/config.ts (1)

29-33: WelcomeRoleOption.id is a good contract addition.

This supports stable option identity for UI keying and option-level updates/removals.

web/package.json (1)

28-37: Dependency additions look coherent for this PR scope.

next-themes, react-hook-form, and zustand align with the selector/theming/form updates.

web/src/stores/discord-entities.ts (1)

40-69: Store implementation is clean and correctly immutable.

The per-guild cache API (get/set/clear for channels and roles) is well-structured for selector reuse.

web/src/components/dashboard/config-editor.tsx (3)

218-223: Validation guard for incomplete role options is correctly enforced.

Blocking save on empty label/roleId closes the malformed-role-menu path.


787-860: Role-menu option editing flow is a solid upgrade.

Stable keys (opt.id), ID-based deletion, and single-select RoleSelector integration are correctly wired.


1392-1418: Permissions role fields are correctly migrated to RoleSelector.

Single-selection mapping to nullable IDs (selected[0] ?? null) is correct for adminRoleId and moderatorRoleId.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 20 changed files in this pull request and generated 7 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
web/src/components/providers.tsx (1)

9-34: ⚠️ Potential issue | 🔴 Critical

Duplicate function declaration causes lint failures.

ThemedToaster is defined twice—once without JSDoc (lines 9–18) and again with JSDoc (lines 25–34). This triggers noUnusedVariables on the first definition and noRedeclare on the second. Remove the original definition and keep only the documented version.

🐛 Proposed fix to remove duplicate definition
 'use client';
 
 import { SessionProvider } from 'next-auth/react';
 import { useTheme } from 'next-themes';
 import type { ReactNode } from 'react';
 import { Toaster } from 'sonner';
 import { ThemeProvider } from '@/components/theme-provider';
 
-function ThemedToaster() {
-  const { resolvedTheme } = useTheme();
-  return (
-    <Toaster
-      position="bottom-right"
-      theme={(resolvedTheme as 'light' | 'dark') ?? 'system'}
-      richColors
-    />
-  );
-}
-
 /**
  * Render a global Toaster whose visual theme follows the resolved app theme.
  *
  * `@returns` A React element mounting a Toaster at the bottom-right with its `theme` set to the resolved theme (`'light'` or `'dark'`, falling back to `'system'` if unresolved) and `richColors` enabled.
  */
 function ThemedToaster() {
   const { resolvedTheme } = useTheme();
   return (
     <Toaster
       position="bottom-right"
       theme={(resolvedTheme as 'light' | 'dark') ?? 'system'}
       richColors
     />
   );
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/providers.tsx` around lines 9 - 34, Remove the duplicate,
keeping only the documented ThemedToaster implementation: delete the first
ThemedToaster function (the one without JSDoc) and leave the second documented
ThemedToaster that uses useTheme and returns the Toaster with theme
{(resolvedTheme as 'light' | 'dark') ?? 'system'} and richColors; ensure any
imports (useTheme, Toaster) remain referenced and that there are no other
references to the removed duplicate.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@web/src/components/providers.tsx`:
- Around line 9-34: Remove the duplicate, keeping only the documented
ThemedToaster implementation: delete the first ThemedToaster function (the one
without JSDoc) and leave the second documented ThemedToaster that uses useTheme
and returns the Toaster with theme {(resolvedTheme as 'light' | 'dark') ??
'system'} and richColors; ensure any imports (useTheme, Toaster) remain
referenced and that there are no other references to the removed duplicate.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b8d09b3 and 75670a8.

📒 Files selected for processing (2)
  • web/src/components/providers.tsx
  • web/src/components/ui/form.tsx
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (1)
web/src/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Component files should integrate with Zustand stores for state management (e.g., discord-entities store for caching Discord channels and roles per guild)

Files:

  • web/src/components/ui/form.tsx
  • web/src/components/providers.tsx
🧠 Learnings (1)
📓 Common learnings
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T06:03:34.399Z
Learning: Applies to web/src/components/**/*.tsx : Component files should integrate with Zustand stores for state management (e.g., discord-entities store for caching Discord channels and roles per guild)
🪛 GitHub Actions: CI
web/src/components/providers.tsx

[error] 9-9: lint/correctness/noUnusedVariables: Unused function ThemedToaster. FIXABLE


[error] 25-25: lint/suspicious/noRedeclare: 'ThemedToaster' is defined here

🔇 Additional comments (2)
web/src/components/ui/form.tsx (2)

85-104: LGTM!

The JSDoc documentation accurately describes the FormLabel component's behavior, including its prop forwarding, error-aware styling integration, and automatic htmlFor binding to the form control.


110-115: Preserve caller-provided aria-describedby IDs in FormControl.

The current implementation overwrites any aria-describedby value passed via props, which can drop additional assistive descriptions from consuming components. This should merge the internal IDs with any caller-provided value.

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

14 files reviewed, 4 comments

Edit Code Review Agent Settings | Greptile

- Remove duplicate ThemedToaster function in providers.tsx
- Add generateId() fallback for environments without crypto.randomUUID
- Only validate role menu options when role menu is enabled
- Allow removing last role menu option (fix stuck state)
- Make WelcomeRoleOption.id optional (backend may not return it)
Copilot AI review requested due to automatic review settings March 1, 2026 19:18
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated 6 comments.

Files not reviewed (1)
  • pnpm-lock.yaml: Language not supported

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
web/src/components/dashboard/config-editor.tsx (1)

201-207: ⚠️ Potential issue | 🟠 Major

Guard role-menu normalization before reading opt.id.

Line 205 assumes every option is an object. A malformed entry (null/primitive) will break load when opt.id is read.

🛡️ Proposed defensive normalization
-      // Ensure role menu options have stable IDs
-      if (data.welcome?.roleMenu?.options) {
-        data.welcome.roleMenu.options = data.welcome.roleMenu.options.map((opt) => ({
-          ...opt,
-          id: opt.id || generateId(),
-        }));
-      }
+      // Ensure role menu options have stable IDs and safe object shape
+      if (Array.isArray(data.welcome?.roleMenu?.options)) {
+        data.welcome.roleMenu.options = data.welcome.roleMenu.options.map((opt) => {
+          if (typeof opt !== 'object' || opt === null) {
+            return { id: generateId(), label: '', roleId: '' };
+          }
+          const safe = opt as Record<string, unknown>;
+          return {
+            ...safe,
+            id:
+              typeof safe.id === 'string' && safe.id.trim().length > 0
+                ? safe.id
+                : generateId(),
+          };
+        }) as typeof data.welcome.roleMenu.options;
+      }
#!/bin/bash
# Verify the normalization block currently reads `opt.id` without a non-object/null guard.
sed -n '198,208p' web/src/components/dashboard/config-editor.tsx
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboard/config-editor.tsx` around lines 201 - 207, The
role-menu normalization assumes each element in data.welcome.roleMenu.options is
an object and reads opt.id directly, which will throw for null/primitives;
update the mapping in the block handling data.welcome.roleMenu.options to
defensively handle non-object entries by checking typeof opt === 'object' && opt
!== null, and for any non-object replace it with a normalized object (e.g., {
id: generateId(), label: String(opt) } or minimal { id: generateId() }) while
preserving existing object fields and using opt.id || generateId() for objects;
reference the data.welcome.roleMenu.options mapping and generateId() when making
this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@web/src/components/dashboard/config-editor.tsx`:
- Around line 1407-1436: The wrapper elements currently using <label> around the
Admin and Moderator Role selectors cause a11y lint failures because RoleSelector
is a custom control; change those wrapper <label className="space-y-2"> elements
to non-label containers (e.g., <div className="space-y-2">) for both the Admin
and Moderator blocks so the static <span className="text-sm font-medium">
remains the visible label and RoleSelector (used with selected from
draftConfig.permissions?.adminRoleId / moderatorRoleId and onChange calling
updatePermissionsField) is not nested inside a <label>. Ensure no other label
semantics are lost (the span stays as the visual label) and preserve the
existing props like guildId, selected, onChange, placeholder, disabled, and
maxSelections.

In `@web/src/components/ui/form.tsx`:
- Around line 109-115: The new ariaDescribedBy block is misformatted; run the
project formatter (e.g., Prettier/format task) and reformat the block that
defines callerDescribedBy, fieldDescribedBy, and ariaDescribedBy so it matches
the repository style. Ensure the expressions using props['aria-describedby'],
formDescriptionId, formMessageId, and the conditional assembly of
ariaDescribedBy are preserved exactly but reformatted to the project's
spacing/line-break conventions.

---

Duplicate comments:
In `@web/src/components/dashboard/config-editor.tsx`:
- Around line 201-207: The role-menu normalization assumes each element in
data.welcome.roleMenu.options is an object and reads opt.id directly, which will
throw for null/primitives; update the mapping in the block handling
data.welcome.roleMenu.options to defensively handle non-object entries by
checking typeof opt === 'object' && opt !== null, and for any non-object replace
it with a normalized object (e.g., { id: generateId(), label: String(opt) } or
minimal { id: generateId() }) while preserving existing object fields and using
opt.id || generateId() for objects; reference the data.welcome.roleMenu.options
mapping and generateId() when making this change.

ℹ️ Review info

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 75670a8 and f00deac.

📒 Files selected for processing (5)
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/providers.tsx
  • web/src/components/ui/dialog.tsx
  • web/src/components/ui/form.tsx
  • web/src/types/config.ts
📜 Review details
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Greptile Review
  • GitHub Check: Agent
🧰 Additional context used
📓 Path-based instructions (2)
web/src/components/**/*.tsx

📄 CodeRabbit inference engine (AGENTS.md)

Component files should integrate with Zustand stores for state management (e.g., discord-entities store for caching Discord channels and roles per guild)

Files:

  • web/src/components/providers.tsx
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/ui/form.tsx
  • web/src/components/ui/dialog.tsx
web/src/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Use TypeScript with type safety. Share contracts between dashboard UI and API responses via web/src/types/analytics.ts and similar type definition files

Files:

  • web/src/types/config.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T06:03:34.399Z
Learning: Applies to web/src/components/**/*.tsx : Component files should integrate with Zustand stores for state management (e.g., discord-entities store for caching Discord channels and roles per guild)
📚 Learning: 2026-03-01T06:03:34.399Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T06:03:34.399Z
Learning: Applies to web/src/components/**/*.tsx : Component files should integrate with Zustand stores for state management (e.g., discord-entities store for caching Discord channels and roles per guild)

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-01T06:03:34.399Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-01T06:03:34.399Z
Learning: Applies to src/modules/*.js : Per-request modules (AI, spam, moderation) should call `getConfig(guildId)` on every invocation for automatic config changes. Stateful resources should use `onConfigChange` listeners for reactive updates

Applied to files:

  • web/src/components/dashboard/config-editor.tsx
🧬 Code graph analysis (1)
web/src/components/dashboard/config-editor.tsx (1)
web/src/components/ui/role-selector.tsx (1)
  • RoleSelector (52-292)
🪛 GitHub Actions: CI
web/src/components/dashboard/config-editor.tsx

[error] 1407-1419: lint/a11y/noLabelWithoutControl: A form label must be associated with an input. Consider adding a for/htmlFor attribute or nesting the input inside the label.


[error] 1420-1436: lint/a11y/noLabelWithoutControl: A form label must be associated with an input. Consider adding a for/htmlFor attribute or nesting the input inside the label.

web/src/components/ui/form.tsx

[error] 4-4: lint/style/useImportType: Use import type for Label from 'radix-ui' to avoid importing runtime code.


[error] 109-115: Formatter would have printed different content. Run the formatter (e.g., Prettier) to fix formatting changes.

🔇 Additional comments (12)
web/src/components/providers.tsx (1)

9-13: JSDoc is accurate and helpful.

This documentation cleanly describes ThemedToaster behavior and aligns with the implementation.

web/src/components/ui/dialog.tsx (5)

9-17: LGTM!

Clean JSDoc documentation that accurately describes the component's behavior of forwarding props to the Radix Dialog Root.


19-27: LGTM!

Documentation correctly describes the data-slot attribute injection and prop forwarding behavior.


29-37: Previous feedback addressed.

The @param tag has been added to the DialogPortal JSDoc, resolving the inconsistency noted in prior review.


39-47: LGTM!

Consistent documentation style matching the other dialog primitive wrappers.


65-70: LGTM!

Good documentation focusing on the custom showCloseButton prop. The standard React props (className, children) appropriately don't need explicit documentation since they follow common patterns.

web/src/components/ui/form.tsx (3)

85-91: Helpful FormLabel JSDoc addition.

This docblock is concise and accurately describes behavior/props for consumers.


109-123: aria-describedby merge now preserves consumer IDs and internal field metadata.

This resolves the accessibility regression risk by combining caller-provided IDs with form description/error IDs.


4-4: The proposed type-only import change is incorrect and would break the code.

LabelPrimitive is used in a runtime expression on line 92: React.ComponentProps<typeof LabelPrimitive.Root>. The typeof operator requires a runtime value, not a type-only import. The current import statement is correct as-is.

Likely an incorrect or invalid review comment.

web/src/types/config.ts (1)

29-33: WelcomeRoleOption.id is a solid backward-compatible contract extension.

Making id optional supports stable option identity in the editor without breaking older config payloads.

web/src/components/dashboard/config-editor.tsx (2)

236-243: Good save guard for incomplete role-menu options.

This correctly blocks save when role menu is enabled and an option is missing label or roleId.


806-877: Stable keying and ID-based deletion look correct here.

Using key={opt.id}, removing by ID, and generating IDs for new options is a strong improvement for row stability.

Copilot AI review requested due to automatic review settings March 1, 2026 20:51
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Comments suppressed due to low confidence (1)

web/src/components/dashboard/config-editor.tsx:766

  • PR description calls out replacing several channel-id fields in config-editor.tsx with ChannelSelector, but this file still uses plain text inputs for channel IDs (e.g. welcome.rulesChannel here). Either implement the selectors in this editor as described, or update the PR description to reflect what actually changed.
          <div className="grid grid-cols-1 gap-4 md:grid-cols-3">
            <label htmlFor="rules-channel-id" className="space-y-2">
              <span className="text-sm font-medium">Rules Channel ID</span>
              <input
                id="rules-channel-id"
                type="text"
                value={draftConfig.welcome?.rulesChannel ?? ''}
                onChange={(e) => updateWelcomeField('rulesChannel', e.target.value.trim() || null)}
                disabled={saving}

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 1, 2026
@BillChirico BillChirico merged commit a5931c8 into main Mar 1, 2026
9 of 12 checks passed
@BillChirico BillChirico deleted the feat/selector-implementation-v2 branch March 1, 2026 21:30
"tailwind-merge": "^3.5.0",
"next-themes": "^0.4.6",
"react-hook-form": "^7.56.4"
"tailwind-merge": "^3.5.0"
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Removed next-themes but it's still imported in providers.tsx, theme-provider.tsx, and theme-toggle.tsx - this will cause build failure

Suggested change
"tailwind-merge": "^3.5.0"
"tailwind-merge": "^3.5.0",
"next-themes": "^0.4.6",
"react-hook-form": "^7.56.4"
Prompt To Fix With AI
This is a comment left during a code review.
Path: web/package.json
Line: 36

Comment:
Removed `next-themes` but it's still imported in `providers.tsx`, `theme-provider.tsx`, and `theme-toggle.tsx` - this will cause build failure

```suggestion
    "tailwind-merge": "^3.5.0",
    "next-themes": "^0.4.6",
    "react-hook-form": "^7.56.4"
```

How can I resolve this? If you propose a fix, please make it concise.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants