Skip to content

Conversation

@asithade
Copy link
Contributor

@asithade asithade commented Nov 20, 2025

Summary

  • Add self-contained DataCopilot component with PrimeNG drawer integration
  • Integrate Data Copilot button in board member dashboard
  • Implement signal-based viewChild and SSR-safe iframe creation
  • Auto-inject organization/project context from services
  • Add iframe cleanup on drawer close for resource management

Implementation Details

  • Component Type: Standalone, self-contained with button and drawer
  • Responsive Design: Full-screen mobile, 3/4 width desktop
  • Context Integration: Auto-pulls data from AccountContextService and ProjectContextService
  • Angular 19 Patterns: Signal-based viewChild, computed signals, afterNextRender

Related

JIRA: LFXV2-768

Generated with Claude Code

Add self-contained Data Copilot component with drawer UI for AI-powered
data insights. Component integrates with board member dashboard and
automatically pulls organization/project context.

- Create DataCopilotComponent with PrimeNG drawer
- Implement signal-based viewChild and SSR-safe iframe creation
- Add responsive drawer (full width mobile, 3/4 width desktop)
- Integrate with board member dashboard
- Auto-inject organization/project from context services
- Add iframe cleanup on drawer close

LFXV2-768

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Asitha de Silva <[email protected]>
@coderabbitai
Copy link

coderabbitai bot commented Nov 20, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

Walkthrough

Adds a new standalone DataCopilotComponent, inserts it into the Board Member Dashboard template and component imports, adjusts the dashboard header layout, updates the copilot trigger label, and implements lazy creation/destruction of an iframe populated with org/project context in a right-side drawer.

Changes

Cohort / File(s) Summary
Board Member Dashboard Integration
apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.html, apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.ts
Inserted <lfx-data-copilot> into the dashboard header, changed header container class from mb-6 flex items-center gap-4 to mb-6 flex justify-between items-center gap-4, and added DataCopilotComponent to the component's standalone imports.
Data Copilot Component (new)
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts, apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.html, apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.scss
Added standalone DataCopilotComponent with a trigger button (label changed from "Ask LFX One AI" to "Ask LFX Lens"), a right-positioned drawer, a visible signal, ViewChild iframeContainer, SCSS .iframe-container styles, and logic to lazily create/destroy an iframe whose src is built from account/project context.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    actor User
    participant Dashboard as BoardMemberDashboard
    participant DataCopilot as DataCopilotComponent
    participant Drawer as Drawer UI
    participant Iframe as iframe Element

    User->>Dashboard: Load dashboard
    Dashboard->>DataCopilot: Render component
    DataCopilot->>DataCopilot: Resolve org/project context

    User->>DataCopilot: Click "Ask LFX Lens"
    DataCopilot->>Drawer: visible = true
    Drawer->>Drawer: Open
    rect `#E6F2FF`
      DataCopilot->>DataCopilot: afterNextRender -> createIframe()
      DataCopilot->>Iframe: append iframe with src(context)
    end
    Drawer-->>User: Show iframe content

    User->>Drawer: Close
    Drawer->>DataCopilot: onHide()
    DataCopilot->>DataCopilot: destroyIframe()
    DataCopilot->>Drawer: visible = false
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Inspect iframe src construction for proper encoding/sanitization.
  • Review ViewChild usage and timing to avoid race conditions on open/close.
  • Verify destroyIframe() fully cleans up DOM and references to prevent leaks.

Possibly related PRs

Pre-merge checks and finishing touches

✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title 'feat(dashboards): add data copilot integration' clearly and concisely summarizes the main change: adding Data Copilot integration to the dashboards module.
Linked Issues check ✅ Passed The PR successfully implements all primary objectives from LFXV2-768: self-contained DataCopilotComponent with signal-based patterns, responsive PrimeNG drawer, context injection, iframe cleanup, and Board Member Dashboard integration.
Out of Scope Changes check ✅ Passed All changes are directly related to LFXV2-768 objectives: component creation, dashboard integration, and responsive UI implementation. No out-of-scope modifications detected.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Description check ✅ Passed The pull request description clearly relates to the changeset, detailing the implementation of a DataCopilot component with service integration and iframe management.
✨ 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 feat/LFXV2-768-clean

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

@asithade asithade marked this pull request as ready for review November 20, 2025 02:52
@asithade asithade requested a review from jordane as a code owner November 20, 2025 02:52
Copilot AI review requested due to automatic review settings November 20, 2025 02:52
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR adds a Data Copilot AI assistant feature to the board member dashboard by implementing a self-contained component with PrimeNG drawer integration and iframe-based embedding.

  • Implements a new DataCopilotComponent with signal-based state management and SSR-safe iframe creation
  • Integrates the Data Copilot button into the board member dashboard header
  • Uses Angular 19 patterns including signal-based viewChild and computed signals for reactive context injection

Reviewed Changes

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

Show a summary per file
File Description
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts New DataCopilot component with drawer state management, iframe lifecycle handling, and automatic organization/project context injection
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.html Component template with trigger button and PrimeNG drawer containing iframe container
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.scss Styles for iframe container with flexbox layout
apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.ts Imports DataCopilotComponent for use in dashboard
apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.html Adds DataCopilot button to dashboard header with updated layout

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

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

🧹 Nitpick comments (2)
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts (2)

101-101: Consider making the iframe URL configurable.

The iframe URL is hardcoded to https://lfx-data-copilot.onrender.com/embed. For better maintainability and environment flexibility, consider moving this to an environment configuration or injectable config service.

// In environment.ts or config service
export const environment = {
  dataCopilotUrl: 'https://lfx-data-copilot.onrender.com/embed',
  // ...
};

// In component
import { environment } from '@env/environment';

// ...
iframe.src = `${environment.dataCopilotUrl}?${params.toString()}`;

87-89: Silent failure when container is unavailable.

The guard returns silently if the container is not available. While this prevents errors, it could lead to a confusing user experience where the drawer opens but remains empty with no indication of why.

Consider adding console logging or error handling:

  private createIframe(): void {
    const container = this.iframeContainer();
    if (this.iframeCreated || !container?.nativeElement) {
+      if (!container?.nativeElement) {
+        console.warn('DataCopilot: iframe container not available');
+      }
      return;
    }
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira 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 1602644 and 88d3e92.

📒 Files selected for processing (5)
  • apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.html (1 hunks)
  • apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.ts (2 hunks)
  • apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.html (1 hunks)
  • apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.scss (1 hunks)
  • apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts (1)
apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.ts (1)
  • Component (21-94)
⏰ 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: Agent
🔇 Additional comments (6)
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.scss (1)

4-10: LGTM!

The iframe container styling is appropriate and minimal. The full width/height with flex layout and hidden overflow will properly contain the embedded iframe.

apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.html (1)

7-10: LGTM!

The Data Copilot component is cleanly integrated into the dashboard header. The justify-between class properly spaces the foundation title and the copilot button.

apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.html (1)

5-11: LGTM!

The button configuration is clear and appropriate. The icon, label, and size work well for the intended use case.

apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.ts (1)

7-7: LGTM!

The DataCopilotComponent is properly imported and registered in the component's imports array. The integration follows Angular 19 standalone component patterns correctly.

Also applies to: 23-31

apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts (2)

18-33: LGTM!

The component properties are well-structured. The signal-based viewChild, computed context values, and visibility signal follow Angular 19 patterns correctly. The optional chaining in later code properly handles cases where the container may not be available.


71-80: LGTM!

The iframe cleanup logic is correct. It removes all child nodes and resets the tracking flag, properly managing resources when the drawer closes.

However, verify that component destruction is also handled. If the component is destroyed while the drawer is open, the iframe should also be cleaned up:

export class DataCopilotComponent implements OnDestroy {
  // ... existing code ...

  public ngOnDestroy(): void {
    this.destroyIframe();
  }
}

@github-actions
Copy link

github-actions bot commented Nov 20, 2025

🚀 Deployment Status

Your branch has been deployed to: https://ui-pr-167.dev.v2.cluster.linuxfound.info

Deployment Details:

  • Environment: Development
  • Namespace: ui-pr-167
  • ArgoCD App: ui-pr-167

The deployment will be automatically removed when this PR is closed.

- Remove unused afterNextRender constructor logic that never executed
- Replace fragile setTimeout with drawer onShow lifecycle event
- Fix redundant state updates by using one-way binding with explicit event handlers
- Add null-safe navigation to computed signals for account/project data

LFXV2-768

Generated with [Claude Code](https://claude.ai/code)

Signed-off-by: Asitha de Silva <[email protected]>
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

♻️ Duplicate comments (2)
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts (2)

86-111: Critical: Missing required apiKey parameter.

The PR objectives explicitly state: "Require an apiKey input parameter; construct iframe URL with query parameters: apiKey, organization_id, organization_name, project_slug, project_name."

The URLSearchParams construction (lines 95-100) omits the required apiKey parameter, and there's no @Input() to receive it.

Add the apiKey input and include it in the URL:

+ @Input({ required: true }) public apiKey!: string;

  private createIframe(): void {
    const container = this.iframeContainer();
    if (this.iframeCreated || !container?.nativeElement) {
      return;
    }

    const iframe = document.createElement('iframe');

    // Construct the iframe src with query parameters
    const params = new URLSearchParams({
+     apiKey: this.apiKey,
      organization_id: this.organizationId(),
      organization_name: this.organizationName(),
      project_slug: this.projectSlug(),
      project_name: this.projectName(),
    });

    iframe.src = `https://lfx-data-copilot.onrender.com/embed?${params.toString()}`;

Then update the board-member dashboard template to bind the apiKey:

<lfx-data-copilot [apiKey]="yourApiKeyValue"></lfx-data-copilot>

102-106: Critical: Add iframe sandbox security restrictions.

The iframe embeds external content from https://lfx-data-copilot.onrender.com/embed without any security restrictions. This poses security risks such as:

  • Unrestricted script execution
  • Access to same-origin resources
  • Form submission capabilities
  • Top-level navigation

Add a sandbox attribute with minimal required permissions:

  iframe.src = `https://lfx-data-copilot.onrender.com/embed?${params.toString()}`;
  iframe.width = '100%';
  iframe.height = '100%';
  iframe.style.border = 'none';
  iframe.title = 'LFX Data Copilot';
+ iframe.setAttribute('sandbox', 'allow-scripts allow-same-origin');

Adjust the sandbox permissions based on the actual requirements of the embedded content. Consider adding:

  • allow-forms if form submission is needed
  • allow-popups if the iframe needs to open new windows
  • Review and restrict to the minimum necessary permissions
🧹 Nitpick comments (3)
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts (3)

50-57: Remove redundant visible.set(true) call.

Line 51 is unnecessary because the onShow callback only fires after the drawer is already visible (i.e., when visible is already true). The drawer state is managed by PrimeNG, and this explicit set is redundant.

Apply this diff:

  protected onShow(): void {
-    this.visible.set(true);
-
     // Create iframe when drawer is fully visible
     if (!this.iframeCreated) {
       this.createIframe();
     }
   }

62-67: Consider removing redundant visible.set(false) call.

Similar to onShow, line 63 may be redundant since onHide fires when the drawer is already hidden. However, setting it explicitly ensures signal consistency if other code depends on the visible signal state.


102-102: Consider using environment configuration for the iframe URL.

The iframe URL https://lfx-data-copilot.onrender.com/embed is hardcoded, making it difficult to use different endpoints for development, staging, and production environments.

Move the URL to an environment configuration file:

In environment.ts:

export const environment = {
  production: false,
  dataCopilotUrl: 'https://lfx-data-copilot.onrender.com/embed'
};

Then inject and use it:

private readonly environment = inject(ENVIRONMENT); // or however environments are injected

iframe.src = `${this.environment.dataCopilotUrl}?${params.toString()}`;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

Disabled knowledge base sources:

  • Jira 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 50da2a6 and 9d922f1.

📒 Files selected for processing (2)
  • apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.html (1 hunks)
  • apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.html
🧰 Additional context used
🧬 Code graph analysis (1)
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts (1)
apps/lfx-one/src/app/modules/dashboards/board-member/board-member-dashboard.component.ts (1)
  • Component (21-94)
⏰ 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: build-and-push
🔇 Additional comments (3)
apps/lfx-one/src/app/shared/components/data-copilot/data-copilot.component.ts (3)

43-45: LGTM: Clean separation of concerns.

The openDrawer method properly delegates to the drawer's event system. Iframe creation is correctly handled in the onShow callback (lines 50-57), eliminating the race condition flagged in previous reviews.


72-81: LGTM: Proper iframe cleanup.

The cleanup logic correctly removes all child nodes and resets the iframeCreated flag, ensuring proper resource management when the drawer closes.


26-26: Feature flag is properly used.

The isLfxLensEnabled field is used in the template (data-copilot.component.html:5) to conditionally render content with @if (isLfxLensEnabled()). No issues found.

@dealako dealako merged commit ec8a299 into main Nov 20, 2025
7 checks passed
@dealako dealako deleted the feat/LFXV2-768-clean branch November 20, 2025 17:42
@github-actions
Copy link

🧹 Deployment Removed

The deployment for PR #167 has been removed.

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants