-
-
-
-
-
- Utilisateur valide: admin
+
-
-
-
-
-
Mot de passe oublié?
+
+
+
+
+
+ @if (
+ loginForm.password().invalid() &&
+ (loginForm.password().dirty() || loginForm.password().touched())
+ ) {
+
{{ loginForm.password().errors()[0].message }}
+ }
+
- @if (login.controls.password.invalid && (login.controls.password.dirty || login.controls.password.touched)) {
-
Requis
- }
-
-
-
-
+
+
-
}
diff --git a/src/app/features/login/login.ts b/src/app/features/login/login.ts
index 8f8dac9..6cc2000 100644
--- a/src/app/features/login/login.ts
+++ b/src/app/features/login/login.ts
@@ -1,38 +1,59 @@
-import { ChangeDetectionStrategy, Component, inject } from '@angular/core';
-import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
+import { Component, inject, signal } from '@angular/core';
+import { form, FormField, FormRoot, required } from '@angular/forms/signals';
import { Router, RouterLink } from '@angular/router';
+import { Button, ButtonDirective } from '@openng/optimus-ui/button';
+import { InputText } from '@openng/optimus-ui/inputtext';
import { AppStateApi } from '@shared/appSate/app-state-api';
import { AppStateStore } from '@shared/appSate/app-state-store';
-import { Button, ButtonDirective } from "@openng/optimus-ui/button";
-import { InputText } from '@openng/optimus-ui/inputtext';
@Component({
selector: 'app-login',
- imports: [ReactiveFormsModule, ButtonDirective, RouterLink, InputText, Button],
+ imports: [ButtonDirective, RouterLink, InputText, Button, FormField, FormRoot],
templateUrl: './login.html',
- changeDetection: ChangeDetectionStrategy.OnPush
+ styles: [
+ `
+ :host ::ng-deep .untouched-field.p-invalid {
+ border-color: inherit;
+ }
+ `,
+ ],
})
export default class Login {
protected store = inject(AppStateStore);
protected appStateApi = inject(AppStateApi);
private router = inject(Router);
- login = new FormGroup({
- username: new FormControl('', [Validators.required]),
- password: new FormControl('', [Validators.required]),
+ loginModel = signal({
+ username: '',
+ password: '',
});
- handleSubmit() {
- const user = this.appStateApi.getUser();
- const usernameControl = this.login.get('username');
+ loginForm = form(
+ this.loginModel,
+ (schemaPath) => {
+ required(schemaPath.username, { message: "L'utilisateur est obligatoire." });
+ required(schemaPath.password, { message: 'Le mot de passe est obligatoire.' });
+ },
+ {
+ submission: {
+ action: async (form) => {
+ const user = this.appStateApi.getUser();
+
+ if (user.username !== form().value().username) {
+ return [
+ {
+ kind: 'loginNotFound',
+ message: 'Erreur de login',
+ fieldTree: form.username,
+ },
+ ];
+ }
- if (user.username !== usernameControl?.value) {
- usernameControl?.setErrors({
- loginNotFound: 'Erreur de login'
- });
- } else {
- this.store.login(user);
- this.router.navigate(['/']);
- }
- }
+ this.store.login(user);
+ this.router.navigate(['/']);
+ return undefined;
+ },
+ },
+ },
+ );
}
diff --git a/src/app/features/not-found/not-found.ts b/src/app/features/not-found/not-found.ts
index 41fd884..011be31 100644
--- a/src/app/features/not-found/not-found.ts
+++ b/src/app/features/not-found/not-found.ts
@@ -1,10 +1,9 @@
-import { ChangeDetectionStrategy, Component } from '@angular/core';
+import { Component } from '@angular/core';
@Component({
selector: 'app-not-found',
imports: [],
templateUrl: './not-found.html',
- changeDetection: ChangeDetectionStrategy.OnPush
})
export default class NotFound {
diff --git a/src/app/features/sidebar/sidebar.ts b/src/app/features/sidebar/sidebar.ts
index 7da6f09..1733f4a 100644
--- a/src/app/features/sidebar/sidebar.ts
+++ b/src/app/features/sidebar/sidebar.ts
@@ -1,14 +1,13 @@
-import { ChangeDetectionStrategy, Component, computed, inject, signal } from '@angular/core';
+import { Component, computed, inject, signal } from '@angular/core';
import { Router, RouterLink, RouterLinkActive } from '@angular/router';
+import { Avatar } from '@openng/optimus-ui/avatar';
import { AppStateStore } from '@shared/appSate/app-state-store';
import { LayoutService } from '@shared/layout/layout.service';
-import { Avatar } from '@openng/optimus-ui/avatar';
@Component({
selector: 'app-sidebar',
imports: [RouterLink, RouterLinkActive, Avatar],
templateUrl: './sidebar.html',
- changeDetection: ChangeDetectionStrategy.OnPush
})
export class Sidebar {
protected readonly store = inject(AppStateStore);
diff --git a/src/app/features/todo/model/todo.model.ts b/src/app/features/todo/model/todo.model.ts
index d94ea1b..f9e06b3 100644
--- a/src/app/features/todo/model/todo.model.ts
+++ b/src/app/features/todo/model/todo.model.ts
@@ -1,5 +1,5 @@
-export type Todo = {
+export interface Todo {
id: number;
title: string;
completed: boolean;
-};
+}
diff --git a/src/app/features/todo/service/todo.service.ts b/src/app/features/todo/service/todo.service.ts
index ad9185b..143e2bb 100644
--- a/src/app/features/todo/service/todo.service.ts
+++ b/src/app/features/todo/service/todo.service.ts
@@ -1,8 +1,9 @@
-import { Injectable } from "@angular/core";
+import { Service } from "@angular/core";
+
import { TODOS } from "../model/todo.mock";
import { Todo } from "../model/todo.model";
-@Injectable()
+@Service({ autoProvided: false })
export class TodoService {
loadAll(): Todo[] {
return TODOS;
diff --git a/src/app/features/todo/store/todo.store.ts b/src/app/features/todo/store/todo.store.ts
index e114e31..33789cf 100644
--- a/src/app/features/todo/store/todo.store.ts
+++ b/src/app/features/todo/store/todo.store.ts
@@ -1,21 +1,20 @@
+import { computed, inject } from "@angular/core";
import { patchState, signalStore, withComputed, withHooks, withMethods, withProps, withState } from "@ngrx/signals";
+import { MessageService } from "@openng/optimus-ui/api";
+
import { Todo } from "../model/todo.model";
-import { computed, inject } from "@angular/core";
import { TodoService } from "../service/todo.service";
-import { MessageService } from "@openng/optimus-ui/api";
export type TodoFilter = "all" | "pending" | "completed";
-type TodosState = {
+interface TodosState {
todos: Todo[],
- filteredTodos: Todo[],
loading: boolean,
filter: TodoFilter
-};
+}
const initialState: TodosState = {
todos: [],
- filteredTodos: [],
loading: false,
filter: "all"
};
diff --git a/src/app/features/todo/todo.ts b/src/app/features/todo/todo.ts
index ab87129..ed0ecd1 100644
--- a/src/app/features/todo/todo.ts
+++ b/src/app/features/todo/todo.ts
@@ -1,10 +1,11 @@
import { NgClass } from '@angular/common';
-import { ChangeDetectionStrategy, Component, computed, ElementRef, inject, OnInit, signal, viewChild } from '@angular/core';
+import { Component, computed, ElementRef, inject, OnInit, signal, viewChild } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { Button } from '@openng/optimus-ui/button';
import { InputText } from '@openng/optimus-ui/inputtext';
import { SelectButton, SelectButtonChangeEvent } from '@openng/optimus-ui/selectbutton';
import { ToggleSwitch, ToggleSwitchChangeEvent } from '@openng/optimus-ui/toggleswitch';
+
import { TodoService } from './service/todo.service';
import { TodoFilter, TodoStore } from './store/todo.store';
@@ -13,7 +14,6 @@ import { TodoFilter, TodoStore } from './store/todo.store';
imports: [InputText, SelectButton, FormsModule, ToggleSwitch, Button, NgClass],
templateUrl: './todo.html',
providers: [TodoStore, TodoService],
- changeDetection: ChangeDetectionStrategy.OnPush
})
export default class Todo implements OnInit {
protected store = inject(TodoStore);
diff --git a/src/app/shared/appSate/app-state-api.ts b/src/app/shared/appSate/app-state-api.ts
index 525e5eb..1edc587 100644
--- a/src/app/shared/appSate/app-state-api.ts
+++ b/src/app/shared/appSate/app-state-api.ts
@@ -1,11 +1,11 @@
-import { Injectable } from "@angular/core";
-import { Observable, of } from "rxjs";
-import { Settings } from "./model/settings.model";
-import { userData } from "./model/user.data";
-import { User } from "./model/user.model";
+import { Service } from '@angular/core';
+import { Observable, of } from 'rxjs';
+import { Settings } from './model/settings.model';
+import { userData } from './model/user.data';
+import { User } from './model/user.model';
-@Injectable({ providedIn: 'root' })
+@Service()
export class AppStateApi {
getUser(): User {
return userData;
@@ -13,8 +13,8 @@ export class AppStateApi {
getSettings(): Observable
{
return of({
- availableLanguages: ["de", "en", "fr"],
- currentLanguage: "fr",
+ availableLanguages: ['de', 'en', 'fr'],
+ currentLanguage: 'fr',
});
}
}
diff --git a/src/app/shared/appSate/app-state-store.ts b/src/app/shared/appSate/app-state-store.ts
index 8e71aac..a8a06ee 100644
--- a/src/app/shared/appSate/app-state-store.ts
+++ b/src/app/shared/appSate/app-state-store.ts
@@ -1,23 +1,22 @@
import { computed, inject } from "@angular/core";
import { patchState, signalStore, withComputed, withMethods, withState } from "@ngrx/signals";
import { firstValueFrom } from "rxjs";
+
import { AppStateApi } from "./app-state-api";
import { Settings } from "./model/settings.model";
import { User } from "./model/user.model";
-export type AppState = {
+export interface AppState {
user: Partial,
- settings: Settings,
- isConnected: boolean
-};
+ settings: Settings
+}
const initialAppState: AppState = {
user: {},
settings: {
availableLanguages: ["fr"],
currentLanguage: "fr",
- },
- isConnected: false
+ }
}
export const AppStateStore = signalStore(
diff --git a/src/app/shared/appSate/model/settings.model.ts b/src/app/shared/appSate/model/settings.model.ts
index 5913587..2d67972 100644
--- a/src/app/shared/appSate/model/settings.model.ts
+++ b/src/app/shared/appSate/model/settings.model.ts
@@ -1,4 +1,4 @@
-export type Settings = {
+export interface Settings {
availableLanguages: string[];
currentLanguage: string;
}
diff --git a/src/app/shared/appSate/model/user.model.ts b/src/app/shared/appSate/model/user.model.ts
index df526b3..a3ef31c 100644
--- a/src/app/shared/appSate/model/user.model.ts
+++ b/src/app/shared/appSate/model/user.model.ts
@@ -1,4 +1,4 @@
-export type User = {
+export interface User {
id: number;
firstName: string;
lastName: string;
diff --git a/src/app/shared/component/paginator.ts b/src/app/shared/component/paginator.ts
index 5ad87b8..2b3bcfa 100644
--- a/src/app/shared/component/paginator.ts
+++ b/src/app/shared/component/paginator.ts
@@ -1,9 +1,10 @@
-import { ChangeDetectionStrategy, Component, input, output } from '@angular/core';
+import { Component, input, output } from '@angular/core';
import { Paginator, PaginatorState } from '@openng/optimus-ui/paginator';
+
import { Pager } from '../store/paginator-feature';
@Component({
- selector: 'shared-paginator',
+ selector: 'app-paginator',
imports: [Paginator],
template: `
`,
- changeDetection: ChangeDetectionStrategy.OnPush
})
export class PaginatorComponent {
diff --git a/src/app/shared/layout/layout.service.ts b/src/app/shared/layout/layout.service.ts
index 6324f2e..e194ff9 100644
--- a/src/app/shared/layout/layout.service.ts
+++ b/src/app/shared/layout/layout.service.ts
@@ -1,8 +1,6 @@
-import { Injectable, signal } from '@angular/core';
+import { Service, signal } from '@angular/core';
-@Injectable({
- providedIn: 'root'
-})
+@Service()
export class LayoutService {
sidebarExpanded = signal(false);
diff --git a/src/app/shared/model/api-error.ts b/src/app/shared/model/api-error.ts
index 2bf48f9..85e0d57 100644
--- a/src/app/shared/model/api-error.ts
+++ b/src/app/shared/model/api-error.ts
@@ -1,4 +1,4 @@
-export type ApiError = {
+export interface ApiError {
status: number;
message?: string;
}
diff --git a/src/app/shared/model/es-result.ts b/src/app/shared/model/es-result.ts
index 6aa3b4d..55541c3 100644
--- a/src/app/shared/model/es-result.ts
+++ b/src/app/shared/model/es-result.ts
@@ -1,15 +1,13 @@
-export type EsRecord = {
+export interface EsRecord {
created: string;
id: string;
links: Links
- metadata: any,
+ metadata: unknown,
updated: string;
-};
+}
-export type EsResult = {
- aggregations: {
- [key: string]: any;
- },
+export interface EsResult {
+ aggregations: Record,
hits: {
hits: EsRecord[],
total: {
@@ -18,14 +16,14 @@ export type EsResult = {
}
},
links: Links;
-};
+}
-export type Links = {
+export interface Links {
create?: string;
next?: string;
prev?: string;
self: string;
-};
+}
export const EsResultInitialState: EsResult = {
aggregations: {},
diff --git a/src/app/shared/page-title-strategy.spec.ts b/src/app/shared/page-title-strategy.spec.ts
index c590334..c2b8048 100644
--- a/src/app/shared/page-title-strategy.spec.ts
+++ b/src/app/shared/page-title-strategy.spec.ts
@@ -1,6 +1,7 @@
import { TestBed } from '@angular/core/testing';
import { Title } from '@angular/platform-browser';
import { RouterStateSnapshot, TitleStrategy } from '@angular/router';
+
import { PageTitleStrategy } from './page-title-strategy';
const snapshot = {} as RouterStateSnapshot;
diff --git a/src/app/shared/page-title-strategy.ts b/src/app/shared/page-title-strategy.ts
index 68bae23..d6c355f 100644
--- a/src/app/shared/page-title-strategy.ts
+++ b/src/app/shared/page-title-strategy.ts
@@ -1,8 +1,8 @@
-import { inject, Injectable } from "@angular/core";
+import { inject, Service } from "@angular/core";
import { Title } from "@angular/platform-browser";
import { RouterStateSnapshot, TitleStrategy } from "@angular/router";
-@Injectable()
+@Service({ autoProvided: false })
export class PageTitleStrategy extends TitleStrategy {
private title = inject(Title);
diff --git a/src/app/shared/store/paginator-feature.spec.ts b/src/app/shared/store/paginator-feature.spec.ts
index 52552b5..0f3d22a 100644
--- a/src/app/shared/store/paginator-feature.spec.ts
+++ b/src/app/shared/store/paginator-feature.spec.ts
@@ -1,5 +1,6 @@
import { TestBed } from '@angular/core/testing';
import { signalStore } from '@ngrx/signals';
+
import { Pager, withPaginator } from './paginator-feature';
const defaultPager: Pager = {
diff --git a/src/app/shared/store/paginator-feature.ts b/src/app/shared/store/paginator-feature.ts
index 5af4848..737dc3d 100644
--- a/src/app/shared/store/paginator-feature.ts
+++ b/src/app/shared/store/paginator-feature.ts
@@ -1,14 +1,14 @@
import { patchState, signalMethod, signalStoreFeature, withMethods, withState } from "@ngrx/signals";
import { PaginatorState } from "@openng/optimus-ui/paginator";
-export type Pager = {
+export interface Pager {
page: number;
first: number;
rows: number;
rowsPerPageOptions: number[];
-};
+}
-export type Paginator = {
+export interface Paginator {
pager: Pager;
}
diff --git a/src/app/shared/store/request-status-feature.spec.ts b/src/app/shared/store/request-status-feature.spec.ts
index ea269a6..70f6c48 100644
--- a/src/app/shared/store/request-status-feature.spec.ts
+++ b/src/app/shared/store/request-status-feature.spec.ts
@@ -1,5 +1,6 @@
import { TestBed } from '@angular/core/testing';
import { patchState, signalStore, withMethods } from '@ngrx/signals';
+
import {
RequestStatus,
setError,
diff --git a/src/app/shared/store/request-status-feature.ts b/src/app/shared/store/request-status-feature.ts
index f9ebcfd..100d43e 100644
--- a/src/app/shared/store/request-status-feature.ts
+++ b/src/app/shared/store/request-status-feature.ts
@@ -3,7 +3,7 @@ import { signalStoreFeature, withComputed, withState } from "@ngrx/signals";
export type RequestStatus = 'idle' | 'pending' | 'fulfilled' | { error: string };
-export type RequestStatusState = { requestStatus: RequestStatus };
+export interface RequestStatusState { requestStatus: RequestStatus }
export function withRequestStatus() {
return signalStoreFeature(
diff --git a/src/main.ts b/src/main.ts
index 5df75f9..92975ea 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,6 +1,7 @@
import { bootstrapApplication } from '@angular/platform-browser';
-import { appConfig } from './app/app.config';
+
import { App } from './app/app';
+import { appConfig } from './app/app.config';
bootstrapApplication(App, appConfig)
.catch((err) => console.error(err));
diff --git a/src/styles.scss b/src/styles.scss
index 57359d5..69c2ea6 100644
--- a/src/styles.scss
+++ b/src/styles.scss
@@ -1,5 +1,5 @@
-@use "@openng/icons/openng-icons.css";
-@use "tailwindcss";
+@use '@openng/icons/openng-icons.css';
+@use 'tailwindcss';
@plugin "@openng/optimus-ui-tailwindcss";
h1 {
@@ -14,7 +14,7 @@ h3 {
@apply text-2xl;
}
-/* Force PrimeNG Avatar to be circular */
+/* Force Avatar to be circular */
.p-avatar {
border-radius: 50% !important;
overflow: hidden !important;
diff --git a/src/test-setup.ts b/src/test-setup.ts
index 17b7965..5a63b7a 100644
--- a/src/test-setup.ts
+++ b/src/test-setup.ts
@@ -1,5 +1,6 @@
import '@angular/compiler';
import '@analogjs/vitest-angular/setup-snapshots';
+
import { setupTestBed } from '@analogjs/vitest-angular/setup-testbed';
setupTestBed();
diff --git a/vite.config.ts b/vite.config.ts
index 3118564..8a5706b 100644
--- a/vite.config.ts
+++ b/vite.config.ts
@@ -1,6 +1,6 @@
-import { defineConfig } from 'vitest/config';
import angular from '@analogjs/vite-plugin-angular';
import { resolve } from 'path';
+import { defineConfig } from 'vitest/config';
export default defineConfig({
plugins: [angular()],