add Categories management: create CategoriesList component, update admin navbar, and integrate category handling in product forms
This commit is contained in:
@@ -5,5 +5,7 @@
|
||||
<mat-tab label="Plateformes">
|
||||
<app-platforms-list></app-platforms-list>
|
||||
</mat-tab>
|
||||
<mat-tab label="Catégories">Catégories</mat-tab>
|
||||
<mat-tab label="Catégories">
|
||||
<app-categories-list></app-categories-list>
|
||||
</mat-tab>
|
||||
</mat-tab-group>
|
||||
|
||||
@@ -5,6 +5,7 @@ import {RouterLink, RouterLinkActive} from '@angular/router';
|
||||
import {MatTab, MatTabGroup} from '@angular/material/tabs';
|
||||
import {BrandsListComponent} from '../brands-list/brands-list.component';
|
||||
import {PlatformsListComponent} from '../platforms-list/platforms-list.component';
|
||||
import {CategoriesListComponent} from '../categories-list/categories-list.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-admin-navbar',
|
||||
@@ -18,7 +19,8 @@ import {PlatformsListComponent} from '../platforms-list/platforms-list.component
|
||||
MatTabGroup,
|
||||
MatTab,
|
||||
BrandsListComponent,
|
||||
PlatformsListComponent
|
||||
PlatformsListComponent,
|
||||
CategoriesListComponent
|
||||
],
|
||||
templateUrl: './admin-navbar.component.html',
|
||||
styleUrl: './admin-navbar.component.css'
|
||||
|
||||
@@ -38,7 +38,7 @@
|
||||
|
||||
@if (!brands || brands.length === 0) {
|
||||
<div class="no-brands">
|
||||
Aucune marque trouvée.
|
||||
Aucune plateforme trouvée.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
@@ -25,7 +25,7 @@ import {MatButton, MatIconButton} from '@angular/material/button';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {MatFormField} from '@angular/material/form-field';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
import {BrandService} from '../../services/brand/brand.service';
|
||||
import {BrandService} from '../../services/app/brand.service';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import { BrandDialogComponent } from '../brand-dialog/brand-dialog.component';
|
||||
|
||||
@@ -94,6 +94,7 @@ export class BrandsListComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
next: (brands:Brand[]) => {
|
||||
this.brands = brands || []
|
||||
this.dataSource.data = this.brands;
|
||||
console.log("Fetched brands:", this.brands);
|
||||
},
|
||||
error: () => this.brands = []
|
||||
});
|
||||
@@ -101,7 +102,7 @@ export class BrandsListComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
|
||||
onAdd(): void {
|
||||
const ref = this.dialog.open(BrandDialogComponent, {
|
||||
data: { brand: { id: '', name: '' } },
|
||||
data: { brand: { id: '', name: '', brand: undefined } },
|
||||
width: '420px'
|
||||
});
|
||||
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
:host {
|
||||
display: block;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.container {
|
||||
max-width: 900px;
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.toolbar {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.filter {
|
||||
max-width: 240px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
td, th {
|
||||
word-break: break-word;
|
||||
white-space: normal;
|
||||
}
|
||||
|
||||
.actions-cell {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
justify-content: flex-end;
|
||||
min-width: 120px;
|
||||
}
|
||||
|
||||
button.mat-icon-button {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.no-brands {
|
||||
text-align: center;
|
||||
margin-top: 16px;
|
||||
color: rgba(0,0,0,0.6);
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.toolbar {
|
||||
flex-direction: column;
|
||||
align-items: stretch;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.actions-cell {
|
||||
min-width: 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
<div class="container" style="padding:16px;">
|
||||
<div class="toolbar">
|
||||
<button mat-flat-button color="accent" (click)="onAdd()">
|
||||
<mat-icon>add</mat-icon> Ajouter
|
||||
</button>
|
||||
|
||||
<mat-form-field class="filter">
|
||||
<input matInput placeholder="Rechercher" (input)="applyFilter($any($event.target).value)">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z1" matSort>
|
||||
|
||||
<!-- Name Column -->
|
||||
<ng-container matColumnDef="name">
|
||||
<th mat-header-cell *matHeaderCellDef>Nom</th>
|
||||
<td mat-cell *matCellDef="let category">{{ category.name }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Actions Column -->
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let category" class="actions-cell">
|
||||
<button mat-icon-button (click)="onEdit(category)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" (click)="onDelete(category)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator [pageSize]="10" [pageSizeOptions]="[5,10,25]" showFirstLastButtons></mat-paginator>
|
||||
|
||||
@if (!categories || categories.length === 0) {
|
||||
<div class="no-categories">
|
||||
Aucune catégorie trouvée.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -0,0 +1,138 @@
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter,
|
||||
ViewChild,
|
||||
AfterViewInit,
|
||||
OnChanges,
|
||||
SimpleChanges,
|
||||
OnInit,
|
||||
inject
|
||||
} from '@angular/core';
|
||||
import {
|
||||
MatCell, MatCellDef,
|
||||
MatColumnDef,
|
||||
MatHeaderCell,
|
||||
MatHeaderCellDef, MatHeaderRow, MatHeaderRowDef, MatRow, MatRowDef,
|
||||
MatTable,
|
||||
MatTableDataSource
|
||||
} from '@angular/material/table';
|
||||
import {MatPaginator} from '@angular/material/paginator';
|
||||
import {MatSort} from '@angular/material/sort';
|
||||
import {Category} from '../../interfaces/category';
|
||||
import {MatButton, MatIconButton} from '@angular/material/button';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {MatFormField} from '@angular/material/form-field';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
import {CategoryService} from '../../services/app/category.service';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import { CategoryDialogComponent } from '../category-dialog/category-dialog.component';
|
||||
|
||||
@Component({
|
||||
selector: 'app-categories-list',
|
||||
templateUrl: './categories-list.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatButton,
|
||||
MatIcon,
|
||||
MatFormField,
|
||||
MatInput,
|
||||
MatTable,
|
||||
MatColumnDef,
|
||||
MatHeaderCell,
|
||||
MatCell,
|
||||
MatHeaderCellDef,
|
||||
MatCellDef,
|
||||
MatSort,
|
||||
MatIconButton,
|
||||
MatHeaderRow,
|
||||
MatRow,
|
||||
MatHeaderRowDef,
|
||||
MatRowDef,
|
||||
MatPaginator
|
||||
],
|
||||
styleUrls: ['./categories-list.component.css']
|
||||
})
|
||||
export class CategoriesListComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
|
||||
@Input() categories: Category[] = [];
|
||||
@Output() add = new EventEmitter<Category>();
|
||||
@Output() edit = new EventEmitter<Category>();
|
||||
@Output() delete = new EventEmitter<Category>();
|
||||
|
||||
displayedColumns: string[] = ['name', 'actions'];
|
||||
dataSource = new MatTableDataSource<Category>([]);
|
||||
|
||||
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||
@ViewChild(MatSort) sort!: MatSort;
|
||||
|
||||
private readonly categoryService: CategoryService = inject(CategoryService);
|
||||
private readonly dialog = inject(MatDialog);
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.categories || this.categories.length === 0) {
|
||||
this.loadCategories();
|
||||
} else {
|
||||
this.dataSource.data = this.categories;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['categories']) {
|
||||
this.dataSource.data = this.categories || [];
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource.sort = this.sort;
|
||||
}
|
||||
|
||||
loadCategories() {
|
||||
this.categoryService.getCategories().subscribe({
|
||||
next: (categories:Category[]) => {
|
||||
this.categories = categories || []
|
||||
this.dataSource.data = this.categories;
|
||||
},
|
||||
error: () => this.categories = []
|
||||
});
|
||||
}
|
||||
|
||||
onAdd(): void {
|
||||
const ref = this.dialog.open(CategoryDialogComponent, {
|
||||
data: { category: { id: '', name: '' } },
|
||||
width: '420px'
|
||||
});
|
||||
|
||||
ref.afterClosed().subscribe((result?: Category) => {
|
||||
if (result) {
|
||||
this.add.emit(result);
|
||||
this.categoryService.addCategory(result).subscribe(() => this.loadCategories());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onEdit(category: Category): void {
|
||||
const ref = this.dialog.open(CategoryDialogComponent, {
|
||||
data: { category: { ...category } },
|
||||
width: '420px'
|
||||
});
|
||||
|
||||
ref.afterClosed().subscribe((result?: Category) => {
|
||||
if (result) {
|
||||
this.edit.emit(result);
|
||||
this.categoryService.updateCategory((category as any).id, result).subscribe(() => this.loadCategories());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onDelete(category: Category): void {
|
||||
this.delete.emit(category);
|
||||
this.categoryService.deleteCategory((category as any).id).subscribe(() => this.loadCategories());
|
||||
}
|
||||
|
||||
applyFilter(value: string): void {
|
||||
this.dataSource.filter = (value || '').trim().toLowerCase();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
<h2 mat-dialog-title>{{ categoryExists ? 'Modifier la catégorie' : 'Nouvelle catégorie' }}</h2>
|
||||
|
||||
<mat-dialog-content>
|
||||
<mat-form-field appearance="fill" style="width:100%;">
|
||||
<mat-label>Nom</mat-label>
|
||||
<input matInput [(ngModel)]="category.name" />
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="cancel()">Annuler</button>
|
||||
<button mat-flat-button color="primary" (click)="save()">Enregistrer</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,51 @@
|
||||
import { Component, Inject } from '@angular/core';
|
||||
import {
|
||||
MatDialogRef,
|
||||
MAT_DIALOG_DATA,
|
||||
MatDialogTitle,
|
||||
MatDialogContent,
|
||||
MatDialogActions
|
||||
} from '@angular/material/dialog';
|
||||
import { Category } from '../../interfaces/category';
|
||||
import {MatFormField, MatLabel} from '@angular/material/form-field';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
|
||||
@Component({
|
||||
selector: 'app-category-dialog',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatDialogTitle,
|
||||
MatDialogContent,
|
||||
MatFormField,
|
||||
MatLabel,
|
||||
MatInput,
|
||||
FormsModule,
|
||||
MatDialogActions,
|
||||
MatButton
|
||||
],
|
||||
templateUrl: './category-dialog.component.html'
|
||||
})
|
||||
export class CategoryDialogComponent {
|
||||
category: Category = { id: '', name: '' }
|
||||
|
||||
constructor(
|
||||
private readonly dialogRef: MatDialogRef<CategoryDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { category: Category }
|
||||
) {
|
||||
this.category = { ...(data?.category || { id: '', name: '' }) };
|
||||
}
|
||||
|
||||
get categoryExists(): boolean {
|
||||
return !!this.data?.category?.id;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.dialogRef.close(this.category);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
@@ -14,7 +14,7 @@ import {Brand} from '../../interfaces/brand';
|
||||
import {Platform} from '../../interfaces/platform';
|
||||
import {MatOption} from '@angular/material/core';
|
||||
import {MatSelect} from '@angular/material/select';
|
||||
import {BrandService} from '../../services/brand/brand.service';
|
||||
import {BrandService} from '../../services/app/brand.service';
|
||||
|
||||
@Component({
|
||||
selector: 'app-platform-dialog',
|
||||
|
||||
@@ -45,7 +45,7 @@ button.mat-icon-button {
|
||||
height: 40px;
|
||||
}
|
||||
|
||||
.no-brands {
|
||||
.no-platforms {
|
||||
text-align: center;
|
||||
margin-top: 16px;
|
||||
color: rgba(0,0,0,0.6);
|
||||
|
||||
@@ -25,7 +25,7 @@ import {MatButton, MatIconButton} from '@angular/material/button';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {MatFormField} from '@angular/material/form-field';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
import {PlatformService} from '../../services/platform/platform.service';
|
||||
import {PlatformService} from '../../services/app/platform.service';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import { PlatformDialogComponent } from '../platform-dialog/platform-dialog.component';
|
||||
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
<h2 mat-dialog-title>{{ productExists ? 'Modifier la plateforme' : 'Nouvelle plateforme' }}</h2>
|
||||
|
||||
<mat-dialog-content>
|
||||
<mat-form-field appearance="fill" style="width:100%;">
|
||||
<mat-label>Nom</mat-label>
|
||||
<input matInput [(ngModel)]="product.title" />
|
||||
</mat-form-field>
|
||||
</mat-dialog-content>
|
||||
|
||||
<mat-dialog-actions align="end">
|
||||
<button mat-button (click)="cancel()">Annuler</button>
|
||||
<button mat-flat-button color="primary" (click)="save()">Enregistrer</button>
|
||||
</mat-dialog-actions>
|
||||
@@ -0,0 +1,62 @@
|
||||
import {Component, Inject} from '@angular/core';
|
||||
import {
|
||||
MatDialogRef,
|
||||
MAT_DIALOG_DATA,
|
||||
MatDialogTitle,
|
||||
MatDialogContent,
|
||||
MatDialogActions
|
||||
} from '@angular/material/dialog';
|
||||
import {Product} from '../../interfaces/product';
|
||||
import {MatFormField, MatLabel} from '@angular/material/form-field';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
import {FormsModule} from '@angular/forms';
|
||||
import {MatButton} from '@angular/material/button';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-dialog',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatDialogTitle,
|
||||
MatDialogContent,
|
||||
MatFormField,
|
||||
MatLabel,
|
||||
MatInput,
|
||||
FormsModule,
|
||||
MatDialogActions,
|
||||
MatButton
|
||||
],
|
||||
templateUrl: './product-dialog.component.html'
|
||||
})
|
||||
export class ProductDialogComponent {
|
||||
product: Product = {
|
||||
id: '',
|
||||
title: '',
|
||||
description: '',
|
||||
price: 0,
|
||||
quantity: 0,
|
||||
complete: false,
|
||||
manualIncluded: false,
|
||||
category: undefined,
|
||||
platform: undefined,
|
||||
condition: undefined
|
||||
};
|
||||
|
||||
constructor(
|
||||
private readonly dialogRef: MatDialogRef<ProductDialogComponent>,
|
||||
@Inject(MAT_DIALOG_DATA) public data: { product: Product }
|
||||
) {
|
||||
this.product = {...(data?.product || {id: '', name: ''})};
|
||||
}
|
||||
|
||||
get productExists(): boolean {
|
||||
return !!this.data?.product?.id;
|
||||
}
|
||||
|
||||
save() {
|
||||
this.dialogRef.close(this.product);
|
||||
}
|
||||
|
||||
cancel() {
|
||||
this.dialogRef.close();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
<p>product-form works!</p>
|
||||
@@ -0,0 +1,12 @@
|
||||
import { Component } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
selector: 'app-product-form',
|
||||
standalone: true,
|
||||
imports: [],
|
||||
templateUrl: './product-form.component.html',
|
||||
styleUrl: './product-form.component.css'
|
||||
})
|
||||
export class ProductFormComponent {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<div class="container" style="padding:16px;">
|
||||
<div class="toolbar">
|
||||
<button mat-flat-button color="accent" (click)="onAdd()">
|
||||
<mat-icon>add</mat-icon> Ajouter
|
||||
</button>
|
||||
|
||||
<mat-form-field class="filter">
|
||||
<input matInput placeholder="Rechercher" (input)="applyFilter($any($event.target).value)">
|
||||
</mat-form-field>
|
||||
</div>
|
||||
|
||||
<table mat-table [dataSource]="dataSource" class="mat-elevation-z1" matSort>
|
||||
|
||||
<!-- Title Column -->
|
||||
<ng-container matColumnDef="title">
|
||||
<th mat-header-cell *matHeaderCellDef>Nom</th>
|
||||
<td mat-cell *matCellDef="let product">{{ product.title }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Description Column -->
|
||||
<ng-container matColumnDef="description">
|
||||
<th mat-header-cell *matHeaderCellDef>Description</th>
|
||||
<td mat-cell *matCellDef="let product">{{ product.description }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Category Column -->
|
||||
<ng-container matColumnDef="category">
|
||||
<th mat-header-cell *matHeaderCellDef>Catégorie</th>
|
||||
<td mat-cell *matCellDef="let product">{{ product.category.name }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Platform Column -->
|
||||
<ng-container matColumnDef="platform">
|
||||
<th mat-header-cell *matHeaderCellDef>Plateforme</th>
|
||||
<td mat-cell *matCellDef="let product">{{ product.platform.name }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Condition Column -->
|
||||
<ng-container matColumnDef="condition">
|
||||
<th mat-header-cell *matHeaderCellDef>État</th>
|
||||
<td mat-cell *matCellDef="let product">{{ product.condition.displayName }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Complete Column -->
|
||||
<ng-container matColumnDef="complete">
|
||||
<th mat-header-cell *matHeaderCellDef>Complet</th>
|
||||
<td mat-cell *matCellDef="let product">
|
||||
@if (product.complete) {
|
||||
<mat-icon color="primary">check_circle</mat-icon>
|
||||
} @else {
|
||||
<mat-icon color="warn">cancel</mat-icon>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Manual Column -->
|
||||
<ng-container matColumnDef="manual">
|
||||
<th mat-header-cell *matHeaderCellDef>Notice</th>
|
||||
<td mat-cell *matCellDef="let product">
|
||||
@if (product.manual) {
|
||||
<mat-icon color="primary">check_circle</mat-icon>
|
||||
} @else {
|
||||
<mat-icon color="warn">cancel</mat-icon>
|
||||
}
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Price Column -->
|
||||
<ng-container matColumnDef="price">
|
||||
<th mat-header-cell *matHeaderCellDef>Prix</th>
|
||||
<td mat-cell *matCellDef="let product">{{ product.price | currency:'EUR' }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Quantity Column -->
|
||||
<ng-container matColumnDef="quantity">
|
||||
<th mat-header-cell *matHeaderCellDef>Quantité</th>
|
||||
<td mat-cell *matCellDef="let product">{{ product.quantity }}</td>
|
||||
</ng-container>
|
||||
|
||||
<!-- Actions Column -->
|
||||
<ng-container matColumnDef="actions">
|
||||
<th mat-header-cell *matHeaderCellDef></th>
|
||||
<td mat-cell *matCellDef="let product" class="actions-cell">
|
||||
<button mat-icon-button (click)="onEdit(product)">
|
||||
<mat-icon>edit</mat-icon>
|
||||
</button>
|
||||
<button mat-icon-button color="warn" (click)="onDelete(product)">
|
||||
<mat-icon>delete</mat-icon>
|
||||
</button>
|
||||
</td>
|
||||
</ng-container>
|
||||
|
||||
<tr mat-header-row *matHeaderRowDef="displayedColumns"></tr>
|
||||
<tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr>
|
||||
</table>
|
||||
|
||||
<mat-paginator [pageSize]="10" [pageSizeOptions]="[5,10,25]" showFirstLastButtons></mat-paginator>
|
||||
|
||||
@if (!products || products.length === 0) {
|
||||
<div class="no-products">
|
||||
Aucun produit trouvé.
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
@@ -0,0 +1,138 @@
|
||||
import {
|
||||
Component,
|
||||
Input,
|
||||
Output,
|
||||
EventEmitter,
|
||||
ViewChild,
|
||||
AfterViewInit,
|
||||
OnChanges,
|
||||
SimpleChanges,
|
||||
OnInit,
|
||||
inject
|
||||
} from '@angular/core';
|
||||
import {
|
||||
MatCell, MatCellDef, MatColumnDef, MatHeaderCell,
|
||||
MatHeaderCellDef, MatHeaderRow, MatHeaderRowDef, MatRow, MatRowDef, MatTable,
|
||||
MatTableDataSource
|
||||
} from '@angular/material/table';
|
||||
import {MatPaginator} from '@angular/material/paginator';
|
||||
import {MatSort} from '@angular/material/sort';
|
||||
import {Product} from '../../interfaces/product';
|
||||
import {ProductService} from '../../services/app/product.service';
|
||||
import {MatDialog} from '@angular/material/dialog';
|
||||
import {ProductDialogComponent} from '../product-dialog/product-dialog.component';
|
||||
import {MatButton, MatIconButton} from '@angular/material/button';
|
||||
import {MatFormField} from '@angular/material/form-field';
|
||||
import {MatIcon} from '@angular/material/icon';
|
||||
import {MatInput} from '@angular/material/input';
|
||||
import {CurrencyPipe} from '@angular/common';
|
||||
|
||||
@Component({
|
||||
selector: 'app-products-list',
|
||||
templateUrl: './products-list.component.html',
|
||||
standalone: true,
|
||||
imports: [
|
||||
MatButton,
|
||||
MatCell,
|
||||
MatCellDef,
|
||||
MatColumnDef,
|
||||
MatFormField,
|
||||
MatHeaderCell,
|
||||
MatHeaderRow,
|
||||
MatHeaderRowDef,
|
||||
MatIcon,
|
||||
MatIconButton,
|
||||
MatInput,
|
||||
MatPaginator,
|
||||
MatRow,
|
||||
MatRowDef,
|
||||
MatSort,
|
||||
MatTable,
|
||||
MatHeaderCellDef,
|
||||
CurrencyPipe
|
||||
],
|
||||
styleUrls: ['./products-list.component.css']
|
||||
})
|
||||
export class ProductsListComponent implements OnInit, AfterViewInit, OnChanges {
|
||||
|
||||
@Input() products: Product[] = [];
|
||||
@Output() add = new EventEmitter<Product>();
|
||||
@Output() edit = new EventEmitter<Product>();
|
||||
@Output() delete = new EventEmitter<Product>();
|
||||
|
||||
displayedColumns: string[] = ['title', 'description', 'category', 'platform', 'condition', 'complete', 'manual', 'price', 'quantity', 'actions'];
|
||||
dataSource = new MatTableDataSource<Product>([]);
|
||||
|
||||
@ViewChild(MatPaginator) paginator!: MatPaginator;
|
||||
@ViewChild(MatSort) sort!: MatSort;
|
||||
|
||||
private readonly productService: ProductService = inject(ProductService);
|
||||
private readonly dialog = inject(MatDialog);
|
||||
|
||||
ngOnInit(): void {
|
||||
if (!this.products || this.products.length === 0) {
|
||||
this.loadProducts();
|
||||
} else {
|
||||
this.dataSource.data = this.products;
|
||||
}
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
if (changes['products']) {
|
||||
this.dataSource.data = this.products || [];
|
||||
}
|
||||
}
|
||||
|
||||
ngAfterViewInit(): void {
|
||||
this.dataSource.paginator = this.paginator;
|
||||
this.dataSource.sort = this.sort;
|
||||
}
|
||||
|
||||
loadProducts() {
|
||||
this.productService.getProducts().subscribe({
|
||||
next: (products: Product[]) => {
|
||||
this.products = products || []
|
||||
this.dataSource.data = this.products;
|
||||
console.log("Fetched products:", this.products);
|
||||
},
|
||||
error: () => this.products = []
|
||||
});
|
||||
}
|
||||
|
||||
onAdd(): void {
|
||||
const ref = this.dialog.open(ProductDialogComponent, {
|
||||
data: {product: {id: '', name: '', brand: undefined}},
|
||||
width: '420px'
|
||||
});
|
||||
|
||||
ref.afterClosed().subscribe((result?: Product) => {
|
||||
if (result) {
|
||||
this.add.emit(result);
|
||||
this.productService.addProduct(result).subscribe(() => this.loadProducts());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onEdit(product: Product): void {
|
||||
const ref = this.dialog.open(ProductDialogComponent, {
|
||||
data: {product: {...product}},
|
||||
width: '420px'
|
||||
});
|
||||
|
||||
ref.afterClosed().subscribe((result?: Product) => {
|
||||
if (result) {
|
||||
this.edit.emit(result);
|
||||
this.productService.updateProduct((product as any).id, result).subscribe(() => this.loadProducts());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
onDelete(product: Product): void {
|
||||
this.delete.emit(product);
|
||||
this.productService.deleteProduct((product as any).id).subscribe(() => this.loadProducts());
|
||||
}
|
||||
|
||||
applyFilter(value: string): void {
|
||||
this.dataSource.filter = (value || '').trim().toLowerCase();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user