Skip to content

feat(web): improve dashboard ux structure and reduce clutter#305

Open
brian-mwirigi wants to merge 3 commits intoVolvoxLLC:mainfrom
brian-mwirigi:feat/dashboard-ux-pass
Open

feat(web): improve dashboard ux structure and reduce clutter#305
brian-mwirigi wants to merge 3 commits intoVolvoxLLC:mainfrom
brian-mwirigi:feat/dashboard-ux-pass

Conversation

@brian-mwirigi
Copy link
Contributor

Summary

This PR improves the dashboard UI/UX with a more structured layout.

What changed

  • refined the main dashboard shell and content spacing
  • improved header context and mobile navigation behavior
  • reworked sidebar information architecture
  • upgraded server selector styling and hierarchy
  • added shared dashboard page header and empty state components
  • applied the new header or empty state patterns across key dashboard routes:
    • Members
    • Moderation
    • Conversations
    • Tickets
    • Audit Log
  • improved the Members and Tickets content areas to feel more clearly structured

Notes

This is a first pass dashboard UX overhaul focused on structure, consistency, and reducing clutter.
More polish and deeper visual work can follow in a later PR.

Validation

  • ran targeted Biome checks on updated dashboard files
  • did not run the full web test suite

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 14, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Reusable PageHeader and EmptyState components across dashboard pages
    • Top-line statistics cards added to multiple dashboards
  • Style

    • Card-based visual refresh (rounded panels, chips, gradients, spacing)
    • Unified empty-state visuals with adaptive messaging and icons
    • Updated header, sidebar (collapsible secondary nav), mobile sidebar, server selector, tables, pagination, and log viewer styling
  • Refactor

    • Layout and grid reflow for consistent centering and responsive panels

Walkthrough

Introduces two shared UI components (PageHeader, EmptyState) and applies them across dashboard pages and components, converting headers, empty states, panels, and various containers to consistent rounded, bordered, card-like styles while preserving existing data and interaction logic.

Changes

Cohort / File(s) Summary
New shared UI components
web/src/components/dashboard/page-header.tsx, web/src/components/dashboard/empty-state.tsx
Adds PageHeader and EmptyState client components to standardize section headers and empty-state visuals.
Dashboard pages (headers, empty states, stats)
web/src/app/dashboard/audit-log/page.tsx, web/src/app/dashboard/conversations/page.tsx, web/src/app/dashboard/members/page.tsx, web/src/app/dashboard/moderation/page.tsx, web/src/app/dashboard/tickets/page.tsx, web/src/app/dashboard/logs/page.tsx
Replaces inline headers and placeholders with PageHeader/EmptyState; adds top stats rows on several pages; preserves data fetching, filtering, pagination, and expansion logic; converts containers to dashboard-panel/chip styles.
Dashboard components (panels, tables, charts, filters)
web/src/components/dashboard/member-table.tsx, web/src/components/dashboard/log-filters.tsx, web/src/components/dashboard/log-viewer.tsx, web/src/components/dashboard/analytics-dashboard.tsx, web/src/components/dashboard/config-workspace/...
Updates container and control styling (rounded, heights, spacing); swaps bespoke empty UIs for EmptyState; introduces conditional EmptyState fallbacks in analytics charts; adds small API change (SettingsFeatureCard accepts className).
Layout & navigation shell
web/src/components/layout/dashboard-shell.tsx, web/src/components/layout/header.tsx, web/src/components/layout/sidebar.tsx, web/src/components/layout/mobile-sidebar.tsx, web/src/components/layout/server-selector.tsx
Restructures dashboard shell and header; revises sidebar/mobile/server-selector layouts and styling to new card-like visuals; adjusts header title derivation and control cluster; preserves navigation behavior.
Global styles
web/src/app/globals.css
Adds dashboard-specific CSS utilities (dashboard-canvas, dashboard-panel, dashboard-chip, dashboard-fade-in) and keyframes to support the new visual tokens.
Misc small UI files
web/src/components/dashboard/page-header.tsx, web/src/components/dashboard/empty-state.tsx, web/src/components/dashboard/config-workspace/..., web/src/components/layout/...
Various presentational adjustments: input/select sizing, rounded corners, chip/panel tokens, and refined typography across multiple small components.

Possibly related PRs

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the main change: improving dashboard UX structure and reducing clutter, which is reflected throughout the changeset with new components, refined layouts, and consistency improvements.
Description check ✅ Passed The description is directly related to the changeset, providing clear context about the dashboard UX improvements, specific routes modified, and validation approach taken.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • 🛠️ Publish Changes: Commit on current branch
  • 🛠️ Publish Changes: Create PR
📝 Coding Plan
  • Generate coding plan for human review comments

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.

@greptile-apps
Copy link

greptile-apps bot commented Mar 14, 2026

Greptile Summary

This PR delivers a first-pass dashboard UX overhaul across 22 files, introducing two new shared components (PageHeader and EmptyState) and a new CSS utility layer (dashboard-canvas, dashboard-panel, dashboard-chip, dashboard-fade-in) that unifies surface styling across the app. All major dashboard routes — Members, Moderation, Conversations, Tickets, Audit Log, Logs, and the Config Editor — are migrated to the new patterns, replacing inline ad-hoc headers and placeholder divs with consistent, composable building blocks.

Key highlights:

  • PageHeader replaces five per-page header implementations with a single component that accepts icon, title, description, and an actions slot
  • EmptyState replaces inline dashed-border placeholders with a consistent icon + label + description component used in both the "no guild selected" and "no data" states
  • Stat cards added to Audit Log, Conversations, Members, and Config Editor pages to surface quick-glance metrics
  • log-viewer.tsx replaces hardcoded dark-mode-only Tailwind colors (text-gray-200, bg-gray-950) with semantic tokens, improving light-mode rendering
  • analytics-dashboard.tsx adds empty-state guards for all four chart sections and introduces a hexToRgba helper to theme the activity heatmap using the chart palette rather than a hardcoded hex

Three minor polish items worth considering before or in a follow-up:

  • PageHeader renders a hardcoded "Dashboard Section" eyebrow label on every page — this generic text provides no useful contextual signal and should either be made a prop or removed
  • The DashboardShell outer wrapper and PageHeader both apply dashboard-panel (border + box-shadow), creating a panel-within-a-panel on every route
  • The hexToRgba utility only handles 6-digit hex input; 3-char shorthand would silently fall back to a hardcoded colour (safe today since useChartTheme always returns 6-digit values)

Confidence Score: 4/5

  • This PR is safe to merge — changes are purely presentational with no logic, data, or API surface modifications.
  • All changes are UI/UX only: class names, layout restructuring, and new shared components. No data-fetching logic, API calls, or state management was altered. No new console methods introduced. The full test suite was not run but there is nothing logic-sensitive to test. Score is 4 rather than 5 due to the nested-panel styling concern and the hardcoded eyebrow label, which are minor polish items that don't block functionality.
  • web/src/components/dashboard/page-header.tsx (hardcoded eyebrow label) and web/src/components/layout/dashboard-shell.tsx (nested dashboard-panel).

Important Files Changed

Filename Overview
web/src/components/dashboard/page-header.tsx New shared PageHeader component used across all dashboard pages — clean API, but eyebrow label is hardcoded to "Dashboard Section" on every page regardless of context.
web/src/components/dashboard/empty-state.tsx New shared EmptyState component replacing inline placeholder divs; clean interface with optional icon, title, description, and className props.
web/src/components/layout/dashboard-shell.tsx Adds dashboard-canvas dashboard-grid to root div and wraps main content in dashboard-panel — creates a nested panel-in-panel effect when PageHeader also applies dashboard-panel inside it.
web/src/app/globals.css Adds .dashboard-canvas, .dashboard-grid, .dashboard-panel, .dashboard-chip, and .dashboard-fade-in utility classes with appropriate dark-mode overrides.
web/src/components/dashboard/analytics-dashboard.tsx Adds EmptyState guards for all four chart areas and a hexToRgba helper to theme the heatmap; helper doesn't handle 3-char hex shorthand but is safe for the current useChartTheme output.
web/src/components/dashboard/config-editor.tsx Extensive layout restructuring: two-column grid replaced with stacked panels, stat cards added, categories moved to horizontal pill bar; functionally equivalent to previous version but significantly reorganized.
web/src/components/dashboard/log-viewer.tsx Replaces hardcoded dark-mode Tailwind colors (e.g. text-gray-200, bg-gray-950) with semantic design tokens (text-foreground, bg-background); improves light-mode compatibility.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    DS["DashboardShell\n(dashboard-canvas + dashboard-panel)"]
    H["Header"]
    AS["Aside / Desktop Sidebar"]
    SS["ServerSelector"]
    SB["Sidebar"]
    MAIN["main content\n(children)"]

    DS --> H
    DS --> AS
    DS --> MAIN

    AS --> SS
    AS --> SB

    subgraph "Each Dashboard Page"
        PH["PageHeader\n(dashboard-panel)"]
        SC["Stat Cards\n(dashboard-panel)"]
        FB["Filters Bar\n(dashboard-panel)"]
        TBL["Table / Content\n(dashboard-panel)"]
        ES["EmptyState\n(no guild / no data)"]

        PH --> SC
        SC --> FB
        FB --> TBL
        FB --> ES
    end

    MAIN --> PH
Loading
Prompt To Fix All With AI
This is a comment left during a code review.
Path: web/src/components/dashboard/page-header.tsx
Line: 32-34

Comment:
**Hardcoded "Dashboard Section" eyebrow label**

The eyebrow label above every page title is hardcoded to `"Dashboard Section"` regardless of context — so the Audit Log, Members, Moderation, and every other page all display the same identical label. This reduces the value of the eyebrow pattern; ideally it would reflect the actual section or subsystem (e.g. `"Activity"`, `"Governance"`, `"People"`).

Consider adding an optional `eyebrow?: string` prop and falling back to a no-op when not provided, rather than always rendering a static string:

```suggestion
          {Icon && <Icon className="h-5 w-5 text-primary" />}
```

or making it a proper prop:

```tsx
interface PageHeaderProps {
  eyebrow?: string;  // optional contextual label
  icon?: LucideIcon;
  title: string;
  // ...
}
```

and only rendering the `<p>` when `eyebrow` is supplied.

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

---

This is a comment left during a code review.
Path: web/src/components/layout/dashboard-shell.tsx
Line: 35-38

Comment:
**Nested `dashboard-panel` creates double border and shadow stack**

The inner `div` on line 36 applies `dashboard-panel`, which adds a `1px solid border` and a two-layer `box-shadow`. Every page then also renders a `PageHeader` that applies `dashboard-panel` again:

```tsx
// page-header.tsx
<header className={cn('dashboard-panel relative overflow-hidden rounded-2xl …')}>
```

The result is a panel-inside-a-panel — the outer shell panel provides the card frame, and the inner page header adds a second border and shadow on top of it. In practice this may look fine, but on light themes the stacked shadows and double borders can read as visual noise. If the shell panel is intended as a neutral canvas, consider removing `dashboard-panel` from the outer wrapper so individual page components own their own surface styling.

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

---

This is a comment left during a code review.
Path: web/src/components/dashboard/analytics-dashboard.tsx
Line: 1110-1121

Comment:
**`hexToRgba` silently falls back for non-6-digit hex inputs**

The helper falls back to the hardcoded Discord blurple `rgba(88, 101, 242, α)` for any input that is not exactly 6 hex digits after stripping `#`. This means 3-character shorthand (`#abc`) would silently produce the wrong color. While `useChartTheme` always returns full 6-digit values today, it's worth either documenting the limitation or expanding the guard:

```suggestion
function hexToRgba(hex: string, alpha: number): string {
  const normalized = hex.replace('#', '');
  // Expand 3-char shorthand to 6-char (e.g. "abc" → "aabbcc")
  const expanded =
    normalized.length === 3
      ? normalized
          .split('')
          .map((c) => c + c)
          .join('')
      : normalized;

  if (expanded.length !== 6) {
    return `rgba(88, 101, 242, ${alpha})`;
  }

  const r = Number.parseInt(expanded.slice(0, 2), 16);
  const g = Number.parseInt(expanded.slice(2, 4), 16);
  const b = Number.parseInt(expanded.slice(4, 6), 16);

  return `rgba(${r}, ${g}, ${b}, ${alpha})`;
}
```

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

Last reviewed commit: 2bf2f93

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: 3

🤖 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/member-table.tsx`:
- Around line 285-293: Replace the duplicated empty-state markup inside the
TableCell with the shared EmptyState component: import EmptyState and use it
where the current div/span/p block is, passing the Users icon as the icon prop
(e.g., icon={<Users />}), title="No members found", description="Try adjusting
your search or filters.", and propagate the existing TableCell className (or
pass className to EmptyState) so the table context can strip borders/background;
remove the span/div/p markup and ensure TableCell still uses colSpan={8}.

In `@web/src/components/layout/dashboard-shell.tsx`:
- Around line 21-35: The flex container and its children need explicit min-size
resets so the nested overflow-y-auto on the Sidebar body and the main pane can
scroll internally: add the Tailwind min-size utility (e.g. min-h-0 and where
appropriate min-w-0) to the outer flex container (the div with class "flex
flex-1") and to the immediate flex children (the aside element containing
ServerSelector/Sidebar and the main element wrapping {children}) so those panes
can shrink and let their internal overflow-y-auto handle scrolling.

In `@web/src/components/layout/sidebar.tsx`:
- Around line 181-190: The external support Link in the sidebar doesn't close
the mobile drawer because it bypasses the MobileSidebar's onNavClick handler;
update the footer link in the Sidebar/MobileSidebar component to invoke the same
onNavClick (or a wrapper that calls onNavClick and then navigates) when
clicked—i.e., attach an onClick that calls onNavClick (and preserves opening the
external URL via target="_blank"/rel) so the sheet collapses when the support
link is tapped.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 4f52645c-f853-43ba-b791-755273dbb87e

📥 Commits

Reviewing files that changed from the base of the PR and between 72ee8f0 and fdf5a5c.

📒 Files selected for processing (13)
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/components/dashboard/empty-state.tsx
  • web/src/components/dashboard/member-table.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/header.tsx
  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/components/layout/sidebar.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 (5)
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/layout/header.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/components/dashboard/empty-state.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/components/layout/sidebar.tsx
  • web/src/components/dashboard/member-table.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never use console.* methods in web dashboard code; use appropriate logging mechanisms for React applications

Files:

  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/layout/header.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/components/dashboard/empty-state.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/components/layout/sidebar.tsx
  • web/src/components/dashboard/member-table.tsx
**/*.{js,ts,tsx,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,tsx,mjs}: Use ESM syntax (import/export) — CommonJS is not allowed
Use single quotes for strings — double quotes only allowed in JSON files
Always include semicolons at end of statements
Use 2-space indentation for all code
Use Winston logger from src/logger.js — never use console.* methods

Files:

  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/layout/header.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/components/dashboard/empty-state.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/components/layout/sidebar.tsx
  • web/src/components/dashboard/member-table.tsx
web/src/app/dashboard/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

For dashboard routes, add a matcher entry to dashboardTitleMatchers in web/src/lib/page-titles.ts: use exact equality for leaf routes (pathname === '/dashboard/my-route') and subtree checks (pathname.startsWith('/dashboard/my-route/')); export metadata using createPageMetadata(title) for SSR entry points

Files:

  • web/src/app/dashboard/moderation/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
web/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

web/src/app/**/*.{ts,tsx}: Export metadata using createPageMetadata() from web/src/lib/page-titles.ts in SSR entry points for dashboard pages
Use DashboardTitleSync component and getDashboardDocumentTitle() for client-side navigation title updates in the dashboard

Files:

  • web/src/app/dashboard/moderation/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
🧠 Learnings (11)
📓 Common learnings
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/pages/dashboard/**/*.{ts,tsx} : Use shared title helpers from web/src/lib/page-titles.ts for setting browser titles in dashboard pages
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Use `DashboardTitleSync` component and `getDashboardDocumentTitle()` for client-side navigation title updates in the dashboard
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Use `DashboardTitleSync` component and `getDashboardDocumentTitle()` for client-side navigation title updates in the dashboard

Applied to files:

  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/layout/header.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/empty-state.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/components/layout/sidebar.tsx
  • web/src/components/dashboard/member-table.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'

Applied to files:

  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/layout/header.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/empty-state.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/components/layout/sidebar.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)

Applied to files:

  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/layout/header.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/components/layout/sidebar.tsx
  • web/src/components/dashboard/member-table.tsx
📚 Learning: 2026-03-10T20:36:29.483Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/pages/dashboard/**/*.{ts,tsx} : Use shared title helpers from web/src/lib/page-titles.ts for setting browser titles in dashboard pages

Applied to files:

  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/layout/header.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
📚 Learning: 2026-03-10T20:36:29.483Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Apply static metadata to server-rendered dashboard entry pages and use title template format for root app metadata

Applied to files:

  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/layout/header.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/components/dashboard/member-table.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Verify responsive layout behavior when making dashboard layout changes

Applied to files:

  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/components/layout/dashboard-shell.tsx
📚 Learning: 2026-03-12T02:03:36.476Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.476Z
Learning: Applies to web/src/app/dashboard/**/*.tsx : For dashboard routes, add a matcher entry to `dashboardTitleMatchers` in `web/src/lib/page-titles.ts`: use exact equality for leaf routes (`pathname === '/dashboard/my-route'`) and subtree checks (`pathname.startsWith('/dashboard/my-route/')`); export `metadata` using `createPageMetadata(title)` for SSR entry points

Applied to files:

  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/layout/header.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Export `metadata` using `createPageMetadata()` from `web/src/lib/page-titles.ts` in SSR entry points for dashboard pages

Applied to files:

  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/layout/header.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Add a matcher entry to `dashboardTitleMatchers` in `web/src/lib/page-titles.ts` for every new dashboard route

Applied to files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
📚 Learning: 2026-03-10T20:36:29.483Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/app/api/**/route.ts : Include guildId in signed WebSocket ticket payload when issuing tickets from dashboard endpoints

Applied to files:

  • web/src/app/dashboard/tickets/page.tsx
🪛 GitHub Check: SonarCloud Code Analysis
web/src/components/dashboard/empty-state.tsx

[warning] 14-14: Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuYXxDwAnQ9xtGQlpi&open=AZzuYXxDwAnQ9xtGQlpi&pullRequest=305

web/src/components/dashboard/page-header.tsx

[warning] 14-20: Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuYXzawAnQ9xtGQlpj&open=AZzuYXzawAnQ9xtGQlpj&pullRequest=305

🔇 Additional comments (11)
web/src/components/dashboard/empty-state.tsx (1)

14-30: Reusable empty-state primitive looks good.

Small prop surface plus the className escape hatch should make it easy to standardize
placeholder views without duplicating markup.

web/src/components/dashboard/page-header.tsx (1)

14-40: Clean PageHeader API.

Optional icon, description, and actions cover the dashboard variants in this PR without
pushing route-specific markup back into each page.

web/src/components/layout/header.tsx (1)

31-61: Nice reuse of the shared route-title helper.

Deriving the subtitle from getDashboardPageTitle() keeps the header copy aligned with the
existing dashboard title map instead of introducing a second route switch.

web/src/components/layout/sidebar.tsx (1)

97-178: The split between primary and secondary nav is well-factored.

Centralizing active-state checks in isNavItemActive() keeps both sections consistent and
makes future nav additions much less error-prone.

web/src/components/layout/server-selector.tsx (1)

139-267: The selector states are much clearer after this pass.

Using the same bordered-card treatment for loading, error, and empty cases keeps the shell
stable, and the Manage / Member Only split makes the dropdown easier to scan.

web/src/components/layout/mobile-sidebar.tsx (1)

29-39: Good reuse of the shared navigation stack on mobile.

Keeping ServerSelector and Sidebar inside the sheet avoids a separate mobile-only nav
tree.

web/src/app/dashboard/conversations/page.tsx (1)

6-7: Shared header/empty-state refactor is clean and preserves behavior.

The updated PageHeader/EmptyState integration keeps existing fetch/filter/pagination flows intact while improving consistency.

Also applies to: 45-46, 279-295, 299-303, 373-373, 436-448

web/src/app/dashboard/moderation/page.tsx (1)

7-7: Header/empty-state migration looks correct and non-breaking.

The new shared components are wired correctly with existing refresh/disabled behavior and guild gating.

Also applies to: 9-9, 107-125, 129-133

web/src/app/dashboard/members/page.tsx (1)

6-6: Members page UX restructuring is solid and behavior-preserving.

The new header, empty state, and stats/search containers are consistent with the dashboard pattern and keep current logic intact.

Also applies to: 13-13, 236-252, 256-260, 266-289

web/src/app/dashboard/tickets/page.tsx (1)

6-7: Tickets page refactor is cohesive and safely scoped.

Shared header/empty-state adoption and card styling changes are consistent, and the functional data path remains unchanged.

Also applies to: 45-46, 276-292, 297-319, 326-330, 337-337, 397-397, 447-455

web/src/app/dashboard/audit-log/page.tsx (1)

6-7: Audit Log UI standardization is implemented correctly.

The shared component migration and visual refinements are consistent and do not introduce behavior changes.

Also applies to: 272-288, 292-296, 388-388, 441-443, 455-467

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

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

♻️ Duplicate comments (1)
web/src/components/layout/dashboard-shell.tsx (1)

33-35: ⚠️ Potential issue | 🟠 Major

Add min-w-0 to the main flex pane to prevent horizontal overflow regressions.

Line 33 still misses min-w-0; wide content can force page-level overflow instead of keeping overflow behavior scoped to the pane.

Suggested fix
-        <main className="min-h-0 flex-1 overflow-y-auto">
+        <main className="min-h-0 min-w-0 flex-1 overflow-y-auto">
           <div className="mx-auto w-full max-w-[1460px] p-5 md:p-8">{children}</div>
         </main>

Based on learnings: Verify responsive layout behavior when making dashboard layout changes.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/dashboard-shell.tsx` around lines 33 - 35, The main
flex pane in DashboardShell currently uses className "min-h-0 flex-1
overflow-y-auto" which can allow wide children to cause page-level horizontal
overflow; update the main element's className to include "min-w-0" (e.g., add
min-w-0 alongside min-h-0) so the overflow is constrained to the pane and
responsive layout regressions are prevented.
🤖 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/layout/sidebar.tsx`:
- Around line 112-139: Extract the duplicated link/icon/class rendering into a
single shared renderer (e.g., a NavItem component or renderNavItem function) and
use it for both primaryNav and secondaryNav maps: accept the nav item object,
call isNavItemActive(item.href) inside the renderer, apply the same Link,
onClick (onNavClick), cn class logic and the item.icon rendering so both
sections reuse the identical logic; update the primaryNav.map and
secondaryNav.map to call this shared renderer passing item and avoid repeating
the JSX and className calculations.
- Line 183: In Sidebar component (web/src/components/layout/sidebar.tsx) change
the JSX href attribute value from double quotes to single quotes: replace
href="https://joinvolvox.com/" with href='https://joinvolvox.com/' so the
external URL string follows the repository rule for single-quoted strings in
JS/TS/TSX files (locate the anchor/Link element in the Sidebar component).

---

Duplicate comments:
In `@web/src/components/layout/dashboard-shell.tsx`:
- Around line 33-35: The main flex pane in DashboardShell currently uses
className "min-h-0 flex-1 overflow-y-auto" which can allow wide children to
cause page-level horizontal overflow; update the main element's className to
include "min-w-0" (e.g., add min-w-0 alongside min-h-0) so the overflow is
constrained to the pane and responsive layout regressions are prevented.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: c9f793c0-ffa4-47f2-89c4-681a9b7b82dc

📥 Commits

Reviewing files that changed from the base of the PR and between fdf5a5c and e799f09.

📒 Files selected for processing (3)
  • web/src/components/dashboard/member-table.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/sidebar.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 (3)
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • web/src/components/layout/sidebar.tsx
  • web/src/components/dashboard/member-table.tsx
  • web/src/components/layout/dashboard-shell.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never use console.* methods in web dashboard code; use appropriate logging mechanisms for React applications

Files:

  • web/src/components/layout/sidebar.tsx
  • web/src/components/dashboard/member-table.tsx
  • web/src/components/layout/dashboard-shell.tsx
**/*.{js,ts,tsx,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,tsx,mjs}: Use ESM syntax (import/export) — CommonJS is not allowed
Use single quotes for strings — double quotes only allowed in JSON files
Always include semicolons at end of statements
Use 2-space indentation for all code
Use Winston logger from src/logger.js — never use console.* methods

Files:

  • web/src/components/layout/sidebar.tsx
  • web/src/components/dashboard/member-table.tsx
  • web/src/components/layout/dashboard-shell.tsx
🧠 Learnings (8)
📓 Common learnings
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/pages/dashboard/**/*.{ts,tsx} : Use shared title helpers from web/src/lib/page-titles.ts for setting browser titles in dashboard pages
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Use `DashboardTitleSync` component and `getDashboardDocumentTitle()` for client-side navigation title updates in the dashboard
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'

Applied to files:

  • web/src/components/layout/sidebar.tsx
  • web/src/components/layout/dashboard-shell.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)

Applied to files:

  • web/src/components/layout/sidebar.tsx
  • web/src/components/layout/dashboard-shell.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Use `DashboardTitleSync` component and `getDashboardDocumentTitle()` for client-side navigation title updates in the dashboard

Applied to files:

  • web/src/components/layout/sidebar.tsx
  • web/src/components/layout/dashboard-shell.tsx
📚 Learning: 2026-03-10T20:36:29.483Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/pages/dashboard/**/*.{ts,tsx} : Use shared title helpers from web/src/lib/page-titles.ts for setting browser titles in dashboard pages

Applied to files:

  • web/src/components/layout/dashboard-shell.tsx
📚 Learning: 2026-03-10T20:36:29.483Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Apply static metadata to server-rendered dashboard entry pages and use title template format for root app metadata

Applied to files:

  • web/src/components/layout/dashboard-shell.tsx
📚 Learning: 2026-03-12T02:03:36.476Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.476Z
Learning: Applies to web/src/app/dashboard/**/*.tsx : For dashboard routes, add a matcher entry to `dashboardTitleMatchers` in `web/src/lib/page-titles.ts`: use exact equality for leaf routes (`pathname === '/dashboard/my-route'`) and subtree checks (`pathname.startsWith('/dashboard/my-route/')`); export `metadata` using `createPageMetadata(title)` for SSR entry points

Applied to files:

  • web/src/components/layout/dashboard-shell.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Verify responsive layout behavior when making dashboard layout changes

Applied to files:

  • web/src/components/layout/dashboard-shell.tsx
🔇 Additional comments (4)
web/src/components/dashboard/member-table.tsx (3)

17-17: Shared EmptyState import is the right abstraction move.

This keeps the table aligned with the dashboard’s reusable empty-state pattern and reduces UI drift.


239-239: Container surface update improves structure without changing behavior.

The card-like styling is consistent with the UX pass and keeps table overflow handling intact.


286-292: Empty-state integration is correctly adapted for table context.

colSpan={8} is preserved, and the class overrides cleanly remove card chrome so the shared component fits inside the table cell.

web/src/components/layout/dashboard-shell.tsx (1)

18-18: Good placement of dashboard title synchronization.

Keeping DashboardTitleSync mounted in the shell is the right pattern for route-driven title updates across dashboard pages.

Based on learnings: Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'.

Comment on lines 112 to 139
<nav className="space-y-1">
{navigation.map((item) => {
const isActive =
pathname === item.href ||
(item.href !== '/dashboard' && pathname.startsWith(`${item.href}/`));
{primaryNav.map((item) => {
const isActive = isNavItemActive(item.href);

return (
<Link
key={item.name}
href={item.href}
onClick={onNavClick}
className={cn(
'flex items-center gap-3 rounded-lg px-3 py-2 text-sm font-medium transition-all hover:bg-accent hover:text-accent-foreground',
isActive ? 'bg-accent text-accent-foreground' : 'text-muted-foreground',
'group flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium transition-all duration-200',
'hover:bg-muted/75 hover:text-foreground',
isActive
? 'bg-primary/12 text-foreground ring-1 ring-primary/30 shadow-sm'
: 'text-muted-foreground',
)}
>
<item.icon className="h-4 w-4" />
{item.name}
<item.icon
className={cn(
'h-4 w-4 transition-colors',
isActive ? 'text-primary' : 'text-muted-foreground group-hover:text-foreground',
)}
/>
<span>{item.name}</span>
</Link>
);
})}
</nav>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Extract duplicated nav-item rendering to one shared renderer.

The primary and secondary sections duplicate the same link/icon/class logic, which increases drift risk during future UI tweaks.

♻️ Proposed refactor
+interface NavItem {
+  name: string;
+  href: string;
+  icon: React.ComponentType<{ className?: string }>;
+}
+
+const navItemClassName =
+  'group flex items-center gap-3 rounded-xl px-3 py-2.5 text-sm font-medium transition-all duration-200';
+
+function renderNavItem(
+  item: NavItem,
+  isActive: boolean,
+  onNavClick?: () => void,
+) {
+  return (
+    <Link
+      key={item.name}
+      href={item.href}
+      onClick={onNavClick}
+      className={cn(
+        navItemClassName,
+        'hover:bg-muted/75 hover:text-foreground',
+        isActive
+          ? 'bg-primary/12 text-foreground ring-1 ring-primary/30 shadow-sm'
+          : 'text-muted-foreground',
+      )}
+    >
+      <item.icon
+        className={cn(
+          'h-4 w-4 transition-colors',
+          isActive ? 'text-primary' : 'text-muted-foreground group-hover:text-foreground',
+        )}
+      />
+      <span>{item.name}</span>
+    </Link>
+  );
+}

Then both maps become a single-line render call with isNavItemActive(item.href).

Also applies to: 148-177

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/sidebar.tsx` around lines 112 - 139, Extract the
duplicated link/icon/class rendering into a single shared renderer (e.g., a
NavItem component or renderNavItem function) and use it for both primaryNav and
secondaryNav maps: accept the nav item object, call isNavItemActive(item.href)
inside the renderer, apply the same Link, onClick (onNavClick), cn class logic
and the item.icon rendering so both sections reuse the identical logic; update
the primaryNav.map and secondaryNav.map to call this shared renderer passing
item and avoid repeating the JSX and className calculations.

</div>
<div className="border-t border-border/60 p-3">
<Link
href="https://joinvolvox.com/"
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

Use single quotes for the external URL string.

href uses double quotes, which violates the repository string-quote rule.

✏️ Proposed fix
-          href="https://joinvolvox.com/"
+          href='https://joinvolvox.com/'

As per coding guidelines, "**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes".

📝 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
href="https://joinvolvox.com/"
href='https://joinvolvox.com/'
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/sidebar.tsx` at line 183, In Sidebar component
(web/src/components/layout/sidebar.tsx) change the JSX href attribute value from
double quotes to single quotes: replace href="https://joinvolvox.com/" with
href='https://joinvolvox.com/' so the external URL string follows the repository
rule for single-quoted strings in JS/TS/TSX files (locate the anchor/Link
element in the Sidebar component).

@sonarqubecloud
Copy link

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: 14

🤖 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/app/dashboard/conversations/page.tsx`:
- Around line 318-324: The "Active Channels" count is using the full channels
array (all text channels filtered by type === 0), which mislabels idle channels
as active; update the UI in page.tsx to either (A) relabel the card to "Text
Channels" or (B) compute active channels from the conversations dataset
instead—for example derive a Set of unique channel IDs from the conversations
(e.g. conversations.map(c => c.channelId) then new Set(...) and use its size)
and use that value for the displayed count; locate the channels variable and the
effect that filters by type === 0 and replace the displayed channels.length with
the chosen derived value or change the label accordingly.
- Around line 279-295: This route is marked as a client component which prevents
exporting SSR metadata; refactor by turning this file into a server component
that exports metadata via createPageMetadata('Conversations') and move all
interactive UI and hooks into a new client child component (e.g.,
ConversationsClient) that contains PageHeader, the Button with
onClick={handleRefresh}, RefreshCw usage, guildId/loading logic, and any
hooks/state. In this file export const metadata =
createPageMetadata('Conversations') and render the server wrapper that imports
and renders <ConversationsClient />; ensure handlers like handleRefresh, and
symbols PageHeader, RefreshCw remain only in the client child and the route
entry has no 'use client' directive.

In `@web/src/app/dashboard/members/page.tsx`:
- Around line 236-252: Refactor the interactive UI in this page so the top-level
route exports metadata via createPageMetadata('Members'): move the interactive
portions (PageHeader, Button using handleRefresh, loading, guildId state) into a
child component (e.g., MembersClient or MembersPanel) and have the page file
export createPageMetadata('Members') at the module level while rendering the
child component server-side entry; ensure you keep the existing symbols
(PageHeader, handleRefresh, RefreshCw, loading, guildId) inside the new client
component and import/inline it from the page so metadata is available before
client hydration.

In `@web/src/app/dashboard/moderation/page.tsx`:
- Around line 171-177: Run the repository formatter (e.g. prettier/nextjs
format) on the updated JSX block that renders the "User History Lookup" panel so
the whitespace/line breaks match the project's style; specifically reformat the
section containing className="dashboard-panel space-y-3 rounded-2xl p-4 md:p-5"
and its child h3 ("User History Lookup") and p elements, then stage the
resulting changes so CI no longer reports "Formatter would have printed content
differently."
- Around line 202-212: The icon-only clear-history Button (rendered when
lookupUserId is truthy) uses title for accessibility which is not reliable;
update the Button element that calls handleClearUserHistory (the Button wrapping
the X icon) to include an explicit accessible name like aria-label="Clear user
history" so screen readers can identify the action (keep existing title if
desired but add aria-label to the Button component).
- Around line 107-125: Refactor the current moderation/page.tsx into a server
entry that exports metadata = createPageMetadata('Moderation', 'Review cases,
track activity, and audit your moderation team.') and renders a new client child
component (e.g., ModerationClient); move all interactive logic — React hooks,
state, effects, handlers like handleRefresh, statsLoading, casesLoading, and the
JSX that includes PageHeader, Button, RefreshCw, and list rendering — into that
client component, mark it with "use client" at the top, export it as default
from the client file, and update the server page to only import and render
<ModerationClient /> so the server file contains no client-side hooks or state.

In `@web/src/app/dashboard/tickets/page.tsx`:
- Around line 295-322: The summary cards are stale because they use the local
stats state but handleRefresh only refetches tickets; extract the logic that
fetches stats into a reusable async function (e.g., fetchStats or loadStats)
that sets the stats state and call that function both on initial mount and from
handleRefresh; ensure you update the same stats state used by the
dashboard-panel cards (referencing stats and formatDuration) so Refresh triggers
a stats refetch as well as the ticket list.
- Around line 276-292: Remove the top-level "use client" from page.tsx and move
all interactive/client-side logic (hooks, state, event handlers like
handleRefresh, and reactive variables loading and guildId) plus UI that depends
on them (the Button, RefreshCw spinner, and any other interactivity around
PageHeader) into a new client component (e.g., TicketsClient or
TicketsPageClient); leave the page.tsx as a server component that imports and
renders this client child, and add export const metadata =
createPageMetadata('Tickets') at the top-level page export so SSR metadata
works. Ensure the server page still renders PageHeader and the new client
component in the same structure, and keep prop names and handlers consistent
when passing data into the client component.

In `@web/src/components/dashboard/analytics-dashboard.tsx`:
- Around line 672-714: The empty-state branches render before the first
analytics payload arrives because analytics is null while loading is true;
update the conditional that uses hasMessageVolumeData (and the similar checks
around lines referenced) to also require that loading is false or that analytics
!== null (or that analytics.messageVolume is defined/has length) before showing
the EmptyState component (MessageSquare) — while loading is true show the
existing loading skeleton/spinner instead; apply the same guard to the other
empty-state checks for "No model usage", "No token metrics", and "No channel
activity" so they only render after the payload has finished loading.

In `@web/src/components/dashboard/page-header.tsx`:
- Line 36: The decorative Icon rendered in the header ({Icon && <Icon
className="h-5 w-5 text-primary" />}) should be hidden from assistive tech;
update the JSX that renders Icon to include aria-hidden="true" and (optionally)
focusable="false" on the Icon element so it isn’t announced or tabbable while
keeping the existing className and conditional rendering.

In `@web/src/components/layout/dashboard-shell.tsx`:
- Around line 35-38: The root dashboard-shell currently forces a
"dashboard-panel" wrapper (the divs with classNames "dashboard-fade-in" and
"dashboard-panel") around every route which causes nested borders/padding;
update the DashboardShell component in
web/src/components/layout/dashboard-shell.tsx to accept a boolean prop (e.g.,
wrapWithPanel or disablePanel) and render the inner "dashboard-panel" wrapper
conditionally based on that prop so routes that already render their own panel
can pass false, and update calls/pages that need the wrapper to either pass the
new prop or rely on the default.

In `@web/src/components/layout/mobile-sidebar.tsx`:
- Around line 30-33: The SheetContent in mobile-sidebar.tsx uses a fixed class
w-[21.5rem] which can overflow narrow phones; update the SheetContent className
to use a fluid width with a capped max (e.g., replace the fixed width with
w-full and a max width such as max-w-[21.5rem] or max-w-[90vw]) so the sheet
fills available space but never exceeds the viewport; locate the SheetContent
element and modify its className accordingly to prevent clipping on small
viewports.

In `@web/src/components/layout/server-selector.tsx`:
- Around line 188-221: The long Tailwind class string passed into cn for the
Button component (the string starting with "h-[3.2rem] w-full justify-between
rounded-xl border-border/70 bg-gradient-to-r from-background to-muted/40 px-3
shadow-sm") should be broken across lines for readability; refactor by pulling
that class list into a named constant (e.g., buttonClasses) or pass the classes
as a multi-line array into cn([...]) and keep the existing cn(..., className)
call so the styling and className prop behavior remain unchanged; update the
Button JSX to use the new variable/array while leaving the surrounding structure
(Button, cn, and className) intact.

In `@web/src/components/layout/sidebar.tsx`:
- Around line 100-107: The effect that reopens the secondary nav only depends on
hasActiveSecondaryItem so navigating between secondary routes can leave the
section collapsed; update the useEffect that currently references
hasActiveSecondaryItem to also depend on the current pathname (or use pathname
instead of hasActiveSecondaryItem) and call setIsSecondaryOpen(true) when the
pathname changes and hasActiveSecondaryItem is true—adjust the dependency array
for the useEffect (or key the effect to pathname) so back/forward and deep-link
navigation reopen the section; reference the existing hasActiveSecondaryItem,
isSecondaryOpen, setIsSecondaryOpen and the useEffect block when making the
change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 588e0665-7565-4bce-9950-94565a4ba83f

📥 Commits

Reviewing files that changed from the base of the PR and between e799f09 and 2bf2f93.

📒 Files selected for processing (20)
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/app/dashboard/logs/page.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/globals.css
  • web/src/components/dashboard/analytics-dashboard.tsx
  • web/src/components/dashboard/config-editor.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/log-filters.tsx
  • web/src/components/dashboard/log-viewer.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/header.tsx
  • web/src/components/layout/mobile-sidebar.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/components/layout/sidebar.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 (5)
**/*.{js,ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

**/*.{js,ts,tsx}: Use single quotes for strings (except in JSON files); no double quotes
Always include semicolons at the end of statements
Use 2-space indentation (spaces, not tabs)
Always include trailing commas in multi-line arrays, objects, and function parameters
Maintain a maximum line width of 100 characters

Files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/analytics-dashboard.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/dashboard/log-viewer.tsx
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/components/dashboard/log-filters.tsx
  • web/src/components/layout/sidebar.tsx
  • web/src/app/dashboard/logs/page.tsx
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/mobile-sidebar.tsx
web/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Never use console.* methods in web dashboard code; use appropriate logging mechanisms for React applications

Files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/analytics-dashboard.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/dashboard/log-viewer.tsx
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/components/dashboard/log-filters.tsx
  • web/src/components/layout/sidebar.tsx
  • web/src/app/dashboard/logs/page.tsx
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/mobile-sidebar.tsx
**/*.{js,ts,tsx,mjs}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{js,ts,tsx,mjs}: Use ESM syntax (import/export) — CommonJS is not allowed
Use single quotes for strings — double quotes only allowed in JSON files
Always include semicolons at end of statements
Use 2-space indentation for all code
Use Winston logger from src/logger.js — never use console.* methods

Files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/analytics-dashboard.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/dashboard/log-viewer.tsx
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/components/dashboard/log-filters.tsx
  • web/src/components/layout/sidebar.tsx
  • web/src/app/dashboard/logs/page.tsx
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/mobile-sidebar.tsx
web/src/app/dashboard/**/*.tsx

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

For dashboard routes, add a matcher entry to dashboardTitleMatchers in web/src/lib/page-titles.ts: use exact equality for leaf routes (pathname === '/dashboard/my-route') and subtree checks (pathname.startsWith('/dashboard/my-route/')); export metadata using createPageMetadata(title) for SSR entry points

Files:

  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/logs/page.tsx
web/src/app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

web/src/app/**/*.{ts,tsx}: Export metadata using createPageMetadata() from web/src/lib/page-titles.ts in SSR entry points for dashboard pages
Use DashboardTitleSync component and getDashboardDocumentTitle() for client-side navigation title updates in the dashboard

Files:

  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/logs/page.tsx
🧠 Learnings (14)
📓 Common learnings
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/pages/dashboard/**/*.{ts,tsx} : Use shared title helpers from web/src/lib/page-titles.ts for setting browser titles in dashboard pages
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Use `DashboardTitleSync` component and `getDashboardDocumentTitle()` for client-side navigation title updates in the dashboard
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/layout/dashboard-shell.tsx : Dashboard page titles should sync with route changes using DashboardTitleSync component mounted in dashboard-shell.tsx and canonical title string 'Volvox.Bot - AI Powered Discord Bot'

Applied to files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/analytics-dashboard.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/dashboard/log-viewer.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/components/dashboard/log-filters.tsx
  • web/src/components/layout/sidebar.tsx
  • web/src/app/dashboard/logs/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/mobile-sidebar.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Use `DashboardTitleSync` component and `getDashboardDocumentTitle()` for client-side navigation title updates in the dashboard

Applied to files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/analytics-dashboard.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/dashboard/log-viewer.tsx
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/globals.css
  • web/src/components/layout/sidebar.tsx
  • web/src/app/dashboard/logs/page.tsx
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/mobile-sidebar.tsx
📚 Learning: 2026-03-10T20:36:29.483Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/pages/dashboard/**/*.{ts,tsx} : Use shared title helpers from web/src/lib/page-titles.ts for setting browser titles in dashboard pages

Applied to files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/analytics-dashboard.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/dashboard/log-viewer.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/globals.css
  • web/src/app/dashboard/logs/page.tsx
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/mobile-sidebar.tsx
📚 Learning: 2026-03-12T02:03:36.476Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.476Z
Learning: Applies to web/src/app/dashboard/**/*.tsx : For dashboard routes, add a matcher entry to `dashboardTitleMatchers` in `web/src/lib/page-titles.ts`: use exact equality for leaf routes (`pathname === '/dashboard/my-route'`) and subtree checks (`pathname.startsWith('/dashboard/my-route/')`); export `metadata` using `createPageMetadata(title)` for SSR entry points

Applied to files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/logs/page.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/mobile-sidebar.tsx
📚 Learning: 2026-03-10T20:36:29.483Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Apply static metadata to server-rendered dashboard entry pages and use title template format for root app metadata

Applied to files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/components/dashboard/analytics-dashboard.tsx
  • web/src/components/layout/server-selector.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/dashboard/log-viewer.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/logs/page.tsx
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/mobile-sidebar.tsx
📚 Learning: 2026-03-10T23:21:49.730Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T23:21:49.730Z
Learning: Applies to web/src/components/dashboard/config-workspace/**/*.{ts,tsx} : Web dashboard config editor should use category workspace navigation with reusable SettingsFeatureCard pattern (header + master toggle + Basic/Advanced blocks)

Applied to files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/conversations/page.tsx
  • web/src/components/dashboard/config-workspace/category-navigation.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/components/dashboard/log-viewer.tsx
  • web/src/components/dashboard/config-workspace/settings-feature-card.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/globals.css
  • web/src/components/layout/sidebar.tsx
  • web/src/app/dashboard/logs/page.tsx
  • web/src/components/dashboard/config-workspace/config-search.tsx
  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/mobile-sidebar.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to web/src/app/**/*.{ts,tsx} : Export `metadata` using `createPageMetadata()` from `web/src/lib/page-titles.ts` in SSR entry points for dashboard pages

Applied to files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
  • web/src/app/dashboard/members/page.tsx
  • web/src/app/dashboard/moderation/page.tsx
  • web/src/app/dashboard/audit-log/page.tsx
  • web/src/app/dashboard/tickets/page.tsx
  • web/src/app/dashboard/logs/page.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Add a matcher entry to `dashboardTitleMatchers` in `web/src/lib/page-titles.ts` for every new dashboard route

Applied to files:

  • web/src/components/layout/header.tsx
  • web/src/components/dashboard/page-header.tsx
📚 Learning: 2026-03-12T02:03:36.476Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.476Z
Learning: Applies to web/src/**/*.{ts,tsx} : Never use `console.*` methods in web dashboard code; use appropriate logging mechanisms for React applications

Applied to files:

  • web/src/components/dashboard/log-viewer.tsx
  • web/src/components/dashboard/log-filters.tsx
  • web/src/app/dashboard/logs/page.tsx
📚 Learning: 2026-03-10T20:36:29.483Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-03-10T20:36:29.483Z
Learning: Applies to web/src/app/api/**/route.ts : Include guildId in signed WebSocket ticket payload when issuing tickets from dashboard endpoints

Applied to files:

  • web/src/app/dashboard/tickets/page.tsx
📚 Learning: 2026-03-12T02:03:36.476Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-03-12T02:03:36.476Z
Learning: Applies to **/*.{js,ts,tsx} : Use single quotes for strings (except in JSON files); no double quotes

Applied to files:

  • web/src/components/layout/sidebar.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Applies to **/*.{js,ts,tsx,mjs} : Use single quotes for strings — double quotes only allowed in JSON files

Applied to files:

  • web/src/components/layout/sidebar.tsx
📚 Learning: 2026-03-12T02:03:52.689Z
Learnt from: CR
Repo: VolvoxLLC/volvox-bot PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-03-12T02:03:52.689Z
Learning: Verify responsive layout behavior when making dashboard layout changes

Applied to files:

  • web/src/components/layout/dashboard-shell.tsx
  • web/src/components/layout/mobile-sidebar.tsx
🪛 GitHub Actions: CI
web/src/app/dashboard/moderation/page.tsx

[error] 177-177: Formatter would have printed content differently (prettier formatting issue detected).

🪛 GitHub Check: SonarCloud Code Analysis
web/src/components/layout/header.tsx

[warning] 56-56: Ambiguous spacing after previous element span

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzzjMdht5t5DmIVYn8w&open=AZzzjMdht5t5DmIVYn8w&pullRequest=305

web/src/components/dashboard/page-header.tsx

[warning] 14-20: Mark the props of the component as read-only.

See more on https://sonarcloud.io/project/issues?id=VolvoxLLC_volvox-bot&issues=AZzuYXzawAnQ9xtGQlpj&open=AZzuYXzawAnQ9xtGQlpj&pullRequest=305

🔇 Additional comments (20)
web/src/components/layout/mobile-sidebar.tsx (1)

41-42: Good mobile UX: closes sheet after nav click.

onNavClick={() => setOpen(false)} prevents stale open state after navigation.

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

40-40: LGTM: visual refresh keeps behavior intact.

These class updates are purely presentational and preserve existing search/filter/select behavior.

Also applies to: 59-59, 69-69

web/src/app/globals.css (1)

183-264: LGTM: foundational dashboard style primitives are cleanly defined.

The new canvas/grid/panel/chip/animation classes are consistent and reusable for the dashboard UI pass.

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

10-10: Good extensibility improvement.

Passing className through cn(...) is the right change and keeps default layout behavior while enabling per-feature overrides.

Also applies to: 23-23, 50-50, 66-69

web/src/components/dashboard/page-header.tsx (1)

22-49: Nice shared abstraction for dashboard headers.

The props surface is lean and the layout supports icon/title/description/actions cleanly.

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

45-45: LGTM: navigation restructure preserves interaction semantics.

This keeps category switching/dirty badges intact while improving layout responsiveness.

Also applies to: 62-88

web/src/components/dashboard/log-filters.tsx (1)

107-107: LGTM: style updates are safe and non-functional.

The filter UX is visually improved without altering existing debounce and emit logic.

Also applies to: 110-110, 134-134, 145-145, 154-154

web/src/components/layout/dashboard-shell.tsx (1)

21-23: Good fix on flex min-size/scroll containment.

min-h-0 placement on the row and scroll panes is correct and prevents outer-page overflow regression.

Also applies to: 33-34

web/src/components/layout/header.tsx (1)

31-35: LGTM: header refactor improves context without breaking auth/session behavior.

The route-aware subtitle integration and menu restructuring look solid.

Also applies to: 50-51, 75-127

web/src/app/dashboard/logs/page.tsx (1)

27-31: No refactor needed — this page correctly uses the established client component pattern.

The logs route uses useLogStream() and useGuildSelection(), both client hooks. The 'use client' directive is required and correct. The title is managed via DashboardTitleSync (mounted in dashboard-shell.tsx) which looks up the route in dashboardTitleMatchers — the entry for /dashboard/logs already exists there. This matches the pattern used in audit-log and moderation pages. No server wrapper or refactor needed.

			> Likely an incorrect or invalid review comment.
web/src/components/layout/server-selector.tsx (3)

41-41: LGTM!

Adding text-sm for consistent typography in dropdown rows aligns with the design system.


139-157: LGTM!

The loading and error states now use the dashboard-chip styling consistently with the new design system. Adding the explicit Retry button improves usability over the previous refresh icon-only approach.


223-271: LGTM!

The dropdown content and item styling updates are well-implemented:

  • Increased width (w-80) provides better visual hierarchy
  • Consistent label typography with text-[11px] uppercase tracking-[0.14em]
  • Rounded corners and padding on items improve touch targets
web/src/app/dashboard/audit-log/page.tsx (7)

6-7: LGTM!

Clean imports for the new shared dashboard components.


272-296: LGTM!

The adoption of PageHeader and EmptyState components provides consistent structure across dashboard pages. The refresh button is appropriately disabled when no guild is selected or loading.


302-327: LGTM!

The metrics row provides useful at-a-glance information. The gradient styling and consistent typography (text-[11px] uppercase tracking-[0.14em]) align with the design system. Using tabular-nums for numeric values ensures alignment.


330-399: LGTM!

Filter controls are well-organized within the dashboard-panel container. Consistent input heights (h-10) and border radius (rounded-xl) improve visual coherence.


482-494: LGTM!

The dynamic EmptyState that varies based on active filters is a nice UX touch—users get relevant messaging whether they're seeing no results due to filters or simply have no entries yet.


498-521: LGTM!

Pagination container styling is consistent with the dashboard-chip pattern. The disabled states and page calculation logic are correct.


120-120: Matcher entry for /dashboard/audit-log is present in dashboardTitleMatchers.

The route has the required matcher entry with both exact equality and subtree checks.

Comment on lines +279 to +295
<PageHeader
icon={MessageSquare}
title="Conversations"
description="Browse, search, and replay AI conversations."
actions={
<Button
variant="outline"
size="sm"
className="gap-2"
onClick={handleRefresh}
disabled={!guildId || loading}
>
<RefreshCw className={`h-4 w-4 ${loading ? 'animate-spin' : ''}`} />
Refresh
</Button>
}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n web/src/app/dashboard/conversations/page.tsx | head -50

Repository: VolvoxLLC/volvox-bot

Length of output: 2097


🏁 Script executed:

wc -l web/src/app/dashboard/conversations/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 112


🏁 Script executed:

rg -i "metadata|export" web/src/app/dashboard/conversations/page.tsx | head -20

Repository: VolvoxLLC/volvox-bot

Length of output: 109


🏁 Script executed:

fd -e tsx -path "*/app/dashboard/*/page.tsx" | head -10

Repository: VolvoxLLC/volvox-bot

Length of output: 232


🏁 Script executed:

find web/src/app/dashboard -name "page.tsx" -type f | head -10

Repository: VolvoxLLC/volvox-bot

Length of output: 479


🏁 Script executed:

for file in web/src/app/dashboard/page.tsx web/src/app/dashboard/ai/page.tsx web/src/app/dashboard/config/page.tsx; do
  echo "=== $file ===";
  head -5 "$file";
  rg -i "metadata|export" "$file" | head -3;
done

Repository: VolvoxLLC/volvox-bot

Length of output: 1135


🏁 Script executed:

head -10 web/src/app/dashboard/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 436


🏁 Script executed:

for file in web/src/app/dashboard/page.tsx web/src/app/dashboard/config/page.tsx; do
  echo "=== $file ===";
  head -15 "$file" | grep -E "'use client'|import|export const metadata";
done

Repository: VolvoxLLC/volvox-bot

Length of output: 658


🏁 Script executed:

head -1 web/src/app/dashboard/page.tsx web/src/app/dashboard/config/page.tsx web/src/app/dashboard/conversations/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 293


🏁 Script executed:

grep -n "useRouter\|useCallback\|useState\|useEffect\|useRef" web/src/app/dashboard/conversations/page.tsx | wc -l

Repository: VolvoxLLC/volvox-bot

Length of output: 66


🏁 Script executed:

find web/src/app/dashboard -type f -name "*.tsx" | xargs grep -l "'use client'" | head -5

Repository: VolvoxLLC/volvox-bot

Length of output: 279


🏁 Script executed:

for file in web/src/app/dashboard/audit-log/page.tsx web/src/app/dashboard/logs/page.tsx; do
  echo "=== $file ===";
  head -15 "$file" | grep -E "'use client'|import.*Metadata|export const metadata";
done

Repository: VolvoxLLC/volvox-bot

Length of output: 184


🏁 Script executed:

head -30 web/src/app/dashboard/conversations/page.tsx | tail -25

Repository: VolvoxLLC/volvox-bot

Length of output: 732


🏁 Script executed:

rg "export const metadata" web/src/app/dashboard --type tsx -A 1 -B 1

Repository: VolvoxLLC/volvox-bot

Length of output: 91


🏁 Script executed:

rg "export const metadata" web/src/app/dashboard -A 1 -B 1

Repository: VolvoxLLC/volvox-bot

Length of output: 617


🏁 Script executed:

head -1 web/src/app/dashboard/performance/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 101


This route needs refactoring to export SSR metadata.

The 'use client' directive at the top of the file prevents exporting metadata from this route entry. According to the dashboard routing guidelines, all web/src/app/dashboard/**/*.tsx entry points must export metadata using createPageMetadata('Conversations') for proper SSR title handling.

Move the interactive page content into a client child component and make the route entry itself a server component so it can export the metadata. Other dashboard routes (dashboard/page.tsx, config/page.tsx, performance/page.tsx) follow this pattern successfully.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/app/dashboard/conversations/page.tsx` around lines 279 - 295, This
route is marked as a client component which prevents exporting SSR metadata;
refactor by turning this file into a server component that exports metadata via
createPageMetadata('Conversations') and move all interactive UI and hooks into a
new client child component (e.g., ConversationsClient) that contains PageHeader,
the Button with onClick={handleRefresh}, RefreshCw usage, guildId/loading logic,
and any hooks/state. In this file export const metadata =
createPageMetadata('Conversations') and render the server wrapper that imports
and renders <ConversationsClient />; ensure handlers like handleRefresh, and
symbols PageHeader, RefreshCw remain only in the client child and the route
entry has no 'use client' directive.

Comment on lines +318 to +324
<div className="dashboard-panel rounded-2xl bg-gradient-to-br from-secondary/10 to-background p-4 md:p-5">
<p className="text-[11px] font-semibold uppercase tracking-[0.14em] text-muted-foreground">
Active Channels
</p>
<p className="mt-3 text-3xl font-semibold tracking-tight tabular-nums md:text-4xl">
{channels.length.toLocaleString()}
</p>
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 | 🟠 Major

This card is counting all text channels, not active ones.

channels is fetched from /api/guilds/.../channels for the filter dropdown, and the effect explicitly keeps every text channel (type === 0). The new "Active Channels" metric will therefore overstate activity whenever the guild has idle channels. Either relabel this card or derive it from the conversation dataset.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/app/dashboard/conversations/page.tsx` around lines 318 - 324, The
"Active Channels" count is using the full channels array (all text channels
filtered by type === 0), which mislabels idle channels as active; update the UI
in page.tsx to either (A) relabel the card to "Text Channels" or (B) compute
active channels from the conversations dataset instead—for example derive a Set
of unique channel IDs from the conversations (e.g. conversations.map(c =>
c.channelId) then new Set(...) and use its size) and use that value for the
displayed count; locate the channels variable and the effect that filters by
type === 0 and replace the displayed channels.length with the chosen derived
value or change the label accordingly.

Comment on lines +236 to +252
<PageHeader
icon={Users}
title="Members"
description="View member activity, XP, levels, and moderation history."
actions={
<Button
variant="outline"
size="sm"
className="gap-2"
onClick={handleRefresh}
disabled={!guildId || loading}
>
<RefreshCw className={`h-4 w-4 ${loading ? 'animate-spin' : ''}`} />
Refresh
</Button>
}
/>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

🧩 Analysis chain

🏁 Script executed:

# First, check the full content of the file to understand if it's a client or server component
head -30 web/src/app/dashboard/members/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 1104


🏁 Script executed:

# Check if metadata is exported anywhere in the file
rg "export.*metadata" web/src/app/dashboard/members/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 46


🏁 Script executed:

# Check the page-titles.ts file to understand metadata creation and dashboardTitleMatchers
head -100 web/src/lib/page-titles.ts

Repository: VolvoxLLC/volvox-bot

Length of output: 2981


🏁 Script executed:

# Check how other dashboard routes handle metadata
fd "page.tsx" web/src/app/dashboard/ --type f -x head -20 {}

Repository: VolvoxLLC/volvox-bot

Length of output: 8733


Consider refactoring to a server-side entry point for consistency with other dashboard pages.

While the matcher entry exists for /dashboard/members and client-side title syncing will work, refactoring the interactive logic into a child component would allow this route to export metadata via createPageMetadata('Members') at the page level. This aligns with how other dashboard routes like /dashboard, /config, and /performance are structured and improves initial load metadata for SEO and social sharing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/app/dashboard/members/page.tsx` around lines 236 - 252, Refactor the
interactive UI in this page so the top-level route exports metadata via
createPageMetadata('Members'): move the interactive portions (PageHeader, Button
using handleRefresh, loading, guildId state) into a child component (e.g.,
MembersClient or MembersPanel) and have the page file export
createPageMetadata('Members') at the module level while rendering the child
component server-side entry; ensure you keep the existing symbols (PageHeader,
handleRefresh, RefreshCw, loading, guildId) inside the new client component and
import/inline it from the page so metadata is available before client hydration.

Comment on lines +107 to +125
<PageHeader
icon={Shield}
title="Moderation"
description="Review cases, track activity, and audit your moderation team."
actions={
<Button
variant="outline"
size="sm"
className="gap-2"
onClick={handleRefresh}
disabled={!guildId || statsLoading || casesLoading}
>
<RefreshCw
className={`h-4 w-4 ${statsLoading || casesLoading ? 'animate-spin' : ''}`}
/>
Refresh
</Button>
}
/>
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 | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

# First, check if the file exists and get its size
wc -l web/src/app/dashboard/moderation/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 109


🏁 Script executed:

# Read the file to understand its current structure
cat -n web/src/app/dashboard/moderation/page.tsx | head -50

Repository: VolvoxLLC/volvox-bot

Length of output: 2008


🏁 Script executed:

# Check if metadata is exported and if 'use client' directive exists
grep -n "export.*metadata\|'use client'\|\"use client\"" web/src/app/dashboard/moderation/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 79


🏁 Script executed:

# Check the dashboardTitleMatchers in page-titles.ts
cat web/src/lib/page-titles.ts

Repository: VolvoxLLC/volvox-bot

Length of output: 3459


🏁 Script executed:

# Read lines around 107-125 to see the PageHeader context
sed -n '100,130p' web/src/app/dashboard/moderation/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 925


🏁 Script executed:

# Search for DashboardTitleSync usage in the dashboard
grep -r "DashboardTitleSync" web/src/app/dashboard/ --include="*.tsx"

Repository: VolvoxLLC/volvox-bot

Length of output: 46


🏁 Script executed:

# Check if other dashboard routes are server or client components
find web/src/app/dashboard -name "page.tsx" -exec grep -l "'use client'\|\"use client\"" {} \; | head -10

Repository: VolvoxLLC/volvox-bot

Length of output: 507


🏁 Script executed:

# Check how other dashboard routes handle metadata
find web/src/app/dashboard -name "page.tsx" -exec grep -l "export.*metadata" {} \; | head -10

Repository: VolvoxLLC/volvox-bot

Length of output: 175


🏁 Script executed:

# Check the root dashboard page to see how it handles metadata
sed -n '1,30p' web/src/app/dashboard/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 723


🏁 Script executed:

# Check the config page to see how it handles metadata
sed -n '1,30p' web/src/app/dashboard/config/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 589


🏁 Script executed:

# Check the performance page to see how it handles metadata
sed -n '1,30p' web/src/app/dashboard/performance/page.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 454


🏁 Script executed:

# Check if there's a layout.tsx at the dashboard level that might handle metadata
cat web/src/app/dashboard/layout.tsx | head -50

Repository: VolvoxLLC/volvox-bot

Length of output: 668


🏁 Script executed:

# Check the DashboardShell component to see if it uses DashboardTitleSync
cat web/src/components/layout/dashboard-shell.tsx | head -80

Repository: VolvoxLLC/volvox-bot

Length of output: 1671


🏁 Script executed:

# Check if AnalyticsDashboard component (used in server-side dashboard/page.tsx) is client or server
head -20 web/src/components/dashboard/analytics-dashboard.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 347


🏁 Script executed:

# Check if ConfigEditor component (used in server-side config/page.tsx) is client or server
head -20 web/src/components/dashboard/config-editor.tsx

Repository: VolvoxLLC/volvox-bot

Length of output: 885


🏁 Script executed:

# Look for why moderation page is a client component - check what hooks it uses
grep -n "use" web/src/app/dashboard/moderation/page.tsx | head -20

Repository: VolvoxLLC/volvox-bot

Length of output: 1032


Refactor this route to follow the established dashboard page pattern: server component exports metadata, client component handles interactivity.

The moderation/page.tsx should be a server component that exports metadata = createPageMetadata('Moderation', '...'), similar to dashboard/page.tsx, config/page.tsx, and performance/page.tsx. Extract the current client logic (hooks, state, rendering) into a client component child and import it into the server page entry point.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/app/dashboard/moderation/page.tsx` around lines 107 - 125, Refactor
the current moderation/page.tsx into a server entry that exports metadata =
createPageMetadata('Moderation', 'Review cases, track activity, and audit your
moderation team.') and renders a new client child component (e.g.,
ModerationClient); move all interactive logic — React hooks, state, effects,
handlers like handleRefresh, statsLoading, casesLoading, and the JSX that
includes PageHeader, Button, RefreshCw, and list rendering — into that client
component, mark it with "use client" at the top, export it as default from the
client file, and update the server page to only import and render
<ModerationClient /> so the server file contains no client-side hooks or state.

Comment on lines +171 to 177
<section className="dashboard-panel space-y-3 rounded-2xl p-4 md:p-5">
<div>
<h3 className="text-lg font-semibold tracking-tight">User History Lookup</h3>
<p className="text-sm text-muted-foreground">
Look up a single user&apos;s full moderation timeline.
</p>
</div>
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

Run the formatter on this new block before merge.

CI is already failing here with Formatter would have printed content differently at Line 177.

🧰 Tools
🪛 GitHub Actions: CI

[error] 177-177: Formatter would have printed content differently (prettier formatting issue detected).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/app/dashboard/moderation/page.tsx` around lines 171 - 177, Run the
repository formatter (e.g. prettier/nextjs format) on the updated JSX block that
renders the "User History Lookup" panel so the whitespace/line breaks match the
project's style; specifically reformat the section containing
className="dashboard-panel space-y-3 rounded-2xl p-4 md:p-5" and its child h3
("User History Lookup") and p elements, then stage the resulting changes so CI
no longer reports "Formatter would have printed content differently."

Dashboard Section
</p>
<h1 className="flex items-center gap-2 text-2xl font-semibold tracking-tight md:text-[1.9rem]">
{Icon && <Icon className="h-5 w-5 text-primary" />}
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

Mark the decorative icon as hidden from assistive tech.

The optional Icon appears purely visual in this header. Add aria-hidden (and optionally focusable="false") to avoid redundant announcements.

Suggested fix
-            {Icon && <Icon className="h-5 w-5 text-primary" />}
+            {Icon && <Icon className="h-5 w-5 text-primary" aria-hidden="true" focusable="false" />}
📝 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
{Icon && <Icon className="h-5 w-5 text-primary" />}
{Icon && <Icon className="h-5 w-5 text-primary" aria-hidden="true" focusable="false" />}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/dashboard/page-header.tsx` at line 36, The decorative Icon
rendered in the header ({Icon && <Icon className="h-5 w-5 text-primary" />})
should be hidden from assistive tech; update the JSX that renders Icon to
include aria-hidden="true" and (optionally) focusable="false" on the Icon
element so it isn’t announced or tabbable while keeping the existing className
and conditional rendering.

Comment on lines +35 to +38
<div className="dashboard-fade-in rounded-2xl p-1 md:rounded-3xl md:p-1.5">
<div className="dashboard-panel min-h-[calc(100vh-7.9rem)] rounded-2xl p-4 md:rounded-[1.45rem] md:p-7 lg:p-8">
{children}
</div>
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 | 🟠 Major

Avoid forcing a global dashboard-panel wrapper around all route content.

Several dashboard pages already render their own dashboard-panel sections, so this creates nested borders/shadows and extra padding across routes (e.g., web/src/app/dashboard/logs/page.tsx:36-40, web/src/app/dashboard/audit-log/page.tsx:220-260, web/src/app/dashboard/conversations/page.tsx:160-200).

Suggested fix
-            <div className="dashboard-fade-in rounded-2xl p-1 md:rounded-3xl md:p-1.5">
-              <div className="dashboard-panel min-h-[calc(100vh-7.9rem)] rounded-2xl p-4 md:rounded-[1.45rem] md:p-7 lg:p-8">
-                {children}
-              </div>
-            </div>
+            <div className="dashboard-fade-in min-h-[calc(100vh-7.9rem)]">
+              {children}
+            </div>
📝 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
<div className="dashboard-fade-in rounded-2xl p-1 md:rounded-3xl md:p-1.5">
<div className="dashboard-panel min-h-[calc(100vh-7.9rem)] rounded-2xl p-4 md:rounded-[1.45rem] md:p-7 lg:p-8">
{children}
</div>
<div className="dashboard-fade-in min-h-[calc(100vh-7.9rem)]">
{children}
</div>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/dashboard-shell.tsx` around lines 35 - 38, The root
dashboard-shell currently forces a "dashboard-panel" wrapper (the divs with
classNames "dashboard-fade-in" and "dashboard-panel") around every route which
causes nested borders/padding; update the DashboardShell component in
web/src/components/layout/dashboard-shell.tsx to accept a boolean prop (e.g.,
wrapWithPanel or disablePanel) and render the inner "dashboard-panel" wrapper
conditionally based on that prop so routes that already render their own panel
can pass false, and update calls/pages that need the wrapper to either pass the
new prop or rely on the default.

Comment on lines +30 to +33
<SheetContent
side="left"
className="w-[21.5rem] border-r border-border/70 bg-gradient-to-b from-card via-card/90 to-background p-0"
>
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 | 🟠 Major

Avoid fixed sheet width that can overflow narrow phones.

On Line 32, w-[21.5rem] can exceed very small mobile viewports and clip content.
Use a capped width so the sheet never exceeds the viewport.

📱 Proposed fix
-          className="w-[21.5rem] border-r border-border/70 bg-gradient-to-b from-card via-card/90 to-background p-0"
+          className="w-full max-w-[21.5rem] border-r border-border/70 bg-gradient-to-b from-card via-card/90 to-background p-0"

Based on learnings: "Verify responsive layout behavior when making dashboard layout changes".

📝 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
<SheetContent
side="left"
className="w-[21.5rem] border-r border-border/70 bg-gradient-to-b from-card via-card/90 to-background p-0"
>
<SheetContent
side="left"
className="w-full max-w-[21.5rem] border-r border-border/70 bg-gradient-to-b from-card via-card/90 to-background p-0"
>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/mobile-sidebar.tsx` around lines 30 - 33, The
SheetContent in mobile-sidebar.tsx uses a fixed class w-[21.5rem] which can
overflow narrow phones; update the SheetContent className to use a fluid width
with a capped max (e.g., replace the fixed width with w-full and a max width
such as max-w-[21.5rem] or max-w-[90vw]) so the sheet fills available space but
never exceeds the viewport; locate the SheetContent element and modify its
className accordingly to prevent clipping on small viewports.

Comment on lines +188 to 221
<Button
variant="outline"
className={cn(
'h-[3.2rem] w-full justify-between rounded-xl border-border/70 bg-gradient-to-r from-background to-muted/40 px-3 shadow-sm',
className,
)}
>
<div className="flex min-w-0 items-center gap-2 truncate">
<span className="flex h-8 w-8 shrink-0 items-center justify-center rounded-lg border border-border/60 bg-background">
{selectedGuild?.icon ? (
<Image
src={getGuildIconUrl(selectedGuild.id, selectedGuild.icon, 64) ?? ''}
alt={selectedGuild.name}
width={20}
height={20}
className="rounded-full"
/>
) : (
<Server className="h-4 w-4 shrink-0" />
)}
</span>
<span className="min-w-0">
<span className="block truncate text-[11px] font-semibold uppercase tracking-[0.13em] text-muted-foreground">
Active server
</span>
<span className="block truncate text-sm font-medium">
{manageable.length === 0
? 'No manageable servers'
: (selectedGuild?.name ?? 'Select server')}
</span>
</span>
</div>
<ChevronsUpDown className="ml-2 h-4 w-4 shrink-0 opacity-50" />
</Button>
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

Consider breaking the long class string for readability.

The trigger button restyling looks good. However, line 191's Tailwind class string exceeds 100 characters. This is a common trade-off with Tailwind—breaking the string can hurt readability too. Leaving as optional.

♻️ Optional: split class string
         <Button
           variant="outline"
           className={cn(
-            'h-[3.2rem] w-full justify-between rounded-xl border-border/70 bg-gradient-to-r from-background to-muted/40 px-3 shadow-sm',
+            'h-[3.2rem] w-full justify-between rounded-xl px-3 shadow-sm',
+            'border-border/70 bg-gradient-to-r from-background to-muted/40',
             className,
           )}
         >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/server-selector.tsx` around lines 188 - 221, The
long Tailwind class string passed into cn for the Button component (the string
starting with "h-[3.2rem] w-full justify-between rounded-xl border-border/70
bg-gradient-to-r from-background to-muted/40 px-3 shadow-sm") should be broken
across lines for readability; refactor by pulling that class list into a named
constant (e.g., buttonClasses) or pass the classes as a multi-line array into
cn([...]) and keep the existing cn(..., className) call so the styling and
className prop behavior remain unchanged; update the Button JSX to use the new
variable/array while leaving the surrounding structure (Button, cn, and
className) intact.

Comment on lines +100 to +107
const hasActiveSecondaryItem = secondaryNav.some((item) => isNavItemActive(item.href));
const [isSecondaryOpen, setIsSecondaryOpen] = useState(hasActiveSecondaryItem);

useEffect(() => {
if (hasActiveSecondaryItem) {
setIsSecondaryOpen(true);
}
}, [hasActiveSecondaryItem]);
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

Reopen the Extensions section on pathname changes too.

With hasActiveSecondaryItem as the only dependency, navigating between two secondary routes while this <details> is manually collapsed keeps the active item hidden because the boolean stays true. Include pathname (or key the effect off the pathname directly) so back/forward and deep-link navigation reopen the section.

💡 Minimal fix
   useEffect(() => {
     if (hasActiveSecondaryItem) {
       setIsSecondaryOpen(true);
     }
-  }, [hasActiveSecondaryItem]);
+  }, [hasActiveSecondaryItem, pathname]);
📝 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
const hasActiveSecondaryItem = secondaryNav.some((item) => isNavItemActive(item.href));
const [isSecondaryOpen, setIsSecondaryOpen] = useState(hasActiveSecondaryItem);
useEffect(() => {
if (hasActiveSecondaryItem) {
setIsSecondaryOpen(true);
}
}, [hasActiveSecondaryItem]);
const hasActiveSecondaryItem = secondaryNav.some((item) => isNavItemActive(item.href));
const [isSecondaryOpen, setIsSecondaryOpen] = useState(hasActiveSecondaryItem);
useEffect(() => {
if (hasActiveSecondaryItem) {
setIsSecondaryOpen(true);
}
}, [hasActiveSecondaryItem, pathname]);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@web/src/components/layout/sidebar.tsx` around lines 100 - 107, The effect
that reopens the secondary nav only depends on hasActiveSecondaryItem so
navigating between secondary routes can leave the section collapsed; update the
useEffect that currently references hasActiveSecondaryItem to also depend on the
current pathname (or use pathname instead of hasActiveSecondaryItem) and call
setIsSecondaryOpen(true) when the pathname changes and hasActiveSecondaryItem is
true—adjust the dependency array for the useEffect (or key the effect to
pathname) so back/forward and deep-link navigation reopen the section; reference
the existing hasActiveSecondaryItem, isSecondaryOpen, setIsSecondaryOpen and the
useEffect block when making the change.

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

Labels

None yet

Projects

Status: In Review

Development

Successfully merging this pull request may close these issues.

1 participant