Skip to content

fix: Typed Reactive Forms#94

Open
matheusandre1 wants to merge 13 commits intoomatheusmesmo:developfrom
matheusandre1:fix-issue42
Open

fix: Typed Reactive Forms#94
matheusandre1 wants to merge 13 commits intoomatheusmesmo:developfrom
matheusandre1:fix-issue42

Conversation

@matheusandre1
Copy link
Collaborator

@matheusandre1 matheusandre1 commented Mar 4, 2026

This is Pr Closes : #42

Summary by CodeRabbit

  • Documentation

    • Expanded contribution guidelines and updated READMEs with clearer setup, deployment, and usage instructions
  • Refactor

    • Standardized and strengthened form handling across the app for safer, more consistent behavior
  • UI Changes

    • Removed several edit/create dialogs for categories and shopping lists, changing available in-app dialog flows

@github-actions github-actions bot changed the base branch from main to develop March 4, 2026 19:48
@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

🤖 Attention @matheusandre1! Only the repository owner can target the 'main' branch directly. I've automatically updated your PR to target 'develop' instead. Thank you! 🚀

@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

🤖 Hi @matheusandre1, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 4, 2026

🤖 I'm sorry @matheusandre1, but I was unable to process your request. Please see the logs for more details.

Copy link
Owner

@omatheusmesmo omatheusmesmo left a comment

Choose a reason for hiding this comment

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

Hello @matheusandre1 ! I have performed a comprehensive review of the changes in PR #94. The implementation significantly modernizes the application's form management and state handling.

✅ Key Improvements

  1. Strict Type Safety: Most forms (Login, Signup, CategoryDialog, ItemDialog, ShoppingListDialog, UnitDialog, and ListShareDialog) have been successfully migrated to strongly typed FormGroup and FormControl structures.
  2. Type Integrity: The use of this.fb.nonNullable.group is an excellent choice. It ensures that form controls do not revert to null during resets, maintaining strict type consistency.
  3. Modern Angular Patterns:
    • Dependency Injection: Consistent adoption of the inject() function over traditional constructor injection.
    • Signals: Effective use of signals for reactive state management (e.g., existingCategories, units).
    • Change Detection: Application of ChangeDetectionStrategy.OnPush for better performance in new components.
  4. Robust Validation: The addition of the duplicateNameValidator improves data integrity and enhances the user experience by providing real-time feedback for duplicate entries.

⚠️ Required Changes (Blocking 100% Compliance)

1. Incomplete Migration - ListItemDialogComponent

The file frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts is still using FormsModule and ngModel. To fulfill the "100% Typed Reactive Forms" requirement, this component must be refactored to use ReactiveFormsModule with typed controls.

2. Dead Code & Redundancy Cleanup

The following directories contain old, untyped versions of components that are no longer imported anywhere in the project and should be removed:

  • Path: frontend/src/app/list/components/categories-management/category-dialog/
    • Reason: Replaced by the new typed version in frontend/src/app/list/components/category-dialog/.
  • Path: frontend/src/app/list/components/shopping-list/list-dialog/
    • Reason: Replaced by the new typed ShoppingListDialogComponent.

Summary: This PR is a major step forward in code quality. Once the ListItemDialogComponent is migrated and the redundant code is purged, it will be fully compliant with the project goals. Great work! 🚀

You also need to resolve some conflicts to merge.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

🤖 Hi @matheusandre1, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

🤖 Hi @matheusandre1, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

🤖 I'm sorry @matheusandre1, but I was unable to process your request. Please see the logs for more details.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

🤖 Hi @matheusandre1, I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@github-actions
Copy link
Contributor

github-actions bot commented Mar 8, 2026

🤖 I'm sorry @matheusandre1, but I was unable to process your request. Please see the logs for more details.

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5caa2216-10a2-4827-9241-7c7e501eebf8

📥 Commits

Reviewing files that changed from the base of the PR and between 61a1b3b and a21e962.

📒 Files selected for processing (3)
  • frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html
  • frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts
  • frontend/src/app/shared/components/category/category.component.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • frontend/src/app/shared/components/category/category.component.ts
  • frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html

📝 Walkthrough

Walkthrough

This PR strengthens Angular form typing and migrates several templates to reactive forms, removes two dialog components, adds a new GitHub Actions "gemini-dispatch" workflow, and applies assorted documentation, license, and CI formatting updates.

Changes

Cohort / File(s) Summary
Form Type Safety & Non-nullable Forms
frontend/src/app/auth/login/login.component.ts, frontend/src/app/auth/signup/signup.component.ts, frontend/src/app/list/components/category-dialog/category-dialog.component.ts, frontend/src/app/list/components/items-management/item-dialog/item-dialog.component.ts, frontend/src/app/list/components/units-management/unit-dialog/unit-dialog.component.ts, frontend/src/app/shared/components/category/category.component.ts, frontend/src/app/shared/components/item/item.component.ts, frontend/src/app/shared/components/list-item/list-item.component.ts, frontend/src/app/shared/components/unit/unit.component.ts
Converted loose FormGroup usages to strongly-typed FormGroup<...> with FormControl<T>, used fb.nonNullable.group() / nonNullable.control(), replaced .value reads with .getRawValue(), and added defensive checks/trimming where applicable.
Reactive Forms Migration (templates & logic)
frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts, frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html
Replaced signal/ngModel state with a reactive FormGroup + validators; updated template to use formGroup / formControlName and updated save flow to validate via the form.
Dialog Component Removal
frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.ts, frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.html, frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.scss, frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.ts, frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.html, frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.scss
Deleted two dialog components (TS, HTML, SCSS) used for category and shopping-list dialogs, removing their UI, styles, and dialog handling classes.
Dialog & Submission Handler Updates
frontend/src/app/list/components/list-share-dialog/list-share-dialog.component.ts, frontend/src/app/shared/components/*/*.ts
Updated handlers to destructure from .getRawValue() and use extracted values (e.g., permission, email, ids); adjusted null/empty checks and normalization before building payloads.
New Workflow — Gemini Dispatch
.github/workflows/gemini-dispatch.yml
Added a new GitHub Actions workflow that parses incoming events/commands, mints tokens if required, posts acknowledgement comments, and routes to review/triage/invoke/fallthrough orchestration jobs.
Workflows — Formatting Only
.github/workflows/backend.yml, .github/workflows/frontend.yml, .github/workflows/greetings.yml, .github/workflows/root.yml, .github/workflows/sync-develop.yml
Cosmetic YAML/quote/indentation/formatting changes across multiple workflow files; no behavioral changes.
Docs, Licenses & Config Formatting
CONTRIBUTING.md, README.md, LICENSE.md, backend/LICENSE.md, backend/README.md, backend/bruno/README.md, backend/bruno/bruno.json, backend/docker-compose.yml, docker-compose.yml
Expanded CONTRIBUTING.md; updated README and API path references; reformatted license texts; minor JSON/YAML whitespace and quote adjustments.

Sequence Diagram(s)

sequenceDiagram
    participant Event as GitHub Event
    participant Dispatcher as Gemini Dispatch Job
    participant Auth as Token Service
    participant GitHub as GitHub API
    participant Orchestrator as Downstream Orchestration

    Event->>Dispatcher: webhook / comment / PR payload
    Dispatcher->>Dispatcher: parse command & extract context
    alt requires auth
        Dispatcher->>Auth: mint identity token (if APP_ID)
        Auth-->>Dispatcher: token
    end
    Dispatcher->>GitHub: post acknowledgement comment
    Dispatcher->>Orchestrator: set outputs (command, request, context)
    Orchestrator->>Orchestrator: route to review/triage/invoke/fallthrough jobs
    Orchestrator->>GitHub: (optional) post results or failure comment
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 I nibble code with careful paws,

Forms now typed with tidy laws.
Old dialogs hopped off, quiet and sly,
A dispatch workflow scouts the sky.
Hooray — the rabbit gives a cheerful cry!

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: converting reactive forms to strongly-typed forms across the frontend application.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
📝 Coding Plan
  • Generate coding plan for human review comments

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

@omatheusmesmo
Copy link
Owner

@coderabbitai check this PR.

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

@omatheusmesmo Sure! I'll review the PR right away.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@github-actions
Copy link
Contributor

🤖 Hi @coderabbitai[bot], I've received your request, and I'm working on it now! You can track my progress in the logs for more details.

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

Tip

For best results, initiate chat on the files or code changes.

@omatheusmesmo Thanks for the update! The review has been triggered and the GitHub Actions workflow is processing it. The results will be posted once the run completes. You can track the progress in the workflow logs. 🚀

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.

🧹 Nitpick comments (2)
frontend/src/app/list/components/units-management/units-management.component.ts (1)

57-60: Remove the debug console.log calls before merge.

These logs will fire on every units refresh and clutter the browser console with response payloads during normal operation. If you still need diagnostics here, keep them behind a dev-only logger instead.

Proposed cleanup
         tap((response) => {
-          console.log('UnitsManagementComponent - Units loaded:', response);
-          if (!response || response.length === 0) {
-            console.log('UnitsManagementComponent - No units found');
-          }
           this.units.set(response || []);
         }),
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/list/components/units-management/units-management.component.ts`
around lines 57 - 60, Remove the debug console.log calls in
UnitsManagementComponent that print the response and "No units found" during the
units refresh; locate the logs inside the method that handles the units API
response (the block that currently contains
console.log('UnitsManagementComponent - Units loaded:', response) and
console.log('UnitsManagementComponent - No units found')) and either delete them
or replace them with a dev-only logger/verbose flag check so normal production
runs do not emit the payload to the browser console.
frontend/src/app/list/components/category-dialog/category-dialog.component.ts (1)

106-115: Consider using direct control access for better type safety.

With typed forms, you can access controls directly via this.categoryForm.controls.name instead of this.categoryForm.get('name'). This eliminates optional chaining and provides full type inference.

♻️ Suggested improvement
 updateNameValidator(): void {
   const names = this.existingCategories().map((c) => c.name);
   const originalName =
     this.isEdit() && this.data.category ? this.data.category.name : undefined;

-  this.categoryForm
-    .get('name')
-    ?.addValidators(duplicateNameValidator(names, originalName));
-  this.categoryForm.get('name')?.updateValueAndValidity();
+  this.categoryForm.controls.name.addValidators(
+    duplicateNameValidator(names, originalName)
+  );
+  this.categoryForm.controls.name.updateValueAndValidity();
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In
`@frontend/src/app/list/components/category-dialog/category-dialog.component.ts`
around lines 106 - 115, In updateNameValidator(), replace the optional
get('name') calls with a typed direct access to the control via
this.categoryForm.controls.name (or this.categoryForm.controls['name']) so you
avoid optional chaining and gain type inference; update the two spots where
.get('name') is used to reference the control variable (e.g., const nameCtrl =
this.categoryForm.controls.name) and then call
nameCtrl.addValidators(duplicateNameValidator(names, originalName)) and
nameCtrl.updateValueAndValidity(); ensure categoryForm is typed so controls.name
has the correct FormControl type.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In
`@frontend/src/app/list/components/category-dialog/category-dialog.component.ts`:
- Around line 106-115: In updateNameValidator(), replace the optional
get('name') calls with a typed direct access to the control via
this.categoryForm.controls.name (or this.categoryForm.controls['name']) so you
avoid optional chaining and gain type inference; update the two spots where
.get('name') is used to reference the control variable (e.g., const nameCtrl =
this.categoryForm.controls.name) and then call
nameCtrl.addValidators(duplicateNameValidator(names, originalName)) and
nameCtrl.updateValueAndValidity(); ensure categoryForm is typed so controls.name
has the correct FormControl type.

In
`@frontend/src/app/list/components/units-management/units-management.component.ts`:
- Around line 57-60: Remove the debug console.log calls in
UnitsManagementComponent that print the response and "No units found" during the
units refresh; locate the logs inside the method that handles the units API
response (the block that currently contains
console.log('UnitsManagementComponent - Units loaded:', response) and
console.log('UnitsManagementComponent - No units found')) and either delete them
or replace them with a dev-only logger/verbose flag check so normal production
runs do not emit the payload to the browser console.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 0d0a68c2-7589-4737-9270-fabf1bb96a0d

📥 Commits

Reviewing files that changed from the base of the PR and between e4ed853 and b49a572.

📒 Files selected for processing (21)
  • frontend/src/app/auth/login/login.component.ts
  • frontend/src/app/auth/signup/signup.component.ts
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.html
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.scss
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.ts
  • frontend/src/app/list/components/category-dialog/category-dialog.component.ts
  • frontend/src/app/list/components/items-management/item-dialog/item-dialog.component.ts
  • frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html
  • frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts
  • frontend/src/app/list/components/list-share-dialog/list-share-dialog.component.ts
  • frontend/src/app/list/components/shopping-list-dialog/shopping-list-dialog.component.ts
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.html
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.scss
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.ts
  • frontend/src/app/list/components/units-management/unit-dialog/unit-dialog.component.ts
  • frontend/src/app/list/components/units-management/units-management.component.ts
  • frontend/src/app/shared/components/category/category.component.ts
  • frontend/src/app/shared/components/item/item.component.ts
  • frontend/src/app/shared/components/list-item/list-item.component.html
  • frontend/src/app/shared/components/list-item/list-item.component.ts
  • frontend/src/app/shared/components/unit/unit.component.ts
💤 Files with no reviewable changes (6)
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.ts
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.html
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.ts
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.scss
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.html
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.scss

Repository owner deleted a comment from github-actions bot Mar 12, 2026
Repository owner deleted a comment from github-actions bot Mar 12, 2026
Repository owner deleted a comment from github-actions bot Mar 12, 2026
Repository owner deleted a comment from github-actions bot Mar 12, 2026
@github-actions
Copy link
Contributor

🤖 I'm sorry @coderabbitai[bot], but I was unable to process your request. Please see the logs for more details.

@coderabbitai
Copy link

coderabbitai bot commented Mar 12, 2026

@omatheusmesmo — Apologies for the disruption! It looks like the previous review run encountered an error. I'm re-triggering the review now. 🔄

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@omatheusmesmo omatheusmesmo force-pushed the develop branch 2 times, most recently from 4ad5927 to 27ebf42 Compare March 12, 2026 20:56
@omatheusmesmo omatheusmesmo self-assigned this Mar 12, 2026
matheusandre1 and others added 4 commits March 13, 2026 11:38
# Conflicts:
#	frontend/src/app/auth/login/login.component.ts
#	frontend/src/app/auth/signup/signup.component.ts
#	frontend/src/app/list/components/list-share-dialog/list-share-dialog.component.ts
#	frontend/src/app/list/components/shopping-list-dialog/shopping-list-dialog.component.ts
#	frontend/src/app/shared/components/category/category.component.ts
#	frontend/src/app/shared/components/item/item.component.ts
#	frontend/src/app/shared/components/list-item/list-item.component.ts
#	frontend/src/app/shared/components/unit/unit.component.ts
# Conflicts:
#	frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.html
#	frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.ts
#	frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html
#	frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts
#	frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.html
#	frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.ts
… for main

# Conflicts:
#	.github/workflows/branch-enforcer.yml
#	.github/workflows/gemini-dispatch.yml
matheusandre1 and others added 5 commits March 13, 2026 11:42
# Conflicts:
#	.github/workflows/gemini-dispatch.yml
# Conflicts:
#	frontend/src/app/list/components/items-management/items-management.component.ts
#	frontend/src/app/list/components/list-details/list-details.component.ts
#	frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html
#	frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts
#	frontend/src/app/list/components/units-management/units-management.component.ts
#	frontend/src/app/shared/services/auth.service.ts
# Conflicts:
#	frontend/src/app/list/components/items-management/items-management.component.ts
#	frontend/src/app/list/components/list-details/list-details.component.ts
#	frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts
#	frontend/src/app/list/components/units-management/units-management.component.ts
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: 11

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
frontend/src/app/shared/components/list-item/list-item.component.ts (1)

101-109: ⚠️ Potential issue | 🟠 Major

Use extracted raw values when creating listItemData

The quantity variable extracted at line 101 is unused (triggers ESLint unused variable warning), and the DTO unnecessarily rebuilds values from this.listItemForm.value instead of using the already-extracted values from getRawValue(). Extract unitPrice alongside itemId and quantity, then use these variables directly in the DTO to ensure consistency and stronger nullability handling.

Suggested fix
-    const { itemId, quantity } = this.listItemForm.getRawValue();
+    const { itemId, quantity, unitPrice } = this.listItemForm.getRawValue();
     if (itemId === null) return;
 
     const listItemData: ListItemRequestDTO = {
       listId: this.listId,
-      itemId: this.listItemForm.value.itemId,
-      quantity: this.listItemForm.value.quantity,
-      unitPrice: this.listItemForm.value.unitPrice,
+      itemId,
+      quantity,
+      unitPrice,
     };
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@frontend/src/app/shared/components/list-item/list-item.component.ts` around
lines 101 - 109, The extracted values from this.listItemForm.getRawValue() are
not being used when building listItemData, leaving quantity unused and causing
inconsistencies; update the extraction to const { itemId, quantity, unitPrice }
= this.listItemForm.getRawValue() and then construct the ListItemRequestDTO
(listItemData) using these variables (itemId, quantity, unitPrice) along with
this.listId to eliminate the unused-variable warning and ensure
consistent/null-safe values from getRawValue() rather than re-reading
this.listItemForm.value.
🧹 Nitpick comments (5)
.github/workflows/greetings.yml (1)

12-16: Consider pinning to a specific action version for improved security.

The indentation fix looks correct. However, actions/first-interaction@v1 uses a floating major version tag. For better supply chain security and reproducibility, consider pinning to a full commit SHA or a specific semantic version tag (e.g., v1.1.4).

🔒 Suggested improvement
-      - uses: actions/first-interaction@v1
+      - uses: actions/first-interaction@v1.1.4  # Or pin to full SHA

As per coding guidelines: "Use specific action versions (commit SHA or tagged versions)."

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

In @.github/workflows/greetings.yml around lines 12 - 16, The workflow uses the
floating tag actions/first-interaction@v1; replace that with a pinned version
(either a specific semver tag like actions/first-interaction@v1.1.4 or the full
commit SHA) in the step that references actions/first-interaction to improve
supply-chain security and reproducibility, updating the `uses:` value
accordingly and keeping the existing `with:` inputs unchanged.
.github/workflows/gemini-dispatch.yml (1)

10-11: Security-sensitive trigger: pull_request_target with write permissions.

Using pull_request_target runs the workflow in the context of the base branch with elevated permissions. While authorization checks on lines 56-58 restrict command invocation to trusted users (OWNER/MEMBER/COLLABORATOR), ensure that:

  1. The REQUEST variable (line 88) containing user-provided content is never directly interpolated into shell commands
  2. The reusable workflows (gemini-review.yml, etc.) also sanitize any user-controlled input

The current implementation appears safe since user input is processed via actions/github-script rather than shell interpolation.

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

In @.github/workflows/gemini-dispatch.yml around lines 10 - 11, The workflow
uses pull_request_target and a REQUEST variable that may contain user-provided
content; ensure REQUEST is never directly interpolated into any shell/runner
step and instead is passed as a safe input to actions/github-script or to
reusable workflows (e.g., gemini-review.yml) where it is sanitized or
JSON.stringified before use; review all reusable workflows referenced
(gemini-review.yml, etc.) to validate they treat inputs from REQUEST as data
(not shell commands), perform explicit escaping/validation in their entrypoint
functions, and reduce permissions to least-privilege where possible (or consider
switching the trigger from pull_request_target to pull_request) so untrusted PR
content cannot lead to command execution.
frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html (1)

8-8: Use native @for instead of *ngFor

Line 8 should follow the project’s native control-flow standard.

Suggested fix
-      <mat-option *ngFor="let item of items" [value]="item.id">
-        {{ item.name }} ({{ item.unit.symbol }})
-      </mat-option>
+      `@for` (item of items; track item.id) {
+        <mat-option [value]="item.id">
+          {{ item.name }} ({{ item.unit.symbol }})
+        </mat-option>
+      }

As per coding guidelines, "Use native control flow (@if, @for, @switch) instead of *ngIf, *ngFor."

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

In
`@frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html`
at line 8, Replace the template's structural directive "*ngFor" with the
project's native control-flow "@for" in the list-item-dialog template: change
the mat-option line that iterates over items (currently using *ngFor="let item
of items") to use `@for` so the loop binds the same iterator (item) and collection
(items) and continues to set the option value from item.id; ensure the attribute
name is updated on the mat-option element in list-item-dialog.component.html and
that any template expressions referencing item or item.id remain unchanged.
frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts (2)

45-45: Prefer signal state for items

This regresses local state from signal-based to plain property; keep it reactive and consistent.

Suggested fix
-  items: ItemResponseDTO[] = [];
+  readonly items = signal<ItemResponseDTO[]>([]);
...
-      this.items = items;
+      this.items.set(items);

As per coding guidelines, "Use signals for local state. Do NOT use mutate, use update or set."

Also applies to: 76-76

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

In
`@frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts`
at line 45, Replace the plain property items: ItemResponseDTO[] = [] with a
signal-based state and update all usages to use the signal API; specifically,
declare items as a signal (e.g., items = signal<ItemResponseDTO[]>([])) in the
ListItemDialogComponent, and replace any direct array mutations or reads (e.g.,
pushes, assignments, or property accesses referenced around methods in this
component) with items.update(prev => ...) or items.set(...) and read the value
via items() rather than mutating the array directly—ensure no use of mutate and
update all references accordingly.

4-4: Use inject() instead of constructor injection

The constructor now handles all DI; project standard is inject()-based dependency wiring.

Suggested fix
 import {
   ChangeDetectionStrategy,
   Component,
-  Inject,
+  inject,
   OnInit,
 } from '@angular/core';
...
-  constructor(
-    public dialogRef: MatDialogRef<ListItemDialogComponent>,
-    `@Inject`(MAT_DIALOG_DATA)
-    public data: { listItem?: ListItemResponseDTO; listId: number },
-    private itemService: ItemService,
-    private fb: FormBuilder,
-  ) {
+  readonly dialogRef = inject(MatDialogRef<ListItemDialogComponent>);
+  readonly data = inject(MAT_DIALOG_DATA) as {
+    listItem?: ListItemResponseDTO;
+    listId: number;
+  };
+  private readonly itemService = inject(ItemService);
+  private readonly fb = inject(FormBuilder);
+
+  constructor() {
     const isEdit = !!data.listItem;

As per coding guidelines, "Use the inject() function instead of constructor injection."

Also applies to: 51-57

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

In
`@frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts`
at line 4, ListItemDialogComponent currently uses constructor injection; replace
that with Angular's inject() pattern by removing the constructor and creating
class-level readonly properties initialized via inject() for each dependency
previously injected (e.g., MatDialogRef, MAT_DIALOG_DATA, FormBuilder,
ChangeDetectorRef, Store — whatever symbols appear in the component's
constructor). Ensure you import inject from `@angular/core`, assign the exact
tokens/types used in the constructor to readonly fields on the class, and update
any usages to reference those fields; run the request_verification after
changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/README.md`:
- Around line 16-18: The three TOC anchor fragments for "Shopping Lists",
"Shopping List Items", and "Shopping List User Permissions" are invalid; update
their link targets so they match the actual Markdown heading IDs used in the
document (use the exact heading text lowercased, spaces replaced with hyphens,
and strip any parentheses/slashes). Locate the TOC entries that show "[Shopping
Lists (/shopplists)]", "[Shopping List Items (/shopplist/{shopplistId}/items)]"
and "[Shopping List User Permissions (/shopplist/{shopplistId}/permissions)]"
and replace their fragment parts with the correct anchors generated from the
headings (e.g., shopping-lists, shopping-list-items,
shopping-list-user-permissions) so in-page navigation works.

In
`@frontend/src/app/list/components/list-share-dialog/list-share-dialog.component.ts`:
- Around line 79-80: The class currently declares two members named isLoading
(readonly isLoading = signal(false); and isLoading = false;) which conflicts;
remove the plain boolean declaration and keep the signal-based member (readonly
isLoading = signal(false)); then update any usages of isLoading in
ListShareDialogComponent to use the signal APIs (call isLoading() or read
isLoading()/.value per your signal convention) instead of treating it as a
boolean so references compile cleanly.
- Line 11: Remove the unused FormControl import: delete the FormControl import
from the import list at the top of list-share-dialog.component.ts since the
component uses FormBuilder.group() and the returned FormGroup (and its controls)
directly; ensure no other references to FormControl exist in the file (if found,
replace them to use the FormGroup/FormControl accessed via the form instance)
and run a build/linter to confirm the unused import is gone.

In
`@frontend/src/app/list/components/shopping-list-dialog/shopping-list-dialog.component.ts`:
- Around line 13-15: The import list in shopping-list-dialog.component.ts
includes an unused FormControl; remove FormControl from the import statement
(leave FormBuilder and FormGroup intact) so the ShoppingListDialogComponent no
longer imports an unused symbol and the lint/type checks will pass.
- Around line 83-86: Remove the redundant redeclaration that overwrites the
already-validated/trimmed name: do not use "const { name } =
this.listForm.getRawValue()" — instead populate listData.name from the
previously trimmed/validated value (the value you trimmed/validated earlier from
this.listForm) so ShoppingListRequestDTO.listData uses the trimmed name; update
the code that builds listData to reference the existing trimmed variable or
re-read and trim via this.listForm.get('name')?.value.trim() rather than
destructuring getRawValue().

In `@frontend/src/app/shared/components/category/category.component.ts`:
- Line 11: The import FormControl is unused in CategoryComponent; either remove
FormControl from the import list at the top of category.component.ts or consume
it by typing/instantiating a control (e.g., add a typed FormControl field or use
new FormControl in your form group declaration) so the import is used; update
the class (CategoryComponent) form declaration accordingly to eliminate the
unused-import error.
- Around line 79-80: The categoryForm is declared as an untyped FormGroup which
makes getRawValue() return an implicitly typed object and loses the compile-time
guarantee for name; change the categoryForm declaration to FormGroup<{ name:
FormControl<string> }> and build it with this.fb.nonNullable.group(...) (and use
this.fb.nonNullable.control(...) for the name control) so getRawValue() returns
a properly typed object and you can safely destructure name into the Category
payload used by the Category type.

In `@frontend/src/app/shared/components/item/item.component.ts`:
- Line 11: The import list in item.component.ts includes an unused symbol
FormControl; remove FormControl from the import statement so the file no longer
imports an identifier that isn't referenced (update the import that currently
lists FormControl alongside other Angular forms symbols used by ItemComponent,
e.g., remove "FormControl" from that import and run lint/TS compile to confirm
no other references remain).

In `@frontend/src/app/shared/components/list-item/list-item.component.ts`:
- Line 11: The import for FormControl is unused in list-item.component (remove
the unused symbol to satisfy lint); locate the import statement that includes
FormControl and delete FormControl from the import list (or remove the entire
import line if it only contains FormControl) so the file no longer imports the
unused FormControl symbol.

In `@frontend/src/app/shared/components/unit/unit.component.ts`:
- Around line 82-83: The payload uses raw values from unitForm which can contain
leading/trailing or whitespace-only strings; before creating unitData in the
component (where you call this.unitForm.getRawValue()), trim and normalize the
fields: const { name: rawName, symbol: rawSymbol } =
this.unitForm.getRawValue(); const name = (rawName || '').trim(); const symbol =
(rawSymbol || '').trim(); then build const unitData: Unit = { name, symbol } and
handle/validate empty strings (reject or show validation) so whitespace-only
values are not persisted.
- Line 11: The import statement includes an unused symbol FormControl which
triggers a no-unused-vars lint/build error; remove FormControl from the import
list (the import that currently includes FormControl) in
frontend/src/app/shared/components/unit/unit.component.ts or, if FormControl was
intended to be used, replace its usage with the appropriate reference in the
UnitComponent so the symbol is actually used; most likely simply delete
FormControl from the import to resolve the lint failure.

---

Outside diff comments:
In `@frontend/src/app/shared/components/list-item/list-item.component.ts`:
- Around line 101-109: The extracted values from this.listItemForm.getRawValue()
are not being used when building listItemData, leaving quantity unused and
causing inconsistencies; update the extraction to const { itemId, quantity,
unitPrice } = this.listItemForm.getRawValue() and then construct the
ListItemRequestDTO (listItemData) using these variables (itemId, quantity,
unitPrice) along with this.listId to eliminate the unused-variable warning and
ensure consistent/null-safe values from getRawValue() rather than re-reading
this.listItemForm.value.

---

Nitpick comments:
In @.github/workflows/gemini-dispatch.yml:
- Around line 10-11: The workflow uses pull_request_target and a REQUEST
variable that may contain user-provided content; ensure REQUEST is never
directly interpolated into any shell/runner step and instead is passed as a safe
input to actions/github-script or to reusable workflows (e.g.,
gemini-review.yml) where it is sanitized or JSON.stringified before use; review
all reusable workflows referenced (gemini-review.yml, etc.) to validate they
treat inputs from REQUEST as data (not shell commands), perform explicit
escaping/validation in their entrypoint functions, and reduce permissions to
least-privilege where possible (or consider switching the trigger from
pull_request_target to pull_request) so untrusted PR content cannot lead to
command execution.

In @.github/workflows/greetings.yml:
- Around line 12-16: The workflow uses the floating tag
actions/first-interaction@v1; replace that with a pinned version (either a
specific semver tag like actions/first-interaction@v1.1.4 or the full commit
SHA) in the step that references actions/first-interaction to improve
supply-chain security and reproducibility, updating the `uses:` value
accordingly and keeping the existing `with:` inputs unchanged.

In
`@frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html`:
- Line 8: Replace the template's structural directive "*ngFor" with the
project's native control-flow "@for" in the list-item-dialog template: change
the mat-option line that iterates over items (currently using *ngFor="let item
of items") to use `@for` so the loop binds the same iterator (item) and collection
(items) and continues to set the option value from item.id; ensure the attribute
name is updated on the mat-option element in list-item-dialog.component.html and
that any template expressions referencing item or item.id remain unchanged.

In
`@frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts`:
- Line 45: Replace the plain property items: ItemResponseDTO[] = [] with a
signal-based state and update all usages to use the signal API; specifically,
declare items as a signal (e.g., items = signal<ItemResponseDTO[]>([])) in the
ListItemDialogComponent, and replace any direct array mutations or reads (e.g.,
pushes, assignments, or property accesses referenced around methods in this
component) with items.update(prev => ...) or items.set(...) and read the value
via items() rather than mutating the array directly—ensure no use of mutate and
update all references accordingly.
- Line 4: ListItemDialogComponent currently uses constructor injection; replace
that with Angular's inject() pattern by removing the constructor and creating
class-level readonly properties initialized via inject() for each dependency
previously injected (e.g., MatDialogRef, MAT_DIALOG_DATA, FormBuilder,
ChangeDetectorRef, Store — whatever symbols appear in the component's
constructor). Ensure you import inject from `@angular/core`, assign the exact
tokens/types used in the constructor to readonly fields on the class, and update
any usages to reference those fields; run the request_verification after
changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 6b4878fa-2e43-4426-b4a7-b79c9e9bd58b

📥 Commits

Reviewing files that changed from the base of the PR and between b49a572 and f7846b9.

📒 Files selected for processing (34)
  • .github/workflows/backend.yml
  • .github/workflows/frontend.yml
  • .github/workflows/gemini-dispatch.yml
  • .github/workflows/greetings.yml
  • .github/workflows/root.yml
  • .github/workflows/sync-develop.yml
  • CONTRIBUTING.md
  • LICENSE.md
  • README.md
  • backend/LICENSE.md
  • backend/README.md
  • backend/bruno/README.md
  • backend/bruno/bruno.json
  • backend/docker-compose.yml
  • docker-compose.yml
  • frontend/src/app/auth/login/login.component.ts
  • frontend/src/app/auth/signup/signup.component.ts
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.html
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.scss
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.ts
  • frontend/src/app/list/components/category-dialog/category-dialog.component.ts
  • frontend/src/app/list/components/items-management/item-dialog/item-dialog.component.ts
  • frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html
  • frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts
  • frontend/src/app/list/components/list-share-dialog/list-share-dialog.component.ts
  • frontend/src/app/list/components/shopping-list-dialog/shopping-list-dialog.component.ts
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.html
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.scss
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.ts
  • frontend/src/app/list/components/units-management/unit-dialog/unit-dialog.component.ts
  • frontend/src/app/shared/components/category/category.component.ts
  • frontend/src/app/shared/components/item/item.component.ts
  • frontend/src/app/shared/components/list-item/list-item.component.ts
  • frontend/src/app/shared/components/unit/unit.component.ts
💤 Files with no reviewable changes (7)
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.html
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.scss
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.ts
  • frontend/src/app/list/components/categories-management/category-dialog/category-dialog.component.ts
  • backend/bruno/README.md
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.scss
  • frontend/src/app/list/components/shopping-list/list-dialog/list-dialog.component.html
✅ Files skipped from review due to trivial changes (8)
  • backend/bruno/bruno.json
  • README.md
  • .github/workflows/root.yml
  • .github/workflows/backend.yml
  • .github/workflows/frontend.yml
  • docker-compose.yml
  • backend/docker-compose.yml
  • .github/workflows/sync-develop.yml
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/src/app/auth/login/login.component.ts
  • frontend/src/app/list/components/category-dialog/category-dialog.component.ts
  • frontend/src/app/auth/signup/signup.component.ts
  • frontend/src/app/list/components/units-management/unit-dialog/unit-dialog.component.ts

Comment on lines 83 to 86
const { name } = this.listForm.getRawValue();
const listData: ShoppingListRequestDTO = {
name,
idUser: userId,
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify duplicate declarations and inspect the onSubmit block
sed -n '64,90p' frontend/src/app/list/components/shopping-list-dialog/shopping-list-dialog.component.ts
rg -nP 'const\s+name\s*=|const\s+\{\s*name\s*\}\s*=' frontend/src/app/list/components/shopping-list-dialog/shopping-list-dialog.component.ts

Repository: omatheusmesmo/ShoppMate

Length of output: 951


Remove the name redeclaration to use the validated, trimmed value in the request.

Line 83 redeclares name in the same scope and overwrites the trimmed value from line 75. This causes listData.name to include leading/trailing whitespace despite the validation logic on line 75 trimming it.

🔧 Proposed fix
-      const { name } = this.listForm.getRawValue();
       const listData: ShoppingListRequestDTO = {
         name,
         idUser: userId,
       };
🧰 Tools
🪛 Biome (2.4.6)

[error] 83-83: Shouldn't redeclare 'name'. Consider to delete it or rename it.

(lint/suspicious/noRedeclare)

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

In
`@frontend/src/app/list/components/shopping-list-dialog/shopping-list-dialog.component.ts`
around lines 83 - 86, Remove the redundant redeclaration that overwrites the
already-validated/trimmed name: do not use "const { name } =
this.listForm.getRawValue()" — instead populate listData.name from the
previously trimmed/validated value (the value you trimmed/validated earlier from
this.listForm) so ShoppingListRequestDTO.listData uses the trimmed name; update
the code that builds listData to reference the existing trimmed variable or
re-read and trim via this.listForm.get('name')?.value.trim() rather than
destructuring getRawValue().

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.

🧹 Nitpick comments (1)
frontend/src/app/shared/components/item/item.component.ts (1)

59-63: Form lacks strong typing, undermining the PR's "Typed Reactive Forms" objective.

The itemForm is declared as FormGroup without type parameters, so getRawValue() returns any. This loses compile-time type safety when building ItemRequestDTO.

To align with the PR's goal and other components that adopted strongly-typed forms, consider typing the form:

♻️ Suggested strongly-typed form
+import { FormControl } from '@angular/forms';

-  itemForm: FormGroup = this.fb.group({
-    name: ['', [Validators.required, Validators.minLength(2)]],
-    idCategory: ['', Validators.required],
-    idUnit: ['', Validators.required],
-  });
+  itemForm = this.fb.group({
+    name: ['', [Validators.required, Validators.minLength(2)]],
+    idCategory: [null as number | null, Validators.required],
+    idUnit: [null as number | null, Validators.required],
+  });

This enables TypeScript to infer the form type, and getRawValue() will return properly typed values. Initializing with null instead of '' also better represents "no selection" for numeric fields.

As per coding guidelines: "Use strict type checking" and "Avoid the any type; use unknown when type is uncertain."

Also applies to: 100-103

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

In `@frontend/src/app/shared/components/item/item.component.ts` around lines 59 -
63, The form is currently untyped (itemForm: FormGroup) which causes
getRawValue() to return any; define a typed form value interface (e.g.,
ItemFormValue with name: string, idCategory: number|null, idUnit: number|null),
change the declaration to a generic FormGroup or typed controls (e.g., itemForm:
FormGroup<ItemFormValue> or FormGroup<{ name: FormControl<string>; idCategory:
FormControl<number|null>; idUnit: FormControl<number|null> }>), initialize
numeric/select controls with null instead of '' in the fb.group call, and update
any usage (particularly where you build ItemRequestDTO from getRawValue()) so
the compiler infers correct types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@frontend/src/app/shared/components/item/item.component.ts`:
- Around line 59-63: The form is currently untyped (itemForm: FormGroup) which
causes getRawValue() to return any; define a typed form value interface (e.g.,
ItemFormValue with name: string, idCategory: number|null, idUnit: number|null),
change the declaration to a generic FormGroup or typed controls (e.g., itemForm:
FormGroup<ItemFormValue> or FormGroup<{ name: FormControl<string>; idCategory:
FormControl<number|null>; idUnit: FormControl<number|null> }>), initialize
numeric/select controls with null instead of '' in the fb.group call, and update
any usage (particularly where you build ItemRequestDTO from getRawValue()) so
the compiler infers correct types.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: abf23cc6-daab-4916-93ec-8db22514c66f

📥 Commits

Reviewing files that changed from the base of the PR and between f7846b9 and 9836972.

📒 Files selected for processing (5)
  • frontend/src/app/list/components/list-share-dialog/list-share-dialog.component.ts
  • frontend/src/app/shared/components/category/category.component.ts
  • frontend/src/app/shared/components/item/item.component.ts
  • frontend/src/app/shared/components/list-item/list-item.component.ts
  • frontend/src/app/shared/components/unit/unit.component.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • frontend/src/app/shared/components/category/category.component.ts
  • frontend/src/app/list/components/list-share-dialog/list-share-dialog.component.ts
  • frontend/src/app/shared/components/unit/unit.component.ts
  • frontend/src/app/shared/components/list-item/list-item.component.ts

matheusandre1 and others added 2 commits March 13, 2026 12:26
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
@coderabbitai
Copy link

coderabbitai bot commented Mar 13, 2026

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{"name":"HttpError","status":403,"request":{"method":"PATCH","url":"https://api.github.com/repos/omatheusmesmo/ShoppMate/issues/comments/4048668138","headers":{"accept":"application/vnd.github.v3+json","user-agent":"octokit.js/0.0.0-development octokit-core.js/7.0.6 Node.js/24","authorization":"token [REDACTED]","content-type":"application/json; charset=utf-8"},"body":{"body":"<!-- This is an auto-generated comment: summarize by coderabbit.ai -->\n<!-- This is an auto-generated comment: failure by coderabbit.ai -->\n\n> [!CAUTION]\n> ## Review failed\n> \n> The head commit changed during the review from 0aced0ee0c5f6cfacb87d3395d20e10fc7d04146 to 61a1b3befb3860eecbf8195fdd89694f2413b6fd.\n\n<!-- end of auto-generated comment: failure by coderabbit.ai -->\n\n<!-- walkthrough_start -->\n\n<details>\n<summary>📝 Walkthrough</summary>\n\n## Walkthrough\n\nThis PR introduces strong typing for Angular forms across multiple frontend components via FormControl and FormGroup generics, removes two dialog components, migrates list-item-dialog from signals to reactive forms, adds a new GitHub Actions workflow for AI-based dispatch, and updates documentation and configuration files for consistency.\n\n## Changes\n\n|Cohort / File(s)|Summary|\n|---|---|\n|**Form Type Safety** <br> `frontend/src/app/auth/login/login.component.ts`, `frontend/src/app/auth/signup/signup.component.ts`, `frontend/src/app/list/components/category-dialog/category-dialog.component.ts`, `frontend/src/app/list/components/items-management/item-dialog/item-dialog.component.ts`, `frontend/src/app/list/components/units-management/unit-dialog/unit-dialog.component.ts`|Added explicit FormControl and FormGroup type annotations; switched to `nonNullable.group()` and replaced `.value` with `.getRawValue()` for type-safe form value extraction.|\n|**Dialog Component Removal** <br> `frontend/src/app/list/components/categories-management/category-dialog/*`, `frontend/src/app/list/components/shopping-list/list-dialog/*`|Entirely removed two dialog components (HTML, SCSS, TS files), eliminating their UI templates, styling, and dialog interaction logic.|\n|**Reactive Forms Migration** <br> `frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.html`, `frontend/src/app/list/components/list-details/list-item-dialog/list-item-dialog.component.ts`|Converted from template-driven forms with signals (`selectedItemId`, `quantity`) to reactive FormGroup-based architecture with form controls and validators.|\n|**Form Value Extraction Updates** <br> `frontend/src/app/list/components/list-share-dialog/list-share-dialog.component.ts`, `frontend/src/app/shared/components/category/category.component.ts`, `frontend/src/app/shared/components/item/item.component.ts`, `frontend/src/app/shared/components/list-item/list-item.component.ts`, `frontend/src/app/shared/components/unit/unit.component.ts`|Updated form submission handlers to use `getRawValue()` instead of `.value` for extracting form data and improved null-checking for nullable controls.|\n|**GitHub Actions Workflows** <br> `.github/workflows/gemini-dispatch.yml` (new), `.github/workflows/backend.yml`, `.github/workflows/frontend.yml`, `.github/workflows/greetings.yml`, `.github/workflows/root.yml`, `.github/workflows/sync-develop.yml`|Introduced new gemini-dispatch workflow for AI-based command routing (review, triage, invoke); applied cosmetic formatting (quote standardization, indentation) to existing workflows.|\n|**Documentation & Configuration** <br> `CONTRIBUTING.md`, `README.md`, `LICENSE.md`, `backend/LICENSE.md`, `backend/README.md`, `backend/bruno/README.md`, `backend/bruno/bruno.json`, `backend/docker-compose.yml`, `docker-compose.yml`|Enhanced CONTRIBUTING.md with guidelines; reflowed documentation for consistency; updated LICENSE formatting; adjusted YAML and JSON syntax formatting in configuration files.|\n\n## Sequence Diagram(s)\n\n```mermaid\nsequenceDiagram\n    participant User as User\n    participant Form as ListItemDialog Form\n    participant Component as ListItemDialogComponent\n    participant Service as Dialog Service\n\n    User->>Form: Select item & enter quantity\n    Form->>Component: Form value changes\n    Component->>Component: Validate form\n    \n    rect rgba(100, 150, 255, 0.5)\n        User->>Form: Click Save\n        Form->>Component: onSave() triggered\n        Component->>Component: Get form raw values\n        Component->>Component: Validate itemId !== null\n    end\n    \n    alt Valid Form\n        Component->>Service: closeDialog({itemId, quantity})\n        Service->>User: Return form data\n    else Invalid Form\n        Component->>Component: Early return (no action)\n    end\n```\n\n## Estimated code review effort\n\n🎯 4 (Complex) | ⏱️ ~50 minutes\n\n## Poem\n\n> 🐰 *Whiskers twitch with glee so bright,*\n> *Forms now typed, a safer flight!*\n> *getRawValue() saves the day,*\n> *Old dialogs fade away!*\n> *React flows cleaner, strong and keen,*\n> *Most robust code we've ever seen!* 🎉\n\n</details>\n\n<!-- walkthrough_end -->\n\n<!-- pre_merge_checks_walkthrough_start -->\n\n<details>\n<summary>🚥 Pre-merge checks | ✅ 3</summary>\n\n<details>\n<summary>✅ Passed checks (3 passed)</summary>\n\n|     Check name     | Status   | Explanation                                                                                                                                                                                                                                                                                           |\n| :----------------: | :------- | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n|  Description Check | ✅ Passed | Check skipped - CodeRabbit’s high-level summary is enabled.                                                                                                                                                                                                                                           |\n|     Title check    | ✅ Passed | The title 'fix: Typed Reactive Forms' clearly and concisely describes the primary change across the changeset—converting loosely-typed FormGroups to strongly-typed reactive forms with explicit FormControl typing, getRawValue() usage, and nonNullable groups throughout the frontend application. |\n| Docstring Coverage | ✅ Passed | No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.                                                                                                                                                                                            |\n\n</details>\n\n<sub>✏️ Tip: You can configure your own custom pre-merge checks in the settings.</sub>\n\n</details>\n\n<!-- pre_merge_checks_walkthrough_end -->\n\n<!-- finishing_touch_checkbox_start -->\n\n<details>\n<summary>✨ Finishing Touches</summary>\n\n<details>\n<summary>🧪 Generate unit tests (beta)</summary>\n\n- [ ] <!-- {\"checkboxId\": \"f47ac10b-58cc-4372-a567-0e02b2c3d479\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Create PR with unit tests\n- [ ] <!-- {\"checkboxId\": \"07f1e7d6-8a8e-4e23-9900-8731c2c87f58\", \"radioGroupId\": \"utg-output-choice-group-unknown_comment_id\"} -->   Post copyable unit tests in a comment\n\n</details>\n\n</details>\n\n<!-- finishing_touch_checkbox_end -->\n\n<!-- pr_review_plan_action_start -->\n\n<details>\n<summary>📝 Coding Plan</summary>\n\n- [ ] <!-- {\"checkboxId\": \"6ad8a4e1-0b3a-4ea2-9b5b-d82c1f47d1f2\"} --> Generate coding plan for human review comments\n\n</details>\n\n<!-- pr_review_plan_action_end -->\n\n<!-- tips_start -->\n\n---\n\n\n\n<sub>Comment `@coderabbitai help` to get the list of available commands and usage tips.</sub>\n\n<!-- tips_end -->\n\n<!-- internal state start -->\n\n\n<!-- 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 -->\n\n<!-- internal state end -->"},"request":{}},"response":{"url":"https://api.github.com/repos/omatheusmesmo/ShoppMate/issues/comments/4048668138","status":403,"headers":{"access-control-allow-origin":"*","access-control-expose-headers":"ETag, Link, Location, Retry-After, X-GitHub-OTP, X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Used, X-RateLimit-Resource, X-RateLimit-Reset, X-OAuth-Scopes, X-Accepted-OAuth-Scopes, X-Poll-Interval, X-GitHub-Media-Type, X-GitHub-SSO, X-GitHub-Request-Id, Deprecation, Sunset","content-encoding":"gzip","content-security-policy":"default-src 'none'","content-type":"application/json; charset=utf-8","date":"Fri, 13 Mar 2026 15:28:27 GMT","referrer-policy":"origin-when-cross-origin, strict-origin-when-cross-origin","server":"github.com","strict-transport-security":"max-age=31536000; includeSubdomains; preload","transfer-encoding":"chunked","vary":"Accept-Encoding, Accept, X-Requested-With","x-accepted-github-permissions":"issues=write; pull_requests=write","x-content-type-options":"nosniff","x-frame-options":"deny","x-github-api-version-selected":"2022-11-28","x-github-media-type":"github.v3; format=json","x-github-request-id":"D888:241B81:3F32C1D:1092D3B6:69B42D1A","x-ratelimit-limit":"5000","x-ratelimit-remaining":"4816","x-ratelimit-reset":"1773417366","x-ratelimit-resource":"core","x-ratelimit-used":"184","x-xss-protection":"0"},"data":{"message":"Resource not accessible by integration","documentation_url":"https://docs.github.com/rest/issues/comments#update-an-issue-comment","status":"403"}}}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
frontend/src/app/shared/components/unit/unit.component.ts (1)

81-85: Use a non-nullable typed form model before trimming raw values.

getRawValue() here is read from a bare FormGroup, so name and symbol are weakly typed at extraction time. Prefer this.fb.nonNullable.group(...) (and remove explicit : FormGroup) so .trim() is type-safe and resilient.

♻️ Suggested refactor
 import {
   FormBuilder,
-  FormGroup,
   Validators,
   ReactiveFormsModule,
 } from '@angular/forms';
@@
-  readonly unitForm: FormGroup = this.fb.group({
+  readonly unitForm = this.fb.nonNullable.group({
     name: ['', [Validators.required, Validators.minLength(2)]],
     symbol: ['', [Validators.required]],
   });

This aligns with the coding guideline "Use strict type checking."

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

In `@frontend/src/app/shared/components/unit/unit.component.ts` around lines 81 -
85, The form currently reads weakly-typed values via this.unitForm.getRawValue()
before trimming, which makes name/symbol possibly nullable; change the form
creation to use the FormBuilder nonNullable API (this.fb.nonNullable.group(...))
and remove any explicit : FormGroup typing so unitForm is non-nullable; then
keep the existing extraction (const { name, symbol } =
this.unitForm.getRawValue()) and trims safely, and ensure the Unit creation
(const unitData: Unit = { name: normalizedName, symbol: normalizedSymbol }) can
rely on non-null types.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@backend/README.md`:
- Around line 16-18: The three TOC entries for "Shopping Lists (/lists)",
"Shopping List Items (/lists/{listId}/items)", and "Shopping List User
Permissions (/lists/{listId}/permissions)" have backticks around the anchor
fragments which prevents them from becoming working links; remove the backticks
so each entry uses normal Markdown link syntax like [Shopping Lists
(/lists)](`#shopping-lists-lists`) (similarly update the other two) to restore
in-page navigation.

---

Nitpick comments:
In `@frontend/src/app/shared/components/unit/unit.component.ts`:
- Around line 81-85: The form currently reads weakly-typed values via
this.unitForm.getRawValue() before trimming, which makes name/symbol possibly
nullable; change the form creation to use the FormBuilder nonNullable API
(this.fb.nonNullable.group(...)) and remove any explicit : FormGroup typing so
unitForm is non-nullable; then keep the existing extraction (const { name,
symbol } = this.unitForm.getRawValue()) and trims safely, and ensure the Unit
creation (const unitData: Unit = { name: normalizedName, symbol:
normalizedSymbol }) can rely on non-null types.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: db91e93d-1b62-469a-9cfa-d7cbe3a600ea

📥 Commits

Reviewing files that changed from the base of the PR and between 9836972 and 61a1b3b.

📒 Files selected for processing (2)
  • backend/README.md
  • frontend/src/app/shared/components/unit/unit.component.ts

Comment on lines +16 to +18
- [Shopping Lists (/lists)](`#shopping-lists-lists`)
- [Shopping List Items (/lists/{listId}/items)](`#shopping-list-items-listslistiditems`)
- [Shopping List User Permissions (/lists/{listId}/permissions)](`#shopping-list-user-permissions-listslistidpermissions`)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix Markdown link syntax - remove backticks from anchor fragments.

These three TOC links wrap their anchor fragments in backticks, which renders them as literal text instead of working hyperlinks. This breaks in-page navigation.

🔗 Proposed fix
-  - [Shopping Lists (/lists)](`#shopping-lists-lists`)
-  - [Shopping List Items (/lists/{listId}/items)](`#shopping-list-items-listslistiditems`)
-  - [Shopping List User Permissions (/lists/{listId}/permissions)](`#shopping-list-user-permissions-listslistidpermissions`)
+  - [Shopping Lists (/lists)](`#shopping-lists-lists`)
+  - [Shopping List Items (/lists/{listId}/items)](`#shopping-list-items-listslistiditems`)
+  - [Shopping List User Permissions (/lists/{listId}/permissions)](`#shopping-list-user-permissions-listslistidpermissions`)
📝 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
- [Shopping Lists (/lists)](`#shopping-lists-lists`)
- [Shopping List Items (/lists/{listId}/items)](`#shopping-list-items-listslistiditems`)
- [Shopping List User Permissions (/lists/{listId}/permissions)](`#shopping-list-user-permissions-listslistidpermissions`)
- [Shopping Lists (/lists)](`#shopping-lists-lists`)
- [Shopping List Items (/lists/{listId}/items)](`#shopping-list-items-listslistiditems`)
- [Shopping List User Permissions (/lists/{listId}/permissions)](`#shopping-list-user-permissions-listslistidpermissions`)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@backend/README.md` around lines 16 - 18, The three TOC entries for "Shopping
Lists (/lists)", "Shopping List Items (/lists/{listId}/items)", and "Shopping
List User Permissions (/lists/{listId}/permissions)" have backticks around the
anchor fragments which prevents them from becoming working links; remove the
backticks so each entry uses normal Markdown link syntax like [Shopping Lists
(/lists)](`#shopping-lists-lists`) (similarly update the other two) to restore
in-page navigation.

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.

Frontend: Ensure 100% Typed Reactive Forms

2 participants