Skip to content

Add logic to allow warnings for input fields#3256

Open
Exitare wants to merge 20 commits into
azerothcore:masterfrom
Exitare:warnings
Open

Add logic to allow warnings for input fields#3256
Exitare wants to merge 20 commits into
azerothcore:masterfrom
Exitare:warnings

Conversation

@Exitare

@Exitare Exitare commented Jan 18, 2025

Copy link
Copy Markdown
Member

Does address but does not close #3236.
Tests are failing, because I dont want to fix them before this idea is either accepted or denied.
Right now, this is only working for the creature template max level field.

image

Feedback is welcome

Summary by CodeRabbit

  • New Features

    • Added inline validation feedback for invalid, touched form fields, including required-field and generic error messages.
    • Added required-field validation across editor forms.
    • Added a Models information section and direct link to the Creature Template Model editor.
    • Improved form validation tracking to reflect the overall validity of edited data.
  • Bug Fixes

    • Improved form updates after reloads and handling of missing or invalid fields.

@FrancescoBorzi

Copy link
Copy Markdown
Collaborator

Build failing

@Exitare

Exitare commented Jan 18, 2025

Copy link
Copy Markdown
Member Author

@FrancescoBorzi Works on my end. Can you please provide more information ?

@Helias

Helias commented Jan 19, 2025

Copy link
Copy Markdown
Member

We have a lint rule that notifies if a component is not using the OnPush strategy, it's complaining about it
image

This is the issue reported by the pipeline

@Exitare

Exitare commented Jan 19, 2025

Copy link
Copy Markdown
Member Author

Thank you, but the failed test is intentional.
I would love to hear some feedback on my proposed solution before I fix any tests that will break due to the change. If the solution is rejected I didn't waste too much time :)
Hope that makes sense!

@Helias

Helias commented Jan 19, 2025

Copy link
Copy Markdown
Member

ok, I will test this PR locally and let you know ;-)

@Exitare

Exitare commented Jan 19, 2025

Copy link
Copy Markdown
Member Author

Thank you :)

@Exitare

Exitare commented Jan 19, 2025

Copy link
Copy Markdown
Member Author

I also would like to mention, that this will NOT address any editors. The example I added for the maxLevel in creature template is just for showcasing purposes of the proposed solution. The PR aims to provide an easy framework to add form validation, not to add form validation everywhere it is needed. This needs to happen gradually, otherwise this will be a gigantic PR, considering Item Template and Creature Template have to change along with a lot of other forms.

@Helias

Helias commented Jan 19, 2025

Copy link
Copy Markdown
Member

I don't dislike the approach but I am wondering if can be done only with:

              <input
                keiraInputValidation
                [formControlName]="'maxlevel'"
                id="maxlevel"
                type="number"
                class="form-control form-control-sm"
              />

Without requiring any other information or adding components, because may you can find a way to get the formcontrol from the input and you could spawn dynamically a component next to the without already writing a placeholder component <keira-validation-feedback>.
If this is not feasible without using the <keira-validation-feedback> component and the input parameter [keiraInputValidation]="editorService.form.get('maxlevel')" is mandatory, then I can approve this approach.

Thanks by the way for implementing this proof of concept of warnings, it's a good job!

@Exitare

Exitare commented Jan 19, 2025

Copy link
Copy Markdown
Member Author

Thank you for providing feedback. I will try to make it work. I also had a solution in mind that minimizes the changes required to all editors as this can introduce quite the overhead in work. I will explore possible solutions and hopefully something will work :)

@Exitare

Exitare commented Jan 21, 2025

Copy link
Copy Markdown
Member Author

In theory I could do something like this:

import { Directive, ViewContainerRef } from '@angular/core';

@Directive({
  selector: '[dynamicComponentHost]',
})
export class DynamicComponentHostDirective {
  constructor(public viewContainerRef: ViewContainerRef) {}
}
import { Component, ComponentFactoryResolver, OnInit } from '@angular/core';
import { KeiraValidationFeedbackComponent } from './keira-validation-feedback.component';
import { DynamicComponentHostDirective } from './dynamic-component-host.directive';

@Component({
  selector: 'app-dynamic-form',
  template: `
    <div class="form-group col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
      <label for="maxLevel">Max Level</label>
      <input
        [keiraInputValidation]="editorService.form.get('maxlevel')"
        [formControlName]="'maxlevel'"
        id="maxlevel"
        type="number"
        class="form-control form-control-sm"
        (blur)="showValidationFeedback()"
      />
      <ng-template dynamicComponentHost></ng-template>
    </div>
  `,
})
export class DynamicFormComponent implements OnInit {
  @ViewChild(DynamicComponentHostDirective, { static: true })
  dynamicHost!: DynamicComponentHostDirective;

  constructor(
    private resolver: ComponentFactoryResolver,
    public editorService: EditorService
  ) {}

  ngOnInit(): void {}

  showValidationFeedback(): void {
    const control = this.editorService.form.get('maxlevel');
    if (control?.invalid && control.touched) {
      const viewContainerRef = this.dynamicHost.viewContainerRef;
      viewContainerRef.clear(); // Clear any existing components
      const componentFactory =
        this.resolver.resolveComponentFactory(KeiraValidationFeedbackComponent);
      const componentRef = viewContainerRef.createComponent(componentFactory);
      componentRef.instance.control = control; // Pass the control to the component
    }
  }
}

Used like this:

<div class="form-group col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
  <label for="maxLevel">Max Level</label>
  <input
    [keiraInputValidation]="editorService.form.get('maxlevel')"
    [formControlName]="'maxlevel'"
    id="maxlevel"
    type="number"
    class="form-control form-control-sm"
    (blur)="showValidationFeedback()"
  />
  <ng-template dynamicComponentHost></ng-template>
</div>

But personally, I think this is a lot of boilerplate code and super convoluted too, for a worse outcome in terms of readability and most likely maintainability too.

If you have a better solution or something I should check out, please let me know :)

@Helias

Helias commented Jan 23, 2025

Copy link
Copy Markdown
Member

I was convinced that everything can be implemented inside the directive attribute making it agnostic and generic for all inputs without requiring any input information.

import { Directive, ElementRef, inject, OnInit, Renderer2 } from '@angular/core';
import { AbstractControl, NgControl } from '@angular/forms';
import { SubscriptionHandler } from '@keira/shared/utils';

@Directive({
  selector: '[keiraInputValidation]',
  standalone: true,
})
export class InputValidationDirective extends SubscriptionHandler implements OnInit {
  private readonly el: ElementRef = inject(ElementRef);
  private readonly renderer: Renderer2 = inject(Renderer2);
  private readonly ngControl: NgControl = inject(NgControl);

  private errorDiv: HTMLElement | null = null;

  ngOnInit(): void {
    const control = this.ngControl.control;

    if (!control) {
      return;
    }

    this.subscriptions.push(
      control.statusChanges?.subscribe(() => {
        this.updateErrorMessage(control);
      }),
    );
  }

  private updateErrorMessage(control: AbstractControl): void {
    if (this.errorDiv) {
      this.renderer.removeChild(this.el.nativeElement.parentNode, this.errorDiv);
      this.errorDiv = null;
    }

    if (control?.touched && control?.invalid) {
      const errorMessage = control?.errors?.['required'] ? 'This field is required' : 'Invalid field';

      this.errorDiv = this.renderer.createElement('div');

      const text = this.renderer.createText(errorMessage);
      this.renderer.appendChild(this.errorDiv, text);

      const parent = this.el.nativeElement.parentNode;
      this.renderer.appendChild(parent, this.errorDiv);
    }
  }
}
<div class="form-group col-12 col-sm-6 col-md-4 col-lg-3 col-xl-2">
  <label for="maxLevel">Max Level</label>
  <input keiraInputValidation [formControlName]="'maxlevel'" id="maxlevel" type="number" class="form-control form-control-sm" />
</div>

This will allow us to add the directive keiraInputValidation to any input without further boilerplate or implementation, moreover, now the directive will be self-contained and it does not require any other helper component like the keira3-validation-feedback.
I am not a fan of the Renderer2 service but if we need to manipulate the DOM it's the solution for Angular.

You can find the commit here 7021a3a
The branch name is warnings-directive

image

EDIT: this code is a draft based on your code, you can re-use it improving it if possible, we could continue the implementation in this branch/PR

@Exitare

Exitare commented Jan 25, 2025

Copy link
Copy Markdown
Member Author

@Helias
Wow! thank you. I wasn't aware of this possibility. That looks great. I will copy it over :)

Co-Authored-By: Stefano Borzì <stefanoborzi32@gmail.com>
@Exitare

Exitare commented Jan 25, 2025

Copy link
Copy Markdown
Member Author

@Helias
I added the directive and it works, with a twist.
If the creature template is opened up the first time one can clear the field and put another field in focus (e.g. click on minlevel), the error message will not show up. One has to enter a value again and then delete it, to actually trigger the error message.
I will look into this.

Additionally, if we add the validation, we are just one step shy of adding a subject that the keira edit button can subscribe to, to disable them if validation fails.
What are your thoughts on that?

@Helias

Helias commented Jan 25, 2025

Copy link
Copy Markdown
Member

I noticed that, if we remove the control.touched contidion it should work on the first time too, I am not sure if it's 100% correct but we could remove it and then think if we really need that condition or not.

About the validation, that's a good idea actually 🚀

@Exitare

Exitare commented Jan 25, 2025

Copy link
Copy Markdown
Member Author

I added a workaround, by adding markedAsTouched() when the field is invalid. But maybe thats not as good as a solution as it can be?

@Helias

Helias commented Jan 25, 2025

Copy link
Copy Markdown
Member

I added a workaround, by adding markedAsTocuched() when the field is invalid. But maybe thats not as good as a solution as it can be?

it could be in this case, let's keep it

Comment thread libs/shared/base-editor-components/src/query-output/query-output.component.ts Outdated
Comment thread libs/shared/base-editor-components/src/query-output/query-output.component.ts Outdated
Comment thread libs/shared/common-services/src/validation.service.ts Outdated
Comment thread libs/shared/directives/src/validate-input.directive.ts Outdated
Comment thread libs/shared/base-abstract-classes/src/service/editors/editor.service.ts Outdated
for (const k of Object.keys(this._form.controls)) {
console.log(k);
}
}

@Helias Helias Jan 28, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

not sure about this code inside the else block, it seems more a debug code , we could keep it, but could you give me more insight about it?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

@Exitare please do not mark comments as resolved without answering or addressing them

@Exitare Exitare Jan 29, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I resolved it, because I removed it initially. But then one of the tests failed and I added it back, but didn't unresolve the issue here.

Comment thread libs/shared/common-services/src/validation.service.spec.ts Outdated
Comment thread libs/shared/common-services/src/validation.service.ts Outdated
Comment thread libs/shared/directives/src/validate-input.directive.spec.ts Outdated
const parent = this.el.nativeElement.parentNode;
if (parent) {
this.errorDiv = this.renderer.createElement('div');
this.renderer.addClass(this.errorDiv, 'error-message');

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think that I wrote this, but I am not sure if we have a class error-message, if not, we could remove this line of code

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We dont. But the tests use it to get the div in order to verify the correct message.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I would use an id instead of a class for this

@Helias
Helias requested a review from FrancescoBorzi January 28, 2025 09:33
RouterLink,
InputValidationDirective,
],
providers: [ValidationService],

@Francesco-Borzi Francesco-Borzi Jan 29, 2025

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

why does it have to be a singleton?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Because validation should happen on a page by page basis and not on the app level ?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It should not be necessary, we could keep one instance for the entire application, so provide it in "root" and don't put it here.

If you find a strong reason to provide it inside the component then we could put it back.

Comment on lines +91 to +92
const defaultValue = defaultValues[field];
this._form.addControl(field, new FormControl(defaultValue, [Validators.required]));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Suggested change
const defaultValue = defaultValues[field];
this._form.addControl(field, new FormControl(defaultValue, [Validators.required]));
this._form.addControl(field, new FormControl(defaultValues[field], [Validators.required]));


this._form = new FormGroup<ModelForm<T>>({} as any);

// Loop through the fields and initialize controls with default values

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I don't think we need this comment, IMHO the code seems already quite self-explanatory :)

Suggested change
// Loop through the fields and initialize controls with default values

Comment on lines +92 to +93
if (typeof field === 'string') {
const control = this._form.controls[field];

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

is this change needed for the original purpose of the PR? in general, I'd prefer to split changes in several PRs, for example:

  • 1 PR to include new functionality (including its unit tests)
  • a separate PR to improve/refactor existing code that is not related to the functionality being introduced

class="btn btn-primary btn-sm"
(click)="execute()"
id="execute-btn"
[disabled]="(validationService.validationPassed$ | async) === false"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think we should avoid this repetition and wrap validationService.validationPassed$ in a variable. Even better to use a Signal.

@Exitare Exitare Jan 29, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

I don't know how this requested change should look like because I used a variable before and it was requested to use the async pipe. Now it's back to variable again?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Before it was a variable with a subscription running on ngOninit, this affects the component adding a dependency for the OnInit interface and moreover, it forces you to manage the subscribe/unsubscribe.
In the current situation you don't need ngOninit and to manage the subscribe/unsubscribe thanks to the async pipe.

Wrapping this into a variable can be done without ngOninit still and in the following way:

protected readonly isValidationPassed$ =  this.validationService.validationPassed$.pipe(map((val) => val === false));

Keeping in the template

[disabled]="isValidationPassed$ | async"

Or, you could use signals

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

OK, for learning purposes. What's the undesirable part about having a dependency in the ngOnInit, besides the subscription management part. Is it a general best practice not to use the ngOnInit if possible?

@Helias Helias Jan 30, 2025

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Personally I avoid any kind of dependencies, so if we can avoid ngOnInit, ngOnChanges etc. in an elegant way I would not add them because It increases the complexity of the logic component

@@ -0,0 +1,24 @@
{
"name": "keira-shared-directives",

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

I think it's better to create a lib for validation instead of creating one lib for "directives" that can contain several directives that are unrelated to them.

So in the validation lib you can have all generic tools that are being used for the validation, including the validation service.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Interesting approach. I placed the files in the respective libs as this is done with several other functionalities too. There is no lib sql service either and so on. They are all pretty generic.

@FrancescoBorzi

Copy link
Copy Markdown
Collaborator

@Exitare are you planning to complete this?

@Exitare

Exitare commented Jun 22, 2025

Copy link
Copy Markdown
Member Author

Yes, I plan to. I will be able to spend more time on the project again in around 2 months. If thats too long, I am fine with closing the PR.

@FrancescoBorzi

Copy link
Copy Markdown
Collaborator

Yes, I plan to. I will be able to spend more time on the project again in around 2 months. If thats too long, I am fine with closing the PR.

no worries, we will wait for you!

@coderabbitai

coderabbitai Bot commented Jul 19, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds shared form validation tracking and error rendering, applies required validation to editor forms, improves reload handling, and updates creature-template inputs with validation plus navigation to the creature model editor.

Changes

Validation and editor behavior

Layer / File(s) Summary
Shared validation infrastructure
libs/shared/common-services/*, libs/shared/directives/*, tsconfig.base.json
Adds ValidationService and InputValidationDirective, including validity tracking, DOM error messages, exports, tests, and library configuration.
Editor form initialization and reload handling
libs/shared/base-abstract-classes/src/service/editors/*, libs/shared/base-editor-components/src/query-output/query-output.component.ts
Adds required validators and entity defaults to form controls, validates reload field keys, updates lifecycle signatures, and injects validation support into query output.

Creature template UI

Layer / File(s) Summary
Creature template validation and model navigation
libs/features/creature/src/creature-template/*, apps/keira/src/app/scss/_editor.scss
Applies input validation to level fields, adds validation and routing imports, highlights invalid touched inputs, and links to the creature template model editor.

Test assertion maintenance

Layer / File(s) Summary
Quest preview query assertion
libs/features/quest/src/quest-preview/quest-preview.service.spec.ts
Updates the expected reputation faction argument from null to 0.

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

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant InputValidationDirective
  participant ValidationService
  participant EditorForm
  User->>EditorForm: edit required field
  EditorForm->>InputValidationDirective: update control status
  InputValidationDirective->>ValidationService: set control validity
  InputValidationDirective->>EditorForm: render validation message
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title is concise and accurately reflects the main change: adding input-field warning/validation behavior.
Linked Issues check ✅ Passed The PR adds red-border/warning validation behavior for mandatory inputs and demonstrates it on the creature template field, matching #3236.
Out of Scope Changes check ✅ Passed The added services, directive library scaffolding, styling, and test updates all support the validation-warning feature rather than unrelated work.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

libs/shared/directives/src/index.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: Error while loading rule '@typescript-eslint/require-await': You have used a rule which requires type information, but don't have parserOptions set to generate type information for this file. See https://tseslint.com/typed-linting for enabling linting with type information.
Parser: /node_modules/@typescript-eslint/parser/dist/index.js
Occurred while linting /libs/shared/directives/src/index.ts
at throwError (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:40:11)
at getParserServices (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:29:9)
at create (/node_modules/@typescript-eslint/eslint-plugin/dist/rules/require-await.js:58:55)
at Object.create (/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:39:20)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:895:21)
at /node_modules/eslint/lib/linter/linter.js:1066:110
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:1003:34)
at Linter._verifyWithoutProcessors (/node_modules/eslint/lib/linter/linter.js:1355:31)
at /node_modules/eslint/lib/linter/linter.js:1913:29

libs/shared/directives/src/validate-input.directive.spec.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: Error while loading rule '@typescript-eslint/require-await': You have used a rule which requires type information, but don't have parserOptions set to generate type information for this file. See https://tseslint.com/typed-linting for enabling linting with type information.
Parser: /node_modules/@typescript-eslint/parser/dist/index.js
Occurred while linting /libs/shared/directives/src/validate-input.directive.spec.ts
at throwError (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:40:11)
at getParserServices (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:29:9)
at create (/node_modules/@typescript-eslint/eslint-plugin/dist/rules/require-await.js:58:55)
at Object.create (/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:39:20)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:895:21)
at /node_modules/eslint/lib/linter/linter.js:1066:110
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:1003:34)
at Linter._verifyWithoutProcessors (/node_modules/eslint/lib/linter/linter.js:1355:31)
at /node_modules/eslint/lib/linter/linter.js:1913:29

libs/shared/directives/src/validate-input.directive.ts

Oops! Something went wrong! :(

ESLint: 8.57.1

Error: Error while loading rule '@typescript-eslint/require-await': You have used a rule which requires type information, but don't have parserOptions set to generate type information for this file. See https://tseslint.com/typed-linting for enabling linting with type information.
Parser: /node_modules/@typescript-eslint/parser/dist/index.js
Occurred while linting /libs/shared/directives/src/validate-input.directive.ts
at throwError (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:40:11)
at getParserServices (/node_modules/@typescript-eslint/utils/dist/eslint-utils/getParserServices.js:29:9)
at create (/node_modules/@typescript-eslint/eslint-plugin/dist/rules/require-await.js:58:55)
at Object.create (/node_modules/@typescript-eslint/utils/dist/eslint-utils/RuleCreator.js:39:20)
at createRuleListeners (/node_modules/eslint/lib/linter/linter.js:895:21)
at /node_modules/eslint/lib/linter/linter.js:1066:110
at Array.forEach ()
at runRules (/node_modules/eslint/lib/linter/linter.js:1003:34)
at Linter._verifyWithoutProcessors (/node_modules/eslint/lib/linter/linter.js:1355:31)
at /node_modules/eslint/lib/linter/linter.js:1913:29


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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 (3)
libs/features/creature/src/creature-template/creature-template.component.html (1)

188-191: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove empty href="" attribute.

Angular's routerLink directive automatically generates and manages the href attribute for <a> tags. Providing an explicit empty href="" is redundant and can occasionally cause unintended page reloads or routing issues if event propagation is not perfectly intercepted.

♻️ Proposed fix
               <span
                 >Models are now available in the
-                <a href="" [routerLink]="['/creature', 'creature-template-model']">Creature Template Model</a> editor.</span
+                <a [routerLink]="['/creature', 'creature-template-model']">Creature Template Model</a> editor.</span
               >
🤖 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
`@libs/features/creature/src/creature-template/creature-template.component.html`
around lines 188 - 191, Remove the empty href attribute from the anchor using
the routerLink directive in the template, leaving the existing routerLink target
and link text unchanged.
libs/shared/common-services/src/validation.service.ts (1)

8-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Avoid using any for map keys.

Consider replacing any with unknown for the control parameter and the map key to improve type safety. Since the parameter is only used as a reference key in the Map, unknown is sufficient and safer than any.

♻️ Proposed refactor
-  private readonly controlsValidityMap = new Map<any, boolean>();
+  private readonly controlsValidityMap = new Map<unknown, boolean>();
   readonly validationPassed$: BehaviorSubject<boolean> = new BehaviorSubject(true);
 
-  setControlValidity(control: any, isValid: boolean): void {
+  setControlValidity(control: unknown, isValid: boolean): void {
     this.controlsValidityMap.set(control, isValid);
     this.updateValidationState();
   }
 
-  removeControl(control: any): void {
+  removeControl(control: unknown): void {
🤖 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 `@libs/shared/common-services/src/validation.service.ts` around lines 8 - 16,
Replace any with unknown for the controlsValidityMap key type and the control
parameters in setControlValidity and removeControl. Keep the existing map-key
reference behavior and validation-state updates unchanged.
libs/shared/directives/src/validate-input.directive.ts (1)

36-39: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Remove the error element from the DOM on destroy.

If the input element is destroyed (e.g., via *ngIf) but its parent container remains, the dynamically appended error message div will be left orphaned in the DOM. It is recommended to clean it up in ngOnDestroy.

♻️ Proposed refactor
   override ngOnDestroy(): void {
+    if (this.errorDiv) {
+      this.renderer.removeChild(this.el.nativeElement.parentNode, this.errorDiv);
+      this.errorDiv = null;
+    }
     this.validationService.removeControl(this);
     super.ngOnDestroy();
   }
🤖 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 `@libs/shared/directives/src/validate-input.directive.ts` around lines 36 - 39,
Update ValidateInputDirective.ngOnDestroy to remove the dynamically appended
error-message div from the DOM before or alongside removing the control from
validationService. Reuse the directive’s existing reference to that element,
guard cleanup when it is absent, and preserve the superclass destruction call.
🤖 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
`@libs/features/creature/src/creature-template/creature-template.component.html`:
- Around line 50-51: Update the label associated with the maxlevel form control
so its for attribute exactly matches the input id, preserving the existing
maxlevel control binding and markup.

In `@libs/shared/directives/project.json`:
- Around line 9-19: The shared directives unit-test setup still uses Karma
instead of the required Vitest configuration. Update the test target in
libs/shared/directives/project.json (lines 9-19) to use the workspace-approved
Vitest executor and options, delete the obsolete Karma configuration in
libs/shared/directives/karma.conf.js (lines 1-15), and remove "jasmine" from the
types array in libs/shared/directives/tsconfig.spec.json (lines 4-6).

In `@tsconfig.base.json`:
- Around line 61-64: Remove the invalid "texts" path alias from the tsconfig
paths configuration; retain the existing "`@keira/features/texts`" alias and all
other mappings unchanged.

---

Nitpick comments:
In
`@libs/features/creature/src/creature-template/creature-template.component.html`:
- Around line 188-191: Remove the empty href attribute from the anchor using the
routerLink directive in the template, leaving the existing routerLink target and
link text unchanged.

In `@libs/shared/common-services/src/validation.service.ts`:
- Around line 8-16: Replace any with unknown for the controlsValidityMap key
type and the control parameters in setControlValidity and removeControl. Keep
the existing map-key reference behavior and validation-state updates unchanged.

In `@libs/shared/directives/src/validate-input.directive.ts`:
- Around line 36-39: Update ValidateInputDirective.ngOnDestroy to remove the
dynamically appended error-message div from the DOM before or alongside removing
the control from validationService. Reuse the directive’s existing reference to
that element, guard cleanup when it is absent, and preserve the superclass
destruction call.
🪄 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: 22c08172-2d78-4237-a3d3-be89e28f8bb0

📥 Commits

Reviewing files that changed from the base of the PR and between ffb49f0 and af9a09f.

📒 Files selected for processing (23)
  • apps/keira/src/app/scss/_editor.scss
  • libs/features/creature/src/creature-template/creature-template.component.html
  • libs/features/creature/src/creature-template/creature-template.component.ts
  • libs/features/quest/src/quest-preview/quest-preview.service.spec.ts
  • libs/shared/base-abstract-classes/src/service/editors/editor.service.spec.ts
  • libs/shared/base-abstract-classes/src/service/editors/editor.service.ts
  • libs/shared/base-abstract-classes/src/service/editors/single-row-editor.service.spec.ts
  • libs/shared/base-abstract-classes/src/service/editors/single-row-editor.service.ts
  • libs/shared/base-editor-components/src/query-output/query-output.component.ts
  • libs/shared/common-services/src/index.ts
  • libs/shared/common-services/src/validation.service.spec.ts
  • libs/shared/common-services/src/validation.service.ts
  • libs/shared/directives/.eslintrc.json
  • libs/shared/directives/README.md
  • libs/shared/directives/karma.conf.js
  • libs/shared/directives/project.json
  • libs/shared/directives/src/index.ts
  • libs/shared/directives/src/validate-input.directive.spec.ts
  • libs/shared/directives/src/validate-input.directive.ts
  • libs/shared/directives/tsconfig.json
  • libs/shared/directives/tsconfig.lib.json
  • libs/shared/directives/tsconfig.spec.json
  • tsconfig.base.json
💤 Files with no reviewable changes (1)
  • libs/shared/base-abstract-classes/src/service/editors/editor.service.spec.ts

Comment on lines +50 to +51
<label for="maxLevel">maxlevel</label>
<input keiraInputValidation [formControlName]="'maxlevel'" id="maxlevel" type="number" class="form-control form-control-sm" />

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

Match the for attribute with the input id.

The for attribute on the label (maxLevel) does not match the id of the input (maxlevel). HTML id attributes are case-sensitive, so clicking the label will not focus the input.

🐛 Proposed fix
-              <label for="maxLevel">maxlevel</label>
+              <label for="maxlevel">maxlevel</label>
📝 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
<label for="maxLevel">maxlevel</label>
<input keiraInputValidation [formControlName]="'maxlevel'" id="maxlevel" type="number" class="form-control form-control-sm" />
<label for="maxlevel">maxlevel</label>
<input keiraInputValidation [formControlName]="'maxlevel'" id="maxlevel" type="number" class="form-control form-control-sm" />
🤖 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
`@libs/features/creature/src/creature-template/creature-template.component.html`
around lines 50 - 51, Update the label associated with the maxlevel form control
so its for attribute exactly matches the input id, preserving the existing
maxlevel control binding and markup.

Comment on lines +9 to +19
"test": {
"executor": "@angular-devkit/build-angular:karma",
"options": {
"tsConfig": "libs/shared/directives/tsconfig.spec.json",
"karmaConfig": "libs/shared/directives/karma.conf.js",
"polyfills": ["zone.js", "zone.js/testing"],
"sourceMap": true,
"codeCoverage": true,
"scripts": []
}
},

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

Configure Vitest for unit tests instead of Karma.

This library was generated using the default Karma test runner, but the project guidelines mandate Vitest for all unit tests. Please migrate the library's test configuration to use Vitest to ensure consistency across the workspace.

  • libs/shared/directives/project.json#L9-L19: Update the test target to use the Vitest executor (e.g., @nx/vite:test or @analogjs/vite-plugin-angular).
  • libs/shared/directives/karma.conf.js#L1-L15: Delete this Karma configuration file as it is no longer needed.
  • libs/shared/directives/tsconfig.spec.json#L4-L6: Remove "jasmine" from the types array, as Vitest uses its own typings.
📍 Affects 3 files
  • libs/shared/directives/project.json#L9-L19 (this comment)
  • libs/shared/directives/karma.conf.js#L1-L15
  • libs/shared/directives/tsconfig.spec.json#L4-L6
🤖 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 `@libs/shared/directives/project.json` around lines 9 - 19, The shared
directives unit-test setup still uses Karma instead of the required Vitest
configuration. Update the test target in libs/shared/directives/project.json
(lines 9-19) to use the workspace-approved Vitest executor and options, delete
the obsolete Karma configuration in libs/shared/directives/karma.conf.js (lines
1-15), and remove "jasmine" from the types array in
libs/shared/directives/tsconfig.spec.json (lines 4-6).

Source: Coding guidelines

Comment thread tsconfig.base.json
Comment on lines +61 to +64
"@keira/shared/utils": ["libs/shared/utils/src/index.ts"],
"@keira/shared/directives": ["libs/shared/directives/src/index.ts"],
"@keira/shared/error-templates": ["libs/shared/error-templates/src/index.ts"],
"texts": ["libs/features/texts/src/index.ts"]

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

Remove the invalid path alias.

The path alias "texts" violates the naming guideline, which requires @keira/<scope>/<name>. Furthermore, the correct alias "@keira/features/texts" is already defined earlier in this file.

🐛 Proposed fix
       "`@keira/shared/utils`": ["libs/shared/utils/src/index.ts"],
       "`@keira/shared/directives`": ["libs/shared/directives/src/index.ts"],
-      "`@keira/shared/error-templates`": ["libs/shared/error-templates/src/index.ts"],
-      "texts": ["libs/features/texts/src/index.ts"]
+      "`@keira/shared/error-templates`": ["libs/shared/error-templates/src/index.ts"]
📝 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
"@keira/shared/utils": ["libs/shared/utils/src/index.ts"],
"@keira/shared/directives": ["libs/shared/directives/src/index.ts"],
"@keira/shared/error-templates": ["libs/shared/error-templates/src/index.ts"],
"texts": ["libs/features/texts/src/index.ts"]
"`@keira/shared/utils`": ["libs/shared/utils/src/index.ts"],
"`@keira/shared/directives`": ["libs/shared/directives/src/index.ts"],
"`@keira/shared/error-templates`": ["libs/shared/error-templates/src/index.ts"]
🤖 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 `@tsconfig.base.json` around lines 61 - 64, Remove the invalid "texts" path
alias from the tsconfig paths configuration; retain the existing
"`@keira/features/texts`" alias and all other mappings unchanged.

Source: Coding guidelines

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.

[Enchantement] - Mandotory fields colour/warning.

4 participants