Skip to content

feat(web): redesign bot config settings workspace#253

Merged
BillChirico merged 10 commits intomainfrom
bill/feat/config-workspace-redesign
Mar 7, 2026
Merged

feat(web): redesign bot config settings workspace#253
BillChirico merged 10 commits intomainfrom
bill/feat/config-workspace-redesign

Conversation

@BillChirico
Copy link
Collaborator

@BillChirico BillChirico commented Mar 7, 2026

Summary

This redesign replaces the monolithic config form with a category-first workspace so related toggles and controls stay together. It preserves the existing backend contract and manual save + diff confirmation behavior while making dirty state and section ownership clearer.

Key Changes

  • Introduced a new config workspace layer under web/src/components/dashboard/config-workspace/ for category metadata, desktop/mobile navigation, search, and reusable feature cards.
  • Refactored web/src/components/dashboard/config-editor.tsx to use category-scoped rendering, metadata-driven search, advanced auto-open on search hits, and category-level dirty indicators while keeping existing save/patch semantics.
  • Reworked web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx into category-aligned feature cards and standardized toggle behavior using the UI Switch primitive.
  • Removed duplicated patch/deep-equality helpers from the editor and relied on shared web/src/lib/config-utils.ts helpers.
  • Updated dashboard editor tests to assert manual-save flow, category switching, search behavior, advanced auto-open, dirty badges, diff-confirm-before-patch, and validation-disabled save.

Impact

This impacts only the dashboard config UI layer in web/. API routes and PATCH payload semantics remain unchanged.

Testing

  • cd web && pnpm test
  • cd web && pnpm typecheck
  • cd web && pnpm lint

Copilot AI review requested due to automatic review settings March 7, 2026 01:16
@github-project-automation github-project-automation bot moved this to Backlog in Volvox.Bot Mar 7, 2026
@railway-app railway-app bot temporarily deployed to volvox-bot / volvox-bot-pr-253 March 7, 2026 01:16 Destroyed
@railway-app
Copy link

railway-app bot commented Mar 7, 2026

🚅 Deployed to the volvox-bot-pr-253 environment in volvox-bot

Service Status Web Updated (UTC)
docs ◻️ Removed (View Logs) Web Mar 7, 2026 at 2:03 am
bot ◻️ Removed (View Logs) Web Mar 7, 2026 at 2:03 am
web ◻️ Removed (View Logs) Mar 7, 2026 at 2:03 am

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Category-based workspace navigation for dashboard configuration with five focused categories: AI & Automation, Onboarding & Growth, Moderation & Safety, Community Tools, and Support & Integrations
    • Metadata-driven configuration search with cross-category quick jump and automatic focus/scroll targeting to relevant settings
    • Advanced sections automatically expand when search results match advanced controls
  • Improvements

    • Refactored configuration interface to reusable feature card pattern with Basic/Advanced sections and master toggles
    • Enhanced toggle switch component with improved accessibility
  • Removed

    • Persistent GUILD_ID environment variable (CLI-based developer deployment option preserved)

Walkthrough

Redesign of the web dashboard config editor: adds a category-based workspace (navigation, categories metadata), metadata-driven search with cross-category quick-jump, reusable SettingsFeatureCard UI for features (basic/advanced + enable toggle), updates CommunitySettingsSection to use the new system, and removes runtime GUILD_ID env reads.

Changes

Cohort / File(s) Summary
Workspace core & types
web/src/components/dashboard/config-workspace/types.ts, web/src/components/dashboard/config-workspace/config-categories.ts
Adds workspace typings and a centralized category/feature registry, searchable items, and helper lookup functions for category/feature matching.
Workspace UI components
web/src/components/dashboard/config-workspace/category-navigation.tsx, web/src/components/dashboard/config-workspace/config-search.tsx, web/src/components/dashboard/config-workspace/settings-feature-card.tsx
Adds CategoryNavigation (mobile select + desktop vertical nav with dirty badges), ConfigSearch (input + results panel, selection), and SettingsFeatureCard (header, enable switch, basic/advanced sections, force-open support).
Section refactor
web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
Reworks community settings to render features via SettingsFeatureCard, adds props for active category, visible features, and force-open advanced panel; routes state updates through updateDraftConfig.
Primitive toggle update
web/src/components/dashboard/toggle-switch.tsx
Replaces button-based toggle with shadcn Switch; adds required label prop and optional disabled prop; updates aria handling and events.
Tests & mocks
web/tests/components/dashboard/config-editor-autosave.test.tsx
Rewrites autosave/integration tests to workspace-focused interactions, expands/minimizes mocks (ChannelSelector, RoleSelector, SystemPromptEditor), and covers category navigation, search, dirty badges, and manual-save flows.
Other observed removals
CLAUDE.md, .env.example, README.md (implicit diffs)
Observed removal of runtime reads of process.env.GUILD_ID and removal of GUILD_ID from persisted env examples and docs; dev-only guild deploy via CLI flag preserved.

Possibly related PRs

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title accurately summarizes the primary change: a redesign of the bot config settings workspace from monolithic to category-first layout.
Description check ✅ Passed The pull request description is clearly related to the changeset, providing a comprehensive summary of the redesign including key changes, impact scope, and testing instructions.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bill/feat/config-workspace-redesign
  • 🛠️ Publish Changes: Commit on current branch
  • 🛠️ Publish Changes: Create PR

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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

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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coveralls
Copy link

coveralls commented Mar 7, 2026

Coverage Status

coverage: 87.451% (+0.005%) from 87.446%
when pulling 17965f9 on bill/feat/config-workspace-redesign
into 0914172 on main.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 7, 2026

Note

Docstrings generation - SUCCESS
Generated docstrings and committed to branch bill/feat/config-workspace-redesign (commit: 9ab414e974af4599b63eb7696dd7d36b82546dac)

Docstrings generation was requested by @BillChirico.

The following files were modified:

* `web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx`
* `web/src/components/dashboard/config-workspace/category-navigation.tsx`
* `web/src/components/dashboard/config-workspace/config-categories.ts`
* `web/src/components/dashboard/config-workspace/config-search.tsx`
* `web/src/components/dashboard/config-workspace/settings-feature-card.tsx`
* `web/src/components/dashboard/toggle-switch.tsx`

These files were ignored:
* `web/tests/components/dashboard/config-editor-autosave.test.tsx`

These file types are not supported:
* `CLAUDE.md`
@railway-app railway-app bot temporarily deployed to volvox-bot / volvox-bot-pr-253 March 7, 2026 01:20 Destroyed
Docstrings generation was requested by @BillChirico.

The following files were modified:

* `web/src/components/dashboard/config-workspace/config-categories.ts`

These files were kept as they were:
* `web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx`
* `web/src/components/dashboard/config-workspace/category-navigation.tsx`
* `web/src/components/dashboard/config-workspace/config-search.tsx`
* `web/src/components/dashboard/config-workspace/settings-feature-card.tsx`
* `web/src/components/dashboard/toggle-switch.tsx`

These files were ignored:
* `web/tests/components/dashboard/config-editor-autosave.test.tsx`

These file types are not supported:
* `CLAUDE.md`
Copilot AI review requested due to automatic review settings March 7, 2026 01:21
@BillChirico BillChirico review requested due to automatic review settings March 7, 2026 01:21
@railway-app railway-app bot temporarily deployed to volvox-bot / volvox-bot-pr-253 March 7, 2026 01:21 Destroyed
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: 4

🤖 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/CommunitySettingsSection.tsx`:
- Around line 133-185: The activity badge row key is unstable (uses days/label)
causing remounts and the delete path filters an empty array when activityBadges
is undefined; update the map key to a stable identifier (e.g., use the loop
index with key={`badge-${index}`} or a persistent id on Badge) and change the
delete logic to operate on the same fallback array you render (use const badges
= [...(draftConfig.engagement?.activityBadges ??
defaultActivityBadges)].filter((_, idx) => idx !== index) before calling
updateDraftConfig) so removing seeded badges works correctly.

In `@web/src/components/dashboard/config-workspace/config-categories.ts`:
- Around line 364-366: getCategoryById silently falls back to
CONFIG_CATEGORIES[0] which can hide invalid IDs; update getCategoryById to
detect when find(...) returns undefined and, in non-production/dev builds (e.g.
check process.env.NODE_ENV !== 'production' or existing isDev flag), emit a
console.warn or use the app logger with a message that includes the missing
categoryId and optionally the list of CONFIG_CATEGORIES.map(c => c.id) for
debugging; still return CONFIG_CATEGORIES[0] to preserve UI resilience.
- Around line 71-81: Remove the unused "section" property from the
ConfigSearchItem type and from every item in CONFIG_SEARCH_ITEMS; update the
type/interface definition (ConfigSearchItem) to no longer include "section" and
delete the "section" keys from the array entries, then run the build/tests to
ensure nothing else references it—note that handleSearchSelect only uses
categoryId, featureId and id so no call sites need changes (see
CONFIG_SEARCH_ITEMS and handleSearchSelect).

In `@web/src/components/dashboard/config-workspace/config-search.tsx`:
- Around line 64-66: The UI is rendering a slug-derived label via
item.categoryId.replace(/-/g, ' ') which can drift; instead look up the category
metadata by id and render its canonical label. In config-search.tsx change the
span to display the category's label (e.g., find the category with id ===
item.categoryId from the categories/metadata array or via getCategoryById used
elsewhere) and fall back to item.categoryId.replace(/-/g, ' ') only if no
category object or label is found; keep item.description as-is.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: fe0d9609-7981-4d4d-a3df-662f021f6889

📥 Commits

Reviewing files that changed from the base of the PR and between 308e330 and 77da608.

📒 Files selected for processing (10)
  • CLAUDE.md
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/toggle-switch.tsx
  • web/tests/components/dashboard/config-editor-autosave.test.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). (2)
  • GitHub Check: Greptile Review
  • GitHub Check: Docker Build Validation
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{js,mjs,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,mjs,jsx,ts,tsx}: Use single quotes for strings in code, double quotes only allowed in JSON files
Always end statements with semicolons
Use 2-space indentation, enforced by Biome

Files:

  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/toggle-switch.tsx
  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Next.js 16 web dashboard uses App Router with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design

Files:

  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/toggle-switch.tsx
  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
🧠 Learnings (5)
📓 Common learnings
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Applies to web/**/*.{ts,tsx} : Next.js 16 web dashboard uses App Router with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design
📚 Learning: 2026-03-05T18:07:15.752Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Applies to web/**/*.{ts,tsx} : Next.js 16 web dashboard uses App Router with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design

Applied to files:

  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/tests/components/dashboard/config-editor-autosave.test.tsx
  • CLAUDE.md
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
📚 Learning: 2026-03-05T18:07:15.752Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Applies to tests/**/*.{js,mjs,jsx,ts,tsx} : Maintain 80% code coverage threshold minimum, never lower this threshold

Applied to files:

  • web/tests/components/dashboard/config-editor-autosave.test.tsx
📚 Learning: 2026-03-05T18:07:15.752Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Claude CLI runs in headless mode for AI chat, Claude SDK used for auto-moderation (toxicity/spam detection), feedback tracked via reactions

Applied to files:

  • CLAUDE.md
📚 Learning: 2026-03-05T18:07:15.752Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Before starting any coding session, read `CLAUDE.md` for coding standards and persona; after infrastructure work, update `CLAUDE.md` with technical decisions and session notes

Applied to files:

  • CLAUDE.md
🔇 Additional comments (4)
web/src/components/dashboard/config-workspace/config-categories.ts (4)

1-49: LGTM!

The category metadata is well-structured with clear groupings. Type imports are clean, and the data aligns with the ConfigCategoryMeta interface defined in types.ts.


51-69: LGTM!

The default category and feature labels are complete. All feature IDs defined across categories have corresponding human-readable labels.


368-373: LGTM!

The implementation correctly searches categories by feature ID. Same fallback pattern as getCategoryById — the optional warning suggestion above would apply here as well.


375-388: LGTM!

The search implementation is clean:

  • Properly normalizes input with trim and lowercase
  • Returns early for empty queries
  • Searches across label, description, and keywords
  • Uses Set for deduplication in getMatchedFeatureIds

The O(n×m) complexity is appropriate for the static dataset size.

@github-project-automation github-project-automation bot moved this from Backlog to In Review in Volvox.Bot Mar 7, 2026
@greptile-apps
Copy link

greptile-apps bot commented Mar 7, 2026

Greptile Summary

This PR replaces the monolithic config form in web/ with a category-first workspace that groups related settings into five navigable categories (AI & Automation, Onboarding & Growth, Moderation & Safety, Community Tools, Support & Integrations), adds cross-category search with quick-jump and advanced-section auto-open, and introduces a SettingsFeatureCard pattern for consistent Basic/Advanced layout. Backend PATCH semantics and the manual save/diff-confirm flow are preserved unchanged.

Key changes:

  • New config-workspace/ layer: category-navigation, config-search, settings-feature-card, config-categories, and types
  • ConfigEditor gains category state, search state, dirty-per-category counters, focus/scroll targeting, and a / keyboard shortcut to focus the search input
  • CommunitySettingsSection rewritten to use SettingsFeatureCard with Switch primitives directly
  • updateEscalationThresholds and updateWarningsField callbacks removed; their UI surfaces were simplified or moved to the advanced panel
  • Tests updated to cover manual-save, category switching, search filtering, advanced auto-open, dirty badges, diff confirmation, and validation-disabled save

Issues found:

  • console.warn in config-categories.ts violates the project's logging policy — a Winston logger call should be used instead
  • section as never type cast in dirtyCategoryCounts masks a genuine string vs ConfigSectionKey mismatch; the hardcoded initial accumulator also creates a maintenance hazard when new categories are added
  • Timezone <label> in CommunitySettingsSection is missing htmlFor / id pairing, breaking screen-reader association

Confidence Score: 4/5

  • Safe to merge after addressing the logging policy violation and the type-safety issues in dirtyCategoryCounts and timezone label accessibility.
  • The refactor is well-scoped to the frontend UI layer with no changes to API routes or PATCH payloads. Tests are comprehensive and cover the new workspace behaviors. The three flagged issues are all valid style/maintenance concerns that improve code quality and should be fixed before merge.
  • web/src/components/dashboard/config-workspace/config-categories.ts (logging policy violation), web/src/components/dashboard/config-editor.tsx (type safety and maintainability), and web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx (accessibility)

Last reviewed commit: 17965f9

Bill Chirico added 6 commits March 6, 2026 22:40
Using badge content (days/label) as the React key caused remounts while
the user was typing. Switch to index-based key so the row identity is
stable during edits.

Also fix the delete handler to fall back to defaultActivityBadges
instead of an empty array, matching the add handler's behavior.
The section field was defined in the ConfigSearchItem interface and
populated in CONFIG_SEARCH_ITEMS but never read by any consuming code.
Removing it as dead code to keep the type surface clean.
The silent fallback to CONFIG_CATEGORIES[0] could mask bugs where an
invalid categoryId is passed. Add a console.warn so these cases surface
during development instead of silently returning unexpected data.
item.categoryId.replace(/-/g, ' ') produced text like
'support integrations' which doesn't match the actual category names.
Use getCategoryById to render the proper label (e.g. 'Support & Integrations').
Calling setFocusFeatureId(null) synchronously before the RAF fires
triggers a re-render that cancels the scroll/focus effect before it
can run. Moving it inside the RAF callback ensures the DOM operations
complete before the state is cleared.
Remove ineffective JSX biome-ignore comment (JSX expression context
requires the pragma directly on the prop line; the noArrayIndexKey
warning is pre-existing and emitted only as a warning, not an error).
Add biome-ignore line comment to console.warn in getCategoryById so
biome treats it as intentional diagnostic output.
Copilot AI review requested due to automatic review settings March 7, 2026 03:45
coderabbitai[bot]
coderabbitai bot previously approved these changes Mar 7, 2026
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.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

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-sections/CommunitySettingsSection.tsx`:
- Around line 584-602: Add an explicit id on the timezone input and a matching
htmlFor on the label to associate them for accessibility: give the input a
unique id (e.g. id="challenges-timezone" or similar) and change the surrounding
<label> to <label htmlFor="challenges-timezone" ...>. Ensure you keep the
existing props and handlers (value={draftConfig.challenges?.timezone ??
'America/New_York'}, onChange calling updateDraftConfig, disabled={saving},
className={inputClasses}, placeholder) and only add the id attribute to the
input and htmlFor to the label so the Timezone field
(draftConfig.challenges.timezone) matches other inputs' accessibility patterns.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 2a23ca3d-ff6f-48a1-bc7f-452c47451fd5

📥 Commits

Reviewing files that changed from the base of the PR and between 77da608 and 17965f9.

📒 Files selected for processing (8)
  • web/src/components/dashboard/config-editor.tsx
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/toggle-switch.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 (2)
**/*.{js,mjs,jsx,ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,mjs,jsx,ts,tsx}: Use single quotes for strings in code, double quotes only allowed in JSON files
Always end statements with semicolons
Use 2-space indentation, enforced by Biome

Files:

  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/toggle-switch.tsx
web/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Next.js 16 web dashboard uses App Router with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design

Files:

  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/dashboard/config-workspace/types.ts
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
  • web/src/components/dashboard/config-workspace/config-categories.ts
  • web/src/components/dashboard/toggle-switch.tsx
🧠 Learnings (2)
📚 Learning: 2026-03-05T18:07:15.752Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Applies to web/**/*.{ts,tsx} : Next.js 16 web dashboard uses App Router with Discord OAuth2 authentication, dark/light theme support, and mobile-responsive design

Applied to files:

  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
  • web/src/components/dashboard/toggle-switch.tsx
📚 Learning: 2026-03-05T18:07:15.752Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-05T18:07:15.752Z
Learning: Applies to src/modules/**/*.{js,mjs} : Create new modules for features with corresponding config sections in `config.json` and entries in `SAFE_CONFIG_KEYS`

Applied to files:

  • web/src/components/dashboard/config-workspace/config-categories.ts
🔇 Additional comments (12)
web/src/components/dashboard/toggle-switch.tsx (1)

1-29: LGTM!

Clean refactor replacing the custom button-based toggle with the shared Switch primitive. The component correctly maps onChange to onCheckedChange, maintains accessibility via aria-label, and the props interface is well-documented.

web/src/components/dashboard/config-workspace/types.ts (1)

1-57: LGTM!

Well-structured type definitions that establish clear contracts for the config workspace. The ConfigSectionKey union with 'aiAutoMod' properly extends the base ConfigSection type, and all interfaces are appropriately scoped with necessary fields.

web/src/components/dashboard/config-workspace/config-search.tsx (1)

1-87: LGTM!

The search component is well-implemented with proper accessibility attributes, clear result limiting (8 items), and correct category label resolution using getCategoryById(item.categoryId).label. The past review concern about slug-derived labels has been addressed.

web/src/components/dashboard/config-workspace/category-navigation.tsx (1)

1-93: LGTM!

Well-structured responsive navigation with proper accessibility attributes (aria-current, aria-hidden on icons). The icon mapping is complete, and the dirty count badges provide clear visual feedback. The type assertion on line 47 is safe since options are generated from CONFIG_CATEGORIES.

web/src/components/dashboard/config-workspace/settings-feature-card.tsx (2)

60-126: LGTM!

The card structure is well-organized with proper accessibility attributes (aria-expanded, aria-controls, aria-label). The conditional rendering of the enabled switch when both onEnabledChange and enabled are provided is correct. The visually hidden label complements the switch's aria-label.


52-56: This behavior is intentional and matches the documented contract.

The JSDoc states "When true, ensures the Advanced section is opened"—a one-directional guarantee, not bidirectional synchronization. This design aligns with the search workflow: when a user finds an advanced setting via search, the panel opens for inspection. When search clears, the panel remains open (not locked), allowing continued exploration while preserving user agency through the manual toggle button.

The implementation correctly reflects the intended contract.

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

151-213: Past review concerns have been addressed.

The activity badge implementation now uses a stable index-based key (key={\badge-${index}`}) and the delete logic correctly uses the same fallback array as the render path (draftConfig.engagement?.activityBadges ?? defaultActivityBadges`).


1-70: LGTM!

The component refactor is well-structured with clear prop types, proper docstrings, and a clean showFeature helper for visibility gating. The derived TL;DR values are sensibly extracted for reuse.

web/src/components/dashboard/config-workspace/config-categories.ts (4)

341-349: Past review concern has been addressed.

The getCategoryById function now includes a development warning via console.warn when falling back to the default category, with an appropriate biome-ignore comment to suppress the lint rule.


71-333: Past review concern has been addressed.

The section field has been removed from CONFIG_SEARCH_ITEMS entries. The search items now only include the necessary fields: id, featureId, categoryId, label, description, keywords, and isAdvanced.


372-391: LGTM!

The search helper functions getMatchingSearchItems and getMatchedFeatureIds are clean implementations with proper normalization and case-insensitive matching.


41-48: No changes needed. 'tickets' is a valid ConfigSectionKey and is properly included in the ConfigSection union type at line 357 of web/src/types/config.ts. No type error occurs here.

			> Likely an incorrect or invalid review comment.

Comment on lines +584 to +602
<label className="space-y-2 md:col-span-2">
<span className="text-sm font-medium">Timezone</span>
<input
type="text"
value={draftConfig.challenges?.timezone ?? 'America/New_York'}
onChange={(event) =>
updateDraftConfig((prev) => ({
...prev,
reputation: { ...prev.reputation, xpCooldownSeconds: num },
}));
}}
disabled={saving}
className={inputClasses}
/>
</label>
<label htmlFor="announce-channel-id" className="space-y-2">
<span className="text-sm font-medium">Announce Channel ID</span>
<ChannelSelector
id="announce-channel-id"
guildId={guildId}
selected={
draftConfig.reputation?.announceChannelId
? [draftConfig.reputation.announceChannelId]
: []
}
onChange={(selected) =>
updateDraftConfig((prev) => ({
...prev,
reputation: {
...prev.reputation,
announceChannelId: selected[0] ?? null,
},
}))
}
disabled={saving}
placeholder="Select announcement channel"
maxSelections={1}
filter="text"
/>
</label>
</div>
<label htmlFor="level-thresholds-comma-separated" className="space-y-2">
<span className="text-sm font-medium">
Level Thresholds (comma-separated XP values)
</span>
<input
id="level-thresholds-comma-separated"
type="text"
value={(
draftConfig.reputation?.levelThresholds ?? [
100, 300, 600, 1000, 1500, 2500, 4000, 6000, 8500, 12000,
]
).join(', ')}
onChange={(e) => {
const nums = e.target.value
.split(',')
.map((s) => Number(s.trim()))
.filter((n) => Number.isFinite(n) && n > 0);
if (nums.length > 0) {
const sorted = [...nums].sort((a, b) => a - b);
updateDraftConfig((prev) => ({
...prev,
reputation: { ...prev.reputation, levelThresholds: sorted },
}));
}
}}
disabled={saving}
className={inputClasses}
placeholder="100, 300, 600, 1000, ..."
/>
challenges: { ...prev.challenges, timezone: event.target.value },
}))
}
disabled={saving}
className={inputClasses}
placeholder="America/New_York"
/>
<p className="text-xs text-muted-foreground">
IANA timezone (e.g. America/Chicago, Europe/London)
</p>
</label>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Missing id and htmlFor attributes on the Timezone input.

The timezone label and input are not properly associated for accessibility. Other inputs in this file have proper id and htmlFor pairings.

♿ Proposed fix
-            <label className="space-y-2 md:col-span-2">
+            <label htmlFor="challenges-timezone" className="space-y-2 md:col-span-2">
               <span className="text-sm font-medium">Timezone</span>
               <input
+                id="challenges-timezone"
                 type="text"
                 value={draftConfig.challenges?.timezone ?? 'America/New_York'}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<label className="space-y-2 md:col-span-2">
<span className="text-sm font-medium">Timezone</span>
<input
type="text"
value={draftConfig.challenges?.timezone ?? 'America/New_York'}
onChange={(event) =>
updateDraftConfig((prev) => ({
...prev,
reputation: { ...prev.reputation, xpCooldownSeconds: num },
}));
}}
disabled={saving}
className={inputClasses}
/>
</label>
<label htmlFor="announce-channel-id" className="space-y-2">
<span className="text-sm font-medium">Announce Channel ID</span>
<ChannelSelector
id="announce-channel-id"
guildId={guildId}
selected={
draftConfig.reputation?.announceChannelId
? [draftConfig.reputation.announceChannelId]
: []
}
onChange={(selected) =>
updateDraftConfig((prev) => ({
...prev,
reputation: {
...prev.reputation,
announceChannelId: selected[0] ?? null,
},
}))
}
disabled={saving}
placeholder="Select announcement channel"
maxSelections={1}
filter="text"
/>
</label>
</div>
<label htmlFor="level-thresholds-comma-separated" className="space-y-2">
<span className="text-sm font-medium">
Level Thresholds (comma-separated XP values)
</span>
<input
id="level-thresholds-comma-separated"
type="text"
value={(
draftConfig.reputation?.levelThresholds ?? [
100, 300, 600, 1000, 1500, 2500, 4000, 6000, 8500, 12000,
]
).join(', ')}
onChange={(e) => {
const nums = e.target.value
.split(',')
.map((s) => Number(s.trim()))
.filter((n) => Number.isFinite(n) && n > 0);
if (nums.length > 0) {
const sorted = [...nums].sort((a, b) => a - b);
updateDraftConfig((prev) => ({
...prev,
reputation: { ...prev.reputation, levelThresholds: sorted },
}));
}
}}
disabled={saving}
className={inputClasses}
placeholder="100, 300, 600, 1000, ..."
/>
challenges: { ...prev.challenges, timezone: event.target.value },
}))
}
disabled={saving}
className={inputClasses}
placeholder="America/New_York"
/>
<p className="text-xs text-muted-foreground">
IANA timezone (e.g. America/Chicago, Europe/London)
</p>
</label>
<label htmlFor="challenges-timezone" className="space-y-2 md:col-span-2">
<span className="text-sm font-medium">Timezone</span>
<input
id="challenges-timezone"
type="text"
value={draftConfig.challenges?.timezone ?? 'America/New_York'}
onChange={(event) =>
updateDraftConfig((prev) => ({
...prev,
challenges: { ...prev.challenges, timezone: event.target.value },
}))
}
disabled={saving}
className={inputClasses}
placeholder="America/New_York"
/>
<p className="text-xs text-muted-foreground">
IANA timezone (e.g. America/Chicago, Europe/London)
</p>
</label>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboard/config-sections/CommunitySettingsSection.tsx`
around lines 584 - 602, Add an explicit id on the timezone input and a matching
htmlFor on the label to associate them for accessibility: give the input a
unique id (e.g. id="challenges-timezone" or similar) and change the surrounding
<label> to <label htmlFor="challenges-timezone" ...>. Ensure you keep the
existing props and handlers (value={draftConfig.challenges?.timezone ??
'America/New_York'}, onChange calling updateDraftConfig, disabled={saving},
className={inputClasses}, placeholder) and only add the id attribute to the
input and htmlFor to the label so the Timezone field
(draftConfig.challenges.timezone) matches other inputs' accessibility patterns.

@BillChirico BillChirico merged commit aab414b into main Mar 7, 2026
17 of 18 checks passed
@BillChirico BillChirico deleted the bill/feat/config-workspace-redesign branch March 7, 2026 15:34
@github-project-automation github-project-automation bot moved this from In Review to Done in Volvox.Bot Mar 7, 2026
@github-actions
Copy link
Contributor

github-actions bot commented Mar 7, 2026

🧹 Preview Environment Cleaned Up

The Railway preview environment for this PR has been removed.

Environment: pr-253

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

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

3 participants