agent and tpe save done

This commit is contained in:
OnlyPapy98
2026-01-06 14:07:09 +01:00
parent 95095016d2
commit 0ae7fa316e
33 changed files with 1910 additions and 525 deletions

View File

@@ -0,0 +1,41 @@
<div class="flex flex-col gap-2 min-h-screen">
<div class="flex items-center justify-between">
<h2 class="text-2xl font-semibold">Gestion des Points de Vente</h2>
<z-button (click)="openCreate()">Nouveau point de vente</z-button>
</div>
<app-search-bar (search)="onSearch($event)"></app-search-bar>
<app-data-table [data]="rows()" [columns]="cols" [sort]="sort()" (sortChange)="sort.set($event)">
<ng-template #rowActions let-row>
<div class="flex gap-3">
<button z-button zType="ghost" (click)="openEdit(row)" title="Modifier">
<i class="icon-pen"></i>
</button>
<button z-button zType="destructive" (click)="remove(row)" title="Supprimer">
<i class="icon-trash"></i>
</button>
</div>
</ng-template>
</app-data-table>
<app-paginator
[total]="total()"
[page]="page()"
[perPage]="size()"
(pageChange)="page.set($event - 1)"
(perPageChange)="size.set($event)"
></app-paginator>
</div>
<app-modal [open]="modalOpen()" [title]="modalTitle()" (close)="closeModal()" size="xl">
<app-points-vente-form
[value]="editingItem() ?? undefined"
(save)="onFormSave($event)"
(cancel)="closeModal()"
/>
<div modal-actions class="flex justify-end gap-2">
<z-button zType="destructive" (click)="closeModal()">Annuler</z-button>
<z-button (click)="submitChildForm()">Enregistrer</z-button>
</div>
</app-modal>

View File

@@ -0,0 +1,23 @@
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { PointVente } from './point-vente';
describe('PointVente', () => {
let component: PointVente;
let fixture: ComponentFixture<PointVente>;
beforeEach(async () => {
await TestBed.configureTestingModule({
imports: [PointVente]
})
.compileComponents();
fixture = TestBed.createComponent(PointVente);
component = fixture.componentInstance;
fixture.detectChanges();
});
it('should create', () => {
expect(component).toBeTruthy();
});
});

View File

@@ -0,0 +1,209 @@
import { CommonModule } from '@angular/common';
import {
ChangeDetectionStrategy,
Component,
ViewChild,
effect,
signal,
untracked,
} from '@angular/core';
import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table';
import { Paginator } from '@shared/components/paginator/paginator';
import { SearchBar } from '@shared/components/search-bar/search-bar';
import { Modal } from '@shared/components/modal/modal';
import { ZardButtonComponent } from '@shared/components/button/button.component';
import { SortDir } from '@shared/paging/paging';
import { PointVente, PointVenteStatut } from 'src/app/core/interfaces/points-ventes';
import { PointsVenteService } from 'src/app/core/services/points-vente';
import { PointsVenteForm } from '@shared/forms/points-vente-form/points-vente-form';
import { toast } from 'ngx-sonner';
@Component({
standalone: true,
selector: 'app-point-vente',
templateUrl: './point-vente.html',
changeDetection: ChangeDetectionStrategy.OnPush,
imports: [
CommonModule,
DataTable,
Paginator,
SearchBar,
Modal,
ZardButtonComponent,
PointsVenteForm,
],
})
export class PointVentePage {
rows = signal<PointVente[]>([]);
total = signal(0);
loading = signal(false);
page = signal(0);
size = signal(10);
search = signal('');
sort = signal<SortState>({ key: 'nom', dir: 'asc' });
modalOpen = signal(false);
modalTitle = signal('Nouveau point de vente');
editingItem = signal<PointVente | null>(null);
@ViewChild(PointsVenteForm) formComp?: PointsVenteForm;
cols: TableColumn<PointVente>[] = [
{ key: 'nom', label: 'Nom', sortable: true, defaultVisible: true },
{ key: 'adresse', label: 'Adresse', sortable: true, defaultVisible: true },
{ key: 'ville', label: 'Ville', sortable: true, defaultVisible: true },
{
key: 'statut',
label: 'Statut',
sortable: true,
defaultVisible: true,
cell: (pv) => this.renderStatutBadge(pv.statut),
},
{
key: 'latitude',
label: 'Latitude',
cell: (pv) => pv.latitude?.toFixed(6) || '—',
},
{
key: 'longitude',
label: 'Longitude',
cell: (pv) => pv.longitude?.toFixed(6) || '—',
},
];
constructor(private api: PointsVenteService) {
effect(() => {
const params = {
page: this.page(),
size: this.size(),
search: this.search(),
sortKey: this.sort().key,
sortDir: this.sort().dir as SortDir,
};
untracked(() => this.fetch(params));
});
}
private fetch(params: {
page: number;
size: number;
search: string;
sortKey: string;
sortDir: SortDir;
}) {
this.loading.set(true);
this.api.list(params).subscribe({
next: (res) => {
this.rows.set(res.content);
this.total.set(res.pageable.total);
this.loading.set(false);
},
error: () => {
this.rows.set([]);
this.total.set(0);
this.loading.set(false);
},
});
}
renderStatutBadge(statut: PointVenteStatut | string | undefined): string {
if (!statut) return '';
const s = String(statut).toUpperCase();
if (s === 'ACTIVE') {
return `<span class="inline-flex items-center gap-1 px-2 py-1 rounded bg-green-500/10 text-green-600 dark:text-green-400 text-xs font-medium"><i class="icon-check"></i> Actif</span>`;
}
return `<span class="inline-flex items-center gap-1 px-2 py-1 rounded bg-gray-500/10 text-gray-600 dark:text-gray-400 text-xs font-medium"><i class="icon-x"></i> Inactif</span>`;
}
onSearch(q: string) {
this.search.set(q);
this.page.set(0);
}
openCreate() {
this.modalTitle.set('Nouveau point de vente');
this.editingItem.set(null);
queueMicrotask(() => this.modalOpen.set(true));
}
openEdit(row: PointVente) {
this.modalTitle.set("Modifier le point de vente");
this.editingItem.set(row);
queueMicrotask(() => this.modalOpen.set(true));
}
closeModal() {
this.modalOpen.set(false);
}
submitChildForm() {
this.formComp?.onSubmit();
}
onFormSave(payload: Partial<PointVente>) {
const current = this.editingItem();
const isCreating = !current?.id;
const req$ = current?.id
? this.api.update(current.id, payload)
: this.api.create(payload as Omit<PointVente, 'id'>);
req$.subscribe({
next: (result) => {
if (result) {
toast.success(
isCreating
? 'Point de vente créé avec succès'
: 'Point de vente modifié avec succès'
);
// Close modal first
this.closeModal();
// Reset form
this.formComp?.resetForm();
// Clear editing item
this.editingItem.set(null);
// Refresh data
this.fetch({
page: this.page(),
size: this.size(),
search: this.search(),
sortKey: this.sort().key,
sortDir: this.sort().dir as SortDir,
});
}
},
error: (err) => {
console.error('Error saving point de vente:', err);
alert(
isCreating
? "Erreur lors de la création du point de vente"
: "Erreur lors de la modification du point de vente"
);
},
});
}
remove(row: PointVente) {
if (!confirm(`Supprimer le point de vente "${row.nom}" ?`)) return;
this.api.delete(row.id).subscribe({
next: (success) => {
if (success) {
toast.success('Point de vente supprimé avec succès');
this.fetch({
page: this.page(),
size: this.size(),
search: this.search(),
sortKey: this.sort().key,
sortDir: this.sort().dir as SortDir,
});
} else {
alert("Erreur lors de la suppression du point de vente");
}
},
error: () => {
alert("Erreur lors de la suppression du point de vente");
},
});
}
}