Skip to content

Conversation

@pradnk
Copy link
Contributor

@pradnk pradnk commented Nov 19, 2025

  • Logging prompt and responses
  • Metering tokens with org and user ID
  • Consent to use AI and to log
  • Check to ensure AI is enabled before processing every prompt
  • System prompt to respond as a data analyst to explain to non-tech users.

Summary by CodeRabbit

  • New Features
    • Organization Settings UI and page: view org info, upload/remove logo, and manage AI data-sharing and logging preferences.
    • Dashboard AI Assistant: floating chat trigger, side chat panel with send/stream, share-data settings, export/copy, and chat utilities.
    • Chart selection for AI: click charts to select context for the assistant.
    • Permissions & Nav: permission-aware Org Settings visibility and a permissions hook.
    • AI status & chat hooks: client hooks for AI status and dashboard chat interactions.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

Walkthrough

Adds an organization settings page and OrganizationSettings component, a dashboard AI chat feature (floating trigger, selectable charts, streamed right-side chat panel), API hooks for AI status and dashboard chat, a permissions hook, and permission-gated navigation for Organization Settings.

Changes

Cohort / File(s) Summary
Organization Settings Page & Component
app/settings/organization/page.tsx, components/settings/organization.tsx
Adds a Next.js page exporting SettingsOrganizationPage that renders OrganizationSettings; adds OrganizationSettings component with org info display, logo upload/remove, AI data-sharing and logging toggles, load/save API calls, preview state, and toasts.
AI Chat UI Components
components/dashboard/dashboard-chat-trigger.tsx, components/dashboard/enhanced-dashboard-chat.tsx
Adds DashboardChatTrigger (floating button, AI status gating, new-activity indicator, public-mode hide) and EnhancedDashboardChat (right-side streamed chat panel with messages, settings, context load, send/stream support, export/copy/clear utilities).
Dashboard Integration (Chart Selection)
components/dashboard/dashboard-native-view.tsx
Adds selectable chart state (selectedChartForChat) with click-to-select/deselect, visual ring/label per chart, and mounts DashboardChatTrigger with selectedChartId and isPublicMode.
API Hooks
hooks/api/useAIStatus.ts, hooks/api/useDashboardChat.ts
Adds useAIStatus hook to fetch aiEnabled/dataSharing/logging status; adds useDashboardChat hook with loadContext, sendMessage, streamMessage (stream parser), updateSettings, models/types (ChatMessage, DashboardChatSettings, DashboardContext), and error/loading handling.
Permissions & Navigation
hooks/usePermissions.ts, components/main-layout.tsx
Adds usePermissions hook exposing hasPermission/hasAny/hasAll and isAccountManager; updates getNavItems signature to accept hasPermission and gates Organization Settings nav item on can_manage_org_settings.
Routing / Manifest
package.json, app/...
package.json referenced in manifest; new app route file added at app/settings/organization/page.tsx.

Sequence Diagram(s)

sequenceDiagram
    participant U as User
    participant D as Dashboard View
    participant CTr as Chat Trigger
    participant CP as Chat Panel
    participant API as Backend API

    U->>D: Open dashboard
    Note right of D: Optional: select chart for AI context
    U->>CTr: Click chat button
    CTr->>CTr: Check aiEnabled & isPublicMode
    alt AI Disabled or Public Mode
        CTr->>U: Show toast / no-op
    else AI Enabled
        CTr->>CP: Open panel (isOpen=true)
        CP->>API: POST /api/ai/dashboard/{id}/context (loadContext)
        API-->>CP: Return context (charts, sample_data, summary)
        U->>CP: Send message
        CP->>API: POST /api/ai/dashboard/{id}/chat (streaming)
        loop Streaming chunks
            API-->>CP: data: ... (chunk)
            CP->>CP: Append streamed assistant content
        end
        CP->>U: Display final response + metadata
    end
Loading
sequenceDiagram
    participant U as User
    participant Chart as Chart Container
    participant D as Dashboard
    participant CP as Chat Panel
    participant API as Backend

    U->>Chart: Click chart (select)
    Chart->>D: set selectedChartForChat
    D->>Chart: Render selected ring + "Selected for AI"
    D->>CP: Pass selectedChartId prop
    CP->>API: Include selected_chart_id in requests
    API-->>CP: Responses may reference selected chart
    U->>Chart: Click again (deselect)
    Chart->>D: Clear selection
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

  • Pay extra attention to:
    • hooks/api/useDashboardChat.ts — streamMessage streaming parser, cancellation, header handling (x-dalgo-org).
    • components/enhanced-dashboard-chat.tsx — state transitions, streaming integration, input/send edge cases, privacy gating.
    • components/dashboard/dashboard-native-view.tsx & dashboard-chat-trigger.tsx — selection UX and public-mode gating interactions.
    • components/settings/organization.tsx — file upload validation, FormData upload flow, save PUT behavior.
    • components/main-layout.tsx — propagated getNavItems signature and permission check correctness.

Possibly related PRs

Suggested reviewers

  • Ishankoradia
  • himanshudube97

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'Dalgo AI chat implementation - Frontend' directly reflects the primary objective of this PR: implementing AI chat functionality on the frontend.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch aichat

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between 5892e18 and f5cdf0f.

📒 Files selected for processing (1)
  • components/dashboard/dashboard-native-view.tsx (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • components/dashboard/dashboard-native-view.tsx

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.

@codecov-commenter
Copy link

codecov-commenter commented Nov 19, 2025

Codecov Report

❌ Patch coverage is 0% with 452 lines in your changes missing coverage. Please review.
✅ Project coverage is 13.53%. Comparing base (8da7aa4) to head (f5cdf0f).
⚠️ Report is 538 commits behind head on main.

Files with missing lines Patch % Lines
components/dashboard/enhanced-dashboard-chat.tsx 0.00% 150 Missing ⚠️
components/settings/organization.tsx 0.00% 129 Missing ⚠️
hooks/api/useDashboardChat.ts 0.00% 79 Missing ⚠️
components/dashboard/dashboard-chat-trigger.tsx 0.00% 52 Missing ⚠️
hooks/usePermissions.ts 0.00% 20 Missing ⚠️
hooks/api/useAIStatus.ts 0.00% 8 Missing ⚠️
components/dashboard/dashboard-native-view.tsx 0.00% 6 Missing ⚠️
components/main-layout.tsx 0.00% 5 Missing ⚠️
app/settings/organization/page.tsx 0.00% 3 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##            main     #162       +/-   ##
==========================================
+ Coverage   1.72%   13.53%   +11.81%     
==========================================
  Files        131      210       +79     
  Lines       3767    13960    +10193     
  Branches    1062     5026     +3964     
==========================================
+ Hits          65     1890     +1825     
- Misses      3702    12065     +8363     
- Partials       0        5        +5     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (5)
components/dashboard/dashboard-native-view.tsx (1)

283-285: Chart selection for AI works but may conflict with chart interactions

Wrapping the whole chart tile in a cursor-pointer div and toggling selectedChartForChat on any click is straightforward, but this will also fire when users interact with the chart itself (e.g., click-to-filter, tooltips with clicks). That may cause accidental AI selection/deselection.

Consider narrowing the selection click target (e.g., a small overlay button/badge) or checking for modifier keys to avoid interfering with normal chart interactions.

Also applies to: 545-578

hooks/usePermissions.ts (1)

1-45: Centralized permissions hook looks solid

usePermissions provides a clear, reusable API (hasPermission, hasAnyPermission, hasAllPermissions, isAccountManager) over the auth store and should simplify permission checks across the app. Memoization on currentOrgUser?.permissions and new_role_slug is appropriate for typical immutable store updates.

If the auth store ever mutates the permissions array in place instead of replacing it, you may want to switch the useMemo dependencies to the whole currentOrgUser object to ensure reactivity.

components/settings/organization.tsx (1)

158-169: Align AI logging acknowledgment behavior with UI messaging

The dependency logic in handleInputChange ensures that turning off ai_data_sharing_enabled also clears ai_logging_acknowledged, and the main “I acknowledge AI logging” switch is disabled when data sharing is off. However, the Info button in the “AI Logging Notice” alert toggles ai_logging_acknowledged directly, even when data sharing is disabled.

Given the helper text (“Enable data sharing first to acknowledge AI logging”), this can lead to a state where:

  • ai_data_sharing_enabled === false and
  • ai_logging_acknowledged === true

Consider one of:

  • Removing the onClick toggle from the Info button so it’s purely informational, or
  • Respecting the same gating there (no-op when !settings.ai_data_sharing_enabled), or
  • Updating the copy to reflect that logging can be acknowledged independently of data sharing if that’s actually intended.

Also applies to: 401-434, 471-492

components/dashboard/dashboard-chat-trigger.tsx (1)

13-151: AI chat trigger gating is correct; consider trimming unused activity state

The trigger correctly:

  • Hides itself for public dashboards, and
  • Checks aiEnabled on click, showing a toast instead of opening chat when AI is disabled.

hasNewActivity is currently never set to true, so the notification ring / badge effects never appear. You can either wire this up via a callback from EnhancedDashboardChat (e.g., on new assistant message) or remove the state + related class logic to keep the component lean.

components/main-layout.tsx (1)

27-28: Fix transformType parameter type to TransformType | undefined

The permission-gated "Org Settings" nav item is wired correctly and integrates cleanly with existing navigation rendering. However, verification confirms the first suggestion: transformType is declared as string | undefined (line 98) but compared against the TransformType.UI enum (line 191), causing a type mismatch. Change the parameter type to TransformType | undefined to align with actual usage.

The permission slug can_manage_org_settings appears consistently only once in the codebase at line 224; backend verification of this slug would require checking the backend code separately.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between c797674 and a41ba6f.

📒 Files selected for processing (9)
  • app/settings/organization/page.tsx (1 hunks)
  • components/dashboard/dashboard-chat-trigger.tsx (1 hunks)
  • components/dashboard/dashboard-native-view.tsx (5 hunks)
  • components/dashboard/enhanced-dashboard-chat.tsx (1 hunks)
  • components/main-layout.tsx (5 hunks)
  • components/settings/organization.tsx (1 hunks)
  • hooks/api/useAIStatus.ts (1 hunks)
  • hooks/api/useDashboardChat.ts (1 hunks)
  • hooks/usePermissions.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (4)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Define TypeScript interfaces/types for API responses to ensure type safety
Use the @/* path alias for imports instead of deep relative paths

Files:

  • hooks/usePermissions.ts
  • components/settings/organization.tsx
  • components/main-layout.tsx
  • components/dashboard/dashboard-chat-trigger.tsx
  • hooks/api/useAIStatus.ts
  • components/dashboard/dashboard-native-view.tsx
  • hooks/api/useDashboardChat.ts
  • components/dashboard/enhanced-dashboard-chat.tsx
  • app/settings/organization/page.tsx
{components,app}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{components,app}/**/*.{ts,tsx}: Use Tailwind utility-first styling, CVA for component variants, and the cn() utility for class merging
Guard browser-only APIs with typeof window !== 'undefined' in client-side code

Files:

  • components/settings/organization.tsx
  • components/main-layout.tsx
  • components/dashboard/dashboard-chat-trigger.tsx
  • components/dashboard/dashboard-native-view.tsx
  • components/dashboard/enhanced-dashboard-chat.tsx
  • app/settings/organization/page.tsx
hooks/api/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Add new SWR-based API hooks under hooks/api/ and start new features by creating an API hook first

Files:

  • hooks/api/useAIStatus.ts
  • hooks/api/useDashboardChat.ts
app/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

Create Next.js App Router pages and related route files under the app/ directory following App Router conventions

Files:

  • app/settings/organization/page.tsx
🧠 Learnings (4)
📚 Learning: 2025-10-13T07:21:21.746Z
Learnt from: CR
Repo: DalgoT4D/webapp_v2 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-13T07:21:21.746Z
Learning: Applies to hooks/api/**/*.{ts,tsx} : Add new SWR-based API hooks under hooks/api/ and start new features by creating an API hook first

Applied to files:

  • hooks/usePermissions.ts
  • hooks/api/useAIStatus.ts
  • hooks/api/useDashboardChat.ts
📚 Learning: 2025-10-13T07:21:21.746Z
Learnt from: CR
Repo: DalgoT4D/webapp_v2 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-13T07:21:21.746Z
Learning: Applies to components/ui/**/*.{ts,tsx} : Build reusable UI on top of Radix UI primitives in components/ui/

Applied to files:

  • components/main-layout.tsx
📚 Learning: 2025-10-13T07:21:21.746Z
Learnt from: CR
Repo: DalgoT4D/webapp_v2 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-13T07:21:21.746Z
Learning: Applies to components/charts/**/*.{ts,tsx} : Implement charts following existing patterns in components/charts/, selecting the appropriate library (ECharts/Nivo/Recharts) and supporting PNG/PDF export

Applied to files:

  • components/dashboard/dashboard-native-view.tsx
📚 Learning: 2025-10-13T07:21:21.746Z
Learnt from: CR
Repo: DalgoT4D/webapp_v2 PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-10-13T07:21:21.746Z
Learning: Applies to app/**/*.{ts,tsx} : Create Next.js App Router pages and related route files under the app/ directory following App Router conventions

Applied to files:

  • app/settings/organization/page.tsx
🧬 Code graph analysis (8)
components/settings/organization.tsx (3)
lib/api.ts (2)
  • apiGet (191-193)
  • apiPut (205-211)
hooks/use-toast.ts (1)
  • toast (23-37)
test-integration.js (1)
  • headers (12-16)
components/main-layout.tsx (1)
hooks/usePermissions.ts (1)
  • usePermissions (7-45)
components/dashboard/dashboard-chat-trigger.tsx (4)
hooks/api/useAIStatus.ts (1)
  • useAIStatus (11-30)
hooks/use-toast.ts (1)
  • toast (23-37)
lib/utils.ts (1)
  • cn (4-6)
components/dashboard/enhanced-dashboard-chat.tsx (1)
  • EnhancedDashboardChat (92-710)
hooks/api/useAIStatus.ts (1)
lib/api.ts (1)
  • apiGet (191-193)
components/dashboard/dashboard-native-view.tsx (2)
lib/utils.ts (1)
  • cn (4-6)
components/dashboard/dashboard-chat-trigger.tsx (1)
  • DashboardChatTrigger (21-153)
hooks/api/useDashboardChat.ts (2)
lib/api.ts (1)
  • apiPost (196-202)
lib/config.ts (1)
  • API_BASE_URL (9-9)
components/dashboard/enhanced-dashboard-chat.tsx (2)
hooks/api/useAIStatus.ts (1)
  • useAIStatus (11-30)
hooks/api/useDashboardChat.ts (3)
  • useDashboardChat (78-269)
  • ChatMessage (4-18)
  • DashboardChatSettings (20-25)
app/settings/organization/page.tsx (1)
components/settings/organization.tsx (1)
  • OrganizationSettings (38-552)
🪛 ast-grep (0.40.0)
components/dashboard/enhanced-dashboard-chat.tsx

[warning] 79-79: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
components/dashboard/enhanced-dashboard-chat.tsx

[error] 80-80: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🔇 Additional comments (6)
app/settings/organization/page.tsx (1)

1-5: Org settings page wiring looks good

Simple App Router page that cleanly delegates to OrganizationSettings; imports and export shape look correct.

components/dashboard/dashboard-native-view.tsx (1)

85-85: AI chat trigger integration looks consistent with view modes

Importing and rendering DashboardChatTrigger with dashboardId, dashboardTitle, selectedChartId, and isPublicMode cleanly wires the dashboard view into the AI chat flow. Relying on isPublicMode inside the trigger to short‑circuit rendering keeps public dashboards free of the chat UI without complicating this component further.

Also applies to: 1344-1350

hooks/api/useAIStatus.ts (1)

1-29: SWR AI status hook is clean and type-safe

AIStatusResponse plus the SWR wrapper give a clear, minimal contract (aiEnabled, dataSharingEnabled, loggingAcknowledged, isLoading, error, refetch). Defaulting booleans to false when data is undefined is a safe fallback.

components/settings/organization.tsx (1)

38-156: Org settings + AI consent UI is cohesive and robust

The organization settings page covers name, website, logo upload (with type/size validation and org header propagation), and AI consent (data sharing + logging) with clear messaging and error handling. The load/save flows handle nulls gracefully and synchronize local state from API responses, and the “just saved” visual feedback is a nice UX touch.

No functional issues stand out beyond the AI logging toggle nuance noted above.

Also applies to: 171-261, 263-551

components/dashboard/enhanced-dashboard-chat.tsx (1)

84-709: Overall chat UX and AI gating behavior are well thought out

The chat panel:

  • Honors aiEnabled before sending messages,
  • Seeds settings from org data sharing status,
  • Provides context loading, settings, quick actions, and clear feedback on errors,
  • Integrates chart focus via selectedChartId and annotates metadata (charts analyzed, data included, token usage).

Aside from the unsafe HTML injection noted separately, the state management and API usage (useDashboardChat, useAIStatus) look consistent and aligned with the PR goals.

hooks/api/useDashboardChat.ts (1)

195-207: Ensure streaming fetch carries all shared headers

By hand-crafting this fetch, we only forward x-dalgo-org and skip the rest of the default headers that apiFetch injects (e.g., the user identifier). If the backend relies on those to meter tokens per organization and user—one of this PR’s stated goals—the streaming path will miss the user context. Please confirm the exact header set and either reuse apiFetch (or its header builder) or explicitly include the same headers here so we don’t lose per-user attribution.

Comment on lines +50 to +82
// Component to format AI responses with better styling
const FormattedMessage = ({ content }: { content: string }) => {
// Split content by lines and apply formatting
const formatContent = (text: string) => {
return (
text
// Convert **bold** to actual bold
.replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
// Convert ## headings to styled headings
.replace(
/^## (.*$)/gm,
'<h3 class="font-semibold text-gray-900 mb-2 mt-3 flex items-center gap-2">$1</h3>'
)
// Convert > callouts to styled callouts
.replace(
/^> (.*$)/gm,
'<div class="bg-blue-50 border-l-4 border-blue-500 p-3 my-2 rounded-r"><p class="text-blue-700 font-medium">$1</p></div>'
)
// Convert bullet points with emojis to styled lists
.replace(/^- (.*$)/gm, '<li class="ml-4 mb-1">$1</li>')
// Convert numbered lists
.replace(/^(\d+)\. (.*$)/gm, '<li class="ml-4 mb-2 font-medium">$2</li>')
// Handle line breaks
.replace(/\n/g, '<br/>')
);
};

const formattedContent = formatContent(content);

return (
<div className="formatted-message" dangerouslySetInnerHTML={{ __html: formattedContent }} />
);
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Remove raw HTML injection from chat messages to avoid XSS

FormattedMessage constructs HTML strings and injects them with dangerouslySetInnerHTML. Because:

  • User messages are rendered through this same component, and
  • AI responses are also untrusted text,

this creates a direct XSS vector (users can trivially type <script> or similar and have it executed), matching the static analysis warnings.

A safer immediate fix is to drop the raw HTML injection and let React escape the content while still preserving line breaks:

-// Component to format AI responses with better styling
-const FormattedMessage = ({ content }: { content: string }) => {
-  // Split content by lines and apply formatting
-  const formatContent = (text: string) => {
-    return (
-      text
-        // Convert **bold** to actual bold
-        .replace(/\*\*(.*?)\*\*/g, '<strong>$1</strong>')
-        // Convert ## headings to styled headings
-        .replace(
-          /^## (.*$)/gm,
-          '<h3 class="font-semibold text-gray-900 mb-2 mt-3 flex items-center gap-2">$1</h3>'
-        )
-        // Convert > callouts to styled callouts
-        .replace(
-          /^> (.*$)/gm,
-          '<div class="bg-blue-50 border-l-4 border-blue-500 p-3 my-2 rounded-r"><p class="text-blue-700 font-medium">$1</p></div>'
-        )
-        // Convert bullet points with emojis to styled lists
-        .replace(/^- (.*$)/gm, '<li class="ml-4 mb-1">$1</li>')
-        // Convert numbered lists
-        .replace(/^(\d+)\. (.*$)/gm, '<li class="ml-4 mb-2 font-medium">$2</li>')
-        // Handle line breaks
-        .replace(/\n/g, '<br/>')
-    );
-  };
-
-  const formattedContent = formatContent(content);
-
-  return (
-    <div className="formatted-message" dangerouslySetInnerHTML={{ __html: formattedContent }} />
-  );
-};
+// Component to format AI responses safely without raw HTML injection
+const FormattedMessage = ({ content }: { content: string }) => {
+  // Preserve line breaks but let React escape the content
+  return (
+    <div className="formatted-message whitespace-pre-line">
+      {content}
+    </div>
+  );
+};

This removes the XSS risk while keeping the door open to later introduce richer formatting via a markdown renderer or a sanitizer (e.g., DOMPurify) if needed.

Also applies to: 490-518

🧰 Tools
🪛 ast-grep (0.40.0)

[warning] 79-79: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)

[error] 80-80: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🤖 Prompt for AI Agents
components/dashboard/enhanced-dashboard-chat.tsx lines 50-82 (and similarly
490-518): the component currently builds raw HTML strings and uses
dangerouslySetInnerHTML, which allows XSS; change it to stop producing HTML
strings and instead return safe React elements by escaping content and rendering
line breaks and simple formatting as JSX. Specifically: remove all string
.replace(...) that injects HTML, do not use dangerouslySetInnerHTML, split the
input text on newlines and map each line to a <p> or fragment (or map list-like
lines to plain text list items wrapped in React elements) so React escapes
content automatically; preserve visual breaks by inserting <br/> or wrapping
lines in blocks, and defer any HTML/markdown rendering to a sanitized markdown
renderer (e.g., DOMPurify + markdown-it) if/when added later.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
components/settings/organization.tsx (3)

64-76: Type API responses for org settings and logo upload instead of relying on implicit any

response from apiGet/apiPut and result from response.json() are treated as untyped objects, so response.res, response.success, result.success, and result.filename are effectively any. This makes it easy for backend changes (field renames, optional fields, etc.) to silently break the UI.

Per the guideline to type API responses, consider:

  • Defining response interfaces such as:

    interface OrgSettingsResponse {
      success: boolean;
      message?: string;
      res?: OrgSettings | null;
    }
    
    interface UploadLogoResponse {
      success: boolean;
      message?: string;
      filename?: string;
    }
  • Either:

    • Generically typing apiGet / apiPut so you can write await apiGet<OrgSettingsResponse>(...), or
    • Casting at the call site: const response = (await apiGet('/api/org-settings/')) as OrgSettingsResponse; and similarly for UploadLogoResponse.

This will give you compile‑time coverage on response.res.organization_logo_filename, ai_settings_accepted_*, and result.filename and help catch API drift early. As per coding guidelines, define TypeScript interfaces/types for API responses.

Also applies to: 106-121, 213-233


193-201: Guard localStorage access when building x-dalgo-org header

handleLogoUpload accesses localStorage directly:

const selectedOrg = localStorage.getItem('selectedOrg');

Even though this runs in an event handler on a client component, the project guideline is to guard browser‑only APIs with typeof window !== 'undefined'. This also makes the code more robust in tests or any non‑browser execution context.

A small adjustment keeps the behavior while following the guideline:

-      const selectedOrg = localStorage.getItem('selectedOrg');
+      const selectedOrg =
+        typeof window !== 'undefined' ? window.localStorage.getItem('selectedOrg') : null;

As per coding guidelines, this keeps browser-specific APIs explicitly guarded.


371-377: Use a cn() utility for conditional classNames instead of string templates

You’re conditionally appending Tailwind classes via string interpolation:

className={`flex items-center ... rounded-md cursor-pointer ${
  isUploadingLogo ? 'opacity-50 cursor-not-allowed' : ''
}`}

and:

className={`flex items-center justify-between p-4 border rounded-lg ${
  !settings.ai_data_sharing_enabled ? 'opacity-50' : ''
}`}

Per the guideline to use cn() for class merging, consider switching to something like:

className={cn(
  'flex items-center gap-2 px-4 py-2 border border-input bg-background hover:bg-accent hover:text-accent-foreground rounded-md cursor-pointer',
  { 'opacity-50 cursor-not-allowed': isUploadingLogo },
)}

and:

className={cn(
  'flex items-center justify-between p-4 border rounded-lg',
  { 'opacity-50': !settings.ai_data_sharing_enabled },
)}

This keeps the classes readable and scales better as conditions grow.

Also applies to: 475-477

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Linear integration is disabled by default for public repositories

You can enable these sources in your CodeRabbit configuration.

📥 Commits

Reviewing files that changed from the base of the PR and between a41ba6f and 5892e18.

📒 Files selected for processing (1)
  • components/settings/organization.tsx (1 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
{components,app}/**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

{components,app}/**/*.{ts,tsx}: Use Tailwind utility-first styling, CVA for component variants, and the cn() utility for class merging
Guard browser-only APIs with typeof window !== 'undefined' in client-side code

Files:

  • components/settings/organization.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Define TypeScript interfaces/types for API responses to ensure type safety
Use the @/* path alias for imports instead of deep relative paths

Files:

  • components/settings/organization.tsx
🧬 Code graph analysis (1)
components/settings/organization.tsx (2)
lib/api.ts (2)
  • apiGet (191-193)
  • apiPut (205-211)
hooks/use-toast.ts (1)
  • toast (23-37)

Comment on lines +150 to +161
const handleInputChange = (field: keyof OrgSettings, value: string | boolean) => {
setSettings((prev) => {
const newSettings = { ...prev, [field]: value };

// Dependency: If data sharing is disabled, also disable logging acknowledgment
if (field === 'ai_data_sharing_enabled' && !value) {
newSettings.ai_logging_acknowledged = false;
}

return newSettings;
});
};
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Enforce invariant: logging cannot be acknowledged when data sharing is disabled

The Info button in the AI Logging notice toggles ai_logging_acknowledged regardless of ai_data_sharing_enabled, while the dedicated switch is disabled and the copy says “Enable data sharing first…”. This lets you end up with ai_logging_acknowledged === true and ai_data_sharing_enabled === false, and that state can be persisted via saveSettings, which is at odds with the intended consent flow and UI messaging.

Also, handleInputChange is typed as (field: keyof OrgSettings, value: string | boolean), but the function is currently only used for the two boolean flags. As the interface grows, it becomes easy to accidentally pass incompatible values to other fields.

Consider:

  • Enforcing the invariant at the UI level by disabling the Info button when data sharing is off and/or short-circuiting its onClick when !settings.ai_data_sharing_enabled.
  • Optionally reinforcing the invariant inside handleInputChange so ai_logging_acknowledged can never be set to true while ai_data_sharing_enabled is false.
  • Narrowing handleInputChange to only handle the boolean AI flags (or using field-specific helpers) to avoid accidental misuse later.

Example minimal UI change:

-                        <Button
-                          variant="ghost"
-                          size="sm"
-                          onClick={() =>
-                            handleInputChange(
-                              'ai_logging_acknowledged',
-                              !settings.ai_logging_acknowledged
-                            )
-                          }
-                        >
+                        <Button
+                          variant="ghost"
+                          size="sm"
+                          disabled={!settings.ai_data_sharing_enabled}
+                          onClick={() => {
+                            if (!settings.ai_data_sharing_enabled) return;
+                            handleInputChange(
+                              'ai_logging_acknowledged',
+                              !settings.ai_logging_acknowledged
+                            );
+                          }}
+                        >

Also applies to: 405-438, 440-496

🤖 Prompt for AI Agents
In components/settings/organization.tsx around lines 150-161 (also review blocks
at 405-438 and 440-496): the UI currently allows toggling
ai_logging_acknowledged even when ai_data_sharing_enabled is false, and the
generic handleInputChange typed as (field: keyof OrgSettings, value: string |
boolean) can accept inappropriate fields/values; update the UI to disable the
Info button (and short-circuit its onClick) when
settings.ai_data_sharing_enabled is false so users cannot acknowledge logging
without enabling data sharing, and inside handleInputChange enforce the
invariant by preventing ai_logging_acknowledged from being set true if
ai_data_sharing_enabled is false (e.g., if field === 'ai_logging_acknowledged'
&& value === true && !prev.ai_data_sharing_enabled then return prev), and
tighten the handler signature to only accept the two boolean AI flags (or add
separate boolean-specific setters) to avoid accidental misuse as the settings
interface grows.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants