Refactor dashboard config-editor into maintainable section architecture#244
Refactor dashboard config-editor into maintainable section architecture#244BillChirico merged 7 commits intomainfrom
Conversation
|
🚅 Deployed to the volvox-bot-pr-244 environment in volvox-bot
|
📝 WalkthroughSummary by CodeRabbit
WalkthroughRefactors the dashboard config editor into modular per-section components, adds config-normalization and immutable update utilities, switches the editor to a patch-based save flow, updates a Next.js type reference, and adds unit tests for the new utilities and update helpers. Changes
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
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. Comment |
|
| Filename | Overview |
|---|---|
| web/src/components/dashboard/config-editor.tsx | Orchestration layer significantly reduced (~1600 lines removed); now delegates to section components via createSectionUpdater. The guildId effect dependency fix and new protectRoleIdsRaw state are correct. Core save/revert/undo logic is unchanged. |
| web/src/components/dashboard/config-sections/AiAutoModSection.tsx | ChannelSelector (flag review channel) replaced with plain text input (UX regression already flagged). Section now always renders when aiAutoMod is absent instead of returning null, which may cause unintended PATCH payloads for guilds without aiAutoMod configured. inputClasses duplicated. |
| web/src/components/dashboard/config-sections/WelcomeSection.tsx | ChannelSelector/RoleSelector for rulesChannel, introChannel, verifiedRole replaced with plain text inputs (UX regression, previously flagged). DM steps onBlur-only commit also previously flagged. Role menu and DM sequence logic otherwise well-extracted. |
| web/src/components/dashboard/config-sections/ModerationSection.tsx | Rate limiting and link filtering sub-sections correctly extracted. protectRoles RoleSelector regressed to text input. blockedDomainsRaw onBlur-only pattern (Ctrl+S data loss) already flagged in previous threads. protectRoleIdsRaw correctly calls onProtectRolesChange on every onChange, so no data loss there. |
| web/src/components/dashboard/config-sections/PermissionsSection.tsx | New file. Admin/Moderator role selectors retained (RoleSelector). botOwners field regressed to onBlur-only commit — data loss if Ctrl+S pressed while input is focused. Previously this field committed on every onChange. |
| web/src/components/dashboard/config-sections/ReputationSection.tsx | New file. announceChannelId regressed from ChannelSelector to text input. levelThresholds uses onBlur-only commit pattern causing Ctrl+S data loss. XP range logic (keeping min ≤ max) correctly preserved. |
| web/src/components/dashboard/config-sections/StarboardSection.tsx | New file. Both channelId and ignoredChannels regressed from ChannelSelector to plain text inputs. Core starboard logic (threshold, emoji, selfStar) correctly extracted. |
| web/src/components/dashboard/config-sections/TicketsSection.tsx | New file. supportRole regressed from RoleSelector to text input; transcriptChannel regressed from ChannelSelector to text input. Ticket mode select, autoCloseHours, and maxOpenPerUser correctly implemented. |
| web/src/components/dashboard/config-sections/TriageSection.tsx | moderationLogChannel regressed from ChannelSelector to plain text input. All other triage fields (models, budgets, intervals, toggles) correctly extracted from the monolith. |
| web/src/lib/config-updates.ts | New utility file with updateSectionEnabled, updateSectionField, updateNestedField, updateArrayItem, removeArrayItem, appendArrayItem. The traversal/rebuild logic in updateArrayItem/removeArrayItem/appendArrayItem correctly tracks levels for paths of any depth (previously flagged path reconstruction bug has been fixed). All current call sites use ≤2-segment paths. |
| web/src/lib/config-normalization.ts | New utility file extracting parseNumberInput, parseCommaSeparatedList, parseNewlineSeparatedList, percentToDecimal, decimalToPercent, normalizeOptionalString. Clean, well-tested, no issues. |
Flowchart
%%{init: {'theme': 'neutral'}}%%
flowchart TD
CE[ConfigEditor] --> CSU[createSectionUpdater]
CSU --> AU[aiUpdater]
CSU --> WU[welcomeUpdater]
CSU --> MU[moderationUpdater]
CSU --> TU[triageUpdater]
CSU --> SU[starboardUpdater]
CSU --> PU[permissionsUpdater]
CSU --> MEU[memoryUpdater]
CSU --> RU[reputationUpdater]
CSU --> CU[challengesUpdater]
CE --> AiSection
CE --> WelcomeSection
CE --> ModerationSection
CE --> AiAutoModSection
CE --> TriageSection
CE --> StarboardSection
CE --> PermissionsSection
CE --> MemorySection
CE --> CommunityFeaturesSection
CE --> EngagementSection
CE --> ReputationSection
CE --> ChallengesSection
CE --> GitHubSection
CE --> TicketsSection
AU --> CUL[config-updates.ts\nupdateSectionEnabled\nupdateSectionField\nupdateNestedField]
CN[config-normalization.ts\nparseNumberInput\npercentToDecimal\ndecimalToPercent\nparseCommaSeparatedList] --> AiAutoModSection
CN --> ModerationSection
CN --> TriageSection
CN --> StarboardSection
CN --> ReputationSection
CN --> GitHubSection
CN --> MemorySection
CN --> TicketsSection
Last reviewed commit: ff80c9f
There was a problem hiding this comment.
Actionable comments posted: 16
🤖 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 626-743: Several sections (AiAutoModSection,
CommunityFeaturesSection, EngagementSection, GitHubSection, TicketsSection) use
inline update callbacks instead of the shared updater helpers; create
corresponding updaters (e.g., aiAutoModUpdater, communityFeaturesUpdater,
engagementUpdater, githubUpdater, ticketsUpdater) that encapsulate the
updateDraftConfig logic and expose methods like setField, setEnabled/setToggle,
setActivityBadges as needed, then replace the inline
onFieldChange/onToggleChange/onEnabledChange props with calls to those updater
methods (similar to triageUpdater.setField, memoryUpdater.setEnabled,
reputationUpdater.setField) so save/revert semantics are centralized and
consistent.
- Around line 394-397: The effect that clears the undo snapshot currently runs
only once; update the useEffect that calls setPrevSavedConfig(null) so it
depends on the guild identifier (e.g., guildId, selectedGuildId, or guild?.id
used in this component) instead of an empty array, ensuring prevSavedConfig is
reset whenever the user switches guilds; keep the setPrevSavedConfig(null) body
but add the correct guild dependency to the dependency array so the undo button
UI no longer shows a stale snapshot after guild changes.
In `@web/src/components/dashboard/config-sections/AiAutoModSection.tsx`:
- Around line 24-29: The component AiAutoModSection currently returns null when
draftConfig.aiAutoMod is missing; instead ensure the section always renders so
users can enable AI Auto-Moderation on partial/default configs by treating
draftConfig.aiAutoMod as optional and providing sensible defaults: compute
thresholds and actions from draftConfig.aiAutoMod if present otherwise use
empty/default objects, and remove the early return that hides the section;
update the component to read thresholds = (draftConfig.aiAutoMod?.thresholds as
Record<string, number>) ?? {} and actions = (draftConfig.aiAutoMod?.actions as
Record<string, string>) ?? {}, and ensure the UI uses onFieldChange to
create/initialize draftConfig.aiAutoMod when the user toggles/enables the
feature.
In `@web/src/components/dashboard/config-sections/ChallengesSection.tsx`:
- Line 44: In ChallengesSection.tsx update the grid container's classes to be
mobile-first: find the div with className "grid grid-cols-2 gap-4" and change it
so the default is one column and switches to two at medium screens (e.g.,
replace with "grid grid-cols-1 md:grid-cols-2 gap-4") to avoid crowding inputs
on narrow screens.
In `@web/src/components/dashboard/config-sections/EngagementSection.tsx`:
- Around line 43-44: The key for each badge uses mutable fields (badge.days and
badge.label) which can collide and change on edit; update the badge list
rendering in EngagementSection to use a stable unique identifier instead: ensure
each badge object has an immutable id (e.g., badge.id) assigned when the badge
is created (or persisted), use that id as the React key in the map instead of
the computed string, and update any add/create handlers (where badges are
created) to generate and attach that id so existing change handlers (for
days/label inputs) do not mutate the key; if a stable id cannot be added
immediately, fall back to a deterministic index-only fallback but prefer the
persistent badge.id for reconciliation.
In `@web/src/components/dashboard/config-sections/ModerationSection.tsx`:
- Around line 238-246: The Blocked Domains input currently normalizes the array
on every keystroke (using draftConfig.moderation?.linkFilter?.blockedDomains
joined) which strips in-progress typing; change it to use a local raw string
state (e.g., blockedDomainsRaw) fed to the input value, update that raw state in
the input's onChange, and only parse (split by ',', trim, filter(Boolean)) and
call onLinkFilterChange('blockedDomains', parsedArray) onBlur — follow the same
protectRoleIdsRaw pattern used elsewhere in ModerationSection.tsx so typing and
delimiters are preserved until blur.
In `@web/src/components/dashboard/config-sections/PermissionsSection.tsx`:
- Around line 82-90: The botOwners input currently parses and normalizes on
every keystroke (see PermissionsSection.tsx using
draftConfig.permissions?.botOwners and onFieldChange inside the input's
onChange), which prevents natural typing of delimiters; fix by introducing a
local string state (e.g., rawBotOwners) initialized from
draftConfig.permissions?.botOwners.join(', ') and use that as the input value,
update rawBotOwners onChange without splitting/filtering, and only
parse/split/trim/filter and call onFieldChange('botOwners', parsedArray) from
the input's onBlur (also update rawBotOwners when draftConfig changes) so
normalization happens on blur instead of every keystroke.
In `@web/src/components/dashboard/config-sections/ReputationSection.tsx`:
- Around line 119-128: The input currently parses and sorts levelThresholds on
every keystroke (value={levelThresholds.join(', ')} and the onChange handler),
which drops partial comma states; change this to use a local raw string state in
the ReputationSection component (e.g., rawLevelThresholds) initialized from
levelThresholds.join(', '), update rawLevelThresholds on every onChange to
preserve typing, and only parse, validate, sort, and call
onFieldChange('levelThresholds', sorted) on onBlur (or Enter) to commit; ensure
the displayed value is bound to rawLevelThresholds while keeping the existing
parsing/sorting logic reused when committing.
In `@web/src/components/dashboard/config-sections/StarboardSection.tsx`:
- Line 54: The grid in StarboardSection uses a fixed "grid-cols-2" which
squashes controls on small screens; change the layout to be responsive (e.g.,
use a single column on small screens and two columns on medium/up) by updating
the container's className in the StarboardSection component (the div with
className "grid grid-cols-2 gap-4") to use responsive Tailwind classes such as
"grid grid-cols-1 md:grid-cols-2 gap-4" (or equivalent sm/md breakpoints per
design) so controls stack on mobile and become two columns on larger viewports.
- Around line 86-90: The active-button check treats an unset emoji as inactive;
update the className conditional in StarboardSection (where
draftConfig.starboard?.emoji is checked) to treat undefined/null as the default
'*' (e.g. use the nullish coalescing result (draftConfig.starboard?.emoji ??
'*') or equivalent) so the "Any" button renders with the active styles when
emoji is unset; adjust the ternary that decides 'bg-primary
text-primary-foreground' vs 'bg-muted ...' to use that normalized value.
In `@web/src/components/dashboard/config-sections/TriageSection.tsx`:
- Around line 215-217: Normalize the moderation log channel input in
TriageSection by trimming whitespace before calling onFieldChange; change the
onChange handler that currently calls onFieldChange('moderationLogChannel',
e.target.value) to compute const v = e.target.value.trim() and then call
onFieldChange('moderationLogChannel', v === '' ? '' : v) so whitespace-only
input clears the field consistent with section-wide channel clear semantics.
In `@web/src/components/dashboard/config-sections/WelcomeSection.tsx`:
- Around line 223-233: The onBlur handler is reading dmStepsRaw (which may be
stale) instead of the textarea's latest value; change the onBlur to accept the
blur event and parse the current textarea value (e.g., use e.currentTarget.value
or cast e.target) to build parsed = value.split(...).map(...).filter(Boolean),
then call onDmSequenceChange('steps', parsed) and
onDmStepsRawChange(parsed.join('\n')); update references to the textarea
handlers (value={dmStepsRaw}, onChange, onBlur) so onBlur uses the event value
rather than the dmStepsRaw variable.
In `@web/src/lib/config-updates.ts`:
- Around line 113-116: The cloned-array mutation doesn't validate index bounds:
before assigning to arr[index] (and before any splice that replaces elements)
verify index is an integer and 0 <= index < arr.length; if invalid, throw or
return a controlled error instead of allowing negative or out-of-range indices
to create sparse arrays or mutate wrong elements. Update the block that creates
arr from target[lastKey] (symbols: lastKey, arr, target, index, item) to check
Number.isInteger(index) and bounds, and apply the replacement only when the
check passes (or normalize the index only for allowed negative semantics after
explicit validation).
In `@web/tests/components/dashboard/config-editor-autosave.test.tsx`:
- Around line 178-252: This file duplicates unit tests for utility functions;
remove the overlapping assertions for parseNumberInput, percentToDecimal,
decimalToPercent, updateSectionEnabled, updateSectionField, and
updateNestedField from
web/tests/components/dashboard/config-editor-autosave.test.tsx and replace them
with integration-focused assertions that exercise the autosave/config-editor
behavior (e.g., simulate editing fields and verify autosave triggers and final
config) so the utilities remain tested only in
web/tests/lib/config-normalization.test.ts and
web/tests/lib/config-updates.test.ts.
In `@web/tests/lib/config-updates.test.ts`:
- Around line 52-56: The test "does not mutate original config" uses a shallow
copy so nested objects (baseConfig.ai) remain shared; replace the shallow clone
with a deep clone of baseConfig (e.g., use structuredClone,
JSON.parse(JSON.stringify(...)), or lodash's cloneDeep) before calling
updateSectionEnabled so original.ai is truly independent, then assert that
baseConfig.ai?.enabled equals the copied original.ai?.enabled to verify
immutability of updateSectionEnabled.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: adab6ccb-abd5-43ac-8df8-094ce8e26b80
📒 Files selected for processing (22)
web/next-env.d.tsweb/src/components/dashboard/config-editor.tsxweb/src/components/dashboard/config-sections/AiAutoModSection.tsxweb/src/components/dashboard/config-sections/AiSection.tsxweb/src/components/dashboard/config-sections/ChallengesSection.tsxweb/src/components/dashboard/config-sections/CommunityFeaturesSection.tsxweb/src/components/dashboard/config-sections/EngagementSection.tsxweb/src/components/dashboard/config-sections/GitHubSection.tsxweb/src/components/dashboard/config-sections/MemorySection.tsxweb/src/components/dashboard/config-sections/ModerationSection.tsxweb/src/components/dashboard/config-sections/PermissionsSection.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/components/dashboard/config-sections/TicketsSection.tsxweb/src/components/dashboard/config-sections/TriageSection.tsxweb/src/components/dashboard/config-sections/WelcomeSection.tsxweb/src/components/dashboard/config-sections/index.tsweb/src/lib/config-normalization.tsweb/src/lib/config-updates.tsweb/tests/components/dashboard/config-editor-autosave.test.tsxweb/tests/lib/config-normalization.test.tsweb/tests/lib/config-updates.test.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). (1)
- GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (2)
{src,web}/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Winston logger from
src/logger.js, NEVER useconsole.*
Files:
web/src/components/dashboard/config-sections/GitHubSection.tsxweb/next-env.d.tsweb/tests/components/dashboard/config-editor-autosave.test.tsxweb/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/components/dashboard/config-sections/AiSection.tsxweb/src/components/dashboard/config-sections/PermissionsSection.tsxweb/src/lib/config-normalization.tsweb/src/components/dashboard/config-sections/MemorySection.tsxweb/src/components/dashboard/config-sections/EngagementSection.tsxweb/src/components/dashboard/config-sections/CommunityFeaturesSection.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/dashboard/config-sections/TicketsSection.tsxweb/src/components/dashboard/config-sections/TriageSection.tsxweb/tests/lib/config-normalization.test.tsweb/src/components/dashboard/config-sections/ModerationSection.tsxweb/src/lib/config-updates.tsweb/src/components/dashboard/config-sections/ChallengesSection.tsxweb/src/components/dashboard/config-sections/AiAutoModSection.tsxweb/tests/lib/config-updates.test.tsweb/src/components/dashboard/config-sections/WelcomeSection.tsxweb/src/components/dashboard/config-sections/index.tsweb/src/components/dashboard/config-editor.tsx
web/**/*.{ts,tsx,jsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Use Next.js 16 App Router for web dashboard, with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design
Files:
web/src/components/dashboard/config-sections/GitHubSection.tsxweb/next-env.d.tsweb/tests/components/dashboard/config-editor-autosave.test.tsxweb/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/components/dashboard/config-sections/AiSection.tsxweb/src/components/dashboard/config-sections/PermissionsSection.tsxweb/src/lib/config-normalization.tsweb/src/components/dashboard/config-sections/MemorySection.tsxweb/src/components/dashboard/config-sections/EngagementSection.tsxweb/src/components/dashboard/config-sections/CommunityFeaturesSection.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/dashboard/config-sections/TicketsSection.tsxweb/src/components/dashboard/config-sections/TriageSection.tsxweb/tests/lib/config-normalization.test.tsweb/src/components/dashboard/config-sections/ModerationSection.tsxweb/src/lib/config-updates.tsweb/src/components/dashboard/config-sections/ChallengesSection.tsxweb/src/components/dashboard/config-sections/AiAutoModSection.tsxweb/tests/lib/config-updates.test.tsweb/src/components/dashboard/config-sections/WelcomeSection.tsxweb/src/components/dashboard/config-sections/index.tsweb/src/components/dashboard/config-editor.tsx
🧠 Learnings (7)
📓 Common learnings
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to web/**/*.{ts,tsx,jsx,js} : Use Next.js 16 App Router for web dashboard, with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Create modules in `src/modules/` for new features, add config section to `config.json`, update `SAFE_CONFIG_KEYS`, create slash command if needed, add database migration if needed, write tests, and update dashboard UI if configurable
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to web/**/*.{ts,tsx,jsx,js} : Use Next.js 16 App Router for web dashboard, with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design
Applied to files:
web/next-env.d.ts
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to src/api/routes/**/*.{js,ts} : API endpoints must include tests in `tests/api/` directory
Applied to files:
web/next-env.d.ts
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to tests/**/*.{js,ts} : Maintain 80% test coverage threshold — Never lower the coverage requirement
Applied to files:
web/tests/components/dashboard/config-editor-autosave.test.tsxweb/tests/lib/config-normalization.test.tsweb/tests/lib/config-updates.test.ts
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to src/modules/**/*.{js,ts} : Config section additions MUST be added to `SAFE_CONFIG_KEYS` in `src/api/utils/configAllowlist.js` to enable API saves
Applied to files:
web/src/components/dashboard/config-sections/PermissionsSection.tsxweb/src/lib/config-normalization.tsweb/src/components/dashboard/config-sections/MemorySection.tsxweb/src/components/dashboard/config-sections/CommunityFeaturesSection.tsxweb/src/components/dashboard/config-sections/ModerationSection.tsxweb/src/lib/config-updates.tsweb/tests/lib/config-updates.test.tsweb/src/components/dashboard/config-sections/index.tsweb/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to src/modules/**/*.{js,ts} : Gate all community features behind `config.<feature>.enabled` configuration checks
Applied to files:
web/src/components/dashboard/config-sections/CommunityFeaturesSection.tsxweb/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Create modules in `src/modules/` for new features, add config section to `config.json`, update `SAFE_CONFIG_KEYS`, create slash command if needed, add database migration if needed, write tests, and update dashboard UI if configurable
Applied to files:
web/src/components/dashboard/config-sections/CommunityFeaturesSection.tsxweb/src/components/dashboard/config-sections/WelcomeSection.tsxweb/src/components/dashboard/config-sections/index.tsweb/src/components/dashboard/config-editor.tsx
🔇 Additional comments (12)
web/next-env.d.ts (1)
3-3: Typed-route reference update looks correct.Line 3 is a valid modernization of the generated route type import path and is safe to keep.
web/src/components/dashboard/config-sections/CommunityFeaturesSection.tsx (1)
42-76: Clean section extraction and toggle flow.This is a solid modular section with safe defaults for missing feature config.
web/src/components/dashboard/config-sections/AiSection.tsx (1)
43-85: AI section wiring looks good.Enabled state, system prompt updates, and blocked-channel updates are all cleanly separated.
web/src/lib/config-normalization.ts (1)
14-93: Good utility extraction and pure behavior.These helpers are clean, side-effect free, and easy to test.
web/src/components/dashboard/config-sections/GitHubSection.tsx (1)
23-69: Clean extraction with consistent field wiring.The section wiring for
saving,onFieldChange, and numeric normalization looks solid.web/src/components/dashboard/config-sections/TicketsSection.tsx (1)
25-137: Section is well-structured and callback usage is consistent.Good extraction and clear per-field constraints (
min/max+parseNumberInput) across the ticket settings.web/tests/lib/config-normalization.test.ts (1)
12-188: Great utility test coverage for normalization edge cases.This suite is broad and directly reinforces behavior contracts for shared parsing/coercion helpers.
web/src/components/dashboard/config-sections/MemorySection.tsx (1)
24-74: Solid section extraction with safe numeric update flow.The component is cohesive, and the
parseNumberInputgate beforeonFieldChangekeeps invalid number transitions out of state.web/tests/lib/config-updates.test.ts (1)
95-177: Good breadth on array helper behavior.The cases cover create/update/remove/append paths and preserve-item behavior, which gives strong regression protection for list operations.
web/tests/components/dashboard/config-editor-autosave.test.tsx (1)
104-128: Good regression guard for unintended autosave-on-mount.The explicit PATCH-call assertion after initial load is a strong integration-level safety check.
web/src/components/dashboard/config-editor.tsx (1)
438-710: Nice orchestration split with reusable section updaters.The updater factory + section composition significantly improves readability versus inline monolithic update logic.
web/src/components/dashboard/config-sections/index.ts (1)
1-14: Clean barrel export for section modules.This improves import ergonomics in
config-editor.tsxand keeps section wiring maintainable.
web/src/components/dashboard/config-sections/ChallengesSection.tsx
Outdated
Show resolved
Hide resolved
web/src/components/dashboard/config-sections/EngagementSection.tsx
Outdated
Show resolved
Hide resolved
* fix(sections): responsive grid layouts * fix(config-editor): clear undo snapshot on guild change * fix(config-updates): correct buildPath for deep nesting * fix(sections): use onBlur for comma-separated inputs --------- Co-authored-by: Bill <[email protected]>
web/src/components/dashboard/config-sections/AiAutoModSection.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Actionable comments posted: 4
♻️ Duplicate comments (2)
web/src/components/dashboard/config-sections/StarboardSection.tsx (1)
86-90:⚠️ Potential issue | 🟡 MinorTreat unset emoji as default
'*'when deciding “Any” button active state.Line 87 currently marks “Any” inactive when emoji is unset, even though Line 76 renders
'*'as the effective default.💡 Proposed fix
- draftConfig.starboard?.emoji === '*' + (draftConfig.starboard?.emoji ?? '*') === '*'🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/dashboard/config-sections/StarboardSection.tsx` around lines 86 - 90, The "Any" button active check in StarboardSection uses draftConfig.starboard?.emoji === '*' but the UI treats unset emoji as '*' elsewhere; change the condition to treat undefined/null as '*' (e.g., use nullish coalescing or equivalent) so the active state uses (draftConfig.starboard?.emoji ?? '*') === '*' when computing the className for the "Any" button in the StarboardSection component.web/src/lib/config-updates.ts (1)
120-122:⚠️ Potential issue | 🟠 MajorValidate array index bounds before mutating cloned arrays.
Line 121 and Line 166 currently accept invalid indices. That can create sparse arrays (
arr[index] = item) or remove unintended elements (splicewith negative index semantics).💡 Proposed fix
const lastKey = path[path.length - 1]; const arr = [...((cursor[lastKey] as T[]) || [])]; + if (!Number.isInteger(index) || index < 0 || index >= arr.length) return config; arr[index] = item;const lastKey = path[path.length - 1]; const arr = [...((cursor[lastKey] as unknown[]) || [])]; + if (!Number.isInteger(index) || index < 0 || index >= arr.length) return config; arr.splice(index, 1);Also applies to: 165-167
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/lib/config-updates.ts` around lines 120 - 122, The clone-and-mutate logic that creates arr from cursor[lastKey] (variables: cursor, lastKey, arr) must validate the provided index (variable: index) before performing arr[index] = item or using splice (also referenced in the other block around lines 165-167); ensure index is an integer and within 0 <= index < arr.length for replacement, allow index === arr.length only if you intend to append (handle that explicitly), and for splice ensure you normalize/clip negative or out-of-range values or bail/throw rather than letting JavaScript create sparse arrays—add an index bounds check and early return or explicit append/insert behavior in the functions that perform these mutations.
🤖 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-sections/ChallengesSection.tsx`:
- Line 69: The timezone field label currently uses an unconditional "col-span-2"
(className "space-y-2 col-span-2") which breaks the mobile one-column grid;
change it to a breakpointed span such as "md:col-span-2" (or another appropriate
breakpoint like "sm:col-span-2") so the label occupies full width on small
screens and two columns only on larger screens; update the className on the
label element in ChallengesSection (the label wrapping the timezone input)
accordingly and verify the grid defined earlier (mobile one-column) behaves
correctly.
In `@web/src/components/dashboard/config-sections/ModerationSection.tsx`:
- Line 151: In ModerationSection, the fixed "grid grid-cols-2" and the other
forced multi-column grid are not responsive and will crowd inputs on small
screens; update both grid containers referenced in the component (the divs
currently using "grid grid-cols-2 gap-4" and the other using the 3-column
variant) to use responsive Tailwind classes such as "grid-cols-1 sm:grid-cols-2"
for the two-column block and "grid-cols-1 sm:grid-cols-2 md:grid-cols-3" for the
three-column block so numeric inputs stack on mobile and expand into columns on
larger breakpoints.
In `@web/src/components/dashboard/config-sections/ReputationSection.tsx`:
- Line 55: The XP settings grid in ReputationSection.tsx currently uses a fixed
two-column layout ("grid grid-cols-2 gap-4") which breaks mobile responsiveness;
change the grid to be mobile-first by using one column by default and enabling
two columns at the small breakpoint (e.g., replace "grid-cols-2" with
"grid-cols-1 sm:grid-cols-2") on the div in the ReputationSection component so
controls stack on narrow screens and expand to two columns on larger viewports.
In `@web/src/components/dashboard/config-sections/StarboardSection.tsx`:
- Around line 115-124: The input currently normalizes
draftConfig.starboard?.ignoredChannels on every keystroke (value=(...).join(',
') + onChange parsing), which collapses intermediate states; fix by introducing
a local string buffer (e.g., ignoredChannelsBuffer state in StarboardSection)
bound to the input value, update that buffer onChange, and only parse+call
onFieldChange('ignoredChannels', parsedArray) onBlur; also initialize/sync the
buffer from draftConfig.starboard?.ignoredChannels via useEffect so the buffer
reflects prop changes.
---
Duplicate comments:
In `@web/src/components/dashboard/config-sections/StarboardSection.tsx`:
- Around line 86-90: The "Any" button active check in StarboardSection uses
draftConfig.starboard?.emoji === '*' but the UI treats unset emoji as '*'
elsewhere; change the condition to treat undefined/null as '*' (e.g., use
nullish coalescing or equivalent) so the active state uses
(draftConfig.starboard?.emoji ?? '*') === '*' when computing the className for
the "Any" button in the StarboardSection component.
In `@web/src/lib/config-updates.ts`:
- Around line 120-122: The clone-and-mutate logic that creates arr from
cursor[lastKey] (variables: cursor, lastKey, arr) must validate the provided
index (variable: index) before performing arr[index] = item or using splice
(also referenced in the other block around lines 165-167); ensure index is an
integer and within 0 <= index < arr.length for replacement, allow index ===
arr.length only if you intend to append (handle that explicitly), and for splice
ensure you normalize/clip negative or out-of-range values or bail/throw rather
than letting JavaScript create sparse arrays—add an index bounds check and early
return or explicit append/insert behavior in the functions that perform these
mutations.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4ba5af0e-6b25-4d29-abf9-93a64e7578ac
📒 Files selected for processing (7)
web/src/components/dashboard/config-editor.tsxweb/src/components/dashboard/config-sections/ChallengesSection.tsxweb/src/components/dashboard/config-sections/ModerationSection.tsxweb/src/components/dashboard/config-sections/PermissionsSection.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/lib/config-updates.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). (1)
- GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (2)
{src,web}/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Winston logger from
src/logger.js, NEVER useconsole.*
Files:
web/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/components/dashboard/config-sections/PermissionsSection.tsxweb/src/components/dashboard/config-sections/ChallengesSection.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/dashboard/config-sections/ModerationSection.tsxweb/src/components/dashboard/config-editor.tsxweb/src/lib/config-updates.ts
web/**/*.{ts,tsx,jsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Use Next.js 16 App Router for web dashboard, with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design
Files:
web/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/components/dashboard/config-sections/PermissionsSection.tsxweb/src/components/dashboard/config-sections/ChallengesSection.tsxweb/src/components/dashboard/config-sections/ReputationSection.tsxweb/src/components/dashboard/config-sections/ModerationSection.tsxweb/src/components/dashboard/config-editor.tsxweb/src/lib/config-updates.ts
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Create modules in `src/modules/` for new features, add config section to `config.json`, update `SAFE_CONFIG_KEYS`, create slash command if needed, add database migration if needed, write tests, and update dashboard UI if configurable
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to web/**/*.{ts,tsx,jsx,js} : Use Next.js 16 App Router for web dashboard, with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to web/**/*.{ts,tsx,jsx,js} : Use Next.js 16 App Router for web dashboard, with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design
Applied to files:
web/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/components/dashboard/config-sections/ChallengesSection.tsx
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to src/modules/**/*.{js,ts} : Config section additions MUST be added to `SAFE_CONFIG_KEYS` in `src/api/utils/configAllowlist.js` to enable API saves
Applied to files:
web/src/components/dashboard/config-sections/PermissionsSection.tsxweb/src/components/dashboard/config-sections/ChallengesSection.tsxweb/src/components/dashboard/config-sections/ModerationSection.tsxweb/src/components/dashboard/config-editor.tsxweb/src/lib/config-updates.ts
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Create modules in `src/modules/` for new features, add config section to `config.json`, update `SAFE_CONFIG_KEYS`, create slash command if needed, add database migration if needed, write tests, and update dashboard UI if configurable
Applied to files:
web/src/components/dashboard/config-editor.tsx
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to src/modules/**/*.{js,ts} : Gate all community features behind `config.<feature>.enabled` configuration checks
Applied to files:
web/src/components/dashboard/config-editor.tsx
- AiAutoModSection: render even when aiAutoMod is missing - EngagementSection: use stable keys for badge rows - StarboardSection: fix default Any button active state - TriageSection: normalize moderationLogChannel before patch - WelcomeSection: read dmSteps from event target in onBlur - config-updates: validate array index bounds before mutations
There was a problem hiding this comment.
Actionable comments posted: 6
♻️ Duplicate comments (2)
web/src/components/dashboard/config-sections/StarboardSection.tsx (1)
115-124:⚠️ Potential issue | 🟠 MajorAvoid normalizing ignored channel IDs on every keystroke.
At Line 115 and Lines 116-124, parsing/splitting in
onChangecauses input rewrites (e.g., trailing commas/spaces), making multi-ID typing unreliable. Keep a raw local string buffer and only normalize on blur.💡 Proposed fix
+'use client'; +import { useEffect, useState } from 'react'; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'; @@ export function StarboardSection({ draftConfig, saving, onFieldChange }: StarboardSectionProps) { + const ignoredChannelsDisplay = (draftConfig.starboard?.ignoredChannels ?? []).join(', '); + const [ignoredChannelsRaw, setIgnoredChannelsRaw] = useState(ignoredChannelsDisplay); + + useEffect(() => { + setIgnoredChannelsRaw(ignoredChannelsDisplay); + }, [ignoredChannelsDisplay]); + return ( @@ <input id="ignored-channels" type="text" - value={(draftConfig.starboard?.ignoredChannels ?? []).join(', ')} - onChange={(e) => - onFieldChange( - 'ignoredChannels', - e.target.value - .split(',') - .map((s) => s.trim()) - .filter(Boolean), - ) - } + value={ignoredChannelsRaw} + onChange={(e) => setIgnoredChannelsRaw(e.target.value)} + onBlur={() => + onFieldChange( + 'ignoredChannels', + ignoredChannelsRaw + .split(',') + .map((s) => s.trim()) + .filter(Boolean), + ) + } disabled={saving} className={inputClasses} placeholder="Comma-separated channel IDs" />🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/dashboard/config-sections/StarboardSection.tsx` around lines 115 - 124, The current onChange in StarboardSection is normalizing draftConfig.starboard?.ignoredChannels on every keystroke which rewrites the input; instead, add a local state string (e.g., ignoredChannelsText) initialized from draftConfig.starboard?.ignoredChannels.join(', ') in a useEffect, bind the text input's value to that local state and update it raw in the input's onChange, and move the split/trim/filter normalization into the input's onBlur handler to call onFieldChange('ignoredChannels', parsedArray); ensure the local state is kept in sync if draftConfig.starboard changes.web/src/components/dashboard/config-sections/TriageSection.tsx (1)
216-216:⚠️ Potential issue | 🟠 MajorKeep
moderationLogChannelas a string when clearing input.Line 216 writes
nullfor empty input.web/src/types/config.tsdefinesmoderationLogChannelasstring, so this can introduce payload/schema drift.💡 Proposed fix
- onChange={(e) => onFieldChange('moderationLogChannel', e.target.value.trim() || null)} + onChange={(e) => onFieldChange('moderationLogChannel', e.target.value.trim())}#!/bin/bash set -euo pipefail # Verify declared type for moderationLogChannel. rg -nP 'moderationLogChannel\s*:' web/src/types/config.ts -C2 # Verify all usages and null handling patterns. rg -nP 'moderationLogChannel' -g '**/*.{ts,tsx}' web/src -C2 rg -nP "onFieldChange\\('moderationLogChannel'" web/src/components/dashboard/config-sections/TriageSection.tsx -C2🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@web/src/components/dashboard/config-sections/TriageSection.tsx` at line 216, The onChange handler sets moderationLogChannel to null for empty input which mismatches the declared string type; update the handler in TriageSection (the onChange using onFieldChange('moderationLogChannel', ...)) to pass an empty string instead of null (e.g., use e.target.value.trim() || ''), ensuring all places that consume moderationLogChannel expect a string.
🤖 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-sections/AiAutoModSection.tsx`:
- Around line 34-35: The issue is that thresholds and actions fall back to {}
and then the update spread `{ ...thresholds, [cat]: v }` loses sibling keys; fix
by ensuring thresholds and actions are initialized by merging
cfg.thresholds/cfg.actions into a complete default object containing all
expected category keys (e.g., create DEFAULT_THRESHOLDS and DEFAULT_ACTIONS or a
single DEFAULTS map), e.g. let thresholds = { ...DEFAULT_THRESHOLDS,
...(cfg.thresholds as Record<string, number>)} and similarly for actions, then
keep the existing update site that does `{ ...thresholds, [cat]: v }` inside the
AiAutoModSection so updates preserve unspecified sibling categories.
In `@web/src/components/dashboard/config-sections/EngagementSection.tsx`:
- Around line 43-79: The form controls in EngagementSection (the number Input
bound to badge.days, the text Input bound to badge.label, and the remove Button
rendering "✕") lack accessible names; update the Input and Button elements to
include clear ARIA names (eg. aria-label or aria-labelledby) that incorporate
the badge context (use the index i or badge.label to produce unique, descriptive
labels like "Badge X days", "Badge X label", and "Remove badge X") so screen
readers can identify each control, without changing onActivityBadgesChange,
badges, badge.days, badge.label, or the removal logic.
- Around line 8-12: The prop type for onActivityBadgesChange is too loose;
update EngagementSectionProps so onActivityBadgesChange accepts the stricter
badge shape required by the config schema (use Array<ActivityBadge> or Array<{
days: number; label: string }> with both fields non-optional) and adjust any
related local types/uses in EngagementSection to match (ensure places that call
onActivityBadgesChange construct badges with both days and label as required).
Reference: EngagementSectionProps and the onActivityBadgesChange prop and the
ActivityBadge schema/type.
In `@web/src/components/dashboard/config-sections/WelcomeSection.tsx`:
- Around line 26-38: The generateId function is duplicated; extract the
implementation into a shared utility (e.g., export function generateId(...) in a
common utils module) and replace local implementations by importing that single
exported generateId; update both the WelcomeSection's generateId and the
config-loading code that currently repeats the logic to import and use the
centralized generateId, and ensure the utility exports the same string return
type and any needed runtime fallback behavior.
- Around line 145-165: The role option rendering and delete handler assume
opt.id exists and is unique; change to use the map index instead: render with a
stable index-based key (e.g., key={`role-opt-${i}`} or key={i}) and update the
delete/update logic to operate by index (use opts.splice(i,1) or opts.filter((_,
idx) => idx !== i)) instead of comparing ids; update the onClick delete and any
other places that rely on id-based removal so they use the index i, leaving id
generation/validation separate when options are created or saved.
In `@web/src/lib/config-updates.ts`:
- Around line 120-121: Guard the array cloning so it only spreads when the
terminal value is actually an array: replace the naive spread of cursor[lastKey]
into arr (the line creating const arr = [...((cursor[lastKey] as T[]) || [])];)
with a safe check that uses Array.isArray(cursor[lastKey]) and falls back to an
empty array when it's not an array; apply the same pattern to the other spots
flagged (the similar clones at the blocks used by updateArrayItem,
removeArrayItem, and appendArrayItem) so cursor, lastKey, and arr are always
handled safely when terminal values are objects/strings/undefined.
---
Duplicate comments:
In `@web/src/components/dashboard/config-sections/StarboardSection.tsx`:
- Around line 115-124: The current onChange in StarboardSection is normalizing
draftConfig.starboard?.ignoredChannels on every keystroke which rewrites the
input; instead, add a local state string (e.g., ignoredChannelsText) initialized
from draftConfig.starboard?.ignoredChannels.join(', ') in a useEffect, bind the
text input's value to that local state and update it raw in the input's
onChange, and move the split/trim/filter normalization into the input's onBlur
handler to call onFieldChange('ignoredChannels', parsedArray); ensure the local
state is kept in sync if draftConfig.starboard changes.
In `@web/src/components/dashboard/config-sections/TriageSection.tsx`:
- Line 216: The onChange handler sets moderationLogChannel to null for empty
input which mismatches the declared string type; update the handler in
TriageSection (the onChange using onFieldChange('moderationLogChannel', ...)) to
pass an empty string instead of null (e.g., use e.target.value.trim() || ''),
ensuring all places that consume moderationLogChannel expect a string.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 1448554a-cbc9-4759-8393-08448895e976
📒 Files selected for processing (6)
web/src/components/dashboard/config-sections/AiAutoModSection.tsxweb/src/components/dashboard/config-sections/EngagementSection.tsxweb/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/components/dashboard/config-sections/TriageSection.tsxweb/src/components/dashboard/config-sections/WelcomeSection.tsxweb/src/lib/config-updates.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). (1)
- GitHub Check: Greptile Review
🧰 Additional context used
📓 Path-based instructions (2)
{src,web}/**/*.{js,ts,jsx,tsx}
📄 CodeRabbit inference engine (AGENTS.md)
Use Winston logger from
src/logger.js, NEVER useconsole.*
Files:
web/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/components/dashboard/config-sections/AiAutoModSection.tsxweb/src/components/dashboard/config-sections/TriageSection.tsxweb/src/lib/config-updates.tsweb/src/components/dashboard/config-sections/EngagementSection.tsxweb/src/components/dashboard/config-sections/WelcomeSection.tsx
web/**/*.{ts,tsx,jsx,js}
📄 CodeRabbit inference engine (AGENTS.md)
Use Next.js 16 App Router for web dashboard, with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design
Files:
web/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/components/dashboard/config-sections/AiAutoModSection.tsxweb/src/components/dashboard/config-sections/TriageSection.tsxweb/src/lib/config-updates.tsweb/src/components/dashboard/config-sections/EngagementSection.tsxweb/src/components/dashboard/config-sections/WelcomeSection.tsx
🧠 Learnings (3)
📓 Common learnings
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Create modules in `src/modules/` for new features, add config section to `config.json`, update `SAFE_CONFIG_KEYS`, create slash command if needed, add database migration if needed, write tests, and update dashboard UI if configurable
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to web/**/*.{ts,tsx,jsx,js} : Use Next.js 16 App Router for web dashboard, with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to src/modules/**/*.{js,ts} : Config section additions MUST be added to `SAFE_CONFIG_KEYS` in `src/api/utils/configAllowlist.js` to enable API saves
Applied to files:
web/src/components/dashboard/config-sections/StarboardSection.tsxweb/src/components/dashboard/config-sections/AiAutoModSection.tsxweb/src/lib/config-updates.ts
📚 Learning: 2026-03-02T21:23:59.512Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-02T21:23:59.512Z
Learning: Applies to web/**/*.{ts,tsx,jsx,js} : Use Next.js 16 App Router for web dashboard, with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design
Applied to files:
web/src/components/dashboard/config-sections/StarboardSection.tsx
🔇 Additional comments (7)
web/src/components/dashboard/config-sections/StarboardSection.tsx (2)
54-54: Responsive grid fix looks good.
grid-cols-1 md:grid-cols-2keeps this section usable on small screens while preserving desktop density.
86-90: Default “Any” active-state logic is correctly normalized.Using
(draftConfig.starboard?.emoji ?? '*')correctly treats unset emoji as the default mode.web/src/components/dashboard/config-sections/AiAutoModSection.tsx (3)
77-106: LGTM!The threshold conversion logic correctly uses
decimalToPercentfor display andpercentToDecimalfor storage. The fieldset with legend provides proper semantic grouping, and labels are correctly associated with inputs.
107-134: LGTM!The actions fieldset follows a consistent pattern with proper label associations and comprehensive moderation action options.
47-52: TheonFieldChangefield paths are correctly scoped toaiAutoMod. The parent component inconfig-editor.tsxwraps the callback to explicitly nest all field updates under theaiAutoModobject:aiAutoMod: { ...prev.aiAutoMod, [field]: value }. When the child component callsonFieldChange('enabled', v), the parent correctly routes it toaiAutoMod.enabled.> Likely an incorrect or invalid review comment.web/src/components/dashboard/config-sections/EngagementSection.tsx (1)
41-42: Good improvement on badge row key source.Using a non-user-editable source for the row key prevents the prior mutable-key collision behavior.
web/src/components/dashboard/config-sections/WelcomeSection.tsx (1)
223-233: Good fix: blur now parses from the event value.Using
e.currentTarget.valuehere avoids stale-state reads and keeps raw text + parsed steps synchronized.
- config-editor: centralize updater methods for AiAutoMod, CommunityFeatures, Engagement, GitHub feed, and Tickets; expose setField/setEnabled/setToggle/setActivityBadges - AiAutoModSection: remove strict dependency on aiAutoMod object; use sensible defaults and optional chaining fallbacks for toggles/inputs - EngagementSection: use stable index key for badge rows (prevents key collisions) - ReputationSection: commit thresholds on blur using event value; keep raw text buffer - StarboardSection: fix Any mode active state with (emoji ?? '*') === '*' - TriageSection: normalize moderationLogChannel on blur (trim/clear) - WelcomeSection: parse DM steps from event; update sequence on every change to avoid Ctrl+S data loss - ModerationSection: update blockedDomains on each change; keep blur normalization - config-updates: add index bounds checks for updateArrayItem/removeArrayItem; return unchanged config when OOB - tests: prune duplicate normalization utility tests from config-editor-autosave; fix immutability test using structuredClone(baseConfig)
UX Regressions (CRITICAL): - Restore ChannelSelector in StarboardSection for starboard channel Mobile Responsive: - ChallengesSection: Use responsive col-span (col-span-1 md:col-span-2) - ModerationSection: Use grid-cols-1 md:grid-cols-2 for rate limit grid - ModerationSection: Use grid-cols-1 md:grid-cols-3 for mute settings grid - ReputationSection: Use grid-cols-1 md:grid-cols-2 for XP settings Input Normalization: - StarboardSection: Add raw buffer + blur pattern for ignored channels - TriageSection: Trim whitespace for moderation log channel (already done) Code Quality: - EngagementSection: Refactor activityBadges updater with updateBadge helper - EngagementSection: Add accessible names to all form controls - EngagementSection: Fix badge row key collision (use days-label composite key) - AiAutoModSection: Handle partial config sibling categories with defaults - config-updates.ts: Guard array cloning against non-array values - config-updates.ts: Validate array index bounds in update/remove/append All tests pass (3628 passed, 2 skipped).
🧹 Preview Environment Cleaned UpThe Railway preview environment for this PR has been removed. Environment: |
…re (#244) * refactor(config): add normalization utilities for config editor * refactor(config): add config update utilities with tests * refactor(config): extract section components from config-editor * refactor(config): simplify config-editor.tsx to orchestration layer * fix: address PR #244 review comments (#245) * fix(sections): responsive grid layouts * fix(config-editor): clear undo snapshot on guild change * fix(config-updates): correct buildPath for deep nesting * fix(sections): use onBlur for comma-separated inputs --------- Co-authored-by: Bill <[email protected]> * fix: address remaining PR #244 review comments - AiAutoModSection: render even when aiAutoMod is missing - EngagementSection: use stable keys for badge rows - StarboardSection: fix default Any button active state - TriageSection: normalize moderationLogChannel before patch - WelcomeSection: read dmSteps from event target in onBlur - config-updates: validate array index bounds before mutations --------- Co-authored-by: Bill <[email protected]>
Closes #243
Summary
Refactor web/src/components/dashboard/config-editor.tsx into maintainable section architecture while preserving behavior and API payload compatibility.
Changes
Validation
Closes #243