Skip to content

Feat/angular roadmap #424

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 33 commits into
base: main
Choose a base branch
from
Open

Feat/angular roadmap #424

wants to merge 33 commits into from

Conversation

dokolyski
Copy link
Contributor

@dokolyski dokolyski commented Apr 4, 2025

Summary by CodeRabbit

  • New Features

    • Introduced an interactive Angular Roadmap page with zoom, pan, and bottom sheet details.
    • Added roadmap node components including clusters, primary, secondary, and special "angular-love" nodes with hover effects.
    • Implemented roadmap layer and SVG connector components for visual node grouping and connections.
    • Added roadmap bottom sheet UI with detailed descriptions, creators, resources, and social sharing.
    • Included a comprehensive Angular learning roadmap dataset.
    • Added English and Polish translations for roadmap navigation and controls.
  • Enhancements

    • Layout adapts dynamically between full-width and constrained modes based on route data.
    • Added route for roadmap feature with SEO title and layout configuration.
  • Documentation

    • Added README files with testing instructions for all new roadmap-related libraries.
  • Chores

    • Established project configurations, ESLint, Jest, and TypeScript settings for roadmap libraries.
    • Configured Nx workspace path aliases for roadmap modules.
    • Added test setup files enforcing strict Angular testing environment.
    • Updated Angular component generator defaults to use OnPush change detection.
  • Dependencies

    • Added panzoom library for smooth zoom and pan interactions on the roadmap.

Copy link

coderabbitai bot commented Apr 4, 2025

Walkthrough

This change introduces a comprehensive Angular feature for a visual, interactive roadmap, including new libraries for roadmap data contracts, data access, UI components, and bottom sheet overlays. It adds a hierarchical roadmap data JSON, localization, configuration, and utility services, and integrates the feature into the blog application with routing, layout, and state management.

Changes

Files / Groups Change Summary
libs/blog/roadmap/feature-roadmap/*, libs/blog/roadmap/ui-roadmap/*, libs/blog/roadmap/ui-roadmap-node/*, libs/blog/roadmap/ui-roadmap-bottomsheet/*, libs/blog/roadmap/data-access/*, libs/blog/roadmap/utils/*, libs/blog-contracts/roadmap/* Added new Angular libraries for roadmap feature: components, services, types, state, utilities, and contracts.
apps/blog/src/assets/roadmap-tiles.json Added a comprehensive JSON file defining the Angular roadmap structure and resources.
apps/blog/src/assets/i18n/en.json, apps/blog/src/assets/i18n/pl.json Added localization keys for roadmap page controls in English and Polish.
libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts, libs/blog/shell/feature-shell-web/src/lib/root-shell.component.ts Integrated roadmap feature route and dynamic layout configuration into the blog shell.
libs/blog/layouts/ui-layouts/src/lib/layout/layout.component.ts, libs/blog/layouts/ui-layouts/src/lib/layout/layout.component.html Enhanced layout component to support full-width layouts and dynamic class binding based on config.
tsconfig.base.json, nx.json Added TypeScript path aliases for new libraries and set Angular generator to use OnPush change detection by default.
package.json Added panzoom dependency for interactive roadmap controls.
*.eslintrc.json, README.md, jest.config.ts, project.json, tsconfig*.json, test-setup.ts (various libs) Added lint, test, build, and config scaffolding for all new libraries.
libs/blog/roadmap/ui-roadmap/src/lib/components/connected-nodes/*, libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/*, libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-svg-controls/*, libs/blog/roadmap/ui-roadmap/src/lib/components/vertical-connector-arrow/* Implemented roadmap visualization components, node connectors, and control UI.
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/* Added bottom sheet overlay components for node details, sharing, and resource lists.
libs/blog/roadmap/data-access/src/lib/infractructure/roadmap.service.ts, libs/blog/roadmap/data-access/src/lib/state/roadmap-store.ts, libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts Implemented roadmap data fetching, transformation, and state management.
libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts Added a service for bottom sheet open notifications.
libs/blog-contracts/roadmap/src/lib/* Defined TypeScript interfaces for roadmap nodes, resources, and creators.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant BlogApp
    participant RoadmapFeatureComponent
    participant RoadmapStore
    participant RoadmapService
    participant RoadmapBottomsheetManager
    participant BottomsheetComponent

    User->>BlogApp: Navigates to /roadmap
    BlogApp->>RoadmapFeatureComponent: Loads FeatureRoadmapComponent
    RoadmapFeatureComponent->>RoadmapStore: getNodes()
    RoadmapStore->>RoadmapService: Fetch roadmap-tiles.json
    RoadmapService-->>RoadmapStore: Returns roadmap node data
    RoadmapStore-->>RoadmapFeatureComponent: Updates roadmapLayers signal
    User->>RoadmapFeatureComponent: Clicks roadmap node
    RoadmapFeatureComponent->>RoadmapBottomsheetManager: open(node)
    RoadmapBottomsheetManager->>BottomsheetComponent: Opens dialog with node data
    User->>BottomsheetComponent: Views details, shares, closes
Loading

Possibly related PRs

Suggested reviewers

  • dokolyski
  • mateuszbasinski
  • majahendzel-va

Poem

A roadmap grows, node by node,
With colors bright and knowledge sowed.
Click to zoom, to pan, to see,
Angular’s journey, wild and free!
Bottom sheets pop with cheerful flair,
Sharing wisdom everywhere.
🐇✨ Hop along—explore, compare!

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://dd388320-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://9b9cfb05.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

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

🧹 Nitpick comments (15)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-angular-love-node.component.ts (1)

9-12: Consider extracting CSS variables to a shared file

The CSS variables defined inline are duplicated across multiple node components (primary, secondary, and this one) as seen in the relevant code snippets.

Consider extracting these variables to a shared styles file to maintain consistency and make theme changes easier. This would reduce duplication across your node components.

- style="
-      --primary-color: #B3004A; --secondary-color: #66002B; --gradient-color: #481CAB; --on-hover-border-1: #923CFF; --on-hover-border-2: #FF006A"
+ class="roadmap-node-theme"

Then define these variables in a shared SCSS file that all node components can import.

libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.spec.ts (1)

1-22: Basic test structure looks good, but could benefit from more comprehensive test coverage.

The test file correctly sets up the TestBed configuration and verifies component creation. However, for a component that manages a complex roadmap structure with SVG visuals, consider adding more test cases to verify:

  1. Proper rendering of different node types
  2. Correct layer calculations
  3. Edge cases like empty layers or single nodes
// Example of additional tests you might want to add:
it('should display layers correctly when data is provided', () => {
  // Setup test data
  component.roadmapData = mockRoadmapData;
  fixture.detectChanges();
  
  // Assert expected DOM elements
  const layerElements = fixture.nativeElement.querySelectorAll('.layer');
  expect(layerElements.length).toBe(mockRoadmapData.length);
});

it('should handle empty roadmap data gracefully', () => {
  // Test with empty data
  component.roadmapData = [];
  fixture.detectChanges();
  
  // Assert appropriate handling
  expect(fixture.nativeElement.querySelector('.no-data-message')).toBeTruthy();
});
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts (1)

5-21: Component template could benefit from improved accessibility and maintainability.

The component is well-structured, but there are potential improvements:

  1. The hardcoded colors in the style attribute should be moved to a theme file for better maintainability
  2. Consider adding ARIA attributes for improved accessibility
  3. Color contrast should be verified for WCAG compliance
@Component({
  selector: 'al-ui-roadmap-primary-node',
  template: `
    <div
      class="roadmap-hover-border-gradient relative w-fit text-nowrap rounded-lg bg-[#FDF5FD] text-[#FDF5FD]"
-      style="
-      --primary-color: #B3004A; --secondary-color: #66002B; --gradient-color: #481CAB; --on-hover-border-1: #923CFF; --on-hover-border-2: #FF006A"
+      [ngClass]="'theme-primary'"
+      role="listitem"
+      aria-label="Primary roadmap node: {{node().title}}"
    >
      <div
        class="relative z-10 m-[4px] rounded-lg bg-[--primary-color] px-6 py-4"
      >
        <div class="text-[24px]">{{ node().title }}</div>
      </div>
    </div>
  `,
  styleUrls: ['./roadmap-hover-border-gradient.scss'],
})

Then define theme classes in your SCSS file:

// In roadmap-hover-border-gradient.scss
.theme-primary {
  --primary-color: #B3004A;
  --secondary-color: #66002B;
  --gradient-color: #481CAB;
  --on-hover-border-1: #923CFF;
  --on-hover-border-2: #FF006A;
}
libs/blog/roadmap/feature-roadmap/src/lib/slice.pipes.ts (3)

3-11: Good implementation but consider making the pipe standalone.

The LeftSlicePipe implementation is clean and functional. For modern Angular applications (v14+), it's recommended to make pipes standalone.

@Pipe({
  name: 'leftSlice',
+  standalone: true,
})
export class LeftSlicePipe implements PipeTransform {
  transform<T>(value: T[]): T[] {
    const halfLength = Math.ceil(value.length / 2);
    return value.slice(0, halfLength);
  }
}

13-21: Good implementation but consider making the pipe standalone.

The RightSlicePipe implementation is clean and functional. For modern Angular applications (v14+), it's recommended to make pipes standalone.

@Pipe({
  name: 'rightSlice',
+  standalone: true,
})
export class RightSlicePipe implements PipeTransform {
  transform<T>(value: T[]): T[] {
    const halfLength = Math.ceil(value.length / 2);
    return value.slice(halfLength);
  }
}

7-10: Consider handling edge cases in pipe implementations.

While the pipe implementations work for valid arrays, they don't handle edge cases like null or undefined values.

// For LeftSlicePipe
transform<T>(value: T[]): T[] {
+  if (!value || !Array.isArray(value)) {
+    return [];
+  }
  const halfLength = Math.ceil(value.length / 2);
  return value.slice(0, halfLength);
}

// For RightSlicePipe
transform<T>(value: T[]): T[] {
+  if (!value || !Array.isArray(value)) {
+    return [];
+  }
  const halfLength = Math.ceil(value.length / 2);
  return value.slice(halfLength);
}

Also applies to: 17-20

libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1)

65-66: Type casting with $any() indicates a potential type mismatch.

The use of $any() for type casting suggests that the node type doesn't match the expected cluster type. This should be resolved with proper typing.

// In the component class, add a type guard:
isCluster(node: RoadmapNode): node is RoadmapCluster {
  return node.nodeType === 'cluster';
}

// Then in the template:
<al-ui-roadmap-cluster
-  [cluster]="$any(node)"
+  [cluster]="node"
  *ngIf="isCluster(node)"
></al-ui-roadmap-cluster>

Also applies to: 77-78

libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts (1)

5-21: Consider accessibility and theming improvements

The component implementation is clean, but consider:

  1. The hardcoded color values like #B3004A, #66002B, etc. might be better placed in a central theme file for easier maintenance
  2. Verify that the text colors provide sufficient contrast with the background for accessibility (WCAG compliance)

You could refactor the inline styles to use application-wide CSS variables:

- style="
- --primary-color: #B3004A; --secondary-color: #66002B; --gradient-color: #481CAB; --on-hover-border-1: #923CFF; --on-hover-border-2: #FF006A"
+ style="
+ --primary-color: var(--theme-primary-color, #B3004A); --secondary-color: var(--theme-secondary-color, #66002B); --gradient-color: var(--theme-gradient-color, #481CAB); --on-hover-border-1: var(--theme-hover-border-1, #923CFF); --on-hover-border-2: var(--theme-hover-border-2, #FF006A)"
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-cluster.component.ts (2)

5-26: Well-structured template with modern Angular syntax

Great use of modern Angular control flow with the @for loop and proper tracking by node ID. The component structure is clean and follows Angular best practices.

As with the other component, consider:

  1. Extracting color values to a theme file
  2. Verifying color contrast for accessibility

27-32: Consider centralizing styling variables

The host binding approach is good, but the inline color variables could be moved to a central theme:

- style:
-   '--primary-color: #B3004A; --secondary-color: #66002B; --gradient-color: #481CAB; --on-hover-border-1: #923CFF; --on-hover-border-2: #FF006A',
+ style:
+   '--primary-color: var(--theme-primary-color, #B3004A); --secondary-color: var(--theme-secondary-color, #66002B); --gradient-color: var(--theme-gradient-color, #481CAB); --on-hover-border-1: var(--theme-hover-border-1, #923CFF); --on-hover-border-2: var(--theme-hover-border-2, #FF006A)',
libs/blog/roadmap/feature-roadmap/src/lib/ui/roadmap-hover-border-gradient.scss (1)

1-32: Well-implemented hover gradient effect with animation

The gradient border effect implementation with the ::before pseudo-element and rotation animation is clean. The exclusion of .cluster-node elements from the hover effect is handled appropriately.

Consider if the 2000px dimensions for the pseudo-element are necessary - while it works, a smaller size might be more performance-friendly, especially on mobile devices.

-    width: 2000px;
-    height: 2000px;
-    top: calc(50% - 1000px);
-    left: calc(50% - 1000px);
+    width: 1000px;
+    height: 1000px;
+    top: calc(50% - 500px);
+    left: calc(50% - 500px);
libs/blog/shell/feature-shell-web/src/lib/roadmap-shell.component.ts (3)

20-30: Simplified template without top banner or router outlet

The template only includes header, roadmap feature, and footer components, which is simpler than the RootShellComponent shown in the relevant snippets. However, the component still initializes and processes ad banner data that isn't used in the template.

If ad banners aren't intended to be shown in this view, consider removing the related code to simplify the component.


59-60: Hardcoded adBannerVisible value

The adBannerVisible computed value is hardcoded to always return false, which means the effect to set viewport offset will always set it to [0, 80].

Consider simplifying this since the value never changes:

-  // todo: temporary solution to keep in mind how banner influence the layout
-  protected readonly adBannerVisible = computed(() => false);
+  // Banner is not displayed in roadmap view
+  protected readonly adBannerVisible = false;

1-89: Component structure duplicates RootShellComponent

This component shares significant code with RootShellComponent from the provided context snippets, suggesting potential duplication.

Consider extracting common functionality to a base class or shared service to avoid duplication between shell components. Alternatively, evaluate if this component could extend RootShellComponent and override only what's different.

libs/blog/roadmap/feature-roadmap/src/lib/temp.component.ts (1)

46-49: Multiple calls to generatePath could accumulate paths in pathData.
Each call appends a new path to pathData. If the intended behavior is to render multiple paths, this is fine. Otherwise, consider clearing or resetting the array before subsequent calls to avoid unintentional duplication.

ngOnInit(): void {
-  this.generatePath();
-  this.generatePath(120);
-  this.generatePath(300);
+  this.pathData = [];
+  [0, 120, 300].forEach((len) => this.generatePath(len));
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 860a9dc and 1fc466b.

📒 Files selected for processing (23)
  • libs/blog/roadmap/feature-roadmap/.eslintrc.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/README.md (1 hunks)
  • libs/blog/roadmap/feature-roadmap/jest.config.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/project.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/index.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.spec.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/slice.pipes.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/temp.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/roadmap-hover-border-gradient.scss (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-angular-love-node.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-cluster.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/test-setup.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/tsconfig.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/tsconfig.lib.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/tsconfig.spec.json (1 hunks)
  • libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts (1 hunks)
  • libs/blog/shell/feature-shell-web/src/lib/roadmap-shell.component.ts (1 hunks)
  • libs/blog/shell/feature-shell-web/src/lib/root-shell.component.ts (1 hunks)
  • tsconfig.base.json (1 hunks)
🧰 Additional context used
🧬 Code Definitions (5)
libs/blog/shell/feature-shell-web/src/lib/roadmap-shell.component.ts (2)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1)
  • Component (39-214)
libs/blog/shell/feature-shell-web/src/lib/root-shell.component.ts (1)
  • Component (22-109)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts (3)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-angular-love-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (2)
  • Component (39-214)
  • RoadmapNode (23-27)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-angular-love-node.component.ts (3)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (2)
  • Component (39-214)
  • RoadmapNode (23-27)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts (3)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-angular-love-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (2)
  • Component (39-214)
  • RoadmapNode (23-27)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (4)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-angular-love-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-cluster.component.ts (1)
  • Component (5-37)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts (1)
  • Component (5-24)
🪛 Biome (1.9.4)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts

[error] 106-106: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 161-161: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 180-180: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (23)
libs/blog/roadmap/feature-roadmap/src/index.ts (1)

1-1: Clear Public API Export

The export statement correctly re-exports all public members from the FeatureRoadmapComponent, making it easier to import from a single entry point. This meets the goal of simplifying the public API for the roadmap feature.

libs/blog/roadmap/feature-roadmap/README.md (1)

1-8: Informative README File

The README provides a succinct description of the library and clear instructions on running the unit tests (i.e., using nx test feature-roadmap). Including the Nx reference is helpful for developers who might be new to this setup.

libs/blog/roadmap/feature-roadmap/src/test-setup.ts (1)

1-6: Robust Test Environment Configuration

The use of setupZoneTestEnv with strict options (errorOnUnknownElements and errorOnUnknownProperties set to true) establishes a solid testing foundation. This configuration will help catch unintended template errors and is aligned with best practices for Angular testing.

libs/blog/shell/feature-shell-web/src/lib/root-shell.component.ts (1)

57-59: Effective Host Styling Addition

The new host property that applies the classes 'flex flex-col min-h-screen' is a clean solution to enforce the desired layout on the component’s host element. This change is well-targeted and integrates seamlessly with the component’s styling strategy.

tsconfig.base.json (1)

191-193: Accurate Module Path Mapping

The newly added path alias "@angular-love/feature-roadmap" correctly points to libs/blog/roadmap/feature-roadmap/src/index.ts. This change facilitates easier imports and enhances TypeScript module resolution for the roadmap feature.

libs/blog/roadmap/feature-roadmap/tsconfig.lib.json (1)

1-17: Configuration looks appropriate for an Angular library

The TypeScript configuration extends the base config and sets up standard library options:

  • Output directory correctly points to the dist folder
  • Declaration files and source maps are enabled for better debugging
  • Test files are properly excluded
  • Only TypeScript files from src are included

This follows standard practices for Angular libraries built with Nx.

libs/blog/roadmap/feature-roadmap/project.json (1)

1-20: Project configuration follows Nx standards

The configuration properly defines:

  • Project name, schema, and source root paths
  • Angular component prefix "al" for consistent component naming
  • Library project type
  • Appropriate tags for monorepo organization
  • Test and lint targets with correct executors

This setup aligns with Nx workspace best practices and will integrate well with the existing architecture.

libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts (1)

24-30: Route implementation follows Angular best practices

The new roadmap route:

  • Is correctly added to the commonRoutes array
  • Uses lazy loading with async/await pattern
  • Matches the module structure pattern used by other routes

This implementation maintains consistency with the existing routing architecture.

libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-angular-love-node.component.ts (3)

1-4: Imports look correct

Angular core imports and the RoadmapNode interface are properly imported.


5-21: Component definition follows Angular standards

The template implementation:

  • Uses Angular's standalone component pattern
  • Follows the project's naming conventions with the 'al-' prefix
  • Includes appropriate styling with CSS variables for theming
  • Uses consistent styling approach with the related node components

The gradient styling and CSS variables approach allows for flexible theming.


22-24: Input property correctly implemented

The component:

  • Uses Angular's modern input API
  • Properly marks the node input as required
  • Correctly types the input with the RoadmapNode interface

This ensures type safety and clear component API design.

libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts (1)

22-24: Component class implementation looks good.

The implementation of the component class with a required input is clean and follows Angular's best practices for modern input syntax.

libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1)

1-88: Overall structure is complex but well-organized.

The SVG-based roadmap visualization is complex but follows a logical structure. Consider adding comments to explain the purpose of different sections for improved maintainability.

libs/blog/roadmap/feature-roadmap/.eslintrc.json (1)

1-36: ESLint configuration looks good

The configuration properly extends the root ESLint config and sets up appropriate rules for Angular components and directives. The prefix "al" is used consistently across the feature components, which is a good practice for maintaining a clear namespace.

libs/blog/roadmap/feature-roadmap/jest.config.ts (1)

1-21: Jest configuration is properly set up

The configuration correctly sets up Jest for Angular testing with appropriate transformations, serializers, and coverage reporting. The setup aligns with best practices for Angular libraries.

libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts (2)

1-4: Imports look good

The component correctly imports the necessary dependencies and the RoadmapNode type from the feature component.


22-24: Input usage looks good

The component correctly uses Angular's input() API with required type checking, which is a modern approach.

libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-cluster.component.ts (2)

1-4: Imports look good

The component correctly imports the necessary dependencies and the RoadmapCluster type.


33-37: Input usage looks good

The component correctly uses Angular's input() API with required type checking for the cluster property.

libs/blog/roadmap/feature-roadmap/tsconfig.spec.json (1)

1-16: Configuration looks good for Jest testing in Angular

The TypeScript configuration properly extends the base config and includes all necessary settings for Jest-based testing, including correct module settings, target ECMAScript version, and type definitions.

libs/blog/roadmap/feature-roadmap/tsconfig.json (1)

1-28: Good TypeScript configuration with strict settings

The TypeScript configuration appropriately enables strict type checking and follows Angular best practices with the compiler options. The references to both lib and spec configurations are correctly set up.

libs/blog/roadmap/feature-roadmap/src/lib/temp.component.ts (2)

52-88: Looks good!
The SVG path generation logic is solid, and dynamically adjusting the SVG's dimensions to fit the path is a nice touch.


23-33:

❓ Verification inconclusive

Potential structural directive concern with @for.
This @for (path of pathData; track $index) syntax is non-standard in Angular unless you're intentionally using experimental features or a custom structural directive. Ensure it's functioning as expected in your application.

Run this command to confirm whether a custom structural directive for @for is defined:


🏁 Script executed:

#!/bin/bash
fd -e ts --exec rg "@Directive" 

Length of output: 226


Attention: Verify Custom @for Directive Implementation

The template syntax @for (path of pathData; track $index) remains non-standard in Angular. Although our initial scan returned multiple occurrences of @Directive({ in the codebase, we found no clear evidence of a custom structural directive specifically implemented for @for.

  • Please confirm that a custom directive supporting the @for syntax is intentionally defined (e.g., with a selector like [for]) and documented.
  • If this syntax was not intended, consider reverting to the standard Angular *ngFor syntax to prevent potential runtime issues.

Comment on lines 18 to 40
@Component({
selector: 'al-root-shell',
template: `
<al-header
class="block w-full"
[language]="language()"
(languageChange)="onLanguageChange($event)"
>
<al-search />
</al-header>
<al-feature-roadmap class="flex-1 basis-0" />
<al-footer />
`,
imports: [
HeaderComponent,
FooterComponent,
SearchComponent,
FeatureRoadmapComponent,
],
host: {
class: 'flex flex-col min-h-screen',
},
})
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Selector name doesn't match component name

The selector al-root-shell doesn't match the component class name RoadmapShellComponent. This could lead to confusion since typically selectors should reflect the component name.

Change the selector to match the component name:

@Component({
-  selector: 'al-root-shell',
+  selector: 'al-roadmap-shell',
  template: `
📝 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
@Component({
selector: 'al-root-shell',
template: `
<al-header
class="block w-full"
[language]="language()"
(languageChange)="onLanguageChange($event)"
>
<al-search />
</al-header>
<al-feature-roadmap class="flex-1 basis-0" />
<al-footer />
`,
imports: [
HeaderComponent,
FooterComponent,
SearchComponent,
FeatureRoadmapComponent,
],
host: {
class: 'flex flex-col min-h-screen',
},
})
@Component({
selector: 'al-roadmap-shell',
template: `
<al-header
class="block w-full"
[language]="language()"
(languageChange)="onLanguageChange($event)"
>
<al-search />
</al-header>
<al-feature-roadmap class="flex-1 basis-0" />
<al-footer />
`,
imports: [
HeaderComponent,
FooterComponent,
SearchComponent,
FeatureRoadmapComponent,
],
host: {
class: 'flex flex-col min-h-screen',
},
})

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://412cd3ad-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://f9fa86df.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

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

♻️ Duplicate comments (5)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1)

29-38: ⚠️ Potential issue

SVG marker ID could cause conflicts with multiple component instances.

The hardcoded id="arrowhead" could cause conflicts if multiple instances of this component exist on the same page.

Generate unique IDs for SVG markers by incorporating the layer's parent node ID:

-    id="arrowhead"
+    [attr.id]="'arrowhead-' + layer.parentNode.id"

And update the reference:

-    marker-end="url(#arrowhead)"
+    [attr.marker-end]="'url(#arrowhead-' + layer.parentNode.id + ')'"
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (4)

52-52: ⚠️ Potential issue

Incorrect Angular configuration with styleUrl.

Angular expects styleUrls (plural) as an array instead of the singular styleUrl property.

- styleUrl: './feature-roadmap.component.scss',
+ styleUrls: ['./feature-roadmap.component.scss'],

103-103: 🛠️ Refactor suggestion

Avoid spread syntax in reducers for better performance.

Using spread syntax in reducers causes O(n²) time complexity which can impact performance with larger datasets.

- (acc, node) => ({ ...acc, [node.id]: node }),
+ (acc, node) => {
+   acc[node.id] = node;
+   return acc;
+ },
🧰 Tools
🪛 Biome (1.9.4)

[error] 103-103: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


157-161: 🛠️ Refactor suggestion

Optimize the cluster creation reducer.

Using spread syntax in reducers causes O(n²) time complexity which can impact performance with larger datasets.

- (acc, primaryNodeId) => ({
-   ...acc,
-   [nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']: primaryNodeId,
- }),
+ (acc, primaryNodeId) => {
+   acc[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode'] = primaryNodeId;
+   return acc;
+ },
🧰 Tools
🪛 Biome (1.9.4)

[error] 158-158: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


176-180: 🛠️ Refactor suggestion

Minimize object creation overhead in layer mapping.

Using spread syntax in reducers causes O(n²) time complexity which can impact performance with larger datasets.

- (acc, primaryNodeId) => ({
-   ...acc,
-   [nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']: primaryNodeId,
- }),
+ (acc, primaryNodeId) => {
+   acc[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode'] = primaryNodeId;
+   return acc;
+ },
🧰 Tools
🪛 Biome (1.9.4)

[error] 177-177: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🧹 Nitpick comments (3)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (3)

56-99: Consider externalizing roadmap data.

Hard-coding the roadmap data directly in the component makes it difficult to maintain, update, or translate.

Consider moving this data to a separate service or configuration file:

// roadmap-data.service.ts
@Injectable({
  providedIn: 'root'
})
export class RoadmapDataService {
  getRoadmapNodes(): RoadmapNodeDTO[] {
    return [
      {
        id: '2',
        title: 'Components',
      },
      // ... other nodes
    ];
  }
}

// Then in your component:
constructor(private roadmapDataService: RoadmapDataService) {
  this.nodesDto.set(this.roadmapDataService.getRoadmapNodes());
}

115-118: Avoid array spread for better performance.

The spread operator creates a new array on each iteration, which is inefficient for large datasets.

- clusterMap[parentClusterNodeDto.id] = [
-   ...(clusterMap[parentClusterNodeDto.id] ?? []),
-   nodeDto.id,
- ];
+ if (!clusterMap[parentClusterNodeDto.id]) {
+   clusterMap[parentClusterNodeDto.id] = [];
+ }
+ clusterMap[parentClusterNodeDto.id].push(nodeDto.id);

130-133: Avoid array spread for better performance.

The spread operator creates a new array on each iteration, which is inefficient for large datasets.

- layerMap[nodeDto.parentNodeId] = [
-   ...(layerMap[nodeDto.parentNodeId] ?? []),
-   nodeDto.id,
- ];
+ if (!layerMap[nodeDto.parentNodeId]) {
+   layerMap[nodeDto.parentNodeId] = [];
+ }
+ layerMap[nodeDto.parentNodeId].push(nodeDto.id);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1fc466b and 8956a43.

📒 Files selected for processing (4)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.scss (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/secondary-arrow.pipe.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.scss
🧰 Additional context used
🧬 Code Definitions (1)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (4)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-angular-love-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-cluster.component.ts (1)
  • Component (5-37)
🪛 Biome (1.9.4)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts

[error] 103-103: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 158-158: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 177-177: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (1)
libs/blog/roadmap/feature-roadmap/src/lib/secondary-arrow.pipe.ts (1)

19-26: LGTM! Implementation is clear and well-documented.

The pipe effectively handles different cases for arrow generation with clear documentation. The arc calculation is properly implemented to create accurate curved corners.

xmlns:svg="http://www.w3.org/1999/html"
>
<svg:foreignObject
style="transform: translateX(calc(50% - {{ layoutEl.clientWidth / 2 }}px))"
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Template reference variable used before definition could cause rendering issues.

The translateX calculation references layoutEl.clientWidth before the #layoutEl element is fully rendered, which may cause incorrect initial positioning or flickering.

Consider using Angular's AfterViewInit lifecycle hook with ViewChild to calculate the translation after the view is fully rendered:

// In the component class
@ViewChild('layoutEl') layoutEl: ElementRef;
private translationOffset = signal(0);

ngAfterViewInit() {
  // Calculate after view is initialized
  this.translationOffset.set(this.layoutEl.nativeElement.clientWidth / 2);
}

Then update your template:

- style="transform: translateX(calc(50% - {{ layoutEl.clientWidth / 2 }}px))"
+ style="transform: translateX(calc(50% - {{ translationOffset() }}px))"

Comment on lines 63 to 65
@let shift =
(allChildNodesEl.clientWidth / 2 - leftChildNodesEl.clientWidth ||
0) - 32;
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Layout calculation uses template references before initialization.

The calculation for shift uses allChildNodesEl and leftChildNodesEl template references potentially before they're fully initialized, which could lead to incorrect positioning.

Consider using a reactive approach with ViewChildren and AfterViewInit:

@ViewChildren('allChildNodesEl') allChildNodesElements: QueryList<ElementRef>;
@ViewChildren('leftChildNodesEl') leftChildNodesElements: QueryList<ElementRef>;
private nodeShifts = signal<{[key: string]: number}>({});

ngAfterViewInit() {
  // Calculate shifts after view is initialized
  this.calculateChildNodeShifts();
}

private calculateChildNodeShifts() {
  // Calculate shifts for each layer's child nodes
  // Store in nodeShifts signal for reactive updates
}

Then in your template:

- @let shift =
-   (allChildNodesEl.clientWidth / 2 - leftChildNodesEl.clientWidth || 0) - 32;
+ @let shift = nodeShifts()[layer.parentNode.id] || 0;

Comment on lines 101 to 242
if (nodeDto.parentNodeId) {
if (nodeDtoMap[nodeDto.parentNodeId].parentNodeId) {
const parentClusterNodeDto = nodeDtoMap[nodeDto.parentNodeId];

clusterMap[parentClusterNodeDto.id] = [
...(clusterMap[parentClusterNodeDto.id] ?? []),
nodeDto.id,
];

if (nodeMap[nodeDto.parentNodeId]) {
nodeMap[parentClusterNodeDto.id].nodeType = 'cluster';
} else {
nodeMap[parentClusterNodeDto.id] = {
id: parentClusterNodeDto.id,
nodeType: 'cluster',
title: parentClusterNodeDto.title,
};
}
} else {
layerMap[nodeDto.parentNodeId] = [
...(layerMap[nodeDto.parentNodeId] ?? []),
nodeDto.id,
];
}
if (!nodeMap[nodeDto.id]) {
nodeMap[nodeDto.id] = {
id: nodeDto.id,
nodeType: 'secondary',
title: nodeDto.title,
};
}
} else {
nodeMap[nodeDto.id] = {
id: nodeDto.id,
nodeType: 'primary',
title: nodeDto.title,
};
if (!layerMap[nodeDto.id]) {
layerMap[nodeDto.id] = [];
}
}
});

// setup clusters
Object.entries(clusterMap).forEach(([clusterNodeId, childrenNodeIds]) => {
const previousClusterNodeIdToNodeIdMap = childrenNodeIds.reduce(
(acc, primaryNodeId) => ({
...acc,
[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']:
primaryNodeId,
}),
{} as { [previousNodeId: string | 'initialNode']: string },
);

const clusterNode = nodeMap[clusterNodeId] as RoadmapCluster;
clusterNode.clusteredNodes = [];
let nextNodeId = previousClusterNodeIdToNodeIdMap['initialNode'];
while (nextNodeId) {
clusterNode.clusteredNodes.push(nodeMap[nextNodeId]);
nextNodeId = previousClusterNodeIdToNodeIdMap[nextNodeId];
}
});

// setup layers
const previousLayerNodeIdToNodeIdMap = Object.keys(layerMap).reduce(
(acc, primaryNodeId) => ({
...acc,
[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']:
primaryNodeId,
}),
{} as { [previousNodeId: string | 'initialNode']: string },
);

const layers: {
parentNode: RoadmapNode;
childNodes: RoadmapNode[];
}[] = [];
let nextParentNodeId = previousLayerNodeIdToNodeIdMap['initialNode'];
while (nextParentNodeId) {
layers.push({
parentNode: nodeMap[nextParentNodeId],
childNodes: layerMap[nextParentNodeId].map(
(childrenNodeId) => nodeMap[childrenNodeId],
),
});
nextParentNodeId = previousLayerNodeIdToNodeIdMap[nextParentNodeId];
}

return [
{
parentNode: {
id: '1',
title: 'Angular.Love Roadmap Introduction',
nodeType: 'angular-love',
},
childNodes: [],
},
...layers,
];
});
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Refactor complex roadmap calculation into smaller functions.

The roadmap calculation logic is very complex and contained in a single computed function, making it difficult to understand and maintain.

Break down the logic into smaller, focused functions:

protected readonly roadmapLayers = computed<RoadmapLayer[]>(() => {
  const nodeDtoMap = this.createNodeDtoMap();
  const { layerMap, clusterMap, nodeMap } = this.createMaps(nodeDtoMap);
  
  this.setupClusters(clusterMap, nodeMap, nodeDtoMap);
  const layers = this.setupLayers(layerMap, nodeMap, nodeDtoMap);
  
  return [this.createRootLayer(), ...layers];
});

private createNodeDtoMap(): { [nodeId: string]: RoadmapNodeDTO } {
  // Extract node map creation logic
}

private createMaps(nodeDtoMap: { [nodeId: string]: RoadmapNodeDTO }): {
  layerMap: { [parentNodeId: string]: string[] },
  clusterMap: { [clusterNodeId: string]: string[] },
  nodeMap: { [nodeId: string]: RoadmapNode }
} {
  // Extract maps creation logic
}

// Additional methods for other parts of the logic
🧰 Tools
🪛 Biome (1.9.4)

[error] 103-103: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 158-158: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 177-177: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

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

♻️ Duplicate comments (4)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (4)

78-78: Use the correct Angular property for component styles.

In Angular, the correct property is styleUrls (plural), not styleUrl. Using styleUrl can lead to build or runtime issues.

- styleUrl: './feature-roadmap.component.scss',
+ styleUrls: ['./feature-roadmap.component.scss'],

169-169: Avoid spread syntax in a .reduce() accumulator for better performance.

Constructing a new object on each iteration using spread (...acc) is potentially O(n^2). Use a mutable approach (direct assignment) to improve performance.

- (acc, node) => ({ ...acc, [node.id]: node }),
+ (acc, node) => {
+   acc[node.id] = node;
+   return acc;
+ },
🧰 Tools
🪛 Biome (1.9.4)

[error] 169-169: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


224-224: Avoid spread syntax in a .reduce() accumulator for better performance.

Same rationale: every iteration triggers a new object creation, leading to suboptimal time complexity.

- (acc, primaryNodeId) => ({
-   ...acc,
-   [nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']: primaryNodeId,
- }),
+ (acc, primaryNodeId) => {
+   acc[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode'] = primaryNodeId;
+   return acc;
+ },
🧰 Tools
🪛 Biome (1.9.4)

[error] 224-224: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


243-243: Avoid spread syntax in a .reduce() accumulator for better performance.

Repeated usage of spread on an accumulator can degrade performance at scale. Switching to direct assignment is more efficient.

- (acc, primaryNodeId) => ({
-   ...acc,
-   [nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']: primaryNodeId,
- }),
+ (acc, primaryNodeId) => {
+   acc[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode'] = primaryNodeId;
+   return acc;
+ },
🧰 Tools
🪛 Biome (1.9.4)

[error] 243-243: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🧹 Nitpick comments (2)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-svg-control.component.ts (2)

14-14: Remove the this. notation in the template for readability.

In Angular templates, referencing signal calls with this. is unconventional. You can safely remove this. for brevity since it’s implicit in the template context.

- <fast-svg [name]="this.iconName()" [size]="this.size()" />
+ <fast-svg [name]="iconName()" [size]="size()" />

19-21: Rename the "event" property to avoid confusion.

Using event as a property name can be confusing since it’s commonly associated with the DOM event object. Consider naming it something more descriptive, like resizeAction, to clarify its purpose.

- readonly event = input.required<EventType>();
+ readonly resizeAction = input.required<EventType>();

- (click)="resizeRoadmap.emit(this.event())"
+ (click)="resizeRoadmap.emit(this.resizeAction())"
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8956a43 and 9a78f90.

⛔ Files ignored due to path filters (5)
  • apps/blog/src/assets/icons/circle-center.svg is excluded by !**/*.svg
  • apps/blog/src/assets/icons/zoom-in.svg is excluded by !**/*.svg
  • apps/blog/src/assets/icons/zoom-out.svg is excluded by !**/*.svg
  • apps/blog/src/assets/icons/zoom-reset.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (4)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-svg-control.component.ts (1 hunks)
  • package.json (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html
🧰 Additional context used
🧬 Code Graph Analysis (2)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-svg-control.component.ts (1)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (2)
  • Component (64-290)
  • EventType (25-25)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (4)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-cluster.component.ts (1)
  • Component (5-37)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-svg-control.component.ts (1)
  • Component (6-24)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts (1)
  • Component (5-24)
libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts (1)
  • Component (5-24)
🪛 Biome (1.9.4)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts

[error] 169-169: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 224-224: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 243-243: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (1)
package.json (1)

66-66:

✅ Verification successful

Confirm and verify the security status of the newly added package.

The addition of "svg-pan-zoom": "^3.6.2" appears beneficial for the SVG pan-and-zoom functionality. However, let's make sure there are no known security advisories or deprecated versions.

Please run the following script to check known vulnerabilities or advisories for this NPM package:


🏁 Script executed:

#!/bin/bash
# Description: Query GitHub for security vulnerabilities related to the "svg-pan-zoom" package in the npm ecosystem

gh api graphql -f query='
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "svg-pan-zoom") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}'
'

Length of output: 462


Action: Verify Vulnerability Query for "svg-pan-zoom" Package

The initial security check for "svg-pan-zoom": "^3.6.2" resulted in an empty vulnerabilities list but also produced a quoting error (unexpected EOF while looking for matching '\''), which prevents us from confidently confirming the package’s security status. Please re-run the query with corrected quoting or verify manually to ensure no vulnerabilities or deprecations exist. For instance, you can try this revised script:

  • File: package.json, Line 66: "svg-pan-zoom": "^3.6.2"
  • Next Steps:
    • Re-run the above script (or an equivalent manual check) to verify that no vulnerabilities are reported for this package.
    • Confirm that the query now returns a valid response (ideally with an empty nodes array) without any syntax errors.

Once you’ve confirmed the security status manually, please update the review accordingly.


🏁 Script executed:

#!/bin/bash
# Revised script to query GitHub for known security vulnerabilities for the "svg-pan-zoom" package using a corrected quoting approach.
gh api graphql -f query="$(cat <<'EOF'
{
  securityVulnerabilities(first: 5, ecosystem: NPM, package: "svg-pan-zoom") {
    nodes {
      advisory {
        summary
        severity
        publishedAt
      }
      vulnerableVersionRange
      firstPatchedVersion {
        identifier
      }
    }
  }
}
EOF
)"

Length of output: 473


Action: Confirm Package "svg-pan-zoom" Security Status

The GitHub GraphQL API query executed with the revised script returned an empty vulnerabilities list, indicating that no known security issues were found for "svg-pan-zoom": "^3.6.2". Please ensure that this verification covers your security standards, but based on the query, the package appears secure for use.

  • File: package.json, Line 66: "svg-pan-zoom": "^3.6.2"

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 (8)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (3)

10-12: Template reference variable used before definition could cause rendering issues.

The translateX calculation references layoutEl.clientWidth before the #layoutEl element is fully rendered, which may cause incorrect initial positioning or flickering.

Consider using Angular's AfterViewInit lifecycle hook with ViewChild to calculate the translation after the view is fully rendered:

// In the component class
@ViewChild('layoutEl') layoutEl: ElementRef;
private translationOffset = signal(0);

ngAfterViewInit() {
  // Calculate after view is initialized
  this.translationOffset.set(this.layoutEl.nativeElement.clientWidth / 2);
}

Then update your template:

- style="transform: translateX(calc(50% - {{
-   layoutEl.clientWidth / 2
- }}px))"
+ style="transform: translateX(calc(50% - {{ translationOffset() }}px))"

30-39: ⚠️ Potential issue

SVG marker ID could cause conflicts with multiple component instances.

The current implementation uses a hardcoded ID for the SVG arrowhead marker, which could cause conflicts if multiple instances of the component are rendered on the same page.

<marker
-  id="arrowhead"
+  [attr.id]="'arrowhead-' + layer.parentNode.id"
  markerWidth="6"
  markerHeight="8"
  refX="0"
  refY="4"
  orient="auto"
>
  <polygon points="0 0, 6 4, 0 8" fill="#FDF5FD" />
</marker>

And update the reference:

<line
  [attr.y2]="layerHeightWithGap"
  x1="50"
  y1="0"
  x2="50"
  stroke="white"
  stroke-width="4"
-  marker-end="url(#arrowhead)"
+  [attr.marker-end]="'url(#arrowhead-' + layer.parentNode.id + ')'"
/>

64-66: ⚠️ Potential issue

Layout calculation uses template references before initialization.

The calculation for shift uses allChildNodesEl and leftChildNodesEl template references potentially before they're fully initialized, which could lead to incorrect positioning.

Consider using a reactive approach with ViewChildren and AfterViewInit:

@ViewChildren('allChildNodesEl') allChildNodesElements: QueryList<ElementRef>;
@ViewChildren('leftChildNodesEl') leftChildNodesElements: QueryList<ElementRef>;
private nodeShifts = signal<{[key: string]: number}>({});

ngAfterViewInit() {
  // Calculate shifts after view is initialized
  this.calculateChildNodeShifts();
}

private calculateChildNodeShifts() {
  // Calculate shifts for each layer's child nodes
  // Store in nodeShifts signal for reactive updates
}

Then in your template:

- @let shift =
-   (allChildNodesEl.clientWidth / 2 - leftChildNodesEl.clientWidth || 0) - 32;
+ @let shift = nodeShifts()[layer.parentNode.id] || 0;
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (5)

129-242: Refactor complex roadmap calculation into smaller functions.

The roadmap calculation logic is very complex and contained in a single computed function, making it difficult to understand and maintain.

Break down the logic into smaller, focused functions:

protected readonly roadmapLayers = computed<RoadmapLayer[]>(() => {
  const nodesDto = this.nodesDto();
  if (!nodesDto) return [];
  
  const nodeDtoMap = this.createNodeDtoMap(nodesDto);
  const { layerMap, clusterMap, nodeMap } = this.createMaps(nodesDto, nodeDtoMap);
  
  this.setupClusters(clusterMap, nodeMap, nodeDtoMap);
  const layers = this.setupLayers(layerMap, nodeMap, nodeDtoMap);
  
  return [this.createRootLayer(), ...layers];
});

private createNodeDtoMap(nodesDto: RoadmapNodeDTO[]): { [nodeId: string]: RoadmapNodeDTO } {
  // Extract node map creation logic
  return nodesDto.reduce((acc, node) => {
    acc[node.id] = node;
    return acc;
  }, {} as { [nodeId: string]: RoadmapNodeDTO });
}

private createMaps(nodesDto: RoadmapNodeDTO[], nodeDtoMap: { [nodeId: string]: RoadmapNodeDTO }): {
  layerMap: { [parentNodeId: string]: string[] },
  clusterMap: { [clusterNodeId: string]: string[] },
  nodeMap: { [nodeId: string]: RoadmapNode }
} {
  // Extract maps creation logic
  // ...
}

// Additional methods for other parts of the logic
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 188-188: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 207-207: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


77-77: ⚠️ Potential issue

Likely Angular configuration issue with styleUrl.

Angular expects a styleUrls array instead of styleUrl. This could cause runtime or compilation issues.

- styleUrl: './feature-roadmap.component.scss',
+ styleUrls: ['./feature-roadmap.component.scss'],

132-135: 🛠️ Refactor suggestion

Performance issue with spread operator in reducer.

Using the spread syntax inside .reduce(...) can degrade performance at scale with O(n²) time complexity.

- const nodeDtoMap = nodesDto.reduce(
-   (acc, node) => ({ ...acc, [node.id]: node }),
-   {} as { [nodeId: string]: RoadmapNodeDTO },
- );
+ const nodeDtoMap = nodesDto.reduce(
+   (acc, node) => {
+     acc[node.id] = node;
+     return acc;
+   },
+   {} as { [nodeId: string]: RoadmapNodeDTO },
+ );
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


186-193: 🛠️ Refactor suggestion

Performance issue with spread operator in reducer.

Using the spread syntax inside .reduce(...) can degrade performance at scale with O(n²) time complexity.

- const previousClusterNodeIdToNodeIdMap = childrenNodeIds.reduce(
-   (acc, primaryNodeId) => ({
-     ...acc,
-     [nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']:
-       primaryNodeId,
-   }),
-   {} as { [previousNodeId: string | 'initialNode']: string },
- );
+ const previousClusterNodeIdToNodeIdMap = childrenNodeIds.reduce(
+   (acc, primaryNodeId) => {
+     acc[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode'] = primaryNodeId;
+     return acc;
+   },
+   {} as { [previousNodeId: string | 'initialNode']: string },
+ );
🧰 Tools
🪛 Biome (1.9.4)

[error] 188-188: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


205-212: 🛠️ Refactor suggestion

Performance issue with spread operator in reducer.

Using the spread syntax inside .reduce(...) can degrade performance at scale with O(n²) time complexity.

- const previousLayerNodeIdToNodeIdMap = Object.keys(layerMap).reduce(
-   (acc, primaryNodeId) => ({
-     ...acc,
-     [nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']:
-       primaryNodeId,
-   }),
-   {} as { [previousNodeId: string | 'initialNode']: string },
- );
+ const previousLayerNodeIdToNodeIdMap = Object.keys(layerMap).reduce(
+   (acc, primaryNodeId) => {
+     acc[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode'] = primaryNodeId;
+     return acc;
+   },
+   {} as { [previousNodeId: string | 'initialNode']: string },
+ );
🧰 Tools
🪛 Biome (1.9.4)

[error] 207-207: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🧹 Nitpick comments (3)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (3)

145-148: Performance issue with array spread operator.

Using the spread operator to append elements to an array can be inefficient, especially in a loop or with large datasets.

- clusterMap[parentClusterNodeDto.id] = [
-   ...(clusterMap[parentClusterNodeDto.id] ?? []),
-   nodeDto.id,
- ];
+ if (!clusterMap[parentClusterNodeDto.id]) {
+   clusterMap[parentClusterNodeDto.id] = [];
+ }
+ clusterMap[parentClusterNodeDto.id].push(nodeDto.id);

160-163: Performance issue with array spread operator.

Using the spread operator to append elements to an array can be inefficient, especially in a loop or with large datasets.

- layerMap[nodeDto.parentNodeId] = [
-   ...(layerMap[nodeDto.parentNodeId] ?? []),
-   nodeDto.id,
- ];
+ if (!layerMap[nodeDto.parentNodeId]) {
+   layerMap[nodeDto.parentNodeId] = [];
+ }
+ layerMap[nodeDto.parentNodeId].push(nodeDto.id);

244-255: Improve error handling for SVG Pan Zoom initialization.

The current SVG Pan Zoom initialization lacks error handling, which could lead to silent failures if the library fails to load or initialize properly.

private async initSvgPanZoom() {
+ try {
    const svgPanZoomModule = await import('svg-pan-zoom');
    const svgPanZoom: SvgPanZoom.Instance =
      (svgPanZoomModule as any)['default'] || svgPanZoomModule;

    const svgRoadmap = this._svgRoadmap();
    if (svgRoadmap) {
      this._svgPanZoom = svgPanZoom(svgRoadmap.nativeElement, {
        ...svgPanZoomInitialConfig,
      });
    }
+ } catch (error) {
+   console.error('Failed to initialize SVG Pan Zoom:', error);
+ }
}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9a78f90 and 5e64047.

📒 Files selected for processing (3)
  • apps/blog/src/assets/roadmap-tiles.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/blog/src/assets/roadmap-tiles.json
🧰 Additional context used
🪛 Biome (1.9.4)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts

[error] 133-133: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 188-188: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 207-207: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

@dokolyski dokolyski force-pushed the feat/angular-roadmap branch from 5e64047 to 268958c Compare April 26, 2025 15:30
@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

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

♻️ Duplicate comments (5)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (5)

77-77: ⚠️ Potential issue

Fix Angular property naming for styles.

Angular expects a styleUrls array instead of styleUrl. This could cause runtime or compilation issues.

- styleUrl: './feature-roadmap.component.scss',
+ styleUrls: ['./feature-roadmap.component.scss'],

132-135: 🛠️ Refactor suggestion

Optimize node mapping for better performance.

Using spread syntax inside reducers leads to O(n²) time complexity. Direct property assignment is more efficient.

- const nodeDtoMap = nodesDto.reduce(
-   (acc, node) => ({ ...acc, [node.id]: node }),
-   {} as { [nodeId: string]: RoadmapNodeDTO },
- );
+ const nodeDtoMap = nodesDto.reduce(
+   (acc, node) => {
+     acc[node.id] = node;
+     return acc;
+   },
+   {} as { [nodeId: string]: RoadmapNodeDTO },
+ );
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


186-193: 🛠️ Refactor suggestion

Optimize cluster mapping reducer.

Using spread syntax inside reducers leads to O(n²) time complexity. Direct property assignment is more efficient.

- const previousClusterNodeIdToNodeIdMap = childrenNodeIds.reduce(
-   (acc, primaryNodeId) => ({
-     ...acc,
-     [nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']:
-       primaryNodeId,
-   }),
-   {} as { [previousNodeId: string | 'initialNode']: string },
- );
+ const previousClusterNodeIdToNodeIdMap = childrenNodeIds.reduce(
+   (acc, primaryNodeId) => {
+     acc[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode'] = primaryNodeId;
+     return acc;
+   },
+   {} as { [previousNodeId: string | 'initialNode']: string },
+ );
🧰 Tools
🪛 Biome (1.9.4)

[error] 188-188: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


205-212: 🛠️ Refactor suggestion

Optimize layer mapping reducer.

Using spread syntax inside reducers leads to O(n²) time complexity. Direct property assignment is more efficient.

- const previousLayerNodeIdToNodeIdMap = Object.keys(layerMap).reduce(
-   (acc, primaryNodeId) => ({
-     ...acc,
-     [nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']:
-       primaryNodeId,
-   }),
-   {} as { [previousNodeId: string | 'initialNode']: string },
- );
+ const previousLayerNodeIdToNodeIdMap = Object.keys(layerMap).reduce(
+   (acc, primaryNodeId) => {
+     acc[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode'] = primaryNodeId;
+     return acc;
+   },
+   {} as { [previousNodeId: string | 'initialNode']: string },
+ );
🧰 Tools
🪛 Biome (1.9.4)

[error] 207-207: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


129-242: 🛠️ Refactor suggestion

Refactor complex roadmap calculation into smaller functions.

The roadmap calculation logic is very complex and contained in a single computed function, making it difficult to understand and maintain.

Break down the logic into smaller, focused functions:

protected readonly roadmapLayers = computed<RoadmapLayer[]>(() => {
  const nodesDto = this.nodesDto();
  if (!nodesDto) return [];
  
  const nodeDtoMap = this.createNodeDtoMap(nodesDto);
  const { layerMap, clusterMap, nodeMap } = this.createMaps(nodesDto, nodeDtoMap);
  
  this.setupClusters(clusterMap, nodeMap, nodeDtoMap);
  const layers = this.setupLayers(layerMap, nodeMap, nodeDtoMap);
  
  return [this.createRootLayer(), ...layers];
});

private createNodeDtoMap(nodesDto: RoadmapNodeDTO[]): { [nodeId: string]: RoadmapNodeDTO } {
  return nodesDto.reduce((acc, node) => {
    acc[node.id] = node;
    return acc;
  }, {} as { [nodeId: string]: RoadmapNodeDTO });
}

private createMaps(nodesDto: RoadmapNodeDTO[], nodeDtoMap: { [nodeId: string]: RoadmapNodeDTO }): {
  layerMap: { [parentNodeId: string]: string[] },
  clusterMap: { [clusterNodeId: string]: string[] },
  nodeMap: { [nodeId: string]: RoadmapNode }
} {
  // Extract maps creation logic
}

// Additional methods for other parts of the logic
🧰 Tools
🪛 Biome (1.9.4)

[error] 133-133: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 188-188: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 207-207: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 5e64047 and 268958c.

⛔ Files ignored due to path filters (5)
  • apps/blog/src/assets/icons/circle-center.svg is excluded by !**/*.svg
  • apps/blog/src/assets/icons/zoom-in.svg is excluded by !**/*.svg
  • apps/blog/src/assets/icons/zoom-out.svg is excluded by !**/*.svg
  • apps/blog/src/assets/icons/zoom-reset.svg is excluded by !**/*.svg
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (27)
  • apps/blog/src/assets/roadmap-tiles.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/.eslintrc.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/README.md (1 hunks)
  • libs/blog/roadmap/feature-roadmap/jest.config.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/project.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/index.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.scss (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.spec.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/secondary-arrow.pipe.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/slice.pipes.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/roadmap-hover-border-gradient.scss (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-angular-love-node.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-cluster.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-svg-control.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/test-setup.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/tsconfig.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/tsconfig.lib.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/tsconfig.spec.json (1 hunks)
  • libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts (1 hunks)
  • libs/blog/shell/feature-shell-web/src/lib/roadmap-shell.component.ts (1 hunks)
  • libs/blog/shell/feature-shell-web/src/lib/root-shell.component.ts (1 hunks)
  • package.json (1 hunks)
  • tsconfig.base.json (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • package.json
🚧 Files skipped from review as they are similar to previous changes (25)
  • libs/blog/shell/feature-shell-web/src/lib/root-shell.component.ts
  • tsconfig.base.json
  • libs/blog/roadmap/feature-roadmap/tsconfig.lib.json
  • libs/blog/roadmap/feature-roadmap/README.md
  • libs/blog/roadmap/feature-roadmap/project.json
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.spec.ts
  • libs/blog/roadmap/feature-roadmap/src/index.ts
  • libs/blog/roadmap/feature-roadmap/src/test-setup.ts
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/roadmap-hover-border-gradient.scss
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-angular-love-node.component.ts
  • libs/blog/shell/feature-shell-web/src/lib/blog-shell.routes.ts
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-svg-control.component.ts
  • libs/blog/roadmap/feature-roadmap/src/lib/slice.pipes.ts
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.scss
  • libs/blog/roadmap/feature-roadmap/jest.config.ts
  • libs/blog/roadmap/feature-roadmap/.eslintrc.json
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-cluster.component.ts
  • libs/blog/roadmap/feature-roadmap/tsconfig.json
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-secondary-node.component.ts
  • libs/blog/shell/feature-shell-web/src/lib/roadmap-shell.component.ts
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html
  • libs/blog/roadmap/feature-roadmap/src/lib/secondary-arrow.pipe.ts
  • apps/blog/src/assets/roadmap-tiles.json
  • libs/blog/roadmap/feature-roadmap/tsconfig.spec.json
  • libs/blog/roadmap/feature-roadmap/src/lib/ui/ui-roadmap-primary-node.component.ts
🧰 Additional context used
🪛 Biome (1.9.4)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts

[error] 133-133: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 188-188: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 207-207: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

Comment on lines 160 to 163
layerMap[nodeDto.parentNodeId] = [
...(layerMap[nodeDto.parentNodeId] ?? []),
nodeDto.id,
];
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Optimize array handling in layer map.

Similar to the cluster map, use push instead of spread operator for better performance.

- layerMap[nodeDto.parentNodeId] = [
-   ...(layerMap[nodeDto.parentNodeId] ?? []),
-   nodeDto.id,
- ];
+ if (!layerMap[nodeDto.parentNodeId]) {
+   layerMap[nodeDto.parentNodeId] = [];
+ }
+ layerMap[nodeDto.parentNodeId].push(nodeDto.id);
📝 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
layerMap[nodeDto.parentNodeId] = [
...(layerMap[nodeDto.parentNodeId] ?? []),
nodeDto.id,
];
if (!layerMap[nodeDto.parentNodeId]) {
layerMap[nodeDto.parentNodeId] = [];
}
layerMap[nodeDto.parentNodeId].push(nodeDto.id);

Comment on lines 145 to 148
clusterMap[parentClusterNodeDto.id] = [
...(clusterMap[parentClusterNodeDto.id] ?? []),
nodeDto.id,
];
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Improve array handling in cluster map.

Using spread operator for array concatenation creates unnecessary copies. Use push for better performance.

- clusterMap[parentClusterNodeDto.id] = [
-   ...(clusterMap[parentClusterNodeDto.id] ?? []),
-   nodeDto.id,
- ];
+ if (!clusterMap[parentClusterNodeDto.id]) {
+   clusterMap[parentClusterNodeDto.id] = [];
+ }
+ clusterMap[parentClusterNodeDto.id].push(nodeDto.id);

Comment on lines 86 to 91
private readonly nodesDto = rxResource({
loader: () =>
this._http.get<RoadmapNodeDTO[]>('assets/roadmap-tiles.json', {
responseType: 'json',
}),
}).value.asReadonly();
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for HTTP requests.

The current implementation doesn't handle potential errors in HTTP requests. Add error handling to provide feedback to users.

private readonly nodesDto = rxResource({
  loader: () =>
    this._http.get<RoadmapNodeDTO[]>('assets/roadmap-tiles.json', {
      responseType: 'json',
    }),
+ errorHandler: (error) => {
+   console.error('Failed to load roadmap data:', error);
+   return []; // Provide fallback or empty data
+ },
}).value.asReadonly();
📝 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
private readonly nodesDto = rxResource({
loader: () =>
this._http.get<RoadmapNodeDTO[]>('assets/roadmap-tiles.json', {
responseType: 'json',
}),
}).value.asReadonly();
private readonly nodesDto = rxResource({
loader: () =>
this._http.get<RoadmapNodeDTO[]>('assets/roadmap-tiles.json', {
responseType: 'json',
}),
errorHandler: (error) => {
console.error('Failed to load roadmap data:', error);
return []; // Provide fallback or empty data
},
}).value.asReadonly();

Comment on lines 244 to 255
private async initSvgPanZoom() {
const svgPanZoomModule = await import('svg-pan-zoom');
const svgPanZoom: SvgPanZoom.Instance =
(svgPanZoomModule as any)['default'] || svgPanZoomModule;

const svgRoadmap = this._svgRoadmap();
if (svgRoadmap) {
this._svgPanZoom = svgPanZoom(svgRoadmap.nativeElement, {
...svgPanZoomInitialConfig,
});
}
}
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add error handling for SVG Pan-Zoom initialization.

The dynamic import and initialization of SVG Pan-Zoom lacks error handling, which could lead to unexpected behavior if the library fails to load or initialize.

private async initSvgPanZoom() {
-  const svgPanZoomModule = await import('svg-pan-zoom');
-  const svgPanZoom: SvgPanZoom.Instance =
-    (svgPanZoomModule as any)['default'] || svgPanZoomModule;
-
-  const svgRoadmap = this._svgRoadmap();
-  if (svgRoadmap) {
-    this._svgPanZoom = svgPanZoom(svgRoadmap.nativeElement, {
-      ...svgPanZoomInitialConfig,
-    });
-  }
+  try {
+    const svgPanZoomModule = await import('svg-pan-zoom');
+    const svgPanZoom: SvgPanZoom.Instance =
+      'default' in svgPanZoomModule ? svgPanZoomModule.default : svgPanZoomModule;
+
+    const svgRoadmap = this._svgRoadmap();
+    if (svgRoadmap) {
+      this._svgPanZoom = svgPanZoom(svgRoadmap.nativeElement, {
+        ...svgPanZoomInitialConfig,
+      });
+    }
+  } catch (error) {
+    console.error('Failed to initialize SVG Pan-Zoom:', error);
+    // Consider providing fallback behavior or notifying the user
+  }
}

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://42df8ac9-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://6f93aaf6.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

Extract additional UI components, introduce UI libs,
update roadmap layering logic - use css-based connectors
(instead of svg-based) and simplify styling.
@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

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 (8)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (8)

91-94: 🛠️ Refactor suggestion

Optimize reducer to avoid O(n²) complexity.

Using the spread operator in reducers leads to O(n²) time complexity as it creates a new object on each iteration.

- const nodeDtoMap = roadmapNodesDto.reduce(
-   (acc, node) => ({ ...acc, [node.id]: node }),
-   {} as { [nodeId: string]: RoadmapNodeDTO },
- );
+ const nodeDtoMap = roadmapNodesDto.reduce(
+   (acc, node) => {
+     acc[node.id] = node;
+     return acc;
+   },
+   {} as { [nodeId: string]: RoadmapNodeDTO },
+ );
🧰 Tools
🪛 Biome (1.9.4)

[error] 92-92: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


104-107: 🛠️ Refactor suggestion

Optimize array handling in cluster map.

Using the spread operator for array concatenation creates unnecessary copies and can lead to performance issues.

- clusterMap[parentClusterNodeDto.id] = [
-   ...(clusterMap[parentClusterNodeDto.id] ?? []),
-   nodeDto.id,
- ];
+ if (!clusterMap[parentClusterNodeDto.id]) {
+   clusterMap[parentClusterNodeDto.id] = [];
+ }
+ clusterMap[parentClusterNodeDto.id].push(nodeDto.id);

119-122: 🛠️ Refactor suggestion

Optimize array handling in layer map.

Similar to the cluster map, use push instead of spread operator for better performance.

- layerMap[nodeDto.parentNodeId] = [
-   ...(layerMap[nodeDto.parentNodeId] ?? []),
-   nodeDto.id,
- ];
+ if (!layerMap[nodeDto.parentNodeId]) {
+   layerMap[nodeDto.parentNodeId] = [];
+ }
+ layerMap[nodeDto.parentNodeId].push(nodeDto.id);

145-152: 🛠️ Refactor suggestion

Optimize the cluster creation reducer.

Using the spread syntax inside .reduce(...) can degrade performance at scale.

- const previousClusterNodeIdToNodeIdMap = childrenNodeIds.reduce(
-   (acc, primaryNodeId) => ({
-     ...acc,
-     [nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']:
-       primaryNodeId,
-   }),
-   {} as { [previousNodeId: string | 'initialNode']: string },
- );
+ const previousClusterNodeIdToNodeIdMap = childrenNodeIds.reduce(
+   (acc, primaryNodeId) => {
+     acc[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode'] = primaryNodeId;
+     return acc;
+   },
+   {} as { [previousNodeId: string | 'initialNode']: string },
+ );
🧰 Tools
🪛 Biome (1.9.4)

[error] 147-147: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


166-173: 🛠️ Refactor suggestion

Minimize object creation overhead in layer mapping.

Same as previous reducers, using spread in reducers causes O(n²) complexity.

- const previousLayerNodeIdToNodeIdMap = Object.keys(layerMap).reduce(
-   (acc, primaryNodeId) => ({
-     ...acc,
-     [nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode']:
-       primaryNodeId,
-   }),
-   {} as { [previousNodeId: string | 'initialNode']: string },
- );
+ const previousLayerNodeIdToNodeIdMap = Object.keys(layerMap).reduce(
+   (acc, primaryNodeId) => {
+     acc[nodeDtoMap[primaryNodeId].previousNodeId || 'initialNode'] = primaryNodeId;
+     return acc;
+   },
+   {} as { [previousNodeId: string | 'initialNode']: string },
+ );
🧰 Tools
🪛 Biome (1.9.4)

[error] 168-168: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


59-64: 🛠️ Refactor suggestion

Add error handling for HTTP requests.

The current implementation doesn't handle potential errors in HTTP requests. This could lead to unexpected behavior if the request fails.

private readonly nodesDto = rxResource({
  loader: () =>
    this._http.get<RoadmapNodeDTO[]>('assets/roadmap-tiles.json', {
      responseType: 'json',
    }),
+ errorHandler: (error) => {
+   console.error('Failed to load roadmap data:', error);
+   return []; // Provide fallback or empty data
+ },
}).value.asReadonly();

200-210: 🛠️ Refactor suggestion

Add error handling for SVG Pan-Zoom initialization.

The dynamic import and initialization of SVG Pan-Zoom lacks error handling, which could lead to unexpected behavior if the library fails to load.

private async initSvgPanZoom() {
-  const svgPanZoomModule = await import('svg-pan-zoom');
-  const svgPanZoom: SvgPanZoom.Instance =
-    'default' in svgPanZoomModule
-      ? (svgPanZoomModule.default as SvgPanZoom.Instance)
-      : svgPanZoomModule;
-
-  this._svgPanZoom = svgPanZoom(this._svgRoadmap().nativeElement, {
-    ...svgPanZoomInitialConfig,
-  });
+  try {
+    const svgPanZoomModule = await import('svg-pan-zoom');
+    const svgPanZoom: SvgPanZoom.Instance =
+      'default' in svgPanZoomModule
+        ? (svgPanZoomModule.default as SvgPanZoom.Instance)
+        : svgPanZoomModule;
+
+    this._svgPanZoom = svgPanZoom(this._svgRoadmap().nativeElement, {
+      ...svgPanZoomInitialConfig,
+    });
+  } catch (error) {
+    console.error('Failed to initialize SVG Pan-Zoom:', error);
+    // Consider providing fallback behavior or notifying the user
+  }
}

83-198: 🛠️ Refactor suggestion

Complex roadmap calculation logic needs refactoring.

The buildRoadmapLayers method is quite complex and contains multiple responsibilities. As noted in the TODO comment, this should be extracted into smaller, more focused functions.

Consider refactoring into separate methods such as:

- // TODO - maybe extract to util function and rewrite this to be more readable
- private buildRoadmapLayers(
-   roadmapNodesDto: RoadmapNodeDTO[] | undefined,
- ): RoadmapLayer[] {
-   // ... complex implementation
- }
+ private buildRoadmapLayers(
+   roadmapNodesDto: RoadmapNodeDTO[] | undefined,
+ ): RoadmapLayer[] {
+   if (!roadmapNodesDto) {
+     return [];
+   }
+   
+   const nodeDtoMap = this.createNodeDtoMap(roadmapNodesDto);
+   const { layerMap, clusterMap, nodeMap } = this.createMaps(roadmapNodesDto, nodeDtoMap);
+   this.setupClusters(clusterMap, nodeMap, nodeDtoMap);
+   const layers = this.setupLayers(layerMap, nodeMap, nodeDtoMap);
+   
+   return [this.createRootLayer(), ...layers];
+ }
+ 
+ private createNodeDtoMap(nodes: RoadmapNodeDTO[]): { [nodeId: string]: RoadmapNodeDTO } {
+   const map: { [nodeId: string]: RoadmapNodeDTO } = {};
+   for (const node of nodes) {
+     map[node.id] = node;
+   }
+   return map;
+ }
+ 
+ // Additional helper methods for maps creation, cluster setup, layer setup, etc.
🧰 Tools
🪛 Biome (1.9.4)

[error] 92-92: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 147-147: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 168-168: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🧹 Nitpick comments (4)
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-cluster/roadmap-cluster.component.ts (1)

1-37: Well-structured component with OnPush change detection.

The component is well-implemented with a clear responsibility for rendering a cluster of roadmap nodes. The use of OnPush change detection is appropriate for performance optimization.

Consider extracting hard-coded color values.

The template contains hard-coded color values like #FDF5FD and #100F15 that would be better maintained as CSS variables.

- class="relative z-10 m-[-2px] rounded-lg border-2 border-[#FDF5FD] bg-[--secondary-color] px-6 py-4"
+ class="relative z-10 m-[-2px] rounded-lg border-2 border-[--border-color] bg-[--secondary-color] px-6 py-4"

Consider adding semantic HTML elements and ARIA attributes.

For improved accessibility, consider using semantic HTML elements and adding appropriate ARIA attributes to clarify the component's role in the roadmap visualization.

libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.ts (2)

12-12: Remove extra space in class attribute.

There's an extra space between "rounded-lg" and "bg-gradient-to-r" in the class attribute.

-        class="relative z-10 m-[4px] rounded-lg  bg-gradient-to-r from-[--secondary-color] to-[--gradient-color] px-6 py-4"
+        class="relative z-10 m-[4px] rounded-lg bg-gradient-to-r from-[--secondary-color] to-[--gradient-color] px-6 py-4"

1-23: Maintain consistency in styling approach across node components.

Unlike the RoadmapPrimaryNodeComponent, this component doesn't use host bindings for styling. Consider adopting a consistent styling approach across all node components.

Extract hard-coded color values to CSS variables.

For better maintainability and theming support, extract hard-coded color values like #FDF5FD to CSS variables.

libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1)

1-23: Ensure consistent styling across node components.

This component uses a margin of 2px, while the primary and angular-love nodes use 4px. Similarly, the text size here is 20px compared to 24px in other components. Consider standardizing these values or documenting the intentional differences.

Extract hard-coded color values to CSS variables.

For better maintainability and theming support, extract hard-coded color values like #FDF5FD to CSS variables.

Consider a shared base class for roadmap nodes.

The roadmap node components share similar templates and functionality. Consider creating a base component class to reduce code duplication across the different node types.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
Cache: Disabled due to data retention organization setting
Knowledge Base: Disabled due to data retention organization setting

📥 Commits

Reviewing files that changed from the base of the PR and between 268958c and a86ce37.

📒 Files selected for processing (49)
  • libs/blog/roadmap/feature-roadmap/README.md (1 hunks)
  • libs/blog/roadmap/feature-roadmap/jest.config.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/project.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.scss (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/.eslintrc.json (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/README.md (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/jest.config.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/project.json (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/index.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.scss (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-cluster/roadmap-cluster.component.scss (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-cluster/roadmap-cluster.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-primary-node/roadmap-primary-node.component.scss (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-primary-node/roadmap-primary-node.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.scss (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/style/_roadmap-hover-border-gradient.scss (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/test-setup.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/tsconfig.json (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/tsconfig.lib.json (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/tsconfig.spec.json (1 hunks)
  • libs/blog/roadmap/ui-roadmap/.eslintrc.json (1 hunks)
  • libs/blog/roadmap/ui-roadmap/README.md (1 hunks)
  • libs/blog/roadmap/ui-roadmap/jest.config.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/project.json (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/index.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/connected-node/connected-node.component.html (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/connected-node/connected-node.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/node-connector-line/node-connector-line.component.scss (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/node-connector-line/node-connector-line.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/left-slice.pipe.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/right-slice.pipe.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.html (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.scss (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-svg-controls/roadmap-svg-controls.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/vertical-connector-arrow/vertical-connector-arrow.component.scss (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/vertical-connector-arrow/vertical-connector-arrow.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/test-setup.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/tsconfig.json (1 hunks)
  • libs/blog/roadmap/ui-roadmap/tsconfig.lib.json (1 hunks)
  • libs/blog/roadmap/ui-roadmap/tsconfig.spec.json (1 hunks)
  • libs/blog/shell/feature-shell-web/src/lib/roadmap-shell.component.ts (1 hunks)
  • nx.json (1 hunks)
  • tsconfig.base.json (1 hunks)
✅ Files skipped from review due to trivial changes (31)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/vertical-connector-arrow/vertical-connector-arrow.component.ts
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.scss
  • libs/blog/roadmap/ui-roadmap/src/index.ts
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.scss
  • libs/blog/roadmap/ui-roadmap/README.md
  • libs/blog/roadmap/ui-roadmap-node/README.md
  • libs/blog/roadmap/ui-roadmap-node/src/test-setup.ts
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.scss
  • libs/blog/roadmap/ui-roadmap/tsconfig.spec.json
  • libs/blog/roadmap/ui-roadmap-node/tsconfig.json
  • libs/blog/roadmap/ui-roadmap/src/lib/components/connected-node/connected-node.component.html
  • libs/blog/roadmap/ui-roadmap/src/test-setup.ts
  • libs/blog/roadmap/ui-roadmap/tsconfig.lib.json
  • libs/blog/roadmap/ui-roadmap/src/lib/components/vertical-connector-arrow/vertical-connector-arrow.component.scss
  • libs/blog/roadmap/ui-roadmap-node/tsconfig.lib.json
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-primary-node/roadmap-primary-node.component.scss
  • libs/blog/roadmap/ui-roadmap-node/.eslintrc.json
  • nx.json
  • libs/blog/roadmap/ui-roadmap-node/jest.config.ts
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-cluster/roadmap-cluster.component.scss
  • libs/blog/roadmap/ui-roadmap-node/tsconfig.spec.json
  • libs/blog/roadmap/ui-roadmap/project.json
  • libs/blog/roadmap/ui-roadmap/tsconfig.json
  • libs/blog/roadmap/ui-roadmap-node/project.json
  • libs/blog/roadmap/ui-roadmap/src/lib/components/node-connector-line/node-connector-line.component.scss
  • libs/blog/roadmap/ui-roadmap/.eslintrc.json
  • libs/blog/roadmap/ui-roadmap-node/src/index.ts
  • libs/blog/roadmap/ui-roadmap-node/src/lib/style/_roadmap-hover-border-gradient.scss
  • libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts
  • libs/blog/roadmap/ui-roadmap/jest.config.ts
  • tsconfig.base.json
🚧 Files skipped from review as they are similar to previous changes (6)
  • libs/blog/roadmap/feature-roadmap/jest.config.ts
  • libs/blog/roadmap/feature-roadmap/project.json
  • libs/blog/roadmap/feature-roadmap/README.md
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.scss
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html
  • libs/blog/shell/feature-shell-web/src/lib/roadmap-shell.component.ts
🧰 Additional context used
🧬 Code Graph Analysis (3)
libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/right-slice.pipe.ts (1)
libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/left-slice.pipe.ts (1)
  • Pipe (3-11)
libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/left-slice.pipe.ts (1)
libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/right-slice.pipe.ts (1)
  • Pipe (3-11)
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-primary-node/roadmap-primary-node.component.ts (5)
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.ts (1)
  • Component (5-23)
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1)
  • Component (5-23)
libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.ts (1)
  • Component (24-48)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1)
  • Component (42-211)
libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (1)
  • RoadmapNode (15-15)
🪛 Biome (1.9.4)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts

[error] 92-92: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 147-147: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)


[error] 168-168: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🔇 Additional comments (7)
libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/right-slice.pipe.ts (1)

1-11: Clean and well-implemented pipe!

This pipe correctly extracts the right half of an array by calculating the midpoint and returning the elements from that point to the end. The use of generics ensures type safety, and the implementation follows Angular's best practices.

libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/left-slice.pipe.ts (1)

1-11: Clean implementation complementing the right-slice pipe!

This pipe correctly extracts the left half of an array, using the same midpoint calculation as the RightSlicePipe. The consistent use of Math.ceil() ensures that for odd-length arrays, the left half gets one more element than the right half, which maintains consistent behavior when these pipes are used together.

libs/blog/roadmap/ui-roadmap/src/lib/components/node-connector-line/node-connector-line.component.ts (1)

1-23: Excellent use of modern Angular patterns!

This component uses several modern Angular best practices:

  • Empty template with host bindings for CSS class application
  • OnPush change detection for better performance
  • The new input() API with default values
  • Clear type definition with a dedicated type alias

The component serves as a visual connector with different styles based on its position, efficiently implemented without unnecessary DOM elements.

libs/blog/roadmap/ui-roadmap/src/lib/components/connected-node/connected-node.component.ts (1)

1-34: Well-designed component with good use of Angular signals!

This component effectively uses:

  • OnPush change detection for performance
  • Modern input() API with default values
  • The computed() signal for deriving the connector type
  • Clear, readable conditionals

The component intelligently determines the appropriate connector type based on the node's position in the sequence, creating a clean visual hierarchy for the roadmap.

libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.ts (1)

1-48: Well-structured component using modern Angular features.

This component is well-organized, using Angular's modern signals-based input API, OnPush change detection, and standalone component architecture. The clear interface definition and component organization follow good practices.

libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.html (1)

1-51: Good use of modern Angular template syntax and layout structure.

The template effectively uses Angular's new control flow syntax (@if, @for) and properly utilizes tracking for performance optimization. The layout is structured well with clear separation between parent node, layer connector, and child nodes.

libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-svg-controls/roadmap-svg-controls.component.ts (1)

1-55: Well-implemented controls component with type safety.

The component is focused and well-organized with clear typing for events and controls. Good use of modern Angular features including output signals and the @for loop syntax.

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://b54bc930-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://64a799d3.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://01e7108e-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://c9a678d6.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

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

♻️ Duplicate comments (5)
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.ts (1)

29-29: Fix the incorrect stylesheet property.

The component uses styleUrl (singular) which is not a valid Angular metadata property. This will cause the stylesheet to be ignored and break styling in production builds.

-  styleUrl: 'roadmap-angular-love-node.component.scss',
+  styleUrls: ['roadmap-angular-love-node.component.scss'],
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1)

29-29: Fix the incorrect stylesheet property.

The component uses styleUrl (singular) which is not valid Angular metadata. Use styleUrls array instead.

-  styleUrl: 'roadmap-secondary-node.component.scss',
+  styleUrls: ['roadmap-secondary-node.component.scss'],
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (3)

134-147: Fix zoom calculation to use relative multipliers.

The zoom calculation incorrectly passes an absolute scale value to smoothZoom instead of a relative multiplier. The smoothZoom method expects a factor like 0.8 (zoom out) or 1.2 (zoom in).

Apply this fix to use relative zoom factors:

    if (event === 'decrement') {
      const currentScale = currentTransform.scale;
-     const multiplier =
-       Math.round(currentScale * 2) / 2 - this._scaleMultiplier;
+     const multiplier = 1 - this._scaleMultiplier; // 0.5 for 50% zoom out

      panZoomInstance.smoothZoom(centerX, centerY, multiplier);
    }

    if (event === 'increment') {
      const currentScale = currentTransform.scale;
-     const multiplier =
-       Math.round(currentScale * 2) / 2 + this._scaleMultiplier;
+     const multiplier = 1 + this._scaleMultiplier; // 1.5 for 50% zoom in

      panZoomInstance.smoothZoom(centerX, centerY, multiplier);
    }

55-184: Add component cleanup to prevent memory leaks.

The panzoom instance creates global event listeners that must be disposed when the component is destroyed.

Implement the OnDestroy lifecycle hook:

-export class FeatureRoadmapComponent {
+export class FeatureRoadmapComponent implements OnDestroy {

Add the cleanup method at the end of the class:

  ngOnDestroy(): void {
    this.panZoomInstance()?.dispose();
  }

Don't forget to import OnDestroy from @angular/core.


178-183: Add error handling for panzoom initialization.

The panzoom initialization could fail and should include error handling to prevent runtime errors.

Wrap the initialization in a try-catch block:

  private initPanZoom() {
+   try {
      const roadmapWrapper = this.roadmapWrapper.nativeElement;
      this.panZoomInstance.set(
        panzoom(roadmapWrapper, this._panZoomInitialConfig),
      );
+   } catch (error) {
+     console.error('Failed to initialize pan-zoom:', error);
+     // Consider providing fallback behavior or user notification
+   }
  }
🧹 Nitpick comments (3)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1)

2-2: Address the TODO comment for better user experience.

The TODO comment indicates missing error handling for cases where roadmap data fails to load or is empty. Consider implementing error states and empty states to provide better user feedback.

Would you like me to help generate error handling templates for loading, error, and empty states?

libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1)

6-6: Remove unused import.

The output import is not used in this component and should be removed to keep the code clean.

  import {
    ChangeDetectionStrategy,
    Component,
    inject,
    input,
-   output,
  } from '@angular/core';
libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (1)

18-103: Consider refactoring complex logic into smaller, focused functions.

The buildRoadmapLayersFromDto function handles multiple responsibilities: building maps, setting up clusters, and creating layers. This makes it difficult to understand and maintain.

Consider breaking it down:

export function buildRoadmapLayersFromDto(
  roadmapNodesDto: RoadmapNodeDTO[] | undefined,
): RoadmapLayer[] {
  if (!roadmapNodesDto) {
    return [];
  }

  const allNodeDtosMap = createNodeDtoMap(roadmapNodesDto);
  const { layerChildNodeIdsMap, clusterChildNodeIdsMap, allNodesMap } = 
    buildInitialMaps(roadmapNodesDto, allNodeDtosMap);
  
  setupClusters(clusterChildNodeIdsMap, allNodesMap, allNodeDtosMap);
  const layers = createLayers(layerChildNodeIdsMap, allNodesMap, allNodeDtosMap);

  return [roadmapTitleLayer, ...layers];
}

function createNodeDtoMap(nodes: RoadmapNodeDTO[]): { [id: string]: RoadmapNodeDTO } {
  // Extract node map creation logic
}

function buildInitialMaps(/* params */) {
  // Extract initial maps building logic
}

// Additional helper functions...
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 463da2d and 5061317.

📒 Files selected for processing (27)
  • libs/blog-contracts/roadmap/src/lib/angular-love-node.type.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/base-node.type.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/content-slug.type.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/creator.type.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/regular-node.type.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1 hunks)
  • libs/blog/roadmap/data-access/src/lib/infractructure/roadmap.service.ts (1 hunks)
  • libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (1 hunks)
  • libs/blog/roadmap/data-access/src/lib/state/roadmap-store.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/roadmap-bottomsheet-menager.service.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-creators/roadmap-bottomsheet-creators.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-regular-content/roadmap-bottomsheet-regular-content.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-cluster/roadmap-cluster.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-primary-node/roadmap-primary-node.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-svg-controls/roadmap-svg-controls.component.ts (1 hunks)
  • libs/blog/roadmap/utils/src/index.ts (1 hunks)
  • libs/blog/roadmap/utils/src/lib/models/index.ts (1 hunks)
  • libs/blog/roadmap/utils/src/lib/services/index.ts (1 hunks)
  • libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts (1 hunks)
✅ Files skipped from review due to trivial changes (6)
  • libs/blog-contracts/roadmap/src/lib/creator.type.ts
  • libs/blog-contracts/roadmap/src/lib/content-slug.type.ts
  • libs/blog-contracts/roadmap/src/lib/regular-node.type.ts
  • libs/blog-contracts/roadmap/src/lib/base-node.type.ts
  • libs/blog/roadmap/feature-roadmap/src/lib/roadmap-bottomsheet-menager.service.ts
  • libs/blog-contracts/roadmap/src/lib/angular-love-node.type.ts
🚧 Files skipped from review as they are similar to previous changes (8)
  • libs/blog/roadmap/utils/src/lib/services/index.ts
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-regular-content/roadmap-bottomsheet-regular-content.component.ts
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.ts
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-creators/roadmap-bottomsheet-creators.component.ts
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-cluster/roadmap-cluster.component.ts
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-primary-node/roadmap-primary-node.component.ts
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-svg-controls/roadmap-svg-controls.component.ts
  • libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.ts
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
libs/blog/roadmap/data-access/src/lib/state/roadmap-store.ts (1)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use signal APIs (input(), output(), model()) instead of decorators (@Input, @Output) for component inputs and outputs.
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.ts (4)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use signal APIs (input(), output(), model()) instead of decorators (@Input, @Output) for component inputs and outputs.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T15:58:21.460Z
Learning: In the angular-love project, prefer using inline Tailwind classes over extracting them into separate CSS files, as it provides better visibility and maintains consistency with the codebase.
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (3)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T15:58:21.460Z
Learning: In the angular-love project, prefer using inline Tailwind classes over extracting them into separate CSS files, as it provides better visibility and maintains consistency with the codebase.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (3)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use signal APIs (input(), output(), model()) instead of decorators (@Input, @Output) for component inputs and outputs.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (3)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T15:58:21.460Z
Learning: In the angular-love project, prefer using inline Tailwind classes over extracting them into separate CSS files, as it provides better visibility and maintains consistency with the codebase.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use signal APIs (input(), output(), model()) instead of decorators (@Input, @Output) for component inputs and outputs.
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (3)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use signal APIs (input(), output(), model()) instead of decorators (@Input, @Output) for component inputs and outputs.
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (2)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
🧬 Code Graph Analysis (6)
libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (2)
libs/blog-contracts/roadmap/src/lib/angular-love-node.type.ts (1)
  • AngularLoveNodeDTO (4-8)
libs/blog-contracts/roadmap/src/lib/regular-node.type.ts (1)
  • RegularNodeDTO (4-8)
libs/blog/roadmap/data-access/src/lib/infractructure/roadmap.service.ts (3)
libs/blog/roadmap/feature-roadmap/src/lib/roadmap-bottomsheet-menager.service.ts (1)
  • Injectable (7-16)
libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts (1)
  • Injectable (4-14)
libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1)
  • RoadmapNodeDTO (4-4)
libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts (2)
libs/blog/roadmap/data-access/src/lib/infractructure/roadmap.service.ts (1)
  • Injectable (60-81)
libs/blog/roadmap/feature-roadmap/src/lib/roadmap-bottomsheet-menager.service.ts (1)
  • Injectable (7-16)
libs/blog/roadmap/data-access/src/lib/state/roadmap-store.ts (1)
libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (1)
  • buildRoadmapLayersFromDto (18-103)
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.ts (5)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1)
  • Component (44-184)
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-primary-node/roadmap-primary-node.component.ts (1)
  • Component (13-40)
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1)
  • Component (13-41)
libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.ts (1)
  • Component (26-46)
libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (1)
  • RoadmapStandardNode (6-8)
libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (3)
libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.ts (1)
  • RoadmapLayer (21-24)
libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1)
  • RoadmapNodeDTO (4-4)
libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (3)
  • RoadmapNode (15-15)
  • RoadmapClusterNode (10-13)
  • RoadmapStandardNode (6-8)
🪛 Biome (1.9.4)
libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts

[error] 26-26: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🪛 HTMLHint (1.5.0)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html

[error] 1-1: Doctype must be declared before any non-comment content.

(doctype-first)

libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html

[error] 1-1: Doctype must be declared before any non-comment content.

(doctype-first)

🔇 Additional comments (8)
libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1)

1-4: Well-structured discriminated union type.

The union type implementation is clean and follows TypeScript best practices. The discriminated union with nodeType property enables type-safe handling of different roadmap node variants.

libs/blog/roadmap/utils/src/index.ts (1)

1-2: Clean barrel export implementation.

Follows standard Angular library patterns for exposing public API through centralized exports.

libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts (1)

4-14: Well-implemented notification service.

The service correctly implements the Subject/Observable pattern for cross-component communication. The singleton scope and clean API design make it suitable for coordinating bottom sheet interactions across the roadmap feature.

libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.html (1)

4-8: LGTM! Modern Angular control flow implementation.

The template correctly uses the modern @for syntax with proper track function and $last context variable, which aligns with the project's preference for modern Angular patterns.

libs/blog/roadmap/data-access/src/lib/state/roadmap-store.ts (1)

29-64: Excellent NgRx Signals store implementation.

The store follows best practices with:

  • Proper state management with loading indicators
  • Computed derived state for roadmap layers
  • Error handling using tapResponse
  • Clean method implementations using rxMethod

The implementation is well-structured and maintainable.

libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.ts (1)

35-40: LGTM! Modern Angular patterns correctly implemented.

The component properly uses:

  • Signal-based input with input.required<RoadmapStandardNode>()
  • Dependency injection with inject() function
  • OnPush change detection strategy

This aligns with the project's preference for modern Angular APIs.

libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (1)

1-57: Excellent modern Angular template implementation.

The template effectively uses:

  • Modern @if control flow syntax instead of structural directives
  • Proper conditional rendering for different node types
  • Event propagation prevention for modal interactions
  • Clean, readable structure with appropriate component composition

This aligns perfectly with the project's preference for modern Angular patterns.

libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1)

48-48: styleUrl usage in component decorator is valid

Angular v17+ officially supports a singular styleUrl for a single stylesheet, so the existing line in feature-roadmap.component.ts is correct and requires no change.

Comment on lines +1 to +6
// export * from './base-node.type';
// export * from './content-slug.type';
// export * from './angular-love-node.type';
// export * from './node-details.type';
// export * from './regular-node.type';
// export * from './creator.type';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Uncomment exports or remove placeholder code.

All model exports are commented out, making these types inaccessible to consumers. This suggests incomplete implementation.

Either activate the exports if the types are ready:

-// export * from './base-node.type';
-// export * from './content-slug.type';
-// export * from './angular-love-node.type';
-// export * from './node-details.type';
-// export * from './regular-node.type';
-// export * from './creator.type';
+export * from './base-node.type';
+export * from './content-slug.type';
+export * from './angular-love-node.type';
+export * from './node-details.type';
+export * from './regular-node.type';
+export * from './creator.type';

Or remove the file if the exports aren't needed yet.

📝 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
// export * from './base-node.type';
// export * from './content-slug.type';
// export * from './angular-love-node.type';
// export * from './node-details.type';
// export * from './regular-node.type';
// export * from './creator.type';
export * from './base-node.type';
export * from './content-slug.type';
export * from './angular-love-node.type';
export * from './node-details.type';
export * from './regular-node.type';
export * from './creator.type';
🤖 Prompt for AI Agents
In libs/blog/roadmap/utils/src/lib/models/index.ts lines 1 to 6, all export
statements are commented out, preventing these types from being accessible. To
fix this, either uncomment all the export lines if the types are implemented and
ready for use, or if these exports are not needed yet, remove the entire file to
avoid placeholder code.

@@ -0,0 +1,81 @@
import { HttpClient } from '@angular/common/http';
import { inject, Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Clean up unused imports and dependencies.

Remove unused imports and injected dependencies to reduce maintenance overhead.

-import { Observable, of } from 'rxjs';
+import { Observable } from 'rxjs';

-import { ConfigService } from '@angular-love/shared/config';

export class RoadmapService {
  private readonly _http = inject(HttpClient);
-  private readonly _apiBaseUrl = inject(ConfigService).get('apiBaseUrl');

Also applies to: 6-6, 63-63

🤖 Prompt for AI Agents
In libs/blog/roadmap/data-access/src/lib/infractructure/roadmap.service.ts at
lines 3, 6, and 63, there are unused imports and injected dependencies. Identify
and remove any imports and constructor-injected dependencies that are not used
anywhere in the file to clean up the code and reduce maintenance overhead.

Comment on lines +71 to +80
// getNodeDetails(id: string): Observable<NodeDetails> {
// console.log(id);
// if (id === 'angular-love') {
// return of(angularLoveNodeMock);
// } else if (id === 'lifecycle') {
// return of(regularNodeMockNoContent);
// } else {
// return of(regularNodeMock);
// }
// }
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove commented-out method.

The commented getNodeDetails method serves no purpose and should be removed to keep the codebase clean.

-  // getNodeDetails(id: string): Observable<NodeDetails> {
-  //   console.log(id);
-  //   if (id === 'angular-love') {
-  //     return of(angularLoveNodeMock);
-  //   } else if (id === 'lifecycle') {
-  //     return of(regularNodeMockNoContent);
-  //   } else {
-  //     return of(regularNodeMock);
-  //   }
-  // }
📝 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
// getNodeDetails(id: string): Observable<NodeDetails> {
// console.log(id);
// if (id === 'angular-love') {
// return of(angularLoveNodeMock);
// } else if (id === 'lifecycle') {
// return of(regularNodeMockNoContent);
// } else {
// return of(regularNodeMock);
// }
// }
🤖 Prompt for AI Agents
In libs/blog/roadmap/data-access/src/lib/infractructure/roadmap.service.ts
between lines 71 and 80, remove the entire commented-out getNodeDetails method
since it is unused and keeping it commented clutters the codebase.

@@ -0,0 +1,81 @@
import { HttpClient } from '@angular/common/http';
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Directory name contains typo.

The directory name "infractructure" should be "infrastructure". This affects the entire directory structure and import paths.

This is a structural issue that affects multiple files and cannot be fixed within this file alone. Verify if this is intentional or needs to be addressed across the codebase:


🏁 Script executed:

#!/bin/bash
# Check for consistency of the directory naming across the project
fd -t d "infractructure"
fd -t d "infrastructure"
rg -l "infractructure" --type typescript

Length of output: 454


Rename typo in Roadmap data-access directory

The folder libs/blog/roadmap/data-access/src/lib/infractructure/ is misspelled and should be renamed to infrastructure to match the other modules. Failing to do so will break imports and impede consistency across the codebase.

Please update:

  • Rename directory
    • from libs/blog/roadmap/data-access/src/lib/infractructure/
    • to libs/blog/roadmap/data-access/src/lib/infrastructure/
  • Update all import paths in the Roadmap data-access module accordingly:
    - import { RoadmapService } from './infractructure/roadmap.service';
    + import { RoadmapService } from './infrastructure/roadmap.service';
  • Verify there are no lingering references to “infractructure” in the module after renaming.

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In libs/blog/roadmap/data-access/src/lib/infractructure/roadmap.service.ts at
line 1, the directory name "infractructure" is misspelled. Rename the folder
from "infractructure" to "infrastructure" and update all import statements in
the Roadmap data-access module to reflect this corrected path. Ensure no
references to the old misspelled directory remain after the change.

Comment on lines +8 to +58
// const angularLoveNodeMock: AngularLoveNode = {
// id: 'angular-love',
// title: 'Angular.Love Roadmap Introduction',
// nodeType: 'angular-love',
// description:
// 'The Angular Roadmap on Angular.Love is your go-to guide for learning Angular the right way. It breaks down key concepts, best practices, and useful resources in a clear, structured way. Plus, it’s packed with valuable materials and constantly updated to keep up with the latest trends.',
// additionalDescription: `Before diving into Angular, it's essential to have a solid understanding of the following concepts: HTML & CSS – Structure and styling of web pages, including Flexbox, Grid, and responsiveness; JavaScript (ES6+) – Core concepts like variables, functions, promises, and async/await; TypeScript – A typed version of JavaScript with interfaces, generics, and decorators; Node.js & npm – Running JavaScript outside the browser and managing packages; Git – Handling code versions, branches, and teamwork; APIs & HTTP – Making requests and working with JSON data`,
// creators: [
// { name: 'Miłosz Rutkowski', slug: 'milosz-rutkowski' },
// { name: 'Łukasz Myszkowski', slug: 'lukasz-myszkowski' },
// { name: 'Damian Brzeziński', slug: 'damian-brzezinski' },
// { name: 'Dominik Kalinowski', slug: 'dominik-kalinowski' },
// ],
// };
// const regularNodeMock: RegularNode = {
// id: 'change-detection',
// title: 'change detection',
// nodeType: 'regular',
// description:
// 'Change detection in Angular synchronizes the component state with the DOM by tracking and updating data changes. It uses a tree-based approach, where updates propagate from the root component down to child components. The framework provides two strategies: Default, which checks all components, and OnPush, which optimizes performance by only checking components when their inputs change.',
// movies: [
// {
// url: 'https://www.youtube.com/watch?v=KSFPOIauEPU',
// title: 'Angular RxJS and Signals: Better Together',
// },
// ],
// articles: [
// {
// title: 'Signals in Angular: deep dive for busy developers',
// url: 'https://angular.love/signals-in-angular-deep-dive-for-busy-developers',
// },
// {
// title: 'Angular Signals: A New Feature in Angular 16',
// url: 'https://angular.love/angular-signals-a-new-feature-in-angular-16',
// },
// {
// title: 'Why Angular signals won’t replace',
// url: 'https://angular.love/why-angular-signals-wont-replace-rxjs',
// },
// ],
// };
//
// const regularNodeMockNoContent: RegularNode = {
// id: 'lifecycle',
// title: 'lifecycle',
// nodeType: 'regular',
// description:
// 'Change detection in Angular synchronizes the component state with the DOM by tracking and updating data changes. It uses a tree-based approach, where updates propagate from the root component down to child components. The framework provides two strategies: Default, which checks all components, and OnPush, which optimizes performance by only checking components when their inputs change.',
// movies: [],
// articles: [],
// };
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove commented-out mock data.

The large block of commented mock data (50+ lines) serves no purpose and clutters the file. Remove this dead code to improve maintainability.

-// const angularLoveNodeMock: AngularLoveNode = {
-//   id: 'angular-love',
-//   title: 'Angular.Love Roadmap Introduction',
-//   nodeType: 'angular-love',
-//   description:
-//     'The Angular Roadmap on Angular.Love is your go-to guide for learning Angular the right way. It breaks down key concepts, best practices, and useful resources in a clear, structured way. Plus, it's packed with valuable materials and constantly updated to keep up with the latest trends.',
-//   additionalDescription: `Before diving into Angular, it's essential to have a solid understanding of the following concepts: HTML & CSS – Structure and styling of web pages, including Flexbox, Grid, and responsiveness; JavaScript (ES6+) – Core concepts like variables, functions, promises, and async/await; TypeScript – A typed version of JavaScript with interfaces, generics, and decorators; Node.js & npm – Running JavaScript outside the browser and managing packages; Git – Handling code versions, branches, and teamwork; APIs & HTTP – Making requests and working with JSON data`,
-//   creators: [
-//     { name: 'Miłosz Rutkowski', slug: 'milosz-rutkowski' },
-//     { name: 'Łukasz Myszkowski', slug: 'lukasz-myszkowski' },
-//     { name: 'Damian Brzeziński', slug: 'damian-brzezinski' },
-//     { name: 'Dominik Kalinowski', slug: 'dominik-kalinowski' },
-//   ],
-// };
-// const regularNodeMock: RegularNode = {
-//   id: 'change-detection',
-//   title: 'change detection',
-//   nodeType: 'regular',
-//   description:
-//     'Change detection in Angular synchronizes the component state with the DOM by tracking and updating data changes. It uses a tree-based approach, where updates propagate from the root component down to child components. The framework provides two strategies: Default, which checks all components, and OnPush, which optimizes performance by only checking components when their inputs change.',
-//   movies: [
-//     {
-//       url: 'https://www.youtube.com/watch?v=KSFPOIauEPU',
-//       title: 'Angular RxJS and Signals: Better Together',
-//     },
-//   ],
-//   articles: [
-//     {
-//       title: 'Signals in Angular: deep dive for busy developers',
-//       url: 'https://angular.love/signals-in-angular-deep-dive-for-busy-developers',
-//     },
-//     {
-//       title: 'Angular Signals: A New Feature in Angular 16',
-//       url: 'https://angular.love/angular-signals-a-new-feature-in-angular-16',
-//     },
-//     {
-//       title: 'Why Angular signals won't replace',
-//       url: 'https://angular.love/why-angular-signals-wont-replace-rxjs',
-//     },
-//   ],
-// };
-//
-// const regularNodeMockNoContent: RegularNode = {
-//   id: 'lifecycle',
-//   title: 'lifecycle',
-//   nodeType: 'regular',
-//   description:
-//     'Change detection in Angular synchronizes the component state with the DOM by tracking and updating data changes. It uses a tree-based approach, where updates propagate from the root component down to child components. The framework provides two strategies: Default, which checks all components, and OnPush, which optimizes performance by only checking components when their inputs change.',
-//   movies: [],
-//   articles: [],
-// };
📝 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 angularLoveNodeMock: AngularLoveNode = {
// id: 'angular-love',
// title: 'Angular.Love Roadmap Introduction',
// nodeType: 'angular-love',
// description:
// 'The Angular Roadmap on Angular.Love is your go-to guide for learning Angular the right way. It breaks down key concepts, best practices, and useful resources in a clear, structured way. Plus, it’s packed with valuable materials and constantly updated to keep up with the latest trends.',
// additionalDescription: `Before diving into Angular, it's essential to have a solid understanding of the following concepts: HTML & CSS – Structure and styling of web pages, including Flexbox, Grid, and responsiveness; JavaScript (ES6+) – Core concepts like variables, functions, promises, and async/await; TypeScript – A typed version of JavaScript with interfaces, generics, and decorators; Node.js & npm – Running JavaScript outside the browser and managing packages; Git – Handling code versions, branches, and teamwork; APIs & HTTP – Making requests and working with JSON data`,
// creators: [
// { name: 'Miłosz Rutkowski', slug: 'milosz-rutkowski' },
// { name: 'Łukasz Myszkowski', slug: 'lukasz-myszkowski' },
// { name: 'Damian Brzeziński', slug: 'damian-brzezinski' },
// { name: 'Dominik Kalinowski', slug: 'dominik-kalinowski' },
// ],
// };
// const regularNodeMock: RegularNode = {
// id: 'change-detection',
// title: 'change detection',
// nodeType: 'regular',
// description:
// 'Change detection in Angular synchronizes the component state with the DOM by tracking and updating data changes. It uses a tree-based approach, where updates propagate from the root component down to child components. The framework provides two strategies: Default, which checks all components, and OnPush, which optimizes performance by only checking components when their inputs change.',
// movies: [
// {
// url: 'https://www.youtube.com/watch?v=KSFPOIauEPU',
// title: 'Angular RxJS and Signals: Better Together',
// },
// ],
// articles: [
// {
// title: 'Signals in Angular: deep dive for busy developers',
// url: 'https://angular.love/signals-in-angular-deep-dive-for-busy-developers',
// },
// {
// title: 'Angular Signals: A New Feature in Angular 16',
// url: 'https://angular.love/angular-signals-a-new-feature-in-angular-16',
// },
// {
// title: 'Why Angular signals won’t replace',
// url: 'https://angular.love/why-angular-signals-wont-replace-rxjs',
// },
// ],
// };
//
// const regularNodeMockNoContent: RegularNode = {
// id: 'lifecycle',
// title: 'lifecycle',
// nodeType: 'regular',
// description:
// 'Change detection in Angular synchronizes the component state with the DOM by tracking and updating data changes. It uses a tree-based approach, where updates propagate from the root component down to child components. The framework provides two strategies: Default, which checks all components, and OnPush, which optimizes performance by only checking components when their inputs change.',
// movies: [],
// articles: [],
// };
🤖 Prompt for AI Agents
In libs/blog/roadmap/data-access/src/lib/infractructure/roadmap.service.ts from
lines 8 to 58, remove the entire block of commented-out mock data as it is
unused and clutters the file, improving code maintainability and readability.

export class RoadmapBottomsheetComponent {
private matDialogData = inject<RoadmapNodeDTO>(DIALOG_DATA);
nodeDetails = signal<RoadmapNodeDTO>(this.matDialogData);
language = signal<string>('');
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Remove unused signals or implement their functionality.

The language and isContentInRegularNodeDetails signals are defined but never used in the component.

Either remove these unused signals or implement their intended functionality:

-  language = signal<string>('');
-  readonly isContentInRegularNodeDetails = signal(true);

If these are intended for future use, consider adding a TODO comment explaining their purpose.

Also applies to: 80-80

🤖 Prompt for AI Agents
In
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts
at lines 66 and 80, the signals `language` and `isContentInRegularNodeDetails`
are declared but not used anywhere in the component. To fix this, either remove
these unused signals to clean up the code or implement their intended
functionality if they are meant to be used. If they are placeholders for future
features, add a clear TODO comment explaining their purpose to avoid confusion.

Comment on lines 8 to 11
// eslint-disable-next-line @nx/enforce-module-boundaries
import { AngularLoveNodeDTO } from '../../../../../blog-contracts/roadmap/src/lib/angular-love-node.type';
// eslint-disable-next-line @nx/enforce-module-boundaries
import { RegularNodeDTO } from '../../../../../blog-contracts/roadmap/src/lib/regular-node.type';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix module boundary violations by using public API imports.

The direct imports from the contracts library violate module boundaries. These types should be exported through the public API of the @angular-love/blog/contracts/roadmap package.

Remove the eslint-disable comments and update the imports:

-// eslint-disable-next-line @nx/enforce-module-boundaries
-import { AngularLoveNodeDTO } from '../../../../../blog-contracts/roadmap/src/lib/angular-love-node.type';
-// eslint-disable-next-line @nx/enforce-module-boundaries
-import { RegularNodeDTO } from '../../../../../blog-contracts/roadmap/src/lib/regular-node.type';
+import { AngularLoveNodeDTO, RegularNodeDTO } from '@angular-love/blog/contracts/roadmap';

Ensure these types are properly exported from the contracts library's public API.

📝 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
// eslint-disable-next-line @nx/enforce-module-boundaries
import { AngularLoveNodeDTO } from '../../../../../blog-contracts/roadmap/src/lib/angular-love-node.type';
// eslint-disable-next-line @nx/enforce-module-boundaries
import { RegularNodeDTO } from '../../../../../blog-contracts/roadmap/src/lib/regular-node.type';
import { AngularLoveNodeDTO, RegularNodeDTO } from '@angular-love/blog/contracts/roadmap';
🤖 Prompt for AI Agents
In
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts
around lines 8 to 11, the imports of AngularLoveNodeDTO and RegularNodeDTO
directly from deep paths violate module boundaries. Remove the eslint-disable
comments and update the imports to use the public API path from
'@angular-love/blog/contracts/roadmap'. Also, ensure these types are exported
from the contracts library's public API index file to allow proper import
without boundary violations.

Comment on lines +25 to +28
const allNodeDtosMap = roadmapNodesDto.reduce(
(acc, node) => ({ ...acc, [node.id]: node }),
{} as { [nodeId: string]: RoadmapNodeDTO },
);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Optimize object creation in reduce to avoid O(n²) complexity.

Using spread operator in reduce accumulator creates a new object on each iteration, resulting in O(n²) time complexity.

Replace with direct assignment:

  const allNodeDtosMap = roadmapNodesDto.reduce(
-   (acc, node) => ({ ...acc, [node.id]: node }),
+   (acc, node) => {
+     acc[node.id] = node;
+     return acc;
+   },
    {} as { [nodeId: string]: RoadmapNodeDTO },
  );
📝 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 allNodeDtosMap = roadmapNodesDto.reduce(
(acc, node) => ({ ...acc, [node.id]: node }),
{} as { [nodeId: string]: RoadmapNodeDTO },
);
const allNodeDtosMap = roadmapNodesDto.reduce(
(acc, node) => {
acc[node.id] = node;
return acc;
},
{} as { [nodeId: string]: RoadmapNodeDTO },
);
🧰 Tools
🪛 Biome (1.9.4)

[error] 26-26: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🤖 Prompt for AI Agents
In libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts
around lines 25 to 28, the reduce function uses the spread operator to create a
new object on each iteration, causing O(n²) complexity. To fix this, initialize
the accumulator as an empty object and directly assign each node to the
accumulator inside the reduce callback without spreading, then return the
accumulator at the end of each iteration.

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://f75a67ac-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://bb7f851a.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://d527d48d-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://30755dad.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://65d6be8f-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://33e03113.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

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

♻️ Duplicate comments (4)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (4)

48-48: Fix the styleUrl property to use the correct Angular property name.

Angular components expect styleUrls as an array, not styleUrl as a string. This appears to be a recurring issue from past reviews.

- styleUrl: './feature-roadmap.component.scss',
+ styleUrls: ['./feature-roadmap.component.scss'],

113-143: Fix zoom calculation to use relative multipliers instead of absolute values.

The smoothZoom method expects relative zoom factors (e.g., 1.2 or 0.8), not absolute scale values. The current implementation causes unpredictable zoom behavior.

  if (event === 'decrement') {
    const currentScale = currentTransform.scale;
-   const multiplier =
-     Math.round(currentScale * 2) / 2 - this._scaleMultiplier;
+   const multiplier = 1 - this._scaleMultiplier;

    panZoomInstance.smoothZoom(centerX, centerY, multiplier);
  }

  if (event === 'increment') {
    const currentScale = currentTransform.scale;
-   const multiplier =
-     Math.round(currentScale * 2) / 2 + this._scaleMultiplier;
+   const multiplier = 1 + this._scaleMultiplier;

    panZoomInstance.smoothZoom(centerX, centerY, multiplier);
  }

179-184: Add error handling for panzoom initialization.

The panzoom initialization can fail if the library doesn't load properly or if there are DOM issues. Adding error handling will prevent runtime crashes.

private initPanZoom() {
+ try {
    const roadmapWrapper = this.roadmapWrapper.nativeElement;
    this.panZoomInstance.set(
      panzoom(roadmapWrapper, this._panZoomInitialConfig),
    );
+ } catch (error) {
+   console.error('Failed to initialize pan-zoom:', error);
+ }
}

55-55: Implement OnDestroy interface and cleanup panzoom instance.

The panzoom instance creates global event listeners that need to be cleaned up when the component is destroyed to prevent memory leaks.

- export class FeatureRoadmapComponent {
+ export class FeatureRoadmapComponent implements OnDestroy {

Add this method to the component:

ngOnDestroy(): void {
  const panZoomInstance = this.panZoomInstance();
  if (panZoomInstance) {
    panZoomInstance.dispose();
  }
}

Don't forget to import OnDestroy from @angular/core.

🧹 Nitpick comments (1)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1)

64-66: Simplify the ElementRef injection.

The explicit type annotation is redundant since TypeScript can infer the type from the generic parameter.

- private readonly elementRef = inject<ElementRef<HTMLElement>>(
-   ElementRef<HTMLElement>,
- );
+ private readonly elementRef = inject(ElementRef<HTMLElement>);
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f339db1 and b163337.

📒 Files selected for processing (2)
  • apps/blog/src/assets/roadmap-tiles.json (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • apps/blog/src/assets/roadmap-tiles.json
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (4)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T15:58:21.460Z
Learning: In the angular-love project, prefer using inline Tailwind classes over extracting them into separate CSS files, as it provides better visibility and maintains consistency with the codebase.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use signal APIs (input(), output(), model()) instead of decorators (@Input, @Output) for component inputs and outputs.
🔇 Additional comments (2)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (2)

84-84: Consider using modern Angular signal input syntax.

Based on the project's learnings, the codebase prefers modern Angular signal APIs over decorators. The current implementation already uses input.required<string>() which is correct.


86-111: Well-structured constructor with proper reactive patterns.

The constructor properly initializes the store, sets up reactive subscriptions with proper cleanup using takeUntilDestroyed(), and handles platform-specific logic correctly.

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://46618447-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://4ca69615.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

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

♻️ Duplicate comments (2)
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (2)

55-212: Critical: Implement panzoom cleanup to prevent memory leaks.

The panzoom instance created in initPanZoom() adds global event listeners but is never properly disposed of when the component is destroyed, causing memory leaks on route changes.

+import { OnDestroy } from '@angular/core';

-export class FeatureRoadmapComponent {
+export class FeatureRoadmapComponent implements OnDestroy {

+  ngOnDestroy(): void {
+    this.panZoomInstance()?.dispose();
+  }

181-211: Add error handling for panzoom initialization.

Panzoom initialization could fail due to various reasons (library loading issues, DOM state, etc.) but lacks error handling, which could lead to unexpected behavior.

private initPanZoom() {
+  try {
    const roadmapWrapper = this.roadmapWrapper.nativeElement;
    this.panZoomInstance.set(
      panzoom(roadmapWrapper, this._panZoomInitialConfig),
    );

    // Find all buttons inside the custom control component
    const controlButtons = document.querySelectorAll(
      'al-roadmap-pan-controls button',
    );

    controlButtons.forEach((btn) => {
      btn.addEventListener('pointerdown', () => {
        this.panZoomInstance()?.pause();
      });

      btn.addEventListener('click', () => {
        // Resume panzoom after the click finishes
        setTimeout(() => this.panZoomInstance()?.resume(), 0);
      });

      btn.addEventListener(
        'wheel',
        (e) => {
          e.preventDefault();
          e.stopPropagation();
        },
        { passive: false },
      );
    });
+  } catch (error) {
+    console.error('Failed to initialize panzoom:', error);
+  }
}
🧹 Nitpick comments (2)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (2)

34-34: Fix inconsistent title text for creators section.

The creators component has title="Description" which should be more descriptive like "Created by" or "Authors" since it displays creator information.

-        title="Description"
+        title="Created by"

41-41: Add action handler for "Become an author" button.

The button appears interactive but lacks a click handler to define what happens when users click it.

Would you like me to help implement the action handler for this button, such as navigating to an author signup page or opening a contact form?

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b163337 and 94af2a3.

📒 Files selected for processing (2)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (4)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T15:58:21.460Z
Learning: In the angular-love project, prefer using inline Tailwind classes over extracting them into separate CSS files, as it provides better visibility and maintains consistency with the codebase.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use signal APIs (input(), output(), model()) instead of decorators (@Input, @Output) for component inputs and outputs.
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (2)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
🪛 HTMLHint (1.5.0)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html

[error] 1-1: Doctype must be declared before any non-comment content.

(doctype-first)

🔇 Additional comments (3)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (1)

1-61: LGTM! Modern Angular template with proper event handling.

The template correctly uses modern Angular control flow (@if) and implements proper event handling with stopPropagation to prevent unwanted interactions. The component composition approach aligns well with the project's architecture patterns.

libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (2)

84-84: LGTM! Modern Angular signal-based input.

Good use of the modern input.required<string>() API instead of the older @Input() decorator, aligning with current Angular best practices.


113-145: LGTM! Zoom calculation logic is now correct.

The zoom implementation properly calculates relative zoom factors and handles different zoom operations correctly. The use of targetScale / currentScale to get the zoom factor for smoothZoom is the correct approach.

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://bd2c24a4-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://c1639eed.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

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

♻️ Duplicate comments (4)
libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (1)

17-20: Optimize object creation in reduce to avoid O(n²) complexity.

This performance issue with the spread operator was already identified in a previous review. The current implementation creates a new object on each iteration, resulting in O(n²) time complexity.

apps/blog/src/assets/roadmap-tiles.json (1)

21-23: Ensure explicit previousNodeId for first‐child nodes

This inconsistency with the previousNodeId field was already identified in a previous review. The "styling" node should have "previousNodeId": null for consistency with other branch starter nodes.

libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (2)

8-11: Fix module boundary violations by using public API imports.

The direct imports from the contracts library violate module boundaries. These types should be exported through the public API of the @angular-love/blog/contracts/roadmap package.


67-67: Remove unused signal or implement its functionality.

The language signal is defined but never used in the component.

🧹 Nitpick comments (1)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (1)

23-25: Consider making type guard more explicit.

The isRegularNode type guard uses a negative check which works but could be more explicit for better maintainability.

 function isRegularNode(node: RoadmapNodeDTO): node is RegularNodeDTO {
-  return node.nodeType !== 'angular-love';
+  return node.nodeType === 'regular';
 }
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 94af2a3 and 05f4991.

📒 Files selected for processing (15)
  • apps/blog/src/assets/roadmap-tiles.json (1 hunks)
  • libs/blog-contracts/roadmap/src/index.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/angular-love-node.interface.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/base-node.interface.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/creator.interface.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/regular-node.type.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/resource.interface.ts (1 hunks)
  • libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1 hunks)
  • libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (1 hunks)
  • libs/blog/roadmap/data-access/src/lib/state/roadmap-store.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/roadmap-bottomsheet-menager.service.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-regular-content/roadmap-bottomsheet-regular-content.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (1 hunks)
✅ Files skipped from review due to trivial changes (4)
  • libs/blog-contracts/roadmap/src/lib/creator.interface.ts
  • libs/blog-contracts/roadmap/src/lib/base-node.interface.ts
  • libs/blog-contracts/roadmap/src/lib/angular-love-node.interface.ts
  • libs/blog-contracts/roadmap/src/lib/resource.interface.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • libs/blog-contracts/roadmap/src/index.ts
  • libs/blog-contracts/roadmap/src/lib/regular-node.type.ts
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts
  • libs/blog/roadmap/data-access/src/lib/state/roadmap-store.ts
  • libs/blog/roadmap/feature-roadmap/src/lib/roadmap-bottomsheet-menager.service.ts
  • libs/blog-contracts/roadmap/src/lib/roadmap-node.ts
🧰 Additional context used
🧠 Learnings (5)
📓 Common learnings
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-regular-content/roadmap-bottomsheet-regular-content.component.ts (1)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (4)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use signal APIs (input(), output(), model()) instead of decorators (@Input, @Output) for component inputs and outputs.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T15:58:21.460Z
Learning: In the angular-love project, prefer using inline Tailwind classes over extracting them into separate CSS files, as it provides better visibility and maintains consistency with the codebase.
apps/blog/src/assets/roadmap-tiles.json (4)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/layouts/ui-navigation/src/lib/language-picker/language-picker.component.ts:28-42
Timestamp: 2025-02-24T15:55:49.569Z
Learning: When form controls (like select elements) have their options strictly controlled by the component itself, additional validation of selected values is redundant as the user can only select from the predefined valid options.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T15:58:21.460Z
Learning: In the angular-love project, prefer using inline Tailwind classes over extracting them into separate CSS files, as it provides better visibility and maintains consistency with the codebase.
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (2)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
🧬 Code Graph Analysis (1)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (9)
libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1)
  • RoadmapNodeDTO (4-4)
libs/blog-contracts/roadmap/src/lib/angular-love-node.interface.ts (1)
  • AngularLoveNodeDTO (4-8)
libs/blog-contracts/roadmap/src/lib/regular-node.type.ts (1)
  • RegularNodeDTO (4-7)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-regular-content/roadmap-bottomsheet-regular-content.component.ts (1)
  • Component (7-28)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-creators/roadmap-bottomsheet-creators.component.ts (1)
  • Component (7-28)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.ts (1)
  • Component (6-15)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-description/roadmap-bottomsheet-description.component.ts (1)
  • Component (7-27)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-subtitle/roadmap-bottomsheet-subtitle.component.ts (1)
  • Component (3-15)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-footer/roadmap-bottomsheet-footer.component.ts (1)
  • Component (13-67)
🪛 Biome (1.9.4)
libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts

[error] 18-18: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🪛 HTMLHint (1.5.0)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html

[error] 1-1: Doctype must be declared before any non-comment content.

(doctype-first)

🔇 Additional comments (5)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-regular-content/roadmap-bottomsheet-regular-content.component.ts (1)

1-28: LGTM! Modern Angular patterns implemented correctly.

This component demonstrates excellent use of modern Angular features:

  • Uses the new input() API for better type safety
  • Employs @for control flow instead of structural directives (consistent with project standards)
  • Clean, focused template with proper resource link handling
  • Appropriate use of Tailwind classes for styling

The implementation is well-structured and follows the established patterns in the codebase.

libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (1)

1-61: LGTM! Well-structured bottom sheet template with modern Angular patterns.

This template demonstrates excellent practices:

  • Uses modern @if control flow instead of structural directives (consistent with project standards)
  • Proper event handling with stopPropagation() to prevent unwanted interactions
  • Clean conditional rendering for different node types (Angular Love vs regular nodes)
  • Appropriate styling with Tailwind classes and responsive design
  • Good accessibility considerations with proper click handling on overlay

The component structure effectively handles the different content types while maintaining a consistent user experience.

libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (3)

27-62: Component configuration looks good.

The component follows modern Angular patterns with standalone components, proper imports, and reactive host bindings. The fade-in animation is well-implemented.


69-99: Well-structured reactive computations.

The computed properties effectively use type guards for type narrowing and resource filtering. The reactive approach ensures efficient updates when node details change.


101-103: Simple and correct dialog closure implementation.

The onClose method properly uses the injected DialogRef to close the dialog.

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://611670f9-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://ce44ad0d.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

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

♻️ Duplicate comments (2)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (2)

9-12: Fix module boundary violations by using public API imports.

The direct imports from the contracts library violate module boundaries as identified in previous reviews.


70-70: Remove unused signal or implement its functionality.

The language signal is defined but never used in the component, as previously identified.

🧹 Nitpick comments (5)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (4)

8-11: [ngClass] condition is redundant inside an @if block.

Because the whole template is rendered only when nodeDetails() is truthy, nodeDetails can never be undefined here. The extra visibility toggling adds dead code and mental load.

-  [ngClass]="{
-    visible: nodeDetails !== undefined,
-    hidden: nodeDetails === undefined,
-  }"
+  class="bottomsheet max-h-[70vh] w-4/5 ... lg:w-3/5"

Remove the visibility classes or move the @if outside if you really need the class-based animation.


12-15: Consider removing unneeded event listeners.

You already stop the root click; additionally stopping wheel, pointerup, touchstart for the whole sheet may break expected interactions (e.g. scrolling with mouse wheel inside). If the intent is to block background scroll, wrap in cdkScrollable or add overflow-y-auto to the sheet instead.


41-43: Use (click) instead of (pointerdown) on buttons.

(pointerdown) fires before the native click and can trigger unintended duplicate navigation or ignore keyboard activation. For accessibility and consistency, rely on (click) unless you need special drag handling.

-  <button al-button (pointerdown)="navigateToAuthor()">
+  <button al-button (click)="navigateToAuthor()">

56-60: Provide ESC-key shortcut to close the sheet.

Users expect the Escape key to dismiss modal dialogs. Implement a keydown listener on the host or leverage @HostListener('document:keydown.escape') in the component TS.

libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (1)

24-26: Consider making the type guard more explicit for future extensibility.

The isRegularNode function assumes any non-'angular-love' node is 'regular', which could break if additional node types are introduced.

Consider making it more explicit:

-function isRegularNode(node: RoadmapNodeDTO): node is RegularNodeDTO {
-  return node.nodeType !== 'angular-love';
-}
+function isRegularNode(node: RoadmapNodeDTO): node is RegularNodeDTO {
+  return node.nodeType === 'regular';
+}
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 05f4991 and 4e87e3e.

📒 Files selected for processing (6)
  • apps/blog/src/assets/i18n/en.json (2 hunks)
  • apps/blog/src/assets/i18n/pl.json (2 hunks)
  • apps/blog/src/assets/roadmap-tiles.json (1 hunks)
  • libs/blog/layouts/ui-navigation/src/lib/navigation/navigation.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (1 hunks)
✅ Files skipped from review due to trivial changes (1)
  • libs/blog/layouts/ui-navigation/src/lib/navigation/navigation.component.ts
🚧 Files skipped from review as they are similar to previous changes (3)
  • apps/blog/src/assets/i18n/en.json
  • apps/blog/src/assets/i18n/pl.json
  • apps/blog/src/assets/roadmap-tiles.json
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (4)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use signal APIs (input(), output(), model()) instead of decorators (@Input, @Output) for component inputs and outputs.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T15:58:21.460Z
Learning: In the angular-love project, prefer using inline Tailwind classes over extracting them into separate CSS files, as it provides better visibility and maintains consistency with the codebase.
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html (2)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
🧬 Code Graph Analysis (1)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (9)
libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1)
  • RoadmapNodeDTO (4-4)
libs/blog-contracts/roadmap/src/lib/angular-love-node.interface.ts (1)
  • AngularLoveNodeDTO (4-8)
libs/blog-contracts/roadmap/src/lib/regular-node.type.ts (1)
  • RegularNodeDTO (4-7)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-regular-content/roadmap-bottomsheet-regular-content.component.ts (1)
  • Component (7-28)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-creators/roadmap-bottomsheet-creators.component.ts (1)
  • Component (7-28)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.ts (1)
  • Component (6-15)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-description/roadmap-bottomsheet-description.component.ts (1)
  • Component (7-27)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-subtitle/roadmap-bottomsheet-subtitle.component.ts (1)
  • Component (3-15)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-footer/roadmap-bottomsheet-footer.component.ts (1)
  • Component (13-67)
🪛 HTMLHint (1.5.0)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html

[error] 1-1: Doctype must be declared before any non-comment content.

(doctype-first)

🔇 Additional comments (3)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (3)

58-61: LGTM! Excellent use of host bindings for dynamic styling.

The host bindings correctly control visibility and CSS classes based on the nodeDetails signal, providing a clean reactive approach to component state management.


72-84: LGTM! Well-implemented type narrowing with computed properties.

The computed properties effectively use the type guard functions to provide type-safe access to node-specific details, maintaining good separation between different node types.


86-102: LGTM! Proper resource filtering with null safety.

The computed properties correctly filter resources by type and handle potential null/undefined cases with the nullish coalescing operator.

Comment on lines +32 to +35
<al-roadmap-bottomsheet-creators
[creators]="angularLoveNodeDetails.creators"
title="Description"
/>
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Incorrect section title for creators.

The al-roadmap-bottomsheet-creators component receives title="Description". This should likely be “Creators” (or similar) to match the actual content and avoid confusing headings.

-  <al-roadmap-bottomsheet-creators
-    [creators]="angularLoveNodeDetails.creators"
-    title="Description"
-  />
+  <al-roadmap-bottomsheet-creators
+    [creators]="angularLoveNodeDetails.creators"
+    title="Creators"
+  />
📝 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
<al-roadmap-bottomsheet-creators
[creators]="angularLoveNodeDetails.creators"
title="Description"
/>
<al-roadmap-bottomsheet-creators
[creators]="angularLoveNodeDetails.creators"
title="Creators"
/>
🤖 Prompt for AI Agents
In
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html
around lines 32 to 35, the al-roadmap-bottomsheet-creators component is
incorrectly given the title "Description" which does not match its content.
Change the title attribute value from "Description" to "Creators" to accurately
reflect the content and avoid confusion.

Comment on lines +3 to +5
class="fixed inset-0 z-[1000] flex items-end justify-center bg-black/50"
(click)="onClose()"
>
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add ARIA semantics & focus management to the overlay.

The outer wrapper acts as a modal but lacks role="dialog" (or role="alertdialog"), aria-modal="true" and initial focus handling (tabindex="-1" + focus trap). Screen-reader and keyboard users may therefore miss the sheet or be able to tab to the obscured background.

-  <div
-    class="fixed inset-0 z-[1000] flex items-end justify-center bg-black/50"
-    (click)="onClose()"
-  >
+  <div
+    class="fixed inset-0 z-[1000] flex items-end justify-center bg-black/50"
+    role="dialog"
+    aria-modal="true"
+    tabindex="-1"
+    (click)="onClose()"
+  >
🤖 Prompt for AI Agents
In
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.html
around lines 3 to 5, the modal overlay lacks essential ARIA attributes and focus
management for accessibility. Add role="dialog" or role="alertdialog",
aria-modal="true", and tabindex="-1" to the outer wrapper element. Implement
initial focus handling and a focus trap to ensure keyboard users cannot tab
outside the modal and screen readers correctly identify the dialog.

standalone: true,
})
export class RoadmapBottomsheetComponent {
private matDialogData = inject<RoadmapNodeDTO>(DIALOG_DATA);
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Add null safety for dialog data injection.

The component assumes DIALOG_DATA will always be present, but there's no null checking which could cause runtime errors.

Add null safety:

-private matDialogData = inject<RoadmapNodeDTO>(DIALOG_DATA);
+private matDialogData = inject<RoadmapNodeDTO | null>(DIALOG_DATA);

Then update the nodeDetails initialization:

-nodeDetails = signal<RoadmapNodeDTO>(this.matDialogData);
+nodeDetails = signal<RoadmapNodeDTO | null>(this.matDialogData);
📝 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
private matDialogData = inject<RoadmapNodeDTO>(DIALOG_DATA);
// before:
// private matDialogData = inject<RoadmapNodeDTO>(DIALOG_DATA);
// nodeDetails = signal<RoadmapNodeDTO>(this.matDialogData);
// after:
private matDialogData = inject<RoadmapNodeDTO | null>(DIALOG_DATA);
nodeDetails = signal<RoadmapNodeDTO | null>(this.matDialogData);
🤖 Prompt for AI Agents
In
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts
at line 65, the injection of DIALOG_DATA assumes it is always present, which can
cause runtime errors if it is null or undefined. Modify the injection to allow
for a nullable type or optional injection, then update the initialization of
nodeDetails to safely handle the case when matDialogData is null or undefined,
ensuring no runtime errors occur due to missing dialog data.

@valueadd-robot
Copy link

PR is detected, will deploy to dev environment

@valueadd-robot
Copy link

Deployed to dev environment
Branch: feat/angular-roadmap
BFF URL: https://8061f025-blog-bff-dev.contact-ef8.workers.dev
Deploy URL: https://9215a9ed.angular-love-client.pages.dev
Alias URL: https://feat-angular-roadmap.angular-love-client.pages.dev

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

♻️ Duplicate comments (3)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.html (1)

3-5: Remove the stray colon in the Tailwind class token.

The trailing : after bg-[--secondary-color] breaks the generated CSS class and prevents the background color from being applied.

-    'bg-[--secondary-color]:': nodeType() !== 'angular-love',
+    'bg-[--secondary-color]': nodeType() !== 'angular-love',
libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (1)

17-20: Fix O(n²) complexity in reduce operation.

The spread operator in the reduce accumulator creates a new object on each iteration, resulting in O(n²) time complexity.

  const allNodeDtosMap = roadmapNodesDto.reduce(
-   (acc, node) => ({ ...acc, [node.id]: node }),
+   (acc, node) => {
+     acc[node.id] = node;
+     return acc;
+   },
    {} as { [nodeId: string]: RoadmapNodeDTO },
  );
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1)

27-28: Fix styleUrl property name.

The styleUrl property should be styleUrls (plural) to ensure the stylesheet is properly bundled.

-  styleUrl: 'roadmap-secondary-node.component.scss',
+  styleUrls: ['roadmap-secondary-node.component.scss'],
🧹 Nitpick comments (4)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.html (1)

3-7: Avoid invoking component methods in the template to cut unnecessary change-detection work.

nodeType() is called twice per change-detection cycle. Expose its value as a readonly property (or use a getter memoised by ChangeDetectionStrategy.OnPush) to prevent redundant execution.

-    'bg-[--secondary-color]': nodeType() !== 'angular-love',
-    'bg-gradient-to-r from-[--secondary-color] to-[--gradient-color]':
-      nodeType() === 'angular-love',
+    'bg-[--secondary-color]': nodeType !== 'angular-love',
+    'bg-gradient-to-r from-[--secondary-color] to-[--gradient-color]':
+      nodeType === 'angular-love',

Corresponding TS change:

readonly nodeType = this._node.type;

This keeps the template declarative while eliminating repeated method calls.

libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1)

1-1: Remove unused import.

The AngularLoveNodeDTO import is not used in this file since RoadmapNodeDTO now exclusively represents RegularNodeDTO.

-import { AngularLoveNodeDTO } from './angular-love-node.interface';
 import { RegularNodeDTO } from './regular-node.type';
libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (1)

26-65: Complex logic is well-structured but could benefit from documentation.

The main loop handles three distinct cases (primary nodes, cluster children, layer children) with clear separation. However, the cluster node promotion logic (lines 43-53) is particularly complex.

Consider adding inline comments to clarify the cluster promotion logic:

      if (allNodesMap[nodeDto.parentNodeId]) {
+       // Convert existing secondary node to cluster node when we discover it has children
        allNodesMap[parentClusterNodeDto.id] = {
          ...allNodesMap[nodeDto.parentNodeId],
          nodeType: 'cluster',
          clusteredNodes: [],
        };
      } else {
+       // Create new cluster node
        allNodesMap[parentClusterNodeDto.id] =
          createClusterNode(parentClusterNodeDto);
      }
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1)

29-29: Remove empty host object.

The empty host: {} object serves no purpose and can be removed for cleaner code.

-  host: {},
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 4e87e3e and 9bca461.

📒 Files selected for processing (17)
  • libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1 hunks)
  • libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (1 hunks)
  • libs/blog/roadmap/data-access/src/lib/state/roadmap-store.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts (1 hunks)
  • libs/blog/roadmap/feature-roadmap/src/lib/roadmap-bottomsheet-menager.service.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.html (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/index.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-cluster/roadmap-cluster.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-primary-node/roadmap-primary-node.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/services/roadmap-bottomsheet-notifier.service.ts (1 hunks)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (1 hunks)
  • libs/blog/roadmap/utils/src/index.ts (1 hunks)
  • libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (10)
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-cluster/roadmap-cluster.component.ts
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.ts
  • libs/blog/roadmap/feature-roadmap/src/lib/roadmap-bottomsheet-menager.service.ts
  • libs/blog/roadmap/feature-roadmap/src/lib/feature-roadmap.component.ts
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-primary-node/roadmap-primary-node.component.ts
  • libs/blog/roadmap/data-access/src/lib/state/roadmap-store.ts
  • libs/blog/roadmap/ui-roadmap-node/src/index.ts
  • libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-angular-love-node/roadmap-angular-love-node.component.ts
  • libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet.component.ts
  • libs/blog/roadmap/utils/src/index.ts
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (1)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (3)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T15:58:21.460Z
Learning: In the angular-love project, prefer using inline Tailwind classes over extracting them into separate CSS files, as it provides better visibility and maintains consistency with the codebase.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use signal APIs (input(), output(), model()) instead of decorators (@Input, @Output) for component inputs and outputs.
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.html (3)
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:09:56.949Z
Learning: In the angular-love project, when multiple UI elements share similar structure and styles (like navigation pills), they should be extracted into reusable components to maintain consistency and reduce duplication.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T16:17:27.933Z
Learning: In the angular-love project, use modern Angular control flow (@for, @if) instead of structural directives (*ngFor, *ngIf) for template iteration and conditional rendering.
Learnt from: DDonochVA
PR: HouseOfAngular/angular-love#390
File: libs/blog/home/feature-home/src/lib/home-page/home-page.component.html:1-26
Timestamp: 2025-02-24T15:58:21.460Z
Learning: In the angular-love project, prefer using inline Tailwind classes over extracting them into separate CSS files, as it provides better visibility and maintains consistency with the codebase.
🧬 Code Graph Analysis (5)
libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1)
libs/blog-contracts/roadmap/src/lib/regular-node.type.ts (1)
  • RegularNodeDTO (4-7)
libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts (4)
libs/blog/roadmap/feature-roadmap/src/lib/roadmap-bottomsheet-menager.service.ts (1)
  • Injectable (8-19)
libs/blog/roadmap/ui-roadmap-node/src/lib/services/roadmap-bottomsheet-notifier.service.ts (1)
  • Injectable (6-17)
libs/blog/roadmap/data-access/src/lib/infractructure/roadmap.service.ts (1)
  • Injectable (60-81)
libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (1)
  • RoadmapStandardNode (31-31)
libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (3)
libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1)
  • RoadmapNodeDTO (4-4)
libs/blog/roadmap/ui-roadmap/src/lib/components/roadmap-layer/roadmap-layer.component.ts (1)
  • RoadmapLayer (21-24)
libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (3)
  • RoadmapNode (38-38)
  • RoadmapClusterNode (33-36)
  • RoadmapStandardNode (31-31)
libs/blog/roadmap/ui-roadmap-node/src/lib/services/roadmap-bottomsheet-notifier.service.ts (3)
libs/blog/roadmap/feature-roadmap/src/lib/roadmap-bottomsheet-menager.service.ts (1)
  • Injectable (8-19)
libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts (1)
  • Injectable (7-17)
libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (1)
  • RoadmapStandardNode (31-31)
libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (1)
libs/blog-contracts/roadmap/src/lib/resource.interface.ts (1)
  • Resource (1-5)
🪛 Biome (1.9.4)
libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts

[error] 18-18: Avoid the use of spread (...) syntax on accumulators.

Spread syntax should be avoided on accumulators (like those in .reduce) because it causes a time complexity of O(n^2).
Consider methods such as .splice or .push instead.

(lint/performance/noAccumulatingSpread)

🪛 HTMLHint (1.5.0)
libs/blog/roadmap/ui-roadmap-bottomsheet/src/lib/roadmap-bottomsheet-header/roadmap-bottomsheet-header.component.html

[error] 1-1: Doctype must be declared before any non-comment content.

(doctype-first)

🔇 Additional comments (5)
libs/blog-contracts/roadmap/src/lib/roadmap-node.ts (1)

4-4: Type definition looks good.

The narrowing of RoadmapNodeDTO from a union type to exclusively RegularNodeDTO is clean and straightforward.

libs/blog/roadmap/data-access/src/lib/state/build-roadmap-layers-from-dto.ts (2)

119-141: Efficient linked-list reconstruction algorithm.

The getOrderedNodeIdsList function cleverly reconstructs node order using previousNodeId references. The implementation is efficient and handles the chain traversal correctly.


97-117: Clean helper functions with consistent patterns.

The node creation helper functions follow a consistent pattern and properly map DTO properties to the appropriate node types.

libs/blog/roadmap/ui-roadmap-node/src/lib/components/roadmap-secondary-node/roadmap-secondary-node.component.ts (1)

1-7: Excellent use of modern Angular patterns.

Great implementation using signal APIs with input.required<>() instead of @Input() decorator, which aligns with modern Angular best practices mentioned in the project learnings.

Also applies to: 36-36

libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts (1)

6-38: Well-structured type definitions.

Excellent TypeScript interface design with proper inheritance hierarchy, discriminated unions using nodeType, and clear separation of concerns. The union types RoadmapRegularNode, RoadmapStandardNode, and RoadmapNode provide good type safety and flexibility.

Comment on lines +4 to +5
// eslint-disable-next-line @nx/enforce-module-boundaries
import { RoadmapStandardNode } from '@angular-love/blog/roadmap/ui-roadmap-node';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix module boundary violation and avoid relative path imports.

The eslint-disable comment indicates a module boundary violation. Instead of importing from @angular-love/blog/roadmap/ui-roadmap-node, consider moving the RoadmapStandardNode type to a shared contracts library to maintain proper module boundaries.

-// eslint-disable-next-line @nx/enforce-module-boundaries
-import { RoadmapStandardNode } from '@angular-love/blog/roadmap/ui-roadmap-node';
+import { RoadmapStandardNode } from '@angular-love/blog/contracts/roadmap';
📝 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
// eslint-disable-next-line @nx/enforce-module-boundaries
import { RoadmapStandardNode } from '@angular-love/blog/roadmap/ui-roadmap-node';
// Remove the module‐boundary override and import from the shared contracts library
import { RoadmapStandardNode } from '@angular-love/blog/contracts/roadmap';
🤖 Prompt for AI Agents
In
libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts
around lines 4 to 5, the import of RoadmapStandardNode from
'@angular-love/blog/roadmap/ui-roadmap-node' violates module boundaries and uses
a disabled eslint rule. To fix this, move the RoadmapStandardNode type
definition to a shared contracts library accessible by both modules, then update
the import statement here to reference that shared library instead, ensuring
proper module boundaries and removing the eslint-disable comment.

Comment on lines +7 to +17
@Injectable({
providedIn: 'root',
})
export class RoadmapBottomSheetNotifierService {
private node = new Subject<RoadmapStandardNode>();
public readonly nodeIdAsObservable = this.node.asObservable();

openBottomSheet(node: RoadmapStandardNode) {
this.node.next(node);
}
}
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

Resolve service duplication and potential conflicts.

This service is nearly identical to RoadmapBottomSheetNotifierService in libs/blog/roadmap/ui-roadmap-node/src/lib/services/roadmap-bottomsheet-notifier.service.ts. Both services are provided in root, which could cause injection conflicts and confusion about which service is being used.

Consider consolidating these services into a single shared service or clearly differentiating their purposes. Run this script to verify the duplication:


🏁 Script executed:

#!/bin/bash
# Search for duplicate RoadmapBottomSheetNotifierService implementations
ast-grep --pattern 'export class RoadmapBottomSheetNotifierService {
  $$$
}'

Length of output: 79


To confirm whether both implementations exist, let’s search the repo for the service file and its class declaration:


🏁 Script executed:

#!/bin/bash
# List all instances of the notifier service file
fd "roadmap-bottomsheet-notifier.service.ts" -t f

# Search for the class declaration across the libs/blog directory
rg -n "class RoadmapBottomSheetNotifierService" -R libs/blog

Length of output: 689


Let’s directly compare the two service implementations to confirm if they’re identical:


🏁 Script executed:

#!/bin/bash
diff -u \
  libs/blog/roadmap/ui-roadmap-node/src/lib/services/roadmap-bottomsheet-notifier.service.ts \
  libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts || true

Length of output: 1004


Consolidate duplicated RoadmapBottomSheetNotifierService into a single shared implementation

Both of these files define the same service, providedIn: root, which risks injection conflicts:

  • libs/blog/roadmap/ui-roadmap-node/src/lib/services/roadmap-bottomsheet-notifier.service.ts
  • libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts

Action items:

  • Extract RoadmapBottomSheetNotifierService into one shared location (e.g. libs/blog/roadmap/shared/services).
  • Update imports in both ui-roadmap-node and utils to point at the shared service.
  • Delete the now-redundant duplicate file.
  • Ensure any debug leftovers (e.g. console.log) are cleaned up.
🤖 Prompt for AI Agents
In
libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts
lines 7 to 17, the RoadmapBottomSheetNotifierService is duplicated in another
location causing injection conflicts. Move this service to a new shared folder
like libs/blog/roadmap/shared/services, update all imports in ui-roadmap-node
and utils modules to reference this new shared service path, delete the
duplicate service file from the original locations, and remove any leftover
debug statements such as console.log calls.

Comment on lines +6 to +17
@Injectable({
providedIn: 'root',
})
export class RoadmapBottomSheetNotifierService {
private node = new Subject<RoadmapStandardNode>();
public readonly nodeIdAsObservable = this.node.asObservable();

openBottomSheet(node: RoadmapStandardNode) {
console.log('openBOttomsheet', node);
this.node.next(node);
}
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix typo and resolve service duplication.

Two issues identified:

  1. Typo in console.log: "openBOttomsheet" should be "openBottomSheet"
  2. This service duplicates the one in libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts
-    console.log('openBOttomsheet', node);
+    console.log('openBottomSheet', node);

Consider removing one of the duplicate services to avoid confusion and potential injection conflicts.

📝 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
@Injectable({
providedIn: 'root',
})
export class RoadmapBottomSheetNotifierService {
private node = new Subject<RoadmapStandardNode>();
public readonly nodeIdAsObservable = this.node.asObservable();
openBottomSheet(node: RoadmapStandardNode) {
console.log('openBOttomsheet', node);
this.node.next(node);
}
}
@Injectable({
providedIn: 'root',
})
export class RoadmapBottomSheetNotifierService {
private node = new Subject<RoadmapStandardNode>();
public readonly nodeIdAsObservable = this.node.asObservable();
openBottomSheet(node: RoadmapStandardNode) {
console.log('openBottomSheet', node);
this.node.next(node);
}
}
🤖 Prompt for AI Agents
In
libs/blog/roadmap/ui-roadmap-node/src/lib/services/roadmap-bottomsheet-notifier.service.ts
lines 6 to 17, fix the typo in the console.log statement by changing
"openBOttomsheet" to "openBottomSheet". Additionally, since this service
duplicates the one in
libs/blog/roadmap/utils/src/lib/services/roadmap-bottomsheet-notifier.service.ts,
remove this duplicate service file to prevent confusion and injection conflicts.

Comment on lines +3 to +4
// eslint-disable-next-line @nx/enforce-module-boundaries
import { Creator } from '../../../../../../blog-contracts/roadmap/src/lib/creator.interface';
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue

Fix module boundary violation with proper import.

The eslint-disable comment and long relative path indicate a module boundary violation. The Creator interface should be imported through proper module paths or moved to an appropriate shared location.

-// eslint-disable-next-line @nx/enforce-module-boundaries
-import { Creator } from '../../../../../../blog-contracts/roadmap/src/lib/creator.interface';
+import { Creator } from '@angular-love/blog/contracts/roadmap';
📝 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
// eslint-disable-next-line @nx/enforce-module-boundaries
import { Creator } from '../../../../../../blog-contracts/roadmap/src/lib/creator.interface';
import { Creator } from '@angular-love/blog/contracts/roadmap';
🤖 Prompt for AI Agents
In libs/blog/roadmap/ui-roadmap-node/src/lib/types/roadmap-node.ts at lines 3 to
4, the import of the Creator interface uses a long relative path and disables
the eslint module boundary rule, indicating a violation. To fix this, remove the
eslint-disable comment and refactor the import to use a proper module path by
either exporting Creator from a shared or public API module or relocating the
interface to a shared library that respects module boundaries.

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