diff --git a/client/proxy.conf.json b/client/proxy.conf.json
new file mode 100644
index 0000000..3c46c51
--- /dev/null
+++ b/client/proxy.conf.json
@@ -0,0 +1,12 @@
+{
+ "/ps": {
+ "target": "https://shop.gameovergne.fr",
+ "secure": true,
+ "changeOrigin": true,
+ "logLevel": "debug",
+ "pathRewrite": { "^/ps": "/api" },
+ "headers": {
+ "Authorization": "Basic MkFRUEcxM01KOFgxMTdVNkZKNU5HSFBTOTNIRTM0QUI="
+ }
+ }
+}
diff --git a/client/src/app/admin-presta/admin-presta.module.ts b/client/src/app/admin-presta/admin-presta.module.ts
new file mode 100644
index 0000000..61d2b82
--- /dev/null
+++ b/client/src/app/admin-presta/admin-presta.module.ts
@@ -0,0 +1,32 @@
+import {NgModule} from '@angular/core';
+import {CommonModule} from '@angular/common';
+import {ReactiveFormsModule, FormsModule} from '@angular/forms';
+
+// Angular Material (tous utilisés dans ces composants)
+import {MatTabsModule} from '@angular/material/tabs';
+import {MatTableModule} from '@angular/material/table';
+import {MatFormFieldModule} from '@angular/material/form-field';
+import {MatInputModule} from '@angular/material/input';
+import {MatButtonModule} from '@angular/material/button';
+import {MatIconModule} from '@angular/material/icon';
+
+// Composants de cette feature
+import {PsCrudTabsComponent} from './ps-crud-tabs/ps-crud-tabs.component';
+import {CategoriesCrudComponent} from './categories-crud/categories-crud.component';
+import {ManufacturersCrudComponent} from './manufacturers-crud/manufacturers-crud.component';
+import {SuppliersCrudComponent} from './suppliers-crud/suppliers-crud.component';
+
+@NgModule({
+ declarations: [
+
+ ],
+ imports: [
+ CommonModule,
+ ReactiveFormsModule, FormsModule,
+ MatTabsModule, MatTableModule, MatFormFieldModule, MatInputModule,
+ MatButtonModule, MatIconModule, PsCrudTabsComponent, CategoriesCrudComponent, ManufacturersCrudComponent, SuppliersCrudComponent
+ ],
+ exports: [PsCrudTabsComponent] // pour pouvoir l’utiliser ailleurs
+})
+export class AdminPrestaModule {
+}
diff --git a/client/src/app/admin-presta/categories-crud/categories-crud.component.css b/client/src/app/admin-presta/categories-crud/categories-crud.component.css
new file mode 100644
index 0000000..a6fe2c5
--- /dev/null
+++ b/client/src/app/admin-presta/categories-crud/categories-crud.component.css
@@ -0,0 +1,18 @@
+.crud {
+ display: grid;
+ gap: 16px
+}
+
+.row {
+ display: flex;
+ gap: 12px;
+ align-items: flex-end
+}
+
+.grow {
+ flex: 1
+}
+
+table {
+ width: 100%
+}
diff --git a/client/src/app/admin-presta/categories-crud/categories-crud.component.html b/client/src/app/admin-presta/categories-crud/categories-crud.component.html
new file mode 100644
index 0000000..542d81f
--- /dev/null
+++ b/client/src/app/admin-presta/categories-crud/categories-crud.component.html
@@ -0,0 +1,36 @@
+
+
+
+
+
+ | ID |
+ {{ el.id }} |
+
+
+
+ Nom |
+ {{ el.name }} |
+
+
+
+ Actions |
+
+
+
+ |
+
+
+
+
+
+
diff --git a/client/src/app/admin-presta/categories-crud/categories-crud.component.ts b/client/src/app/admin-presta/categories-crud/categories-crud.component.ts
new file mode 100644
index 0000000..e783492
--- /dev/null
+++ b/client/src/app/admin-presta/categories-crud/categories-crud.component.ts
@@ -0,0 +1,99 @@
+import {Component, inject, OnInit} from '@angular/core';
+import {FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
+import {PrestaService, PsItem} from '../../services/presta.serivce';
+import {map} from 'rxjs';
+import {MatIcon} from '@angular/material/icon';
+import {MatButton, MatIconButton} from '@angular/material/button';
+import {
+ MatCell,
+ MatCellDef,
+ MatColumnDef,
+ MatHeaderCell, MatHeaderCellDef,
+ MatHeaderRow,
+ MatHeaderRowDef,
+ MatRow,
+ MatRowDef,
+ MatTable
+} from '@angular/material/table';
+import {MatFormField, MatLabel} from '@angular/material/form-field';
+import {MatInput} from '@angular/material/input';
+import {NgIf} from '@angular/common';
+
+@Component({
+ standalone : true,
+ selector: 'app-categories-crud',
+ templateUrl: './categories-crud.component.html',
+ styleUrls: ['./categories-crud.component.css'],
+ imports: [
+ MatIcon,
+ MatIconButton,
+ MatHeaderRow,
+ MatRow,
+ MatRowDef,
+ MatHeaderRowDef,
+ ReactiveFormsModule,
+ MatFormField,
+ MatLabel,
+ MatInput,
+ MatButton,
+ MatTable,
+ MatColumnDef,
+ MatHeaderCell,
+ NgIf,
+ MatHeaderCellDef,
+ MatCellDef,
+ MatCell
+ ]
+})
+export class CategoriesCrudComponent implements OnInit {
+ private readonly fb = inject(FormBuilder);
+ private readonly ps = inject(PrestaService);
+
+ items: PsItem[] = [];
+ cols = ['id', 'name', 'actions'];
+ form = this.fb.group({name: ['', Validators.required]});
+ editId: number | null = null;
+
+ ngOnInit() {
+ this.reload();
+ }
+
+ reload() {
+ this.ps.list('categories').subscribe(data => this.items = data);
+ }
+
+ startEdit(el: PsItem) {
+ this.editId = el.id;
+ this.form.patchValue({name: el.name});
+ }
+
+ cancelEdit() {
+ this.editId = null;
+ this.form.reset({name: ''});
+ }
+
+ onSubmit() {
+ const name = this.form.value.name!.trim();
+ if (!name) return;
+
+ const op$ = this.editId
+ ? this.ps.update('categories', this.editId, name).pipe(map(() => undefined))
+ : this.ps.create('categories', name).pipe(map(() => undefined));
+
+ op$.subscribe({
+ next: () => {
+ this.cancelEdit();
+ this.reload();
+ },
+ error: (e: unknown) => alert('Erreur: ' + (e instanceof Error ? e.message : String(e)))
+ });
+ }
+
+ remove(el: PsItem) {
+ if (!confirm(`Supprimer la catégorie "${el.name}" (#${el.id}) ?`)) return;
+ this.ps.delete('categories', el.id).subscribe({
+ next: () => this.reload(),
+ error: e => alert('Erreur: ' + (e?.message || e))
+ });
+ }
+}
diff --git a/client/src/app/admin-presta/manufacturers-crud/manufacturers-crud.component.css b/client/src/app/admin-presta/manufacturers-crud/manufacturers-crud.component.css
new file mode 100644
index 0000000..a6fe2c5
--- /dev/null
+++ b/client/src/app/admin-presta/manufacturers-crud/manufacturers-crud.component.css
@@ -0,0 +1,18 @@
+.crud {
+ display: grid;
+ gap: 16px
+}
+
+.row {
+ display: flex;
+ gap: 12px;
+ align-items: flex-end
+}
+
+.grow {
+ flex: 1
+}
+
+table {
+ width: 100%
+}
diff --git a/client/src/app/admin-presta/manufacturers-crud/manufacturers-crud.component.html b/client/src/app/admin-presta/manufacturers-crud/manufacturers-crud.component.html
new file mode 100644
index 0000000..1b4beff
--- /dev/null
+++ b/client/src/app/admin-presta/manufacturers-crud/manufacturers-crud.component.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+ | ID |
+ {{ el.id }} |
+
+
+
+ Nom |
+ {{ el.name }} |
+
+
+
+ Actions |
+
+
+
+ |
+
+
+
+
+
+
diff --git a/client/src/app/admin-presta/manufacturers-crud/manufacturers-crud.component.ts b/client/src/app/admin-presta/manufacturers-crud/manufacturers-crud.component.ts
new file mode 100644
index 0000000..658c661
--- /dev/null
+++ b/client/src/app/admin-presta/manufacturers-crud/manufacturers-crud.component.ts
@@ -0,0 +1,99 @@
+import {Component, inject, OnInit} from '@angular/core';
+import {FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
+import {PrestaService, PsItem} from '../../services/presta.serivce';
+import {map} from 'rxjs';
+import {MatFormField, MatLabel} from '@angular/material/form-field';
+import {MatIcon} from '@angular/material/icon';
+import {MatButton, MatIconButton} from '@angular/material/button';
+import {
+ MatCell,
+ MatCellDef,
+ MatColumnDef,
+ MatHeaderCell, MatHeaderCellDef,
+ MatHeaderRow,
+ MatHeaderRowDef,
+ MatRow,
+ MatRowDef,
+ MatTable
+} from '@angular/material/table';
+import {MatInput} from '@angular/material/input';
+import {NgIf} from '@angular/common';
+
+@Component({
+ standalone: true,
+ selector: 'app-manufacturers-crud',
+ templateUrl: './manufacturers-crud.component.html',
+ imports: [
+ MatIcon,
+ MatIconButton,
+ MatHeaderRow,
+ MatRow,
+ MatRowDef,
+ MatHeaderRowDef,
+ ReactiveFormsModule,
+ MatFormField,
+ MatLabel,
+ MatInput,
+ MatButton,
+ MatTable,
+ MatColumnDef,
+ MatHeaderCell,
+ NgIf,
+ MatHeaderCellDef,
+ MatCellDef,
+ MatCell
+ ],
+ styleUrls: ['./manufacturers-crud.component.css']
+})
+export class ManufacturersCrudComponent implements OnInit {
+ private readonly fb = inject(FormBuilder);
+ private readonly ps = inject(PrestaService);
+
+ items: PsItem[] = [];
+ cols = ['id', 'name', 'actions'];
+ form = this.fb.group({name: ['', Validators.required]});
+ editId: number | null = null;
+
+ ngOnInit() {
+ this.reload();
+ }
+
+ reload() {
+ this.ps.list('manufacturers').subscribe(d => this.items = d);
+ }
+
+ startEdit(el: PsItem) {
+ this.editId = el.id;
+ this.form.patchValue({name: el.name});
+ }
+
+ cancelEdit() {
+ this.editId = null;
+ this.form.reset({name: ''});
+ }
+
+ onSubmit() {
+ const name = this.form.value.name!.trim();
+ if (!name) return;
+
+ const op$ = this.editId
+ ? this.ps.update('manufacturers', this.editId, name).pipe(map(() => undefined))
+ : this.ps.create('manufacturers', name).pipe(map(() => undefined));
+
+ op$.subscribe({
+ next: () => {
+ this.cancelEdit();
+ this.reload();
+ },
+ error: (e: unknown) => alert('Erreur: ' + (e instanceof Error ? e.message : String(e)))
+ });
+ }
+
+ remove(el: PsItem) {
+ if (!confirm(`Supprimer la marque "${el.name}" (#${el.id}) ?`)) return;
+ this.ps.delete('manufacturers', el.id).subscribe({
+ next: () => this.reload(),
+ error: e => alert('Erreur: ' + (e?.message || e))
+ });
+ }
+}
diff --git a/client/src/app/admin-presta/ps-crud-tabs/ps-crud-tabs.component.css b/client/src/app/admin-presta/ps-crud-tabs/ps-crud-tabs.component.css
new file mode 100644
index 0000000..239d9d3
--- /dev/null
+++ b/client/src/app/admin-presta/ps-crud-tabs/ps-crud-tabs.component.css
@@ -0,0 +1,5 @@
+.wrap {
+ padding: 16px;
+ max-width: 900px;
+ margin: auto
+}
diff --git a/client/src/app/admin-presta/ps-crud-tabs/ps-crud-tabs.component.html b/client/src/app/admin-presta/ps-crud-tabs/ps-crud-tabs.component.html
new file mode 100644
index 0000000..017ddcf
--- /dev/null
+++ b/client/src/app/admin-presta/ps-crud-tabs/ps-crud-tabs.component.html
@@ -0,0 +1,8 @@
+
+
PrestaShop — CRUD simplifié
+
+
+
+
+
+
diff --git a/client/src/app/admin-presta/ps-crud-tabs/ps-crud-tabs.component.ts b/client/src/app/admin-presta/ps-crud-tabs/ps-crud-tabs.component.ts
new file mode 100644
index 0000000..a80b0fd
--- /dev/null
+++ b/client/src/app/admin-presta/ps-crud-tabs/ps-crud-tabs.component.ts
@@ -0,0 +1,20 @@
+import { Component } from '@angular/core';
+import {MatTab, MatTabGroup} from '@angular/material/tabs';
+import {CategoriesCrudComponent} from '../categories-crud/categories-crud.component';
+import {ManufacturersCrudComponent} from '../manufacturers-crud/manufacturers-crud.component';
+import {SuppliersCrudComponent} from '../suppliers-crud/suppliers-crud.component';
+
+@Component({
+ standalone: true,
+ selector: 'app-ps-crud-tabs',
+ templateUrl: './ps-crud-tabs.component.html',
+ imports: [
+ MatTabGroup,
+ MatTab,
+ CategoriesCrudComponent,
+ ManufacturersCrudComponent,
+ SuppliersCrudComponent
+ ],
+ styleUrls: ['./ps-crud-tabs.component.css']
+})
+export class PsCrudTabsComponent {}
diff --git a/client/src/app/admin-presta/suppliers-crud/suppliers-crud.component.css b/client/src/app/admin-presta/suppliers-crud/suppliers-crud.component.css
new file mode 100644
index 0000000..a6fe2c5
--- /dev/null
+++ b/client/src/app/admin-presta/suppliers-crud/suppliers-crud.component.css
@@ -0,0 +1,18 @@
+.crud {
+ display: grid;
+ gap: 16px
+}
+
+.row {
+ display: flex;
+ gap: 12px;
+ align-items: flex-end
+}
+
+.grow {
+ flex: 1
+}
+
+table {
+ width: 100%
+}
diff --git a/client/src/app/admin-presta/suppliers-crud/suppliers-crud.component.html b/client/src/app/admin-presta/suppliers-crud/suppliers-crud.component.html
new file mode 100644
index 0000000..e6118f6
--- /dev/null
+++ b/client/src/app/admin-presta/suppliers-crud/suppliers-crud.component.html
@@ -0,0 +1,40 @@
+
+
+
+
+
+ | ID |
+ {{ el.id }} |
+
+
+
+ Nom |
+ {{ el.name }} |
+
+
+
+ Actions |
+
+
+
+ |
+
+
+
+
+
+
diff --git a/client/src/app/admin-presta/suppliers-crud/suppliers-crud.component.ts b/client/src/app/admin-presta/suppliers-crud/suppliers-crud.component.ts
new file mode 100644
index 0000000..f9364a1
--- /dev/null
+++ b/client/src/app/admin-presta/suppliers-crud/suppliers-crud.component.ts
@@ -0,0 +1,99 @@
+import {Component, inject, OnInit} from '@angular/core';
+import {FormBuilder, ReactiveFormsModule, Validators} from '@angular/forms';
+import {PrestaService, PsItem} from '../../services/presta.serivce';
+import {map} from 'rxjs';
+import {MatIcon} from '@angular/material/icon';
+import {MatButton, MatIconButton} from '@angular/material/button';
+import {
+ MatCell,
+ MatCellDef,
+ MatColumnDef,
+ MatHeaderCell, MatHeaderCellDef,
+ MatHeaderRow,
+ MatHeaderRowDef,
+ MatRow,
+ MatRowDef,
+ MatTable
+} from '@angular/material/table';
+import {MatFormField, MatLabel} from '@angular/material/form-field';
+import {MatInput} from '@angular/material/input';
+import {NgIf} from '@angular/common';
+
+@Component({
+ standalone: true,
+ selector: 'app-suppliers-crud',
+ templateUrl: './suppliers-crud.component.html',
+ imports: [
+ MatIcon,
+ MatIconButton,
+ MatHeaderRow,
+ MatRow,
+ MatRowDef,
+ MatHeaderRowDef,
+ ReactiveFormsModule,
+ MatFormField,
+ MatLabel,
+ MatInput,
+ MatButton,
+ MatTable,
+ MatColumnDef,
+ MatHeaderCell,
+ NgIf,
+ MatHeaderCellDef,
+ MatCellDef,
+ MatCell
+ ],
+ styleUrls: ['./suppliers-crud.component.css']
+})
+export class SuppliersCrudComponent implements OnInit {
+ private readonly fb = inject(FormBuilder);
+ private readonly ps = inject(PrestaService);
+
+ items: PsItem[] = [];
+ cols = ['id', 'name', 'actions'];
+ form = this.fb.group({name: ['', Validators.required]});
+ editId: number | null = null;
+
+ ngOnInit() {
+ this.reload();
+ }
+
+ reload() {
+ this.ps.list('suppliers').subscribe(d => this.items = d);
+ }
+
+ startEdit(el: PsItem) {
+ this.editId = el.id;
+ this.form.patchValue({name: el.name});
+ }
+
+ cancelEdit() {
+ this.editId = null;
+ this.form.reset({name: ''});
+ }
+
+ onSubmit() {
+ const name = this.form.value.name!.trim();
+ if (!name) return;
+
+ const op$ = this.editId
+ ? this.ps.update('suppliers', this.editId, name).pipe(map(() => undefined))
+ : this.ps.create('suppliers', name).pipe(map(() => undefined));
+
+ op$.subscribe({
+ next: () => {
+ this.cancelEdit();
+ this.reload();
+ },
+ error: (e: unknown) => alert('Erreur: ' + (e instanceof Error ? e.message : String(e)))
+ });
+ }
+
+ remove(el: PsItem) {
+ if (!confirm(`Supprimer le fournisseur "${el.name}" (#${el.id}) ?`)) return;
+ this.ps.delete('suppliers', el.id).subscribe({
+ next: () => this.reload(),
+ error: e => alert('Erreur: ' + (e?.message || e))
+ });
+ }
+}
diff --git a/client/src/app/app.routes.ts b/client/src/app/app.routes.ts
index 2b0bda3..58db7e1 100644
--- a/client/src/app/app.routes.ts
+++ b/client/src/app/app.routes.ts
@@ -9,6 +9,7 @@ import {AdminComponent} from './pages/admin/admin.component';
import {adminOnlyCanActivate, adminOnlyCanMatch} from './guards/admin-only.guard';
import {ProductsComponent} from './pages/products/products.component';
import {AddProductComponent} from './pages/add-product/add-product.component';
+import {PsCrudTabsComponent} from './admin-presta/ps-crud-tabs/ps-crud-tabs.component';
export const routes: Routes = [
{
@@ -61,6 +62,12 @@ export const routes: Routes = [
canMatch: [authOnlyCanMatch],
canActivate: [authOnlyCanActivate],
},
+ {
+ path: 'prestashop',
+ component: PsCrudTabsComponent,
+ canMatch: [authOnlyCanMatch],
+ canActivate: [authOnlyCanActivate],
+ },
{
path: '**',
redirectTo: ''
diff --git a/client/src/app/components/navbar/main-navbar/main-navbar.component.html b/client/src/app/components/navbar/main-navbar/main-navbar.component.html
index bf5757c..3744d3f 100644
--- a/client/src/app/components/navbar/main-navbar/main-navbar.component.html
+++ b/client/src/app/components/navbar/main-navbar/main-navbar.component.html
@@ -10,6 +10,10 @@
@if (authService.hasRole('Administrator')) {
+