diff --git a/client/src/app/pages/add-product/add-product.component.css b/client/src/app/pages/add-product/add-product.component.css new file mode 100644 index 0000000..552673e --- /dev/null +++ b/client/src/app/pages/add-product/add-product.component.css @@ -0,0 +1,32 @@ +.auth-wrap { + min-height: 100vh; + display: grid; + place-items: center; + padding: 16px; +} + +.auth-card { + width: 100%; + max-width: 520px; +} + +.form-grid { + display: grid; + gap: 16px; + margin-top: 16px; +} + +.actions { + display: flex; + margin: 8px; + + button { + display: inline-flex; + align-items: center; + gap: 8px; + } +} + +.ml-8 { + margin-left: 8px; +} diff --git a/client/src/app/pages/add-product/add-product.component.html b/client/src/app/pages/add-product/add-product.component.html new file mode 100644 index 0000000..41ab698 --- /dev/null +++ b/client/src/app/pages/add-product/add-product.component.html @@ -0,0 +1,148 @@ +
+ + + Gestion des produits + Create a new account + + + +
+ + + + Titre + + @if (isFieldInvalid('title')) { + {{ getFieldError('title') }} + } + + + + + Description + + @if (isFieldInvalid('description')) { + {{ getFieldError('description') }} + } + + + + + Catégorie + + Option 1 + Option 2 + Option 3 + + + + + + État + + Option 1 + Option 2 + Option 3 + + + + + + Marque + + @for (brand of brands; track brand.name) { + {{ brand.name }} + } + + + + + + Plateforme + + Option 1 + Option 2 + Option 3 + + + + + + Complet + + @if (isFieldInvalid('complete')) { +
{{ getFieldError('complete') }}
+ } + + + + Avec notice + + @if (isFieldInvalid('manual')) { +
{{ getFieldError('manual') }}
+ } + + + + Prix TTC + + @if (isFieldInvalid('price')) { + {{ getFieldError('price') }} + } + + + + + Quantité + + @if (isFieldInvalid('quantity')) { + {{ getFieldError('quantity') }} + } + + + +
+ +
+
+
+ + + + + + Voir la liste des produits + + +
+
diff --git a/client/src/app/pages/add-product/add-product.component.spec.ts b/client/src/app/pages/add-product/add-product.component.spec.ts new file mode 100644 index 0000000..b8d7fed --- /dev/null +++ b/client/src/app/pages/add-product/add-product.component.spec.ts @@ -0,0 +1,23 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { AddProductComponent } from './add-product.component'; + +describe('AddProductComponent', () => { + let component: AddProductComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [AddProductComponent] + }) + .compileComponents(); + + fixture = TestBed.createComponent(AddProductComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/client/src/app/pages/add-product/add-product.component.ts b/client/src/app/pages/add-product/add-product.component.ts new file mode 100644 index 0000000..acc6d1d --- /dev/null +++ b/client/src/app/pages/add-product/add-product.component.ts @@ -0,0 +1,163 @@ +import {Component, inject, OnDestroy, OnInit} from '@angular/core'; +import { + FormBuilder, + FormGroup, + FormsModule, + ReactiveFormsModule, + Validators +} from "@angular/forms"; +import {MatButton} from "@angular/material/button"; +import { + MatCard, + MatCardActions, + MatCardContent, + MatCardHeader, + MatCardSubtitle, + MatCardTitle +} from "@angular/material/card"; +import {MatCheckbox} from "@angular/material/checkbox"; +import {MatDivider} from "@angular/material/divider"; +import {MatError, MatFormField, MatLabel} from "@angular/material/form-field"; +import {MatInput} from "@angular/material/input"; +import {MatProgressSpinner} from "@angular/material/progress-spinner"; +import {MatOption, MatSelect} from '@angular/material/select'; +import {Router, RouterLink} from '@angular/router'; +import {Subscription} from 'rxjs'; +import {BrandService} from '../../services/brand/brand.service'; +import {Brand} from '../../models/brand/brand'; + +@Component({ + selector: 'app-add-product', + standalone: true, + imports: [ + FormsModule, + MatButton, + MatCard, + MatCardActions, + MatCardContent, + MatCardHeader, + MatCardSubtitle, + MatCardTitle, + MatCheckbox, + MatDivider, + MatError, + MatFormField, + MatInput, + MatLabel, + MatProgressSpinner, + ReactiveFormsModule, + MatSelect, + MatOption, + RouterLink + ], + templateUrl: './add-product.component.html', + styleUrl: './add-product.component.css' +}) +export class AddProductComponent implements OnInit, OnDestroy { + + addProductForm: FormGroup; + isSubmitted = false; + isLoading = false; + + brands: Brand[] = []; + + private readonly router: Router = inject(Router); + + private addProductSubscription: Subscription | null = null; + private readonly brandService: BrandService = inject(BrandService); + + constructor(private readonly formBuilder: FormBuilder) { + this.addProductForm = this.formBuilder.group({ + title: ['', [ + Validators.required, + Validators.minLength(3), + Validators.maxLength(50), + Validators.pattern('^[a-zA-Z]+$') + ]], + description: ['', [ + Validators.required, + Validators.minLength(10), + Validators.maxLength(255), + Validators.pattern('^[a-zA-Z]+$') + ]], + category: ['', [ + Validators.required + ]], + condition: ['', [ + Validators.required + ]], + brand: ['', [ + Validators.required + ]], + platform: ['', [ + Validators.required + ]], + complete: [true, + Validators.requiredTrue + ], + manual: [true, + Validators.requiredTrue + ], + price: ['', [ + Validators.required, + Validators.min(0), + Validators.max(9999), + Validators.pattern('^[0-9]+$') + ]], + quantity: ['', [ + Validators.required, + Validators.min(1), + Validators.max(999), + Validators.pattern('^[0-9]+$') + ]] + }, + ); + } + + ngOnInit(): void { + this.brandService.getBrands().subscribe({ + next: (brands) => { + this.brands = brands; + }, + error: (error) => { + console.error('Error fetching brands:', error); + }, + complete: () => { + console.log('Finished fetching brands:', this.brands); + } + }); + } + + ngOnDestroy(): void { + this.addProductSubscription?.unsubscribe(); + } + + onProductAdd() { + + this.isSubmitted = true; + + if (this.addProductForm.valid) { + this.isLoading = true; + const productData = this.addProductForm.value; + alert("Produit ajouté avec succès !"); + console.log(productData); + } + } + + isFieldInvalid(fieldName: string): boolean { + const field = this.addProductForm.get(fieldName); + return Boolean(field && field.invalid && (field.dirty || field.touched || this.isSubmitted)); + } + + getFieldError(fieldName: string): string { + const field = this.addProductForm.get(fieldName); + + if (field && field.errors) { + if (field.errors['required']) return `Ce champ est obligatoire`; + if (field.errors['email']) return `Format d'email invalide`; + if (field.errors['minlength']) return `Minimum ${field.errors['minlength'].requiredLength} caractères`; + if (field.errors['maxlength']) return `Maximum ${field.errors['maxlength'].requiredLength} caractères`; + } + return ''; + } +} diff --git a/client/src/app/pages/home/home.component.html b/client/src/app/pages/home/home.component.html index 1e337dd..aecd790 100644 --- a/client/src/app/pages/home/home.component.html +++ b/client/src/app/pages/home/home.component.html @@ -1,13 +1,15 @@
@if (getUser(); as user) { -

Welcome, {{ user.firstName }}!

-

What would you like to do today?

+

Bonjour, {{ user.firstName }}!

+

Que souhaitez-vous faire ?

+
+ + } @else { -

Welcome to the demo

-

Create an account or sign in to get started.

+

Gestion des produits

- - + +
}
diff --git a/client/src/app/pages/home/home.component.ts b/client/src/app/pages/home/home.component.ts index 0613a49..17b64bf 100644 --- a/client/src/app/pages/home/home.component.ts +++ b/client/src/app/pages/home/home.component.ts @@ -2,13 +2,15 @@ import {Component, inject} from '@angular/core'; import {MatButton} from '@angular/material/button'; import {AuthService} from '../../services/auth/auth.service'; import {RouterLink} from '@angular/router'; +import {AddProductComponent} from '../add-product/add-product.component'; @Component({ selector: 'app-home', standalone: true, imports: [ MatButton, - RouterLink + RouterLink, + AddProductComponent ], templateUrl: './home.component.html', styleUrl: './home.component.css' diff --git a/client/src/app/pages/login/login.component.html b/client/src/app/pages/login/login.component.html index e1f368c..020b1e4 100644 --- a/client/src/app/pages/login/login.component.html +++ b/client/src/app/pages/login/login.component.html @@ -1,17 +1,17 @@
-

Sign in

+

Se connecter

- Username + Nom d'utilisateur - Password + Mot de passe - + @if (invalidCredentials) { - Invalid credentials + Nom d'utilisateur ou mot de passe invalide }
diff --git a/client/src/app/pages/login/login.component.ts b/client/src/app/pages/login/login.component.ts index 7d60443..586e8f5 100644 --- a/client/src/app/pages/login/login.component.ts +++ b/client/src/app/pages/login/login.component.ts @@ -51,9 +51,11 @@ export class LoginComponent implements OnDestroy { this.loginFormGroup.value as Credentials).subscribe({ next: (result: User | null | undefined) => { this.navigateHome(); + alert('Login successful!'); }, error: (error) => { console.log(error); + alert(error.message); this.invalidCredentials = true; } }); diff --git a/client/src/app/pages/register/register.component.html b/client/src/app/pages/register/register.component.html index 8613168..30adda6 100644 --- a/client/src/app/pages/register/register.component.html +++ b/client/src/app/pages/register/register.component.html @@ -1,8 +1,8 @@
- Registration - Create a new account + Inscription + Créer un nouveau compte @@ -10,7 +10,7 @@ - First Name + Prénom - Last Name + Nom - Username + Nom d'utilisateur - Password + Mot de passe - Confirm Password + Confirmer le mot de passe - I agree to the terms and conditions + J'accepte les conditions générales d'utilisation @if (isFieldInvalid('termsAndConditions')) {
{{ getFieldError('termsAndConditions') }}
@@ -116,9 +116,9 @@ [disabled]="isLoading || registerForm.invalid"> @if (isLoading) { - Signing up… + Inscription… } @else { - Sign up + S'inscrire } @@ -129,8 +129,8 @@ - Already have an account? - Sign in + Vous avez déjà un compte ? + Se connecter