Skip to content

Develop#1526

Closed
michal13171 wants to merge 5 commits into
tomalaforge:mainfrom
michal13171:develop
Closed

Develop#1526
michal13171 wants to merge 5 commits into
tomalaforge:mainfrom
michal13171:develop

Conversation

@michal13171

@michal13171 michal13171 commented Jul 5, 2026

Copy link
Copy Markdown

✅ Challenge Submission Checklist

Start your PR title with: Answer:${challenge_number}

⚠️ Important Notice

If you would like personal feedback or a detailed review, please support the project on GitHub:

You can also submit a PR without sponsorship to:

  • Be listed among the answered challenges, or
  • Receive a review from a community member. 🔥

Summary by CodeRabbit

  • New Features

    • Added city cards alongside teacher and student cards, with matching images and content.
    • Introduced a shared “add one” action so users can add a new item from any card type.
  • Bug Fixes

    • Improved list rendering so names display correctly across different item types.
    • Deleting items now works consistently for city entries as well as teacher and student entries.

@coderabbitai

coderabbitai Bot commented Jul 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a functional CityCardComponent that fetches and syncs city data via FakeHttpService and CityStore, introduces CardModel and CardService for centralized card add/delete logic, refactors CardComponent to use an image input and delegate additions to CardService, and updates ListItemComponent to support city deletion. Student/teacher card components gain image bindings.

Changes

Card feature implementation

Layer / File(s) Summary
Card data shape
apps/angular/projection/src/app/model/card.model.ts
Adds CardModel interface with name, firstName, lastName, subject, and id fields.
CityCardComponent implementation
apps/angular/projection/src/app/component/city-card/city-card.component.ts
Converts the stub into a functional component injecting FakeHttpService and CityStore, adding cities, image, cardType fields, and syncing cities via ngOnInit.
CardService add logic
apps/angular/projection/src/app/services/card.service.ts
New root-provided service wiring TeacherStore, StudentStore, CityStore, exposing addOne(type) to add a generated entity to the matching store.
CardComponent image binding and service delegation
apps/angular/projection/src/app/ui/card/card.component.ts
Replaces store dependencies with CardService, restructures template with ngTemplateOutlet and an image input, updates list to non-null with firstName/name fallback, and delegates addNewItem to cardService.addOne.
ListItemComponent city deletion
apps/angular/projection/src/app/ui/list-item/list-item.component.ts
Injects CityStore and adds a CardType.CITY branch in delete calling cityStore.deleteOne.
Student and teacher card image bindings
apps/angular/projection/src/app/component/student-card/student-card.component.ts, apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts
Adds image template bindings and corresponding image properties ('student.webp', 'teacher.png').

Estimated code review effort: 3 (Moderate) | ~25 minutes

Sequence Diagram(s)

sequenceDiagram
  participant CityCardComponent
  participant FakeHttpService
  participant CityStore

  CityCardComponent->>FakeHttpService: fetch cities
  FakeHttpService-->>CityCardComponent: emit city data
  CityCardComponent->>CityStore: subscribe to cities$
  CityStore-->>CityCardComponent: emit updated cities
  CityCardComponent->>CityCardComponent: update cities array
Loading
sequenceDiagram
  participant CardComponent
  participant CardService
  participant TeacherStore
  participant StudentStore
  participant CityStore

  CardComponent->>CardService: addOne(type)
  alt type is TEACHER
    CardService->>TeacherStore: add generated teacher
  else type is STUDENT
    CardService->>StudentStore: add generated student
  else type is CITY
    CardService->>CityStore: add generated city
  end
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 inconclusive)

Check name Status Explanation Resolution
Title check ❓ Inconclusive The title is too generic and does not identify the challenge number or the main change. Rename it to a specific title that starts with Answer:1526 and briefly summarizes the primary code change.
✅ Passed checks (4 passed)
Check name Status Explanation
Description check ✅ Passed The description matches the required checklist and sponsorship notice template provided in this repository.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch develop

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

apps/angular/projection/src/app/component/city-card/city-card.component.ts

Oops! Something went wrong! :(

ESLint: 8.48.0

TypeError: Error while loading rule '@nx/enforce-module-boundaries': Cannot read properties of undefined (reading 'keyword')
Occurred while linting /apps/angular/projection/src/app/component/city-card/city-card.component.ts
at ensureGlobalProjectGraph (/node_modules/@nx/eslint-plugin/src/utils/project-graph-utils.js:35:51)
at readProjectGraph (/node_modules/@nx/eslint-plugin/src/utils/project-graph-utils.js:46:5)
at create (/node_modules/@nx/eslint-plugin/src/rules/enforce-module-boundaries.js:134:137)
at Object.create (/node_modules/@typescript-eslint/utils/src/eslint-utils/RuleCreator.ts:102:14)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:870:21)
at /node_modules/eslint/lib/linter/linter.js:1040:110
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:977:34)
at Linter._verifyWithoutProcessors (/node_modules/eslint/lib/linter/linter.js:1330:31)
at /node_modules/eslint/lib/linter/linter.js:1812:29

apps/angular/projection/src/app/component/student-card/student-card.component.ts

Oops! Something went wrong! :(

ESLint: 8.48.0

TypeError: Error while loading rule '@nx/enforce-module-boundaries': Cannot read properties of undefined (reading 'keyword')
Occurred while linting /apps/angular/projection/src/app/component/student-card/student-card.component.ts
at ensureGlobalProjectGraph (/node_modules/@nx/eslint-plugin/src/utils/project-graph-utils.js:35:51)
at readProjectGraph (/node_modules/@nx/eslint-plugin/src/utils/project-graph-utils.js:46:5)
at create (/node_modules/@nx/eslint-plugin/src/rules/enforce-module-boundaries.js:134:137)
at Object.create (/node_modules/@typescript-eslint/utils/src/eslint-utils/RuleCreator.ts:102:14)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:870:21)
at /node_modules/eslint/lib/linter/linter.js:1040:110
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:977:34)
at Linter._verifyWithoutProcessors (/node_modules/eslint/lib/linter/linter.js:1330:31)
at /node_modules/eslint/lib/linter/linter.js:1812:29

apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts

Oops! Something went wrong! :(

ESLint: 8.48.0

TypeError: Error while loading rule '@nx/enforce-module-boundaries': Cannot read properties of undefined (reading 'keyword')
Occurred while linting /apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts
at ensureGlobalProjectGraph (/node_modules/@nx/eslint-plugin/src/utils/project-graph-utils.js:35:51)
at readProjectGraph (/node_modules/@nx/eslint-plugin/src/utils/project-graph-utils.js:46:5)
at create (/node_modules/@nx/eslint-plugin/src/rules/enforce-module-boundaries.js:134:137)
at Object.create (/node_modules/@typescript-eslint/utils/src/eslint-utils/RuleCreator.ts:102:14)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:870:21)
at /node_modules/eslint/lib/linter/linter.js:1040:110
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:977:34)
at Linter._verifyWithoutProcessors (/node_modules/eslint/lib/linter/linter.js:1330:31)
at /node_modules/eslint/lib/linter/linter.js:1812:29

  • 4 others

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

Warning

⚠️ This pull request shows signs of AI-generated slop (description_diff_mismatch). It has been flagged by CodeRabbit slop detection and should be reviewed carefully.

@michal13171 michal13171 closed this Jul 5, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🧹 Nitpick comments (1)
apps/angular/projection/src/app/component/city-card/city-card.component.ts (1)

25-34: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Subscriptions in ngOnInit are never cleaned up.

Both fetchCities$ and store.cities$ are subscribed to without takeUntilDestroyed()/take(1)/async pipe. store.cities$ is backed by a singleton store, so if this component is destroyed and recreated (e.g., route navigation), the subscription persists and keeps reassigning this.cities on a destroyed instance — a memory leak and a source of stale updates.

♻️ Suggested fix
+import { DestroyRef, inject } from '`@angular/core`';
+import { takeUntilDestroyed } from '`@angular/core/rxjs-interop`';
...
   ngOnInit(): void {
-    this.http.fetchCities$.subscribe((s) => this.store.addAll(s));
-
-    this.store.cities$.subscribe((s) => (this.cities = s));
+    this.http.fetchCities$
+      .pipe(takeUntilDestroyed(this.destroyRef))
+      .subscribe((s) => this.store.addAll(s));
+
+    this.store.cities$
+      .pipe(takeUntilDestroyed(this.destroyRef))
+      .subscribe((s) => (this.cities = s));
   }

Alternatively, prefer the async pipe in the template for store.cities$ to avoid manual subscription management entirely.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/angular/projection/src/app/component/city-card/city-card.component.ts`
around lines 25 - 34, The `CityCardComponent.ngOnInit` subscriptions to
`fetchCities$` and `store.cities$` are never torn down, so update them to use
Angular-safe cleanup such as `takeUntilDestroyed()` (or replace the
`store.cities$` subscription with `async` in the template). Keep the existing
logic in `CityCardComponent`, `FakeHttpService.fetchCities$`, and `CityStore`,
but ensure `this.cities` is only assigned while the component is alive and the
one-time city fetch subscription completes or auto-unsubscribes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@apps/angular/projection/src/app/component/city-card/city-card.component.ts`:
- Around line 11-15: `CityCardComponent` is reusing the `bg-light-red`
customClass from `TeacherCardComponent`, which makes the city card look
identical to the teacher card. Update the `app-card` usage in
`CityCardComponent` to use a city-specific class instead of `bg-light-red`,
following the pattern used by `StudentCardComponent` and the other card
components so each card type remains visually distinct.

In `@apps/angular/projection/src/app/ui/card/card.component.ts`:
- Around line 37-47: The CardComponent input `list` is still declared as
`any[]`, which removes the type safety introduced by `CardModel` and leaves
template accesses like `item.name` and `item.firstName` unchecked. Update the
`CardComponent` `list` input to use `CardModel[]` and, since the binding is
required, switch it to Angular’s `@Input({ required: true })` instead of relying
on the non-null assertion. Keep the existing `CardComponent` API and
`CardService` usage unchanged.
- Around line 13-32: The card template in card.component.ts should be simplified
and made accessible: add an alt attribute to the img element (use the card’s
type to describe it), and remove the unnecessary `#cardContent` ng-template
wrapper since the content is rendered immediately via ngTemplateOutlet. Update
the template structure so the image, list, and Add button live in the direct
card content instead of being wrapped in an extra template layer.

---

Nitpick comments:
In `@apps/angular/projection/src/app/component/city-card/city-card.component.ts`:
- Around line 25-34: The `CityCardComponent.ngOnInit` subscriptions to
`fetchCities$` and `store.cities$` are never torn down, so update them to use
Angular-safe cleanup such as `takeUntilDestroyed()` (or replace the
`store.cities$` subscription with `async` in the template). Keep the existing
logic in `CityCardComponent`, `FakeHttpService.fetchCities$`, and `CityStore`,
but ensure `this.cities` is only assigned while the component is alive and the
one-time city fetch subscription completes or auto-unsubscribes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

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

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 5ac1f4a1-0366-4fe5-b2c2-64fe1d994f25

📥 Commits

Reviewing files that changed from the base of the PR and between 543770b and 3dd6adf.

📒 Files selected for processing (7)
  • apps/angular/projection/src/app/component/city-card/city-card.component.ts
  • apps/angular/projection/src/app/component/student-card/student-card.component.ts
  • apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts
  • apps/angular/projection/src/app/model/card.model.ts
  • apps/angular/projection/src/app/services/card.service.ts
  • apps/angular/projection/src/app/ui/card/card.component.ts
  • apps/angular/projection/src/app/ui/list-item/list-item.component.ts

Comment on lines +11 to +15
<app-card
[list]="cities"
[type]="cardType"
[image]="image"
customClass="bg-light-red"></app-card>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reused bg-light-red class from TeacherCardComponent.

Per the graph context, TeacherCardComponent already uses customClass="bg-light-red". StudentCardComponent uses a distinct bg-light-green. Reusing bg-light-red for CityCardComponent makes city and teacher cards visually indistinguishable — likely a copy/paste oversight rather than an intentional shared style.

🎨 Suggested fix
     <app-card
       [list]="cities"
       [type]="cardType"
       [image]="image"
-      customClass="bg-light-red"></app-card>
+      customClass="bg-light-blue"></app-card>
📝 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
<app-card
[list]="cities"
[type]="cardType"
[image]="image"
customClass="bg-light-red"></app-card>
<app-card
[list]="cities"
[type]="cardType"
[image]="image"
customClass="bg-light-blue"></app-card>
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/angular/projection/src/app/component/city-card/city-card.component.ts`
around lines 11 - 15, `CityCardComponent` is reusing the `bg-light-red`
customClass from `TeacherCardComponent`, which makes the city card look
identical to the teacher card. Update the `app-card` usage in
`CityCardComponent` to use a city-specific class instead of `bg-light-red`,
following the pattern used by `StudentCardComponent` and the other card
components so each card type remains visually distinct.

Comment on lines +13 to 32
<ng-template #cardContent>
<img src="assets/img/{{ image }}" width="200px" />

<section>
<app-list-item
*ngFor="let item of list"
[name]="item.firstName"
[id]="item.id"
[type]="type"></app-list-item>
</section>
<section>
<app-list-item
*ngFor="let item of list"
[name]="item.firstName ?? item.name"
[id]="item.id"
[type]="type"></app-list-item>
</section>

<button
class="rounded-sm border border-blue-500 bg-blue-300 p-2"
(click)="addNewItem()">
Add
</button>
<button
class="rounded-sm border border-blue-500 bg-blue-300 p-2"
(click)="addNewItem()">
Add
</button>
</ng-template>

<ng-container *ngTemplateOutlet="cardContent"></ng-container>
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the target component and nearby context
git ls-files apps/angular/projection/src/app/ui/card
echo "--- card.component.ts ---"
cat -n apps/angular/projection/src/app/ui/card/card.component.ts

echo "--- search for ng-content / ngTemplateOutlet in projection app ---"
rg -n "ng-content|ngTemplateOutlet|cardContent|CardType|alt=" apps/angular/projection/src/app -S

Repository: tomalaforge/angular-challenges

Length of output: 4295


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- usages of app-card ---"
rg -n "<app-card|selector: 'app-card'|selector: \"app-card\"" apps/angular/projection/src/app -S

echo "--- card-related component files ---"
git ls-files apps/angular/projection/src/app/component apps/angular/projection/src/app/ui

echo "--- card component and likely callers ---"
sed -n '1,220p' apps/angular/projection/src/app/component/city-card/city-card.component.ts
printf '\n---\n'
sed -n '1,220p' apps/angular/projection/src/app/component/student-card/student-card.component.ts
printf '\n---\n'
sed -n '1,220p' apps/angular/projection/src/app/component/teacher-card/teacher-card.component.ts

Repository: tomalaforge/angular-challenges

Length of output: 4184


Add an alt attribute and remove the extra template wrapper

  • The card image needs an alt attribute for screen readers, e.g. alt="{{ type }} card".
  • #cardContent is rendered immediately via *ngTemplateOutlet, so the extra ng-template layer can be inlined unless projected content is coming later.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/angular/projection/src/app/ui/card/card.component.ts` around lines 13 -
32, The card template in card.component.ts should be simplified and made
accessible: add an alt attribute to the img element (use the card’s type to
describe it), and remove the unnecessary `#cardContent` ng-template wrapper since
the content is rendered immediately via ngTemplateOutlet. Update the template
structure so the image, list, and Add button live in the direct card content
instead of being wrapped in an extra template layer.

Comment on lines 37 to 47
export class CardComponent {
@Input() list: any[] | null = null;
@Input() list!: any[];
@Input() type!: CardType;
@Input() customClass = '';
@Input() image: string = '';

CardType = CardType;

constructor(
private teacherStore: TeacherStore,
private studentStore: StudentStore,
) {}
constructor(private cardService: CardService) {}

addNewItem() {
if (this.type === CardType.TEACHER) {
this.teacherStore.addOne(randTeacher());
} else if (this.type === CardType.STUDENT) {
this.studentStore.addOne(randStudent());
}
this.cardService.addOne(this.type);
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Use CardModel[] instead of any[] for list.

list is typed any[], discarding the type safety CardModel (name, firstName, lastName, subject, id) was introduced for. Accesses like item.firstName, item.name, item.id in the template go unchecked. Since the input is now effectively required (non-null asserted), consider also using Angular's @Input({ required: true }) (supported since v16, confirmed for this project's Angular 17.1.0) so the compiler enforces the binding instead of relying solely on the ! assertion.

♻️ Proposed fix
+import { CardModel } from '../../model/card.model';
 ...
-  `@Input`() list!: any[];
+  `@Input`({ required: true }) list!: CardModel[];
📝 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 class CardComponent {
@Input() list: any[] | null = null;
@Input() list!: any[];
@Input() type!: CardType;
@Input() customClass = '';
@Input() image: string = '';
CardType = CardType;
constructor(
private teacherStore: TeacherStore,
private studentStore: StudentStore,
) {}
constructor(private cardService: CardService) {}
addNewItem() {
if (this.type === CardType.TEACHER) {
this.teacherStore.addOne(randTeacher());
} else if (this.type === CardType.STUDENT) {
this.studentStore.addOne(randStudent());
}
this.cardService.addOne(this.type);
}
import { CardModel } from '../../model/card.model';
export class CardComponent {
`@Input`({ required: true }) list!: CardModel[];
`@Input`() type!: CardType;
`@Input`() customClass = '';
`@Input`() image: string = '';
constructor(private cardService: CardService) {}
addNewItem() {
this.cardService.addOne(this.type);
}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@apps/angular/projection/src/app/ui/card/card.component.ts` around lines 37 -
47, The CardComponent input `list` is still declared as `any[]`, which removes
the type safety introduced by `CardModel` and leaves template accesses like
`item.name` and `item.firstName` unchecked. Update the `CardComponent` `list`
input to use `CardModel[]` and, since the binding is required, switch it to
Angular’s `@Input({ required: true })` instead of relying on the non-null
assertion. Keep the existing `CardComponent` API and `CardService` usage
unchanged.

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.

1 participant