feat: add categories and manufacturers CRUD components; implement form handling and image upload functionality
This commit is contained in:
@@ -33,6 +33,8 @@ import {ConditionService} from '../../services/app/condition.service';
|
||||
import {Condition} from '../../interfaces/condition';
|
||||
import {ProductService} from '../../services/app/product.service';
|
||||
import {CdkTextareaAutosize} from '@angular/cdk/text-field';
|
||||
import {ImageService} from '../../services/app/image.service';
|
||||
import {ProductImageService} from '../../services/app/product_images.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-add-product',
|
||||
@@ -67,6 +69,11 @@ export class AddProductComponent implements OnInit, OnDestroy {
|
||||
isSubmitted = false;
|
||||
isLoading = false;
|
||||
|
||||
imageFile: File | null = null;
|
||||
imagePreview: string | null = null;
|
||||
private imageUploadSubscription: Subscription | null = null;
|
||||
private imageLinkSubscription: Subscription | null = null;
|
||||
|
||||
brands: Brand[] = [];
|
||||
platforms: Platform[] = [];
|
||||
categories: Category[] = [];
|
||||
@@ -89,7 +96,9 @@ export class AddProductComponent implements OnInit, OnDestroy {
|
||||
private readonly platformService = inject(PlatformService);
|
||||
private readonly categoryService = inject(CategoryService);
|
||||
private readonly conditionService = inject(ConditionService);
|
||||
private readonly imageService = inject(ImageService)
|
||||
private readonly productService = inject(ProductService);
|
||||
private readonly productImageService = inject(ProductImageService);
|
||||
|
||||
private readonly router: Router = inject(Router);
|
||||
|
||||
@@ -278,60 +287,117 @@ export class AddProductComponent implements OnInit, OnDestroy {
|
||||
this.platformSubscription?.unsubscribe();
|
||||
this.categorySubscription?.unsubscribe();
|
||||
this.conditionSubscription?.unsubscribe();
|
||||
this.imageUploadSubscription?.unsubscribe();
|
||||
this.imageLinkSubscription?.unsubscribe();
|
||||
}
|
||||
|
||||
onFileSelected(event: Event) {
|
||||
const input = event.target as HTMLInputElement;
|
||||
if (!input.files || input.files.length === 0) {
|
||||
this.imageFile = null;
|
||||
this.imagePreview = null;
|
||||
return;
|
||||
}
|
||||
const file = input.files[0];
|
||||
this.imageFile = file;
|
||||
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
this.imagePreview = String(reader.result);
|
||||
};
|
||||
reader.readAsDataURL(file);
|
||||
}
|
||||
|
||||
onProductAdd() {
|
||||
this.isSubmitted = true;
|
||||
|
||||
if (this.addProductForm.valid) {
|
||||
this.isLoading = true;
|
||||
const raw = this.addProductForm.value;
|
||||
if (!this.addProductForm.valid) return;
|
||||
|
||||
const priceStr = raw.price ?? '';
|
||||
const priceNum = Number(String(priceStr).replace(',', '.').trim());
|
||||
if (Number.isNaN(priceNum)) {
|
||||
this.isLoading = false;
|
||||
this.addProductForm.get('price')?.setErrors({pattern: true});
|
||||
return;
|
||||
}
|
||||
this.isLoading = true;
|
||||
const raw = this.addProductForm.value;
|
||||
|
||||
const quantityNum = Number(raw.quantity);
|
||||
|
||||
const brandId = raw.brand;
|
||||
const brandObj = this.brands.find(b => String(b.id) === String(brandId)) ?? {id: brandId, name: undefined};
|
||||
|
||||
const platformId = raw.platform;
|
||||
const foundPlatform = this.platforms.find(p => String(p.id) === String(platformId));
|
||||
const platformObj = {
|
||||
...(foundPlatform ?? {id: platformId, name: undefined}),
|
||||
brand: foundPlatform?.brand ? (typeof foundPlatform.brand === 'object' ? foundPlatform.brand : (this.brands.find(b => String(b.id) === String(foundPlatform.brand)) ?? brandObj)) : brandObj
|
||||
};
|
||||
|
||||
const payload = {
|
||||
...raw,
|
||||
price: priceNum,
|
||||
quantity: quantityNum,
|
||||
brand: brandObj,
|
||||
platform: platformObj
|
||||
};
|
||||
|
||||
this.addProductSubscription = this.productService.add(payload).subscribe({
|
||||
next: (response: any) => {
|
||||
console.log("Product added successfully:", response);
|
||||
this.addProductForm.reset();
|
||||
this.isSubmitted = false;
|
||||
alert("Produit ajouté avec succès !");
|
||||
this.router.navigate(['/products']).then();
|
||||
},
|
||||
error: (error: any) => {
|
||||
console.error("Error adding product:", error);
|
||||
alert("Une erreur est survenue lors de l'ajout du produit.");
|
||||
},
|
||||
complete: () => {
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
// parsing price/quantity etc (même logique que l'original)
|
||||
const priceStr = raw.price ?? '';
|
||||
const priceNum = Number(String(priceStr).replace(',', '.').trim());
|
||||
if (Number.isNaN(priceNum)) {
|
||||
this.isLoading = false;
|
||||
this.addProductForm.get('price')?.setErrors({pattern: true});
|
||||
return;
|
||||
}
|
||||
const quantityNum = Number(raw.quantity);
|
||||
|
||||
const brandId = raw.brand;
|
||||
const brandObj = this.brands.find(b => String(b.id) === String(brandId)) ?? {id: brandId, name: undefined};
|
||||
|
||||
const platformId = raw.platform;
|
||||
const foundPlatform = this.platforms.find(p => String(p.id) === String(platformId));
|
||||
const platformObj = {
|
||||
...(foundPlatform ?? {id: platformId, name: undefined}),
|
||||
brand: foundPlatform?.brand ? (typeof foundPlatform.brand === 'object' ? foundPlatform.brand : (this.brands.find(b => String(b.id) === String(foundPlatform.brand)) ?? brandObj)) : brandObj
|
||||
};
|
||||
|
||||
const payload = {
|
||||
...raw,
|
||||
price: priceNum,
|
||||
quantity: quantityNum,
|
||||
brand: brandObj,
|
||||
platform: platformObj
|
||||
};
|
||||
|
||||
// 1) créer le produit
|
||||
this.addProductSubscription = this.productService.add(payload).subscribe({
|
||||
next: (createdProduct: any) => {
|
||||
const productId = createdProduct?.id;
|
||||
if (!this.imageFile) {
|
||||
// pas d'image => fin
|
||||
this.afterSuccessfulAdd();
|
||||
return;
|
||||
}
|
||||
|
||||
// 2) upload de l'image
|
||||
this.imageUploadSubscription = this.imageService.add(this.imageFile).subscribe({
|
||||
next: (uploadedImage: { id: any; }) => {
|
||||
const imageId = uploadedImage?.id;
|
||||
if (!productId || imageId == null) {
|
||||
console.error('Missing productId or imageId after upload');
|
||||
this.afterSuccessfulAdd(); // navigation quand même ou gérer l'erreur
|
||||
return;
|
||||
}
|
||||
// 3) lier image <-> product
|
||||
this.imageLinkSubscription = this.productImageService.link(productId, imageId).subscribe({
|
||||
next: () => {
|
||||
this.afterSuccessfulAdd();
|
||||
},
|
||||
error: (error: any) => {
|
||||
console.error('Error linking image to product:', error);
|
||||
alert('Produit ajouté, mais la liaison de l\'image a échoué.');
|
||||
this.afterSuccessfulAdd();
|
||||
}
|
||||
});
|
||||
},
|
||||
error: (error: any) => {
|
||||
console.error('Error uploading image:', error);
|
||||
alert('Produit ajouté, mais l\'upload de l\'image a échoué.');
|
||||
this.afterSuccessfulAdd();
|
||||
}
|
||||
});
|
||||
},
|
||||
error: (error: any) => {
|
||||
console.error("Error adding product:", error);
|
||||
alert("Une erreur est survenue lors de l'ajout du produit.");
|
||||
this.isLoading = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private afterSuccessfulAdd() {
|
||||
this.addProductForm.reset();
|
||||
this.imageFile = null;
|
||||
this.imagePreview = null;
|
||||
this.isSubmitted = false;
|
||||
this.isLoading = false;
|
||||
alert("Produit ajouté avec succès !");
|
||||
this.router.navigate(['/products']).then();
|
||||
}
|
||||
|
||||
isFieldInvalid(fieldName: string): boolean {
|
||||
|
||||
Reference in New Issue
Block a user