Compare commits
1 Commits
afa5fab55d
...
feat-depou
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
95095016d2 |
@@ -5,7 +5,7 @@ export type AgentStatus = 'ACTIF' | 'INACTIF' | 'SUSPENDU';
|
|||||||
export interface Agent {
|
export interface Agent {
|
||||||
id: string;
|
id: string;
|
||||||
code: string;
|
code: string;
|
||||||
profile: string; // ex. AGENT, SUPERVISEUR, CAISSIER
|
profil: string; // ex. AGENT, SUPERVISEUR, CAISSIER
|
||||||
principalCode?: string; // Agent principal
|
principalCode?: string; // Agent principal
|
||||||
caisseProfile?: string;
|
caisseProfile?: string;
|
||||||
statut: AgentStatus;
|
statut: AgentStatus;
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import { TpeDevice, TpeStatus, TpeType } from '../interfaces/tpe';
|
|||||||
import { environment } from 'src/environments/environment.development';
|
import { environment } from 'src/environments/environment.development';
|
||||||
import { normalizePage } from '@shared/paging/normalize-page';
|
import { normalizePage } from '@shared/paging/normalize-page';
|
||||||
import { ListParams, PagedResult } from '@shared/paging/paging';
|
import { ListParams, PagedResult } from '@shared/paging/paging';
|
||||||
|
import { ServicesUtils } from './services-utils';
|
||||||
|
|
||||||
const USE_SERVER = true;
|
const USE_SERVER = true;
|
||||||
const API_BASE = '/api/agents';
|
const API_BASE = '/api/agents';
|
||||||
@@ -31,7 +32,7 @@ interface TpeApiResponse {
|
|||||||
interface AgentApiResponse {
|
interface AgentApiResponse {
|
||||||
id: number;
|
id: number;
|
||||||
code: string;
|
code: string;
|
||||||
profile: string;
|
profil: string;
|
||||||
principalCode?: string;
|
principalCode?: string;
|
||||||
caisseProfile?: string;
|
caisseProfile?: string;
|
||||||
statut: string;
|
statut: string;
|
||||||
@@ -75,7 +76,7 @@ interface AgentApiResponse {
|
|||||||
export class AgentService {
|
export class AgentService {
|
||||||
private apiUrl = environment.apiBaseUrl + API_BASE;
|
private apiUrl = environment.apiBaseUrl + API_BASE;
|
||||||
|
|
||||||
constructor(private http: HttpClient) {}
|
constructor(private http: HttpClient, private serviceUtils:ServicesUtils) {}
|
||||||
|
|
||||||
// Helper method to get ngrok bypass headers
|
// Helper method to get ngrok bypass headers
|
||||||
private getNgrokHeaders(): Record<string, string> {
|
private getNgrokHeaders(): Record<string, string> {
|
||||||
@@ -131,7 +132,7 @@ export class AgentService {
|
|||||||
return {
|
return {
|
||||||
id: String(apiAgent.id),
|
id: String(apiAgent.id),
|
||||||
code: apiAgent.code,
|
code: apiAgent.code,
|
||||||
profile: apiAgent.profile,
|
profil: apiAgent.profil,
|
||||||
principalCode: apiAgent.principalCode,
|
principalCode: apiAgent.principalCode,
|
||||||
caisseProfile: apiAgent.caisseProfile,
|
caisseProfile: apiAgent.caisseProfile,
|
||||||
statut: apiAgent.statut as AgentStatus,
|
statut: apiAgent.statut as AgentStatus,
|
||||||
@@ -193,7 +194,7 @@ export class AgentService {
|
|||||||
private transformToApiPayload(agent: Partial<Agent>): any {
|
private transformToApiPayload(agent: Partial<Agent>): any {
|
||||||
const payload: any = {};
|
const payload: any = {};
|
||||||
if (agent.code !== undefined) payload.code = agent.code;
|
if (agent.code !== undefined) payload.code = agent.code;
|
||||||
if (agent.profile !== undefined) payload.profile = agent.profile;
|
if (agent.profil !== undefined) payload.profil = agent.profil;
|
||||||
if (agent.principalCode !== undefined) payload.principalCode = agent.principalCode;
|
if (agent.principalCode !== undefined) payload.principalCode = agent.principalCode;
|
||||||
if (agent.caisseProfile !== undefined) payload.caisseProfile = agent.caisseProfile;
|
if (agent.caisseProfile !== undefined) payload.caisseProfile = agent.caisseProfile;
|
||||||
if (agent.statut !== undefined) payload.statut = agent.statut;
|
if (agent.statut !== undefined) payload.statut = agent.statut;
|
||||||
@@ -233,22 +234,6 @@ export class AgentService {
|
|||||||
if (agent.epoux !== undefined) payload.epoux = agent.epoux;
|
if (agent.epoux !== undefined) payload.epoux = agent.epoux;
|
||||||
if (agent.autreTelephone !== undefined) payload.autreTelephone = agent.autreTelephone;
|
if (agent.autreTelephone !== undefined) payload.autreTelephone = agent.autreTelephone;
|
||||||
if (agent.createdBy !== undefined) payload.createdBy = agent.createdBy;
|
if (agent.createdBy !== undefined) payload.createdBy = agent.createdBy;
|
||||||
// Include tpes if provided - transform to API format
|
|
||||||
if (agent.tpes !== undefined) {
|
|
||||||
payload.tpes = agent.tpes.map((tpe) => ({
|
|
||||||
id: tpe.id ? Number(tpe.id) : undefined,
|
|
||||||
imei: tpe.imei,
|
|
||||||
serial: tpe.serial,
|
|
||||||
type: tpe.type,
|
|
||||||
marque: tpe.marque,
|
|
||||||
modele: tpe.modele,
|
|
||||||
statut: tpe.statut,
|
|
||||||
agent: undefined, // Will be set by backend
|
|
||||||
assigne: tpe.assigne,
|
|
||||||
createdAt: tpe.createdAt,
|
|
||||||
updatedAt: tpe.updatedAt,
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
return payload;
|
return payload;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -269,23 +254,23 @@ export class AgentService {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// GET /api/v1/agents - List all
|
// GET /api/v1/agents - List all
|
||||||
list(params?: ListParams): Observable<PagedResult<Agent>> {
|
list(params: ListParams): Observable<PagedResult<Agent>> {
|
||||||
let httpParams = new HttpParams();
|
// let httpParams = new HttpParams();
|
||||||
if (params) {
|
// if (params) {
|
||||||
if (params.page) httpParams = httpParams.set('page', params.page.toString());
|
// if (params.page) httpParams = httpParams.set('page', params.page.toString());
|
||||||
if (params.size) httpParams = httpParams.set('perPage', params.size.toString());
|
// if (params.size) httpParams = httpParams.set('perPage', params.size.toString());
|
||||||
if (params.search) httpParams = httpParams.set('search', params.search);
|
// if (params.search) httpParams = httpParams.set('search', params.search);
|
||||||
if (params.sortKey) httpParams = httpParams.set('sortKey', params.sortKey);
|
// if (params.sortKey) httpParams = httpParams.set('sortKey', params.sortKey);
|
||||||
if (params.sortDir) httpParams = httpParams.set('sortDir', params.sortDir);
|
// if (params.sortDir) httpParams = httpParams.set('sortDir', params.sortDir);
|
||||||
}
|
// }
|
||||||
|
|
||||||
return this.http
|
return this.http
|
||||||
.get<PagedResult<AgentApiResponse>>(this.apiUrl, {
|
.get<PagedResult<AgentApiResponse>>(this.apiUrl, {
|
||||||
params: httpParams,
|
params: this.serviceUtils.getParamsFromModel(params),
|
||||||
headers: this.getNgrokHeaders(),
|
headers: this.getNgrokHeaders(),
|
||||||
})
|
})
|
||||||
.pipe(
|
.pipe(
|
||||||
map((res) => {
|
map((res) => {
|
||||||
|
console.log(res);
|
||||||
const agents = res.content.map((apiAgent) => {
|
const agents = res.content.map((apiAgent) => {
|
||||||
const transformed = this.transformAgent(apiAgent);
|
const transformed = this.transformAgent(apiAgent);
|
||||||
return transformed;
|
return transformed;
|
||||||
@@ -305,21 +290,18 @@ export class AgentService {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
// POST /api/v1/agents - Create
|
// POST /api/agents - Create
|
||||||
create(payload: Omit<Agent, 'id' | 'createdAt' | 'updatedAt'>): Observable<Agent> {
|
create(payload: Omit<Agent, 'id' | 'createdAt' | 'updatedAt'>): Observable<Agent> {
|
||||||
if (USE_SERVER) {
|
const apiPayload = this.transformToApiPayload(payload);
|
||||||
const apiPayload = this.transformToApiPayload(payload);
|
return this.http
|
||||||
return this.http
|
.post<AgentApiResponse>(this.apiUrl, apiPayload, { headers: this.getNgrokHeaders() })
|
||||||
.post<AgentApiResponse>(this.apiUrl, apiPayload, { headers: this.getNgrokHeaders() })
|
.pipe(
|
||||||
.pipe(
|
map((apiAgent) => this.transformAgent(apiAgent)),
|
||||||
map((apiAgent) => this.transformAgent(apiAgent)),
|
catchError((err) => {
|
||||||
catchError((err) => {
|
console.error('Error creating agent:', err);
|
||||||
console.error('Error creating agent:', err);
|
throw err;
|
||||||
throw err;
|
})
|
||||||
})
|
);
|
||||||
);
|
|
||||||
}
|
|
||||||
throw new Error('Server mode is required');
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// PUT /api/v1/agents/{id} - Update
|
// PUT /api/v1/agents/{id} - Update
|
||||||
|
|||||||
@@ -127,7 +127,7 @@ export class TpeService {
|
|||||||
return {
|
return {
|
||||||
id: String(apiAgent.id),
|
id: String(apiAgent.id),
|
||||||
code: String((apiAgent as any).code || ''),
|
code: String((apiAgent as any).code || ''),
|
||||||
profile: String((apiAgent as any).profile || ''),
|
profil: String((apiAgent as any).profil || ''),
|
||||||
principalCode: (apiAgent as any).principalCode ? String((apiAgent as any).principalCode) : undefined,
|
principalCode: (apiAgent as any).principalCode ? String((apiAgent as any).principalCode) : undefined,
|
||||||
caisseProfile: (apiAgent as any).caisseProfile ? String((apiAgent as any).caisseProfile) : undefined,
|
caisseProfile: (apiAgent as any).caisseProfile ? String((apiAgent as any).caisseProfile) : undefined,
|
||||||
statut: apiAgent.statut as AgentStatus,
|
statut: apiAgent.statut as AgentStatus,
|
||||||
|
|||||||
@@ -29,7 +29,7 @@
|
|||||||
[total]="total()"
|
[total]="total()"
|
||||||
[page]="page()"
|
[page]="page()"
|
||||||
[perPage]="size()"
|
[perPage]="size()"
|
||||||
(pageChange)="page.set($event)"
|
(pageChange)="page.set($event - 1)"
|
||||||
(perPageChange)="size.set($event)"
|
(perPageChange)="size.set($event)"
|
||||||
></app-paginator>
|
></app-paginator>
|
||||||
</div>
|
</div>
|
||||||
@@ -37,6 +37,7 @@
|
|||||||
<app-modal [open]="modalOpen()" [title]="modalTitle()" (close)="closeModal()" size="xxl">
|
<app-modal [open]="modalOpen()" [title]="modalTitle()" (close)="closeModal()" size="xxl">
|
||||||
<app-agent-full-form
|
<app-agent-full-form
|
||||||
[value]="editingItem() ?? undefined"
|
[value]="editingItem() ?? undefined"
|
||||||
|
[compact]="!editingItem()"
|
||||||
(save)="onFormSave($event)"
|
(save)="onFormSave($event)"
|
||||||
(cancel)="closeModal()"
|
(cancel)="closeModal()"
|
||||||
/>
|
/>
|
||||||
@@ -61,7 +62,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs text-muted-foreground mb-1">Profil</div>
|
<div class="text-xs text-muted-foreground mb-1">Profil</div>
|
||||||
<div class="font-medium">{{ agent.profile }}</div>
|
<div class="font-medium">{{ agent.profil }}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
<div class="text-xs text-muted-foreground mb-1">Statut</div>
|
<div class="text-xs text-muted-foreground mb-1">Statut</div>
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import { TpeDevice, TpeStatus } from 'src/app/core/interfaces/tpe';
|
|||||||
import { AgentFullForm } from '@shared/forms/agent-full-form/agent-full-form';
|
import { AgentFullForm } from '@shared/forms/agent-full-form/agent-full-form';
|
||||||
import { forkJoin, of } from 'rxjs';
|
import { forkJoin, of } from 'rxjs';
|
||||||
import { switchMap, catchError } from 'rxjs/operators';
|
import { switchMap, catchError } from 'rxjs/operators';
|
||||||
|
import { toast } from 'ngx-sonner';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
standalone: true,
|
standalone: true,
|
||||||
@@ -50,7 +51,7 @@ export class AgentsPage {
|
|||||||
total = signal(0);
|
total = signal(0);
|
||||||
loading = signal(false);
|
loading = signal(false);
|
||||||
|
|
||||||
page = signal(1);
|
page = signal(0);
|
||||||
size = signal(10);
|
size = signal(10);
|
||||||
search = signal('');
|
search = signal('');
|
||||||
sort = signal<SortState>({ key: 'code', dir: 'asc' });
|
sort = signal<SortState>({ key: 'code', dir: 'asc' });
|
||||||
@@ -90,7 +91,7 @@ export class AgentsPage {
|
|||||||
cols: TableColumn<Agent>[] = [
|
cols: TableColumn<Agent>[] = [
|
||||||
{ key: 'code', label: 'Code', sortable: true, defaultVisible: true },
|
{ key: 'code', label: 'Code', sortable: true, defaultVisible: true },
|
||||||
{ key: 'nomPrenom', label: 'Nom complet', sortable: true, defaultVisible: true, cell: (a) => `${a.nom} ${a.prenom}` },
|
{ key: 'nomPrenom', label: 'Nom complet', sortable: true, defaultVisible: true, cell: (a) => `${a.nom} ${a.prenom}` },
|
||||||
{ key: 'profile', label: 'Profil', sortable: true, defaultVisible: true },
|
{ key: 'profil', label: 'Profil', sortable: true, defaultVisible: true },
|
||||||
{ key: 'statut', label: 'Statut', sortable: true, defaultVisible: true, cell: (a) => this.renderStatutBadge(a.statut) },
|
{ key: 'statut', label: 'Statut', sortable: true, defaultVisible: true, cell: (a) => this.renderStatutBadge(a.statut) },
|
||||||
{ key: 'phone', label: 'Téléphone', sortable: true, defaultVisible: true },
|
{ key: 'phone', label: 'Téléphone', sortable: true, defaultVisible: true },
|
||||||
{ key: 'zone', label: 'Zone', sortable: true },
|
{ key: 'zone', label: 'Zone', sortable: true },
|
||||||
@@ -260,6 +261,7 @@ export class AgentsPage {
|
|||||||
|
|
||||||
onFormSave(payload: Partial<Agent>) {
|
onFormSave(payload: Partial<Agent>) {
|
||||||
const current = this.editingItem();
|
const current = this.editingItem();
|
||||||
|
const isCreating = !current?.id; // Mode création (compact)
|
||||||
const familyMembersData = this.formComp?.getFamilyMembersData() || [];
|
const familyMembersData = this.formComp?.getFamilyMembersData() || [];
|
||||||
|
|
||||||
// Save agent first
|
// Save agent first
|
||||||
@@ -280,7 +282,12 @@ export class AgentsPage {
|
|||||||
throw new Error("Impossible d'obtenir l'ID de l'agent sauvegardé");
|
throw new Error("Impossible d'obtenir l'ID de l'agent sauvegardé");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get existing family members for this agent
|
// In creation mode (compact), skip family members management
|
||||||
|
if (isCreating || familyMembersData.length === 0) {
|
||||||
|
return of([]);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get existing family members for this agent (only for updates)
|
||||||
return this.familyMemberService.getByAgentId(savedAgentId).pipe(
|
return this.familyMemberService.getByAgentId(savedAgentId).pipe(
|
||||||
switchMap((existingMembers) => {
|
switchMap((existingMembers) => {
|
||||||
const existingIds = new Set(existingMembers.map((m) => m.id));
|
const existingIds = new Set(existingMembers.map((m) => m.id));
|
||||||
@@ -354,12 +361,13 @@ export class AgentsPage {
|
|||||||
)
|
)
|
||||||
.subscribe({
|
.subscribe({
|
||||||
next: () => {
|
next: () => {
|
||||||
|
toast.success('Agent sauvegardé avec succès');
|
||||||
|
// Close modal first
|
||||||
|
this.closeModal();
|
||||||
// Reset form after successful save
|
// Reset form after successful save
|
||||||
this.formComp?.resetForm();
|
this.formComp?.resetForm();
|
||||||
// Clear editing item
|
// Clear editing item
|
||||||
this.editingItem.set(null);
|
this.editingItem.set(null);
|
||||||
// Close modal
|
|
||||||
this.closeModal();
|
|
||||||
// Refresh data
|
// Refresh data
|
||||||
this.fetch({
|
this.fetch({
|
||||||
page: this.page(),
|
page: this.page(),
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
</z-form-field>
|
</z-form-field>
|
||||||
<z-form-field>
|
<z-form-field>
|
||||||
<label z-form-label>Profil</label>
|
<label z-form-label>Profil</label>
|
||||||
<div z-form-control [errorMessage]="error('profile') || ''"><input z-input formControlName="profile" /></div>
|
<div z-form-control [errorMessage]="error('profil') || ''"><input z-input formControlName="profil" /></div>
|
||||||
</z-form-field>
|
</z-form-field>
|
||||||
|
|
||||||
<z-form-field>
|
<z-form-field>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ export class AgentForm {
|
|||||||
constructor(private fb: FormBuilder, private limitService: AgentLimitService) {
|
constructor(private fb: FormBuilder, private limitService: AgentLimitService) {
|
||||||
this.form = this.fb.group({
|
this.form = this.fb.group({
|
||||||
code: ['', Validators.required],
|
code: ['', Validators.required],
|
||||||
profile: ['', Validators.required],
|
profil: ['', Validators.required],
|
||||||
statut: ['ACTIF' as AgentStatus, Validators.required],
|
statut: ['ACTIF' as AgentStatus, Validators.required],
|
||||||
zone: [''],
|
zone: [''],
|
||||||
kiosk: [''],
|
kiosk: [''],
|
||||||
@@ -66,12 +66,12 @@ export class AgentForm {
|
|||||||
private hydrateFromValue(v?: Agent) {
|
private hydrateFromValue(v?: Agent) {
|
||||||
if (!v) {
|
if (!v) {
|
||||||
this.form.reset({
|
this.form.reset({
|
||||||
code: '', profile: '', statut: 'ACTIF', zone: '', kiosk: '', fonction: '', dateEmbauche: '', nom: '', prenom: '', phone: '', limiteInferieure: 0, limiteSuperieure: 0, limiteParTransaction: 0, limiteMinAirtime: 0, limiteMaxAirtime: 0, maxPeripheriques: 0, limitId: '',
|
code: '', profil: '', statut: 'ACTIF', zone: '', kiosk: '', fonction: '', dateEmbauche: '', nom: '', prenom: '', phone: '', limiteInferieure: 0, limiteSuperieure: 0, limiteParTransaction: 0, limiteMinAirtime: 0, limiteMaxAirtime: 0, maxPeripheriques: 0, limitId: '',
|
||||||
});
|
});
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
this.form.reset({
|
this.form.reset({
|
||||||
code: v.code, profile: v.profile, statut: v.statut, zone: v.zone ?? '', kiosk: v.kiosk ?? '', fonction: v.fonction ?? '', dateEmbauche: v.dateEmbauche ?? '', nom: v.nom, prenom: v.prenom, phone: v.phone, limiteInferieure: v.limiteInferieure ?? 0, limiteSuperieure: v.limiteSuperieure ?? 0, limiteParTransaction: v.limiteParTransaction ?? 0, limiteMinAirtime: v.limiteMinAirtime ?? 0, limiteMaxAirtime: v.limiteMaxAirtime ?? 0, maxPeripheriques: v.maxPeripheriques ?? 0, limitId: v.limitId ?? '',
|
code: v.code, profil: v.profil, statut: v.statut, zone: v.zone ?? '', kiosk: v.kiosk ?? '', fonction: v.fonction ?? '', dateEmbauche: v.dateEmbauche ?? '', nom: v.nom, prenom: v.prenom, phone: v.phone, limiteInferieure: v.limiteInferieure ?? 0, limiteSuperieure: v.limiteSuperieure ?? 0, limiteParTransaction: v.limiteParTransaction ?? 0, limiteMinAirtime: v.limiteMinAirtime ?? 0, limiteMaxAirtime: v.limiteMaxAirtime ?? 0, maxPeripheriques: v.maxPeripheriques ?? 0, limitId: v.limitId ?? '',
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -79,7 +79,7 @@ export class AgentForm {
|
|||||||
this.submitted = true;
|
this.submitted = true;
|
||||||
if (this.form.invalid) { this.form.markAllAsTouched(); return; }
|
if (this.form.invalid) { this.form.markAllAsTouched(); return; }
|
||||||
const raw = this.form.getRawValue() as any;
|
const raw = this.form.getRawValue() as any;
|
||||||
const payload: Agent = { id: this.value?.id ?? '', code: raw.code, profile: raw.profile, statut: raw.statut, zone: raw.zone, kiosk: raw.kiosk, fonction: raw.fonction, dateEmbauche: raw.dateEmbauche, nom: raw.nom, prenom: raw.prenom, phone: raw.phone, limiteInferieure: +raw.limiteInferieure, limiteSuperieure: +raw.limiteSuperieure, limiteParTransaction: +raw.limiteParTransaction, limiteMinAirtime: +raw.limiteMinAirtime, limiteMaxAirtime: +raw.limiteMaxAirtime, maxPeripheriques: +raw.maxPeripheriques, limitId: raw.limitId };
|
const payload: Agent = { id: this.value?.id ?? '', code: raw.code, profil: raw.profil, statut: raw.statut, zone: raw.zone, kiosk: raw.kiosk, fonction: raw.fonction, dateEmbauche: raw.dateEmbauche, nom: raw.nom, prenom: raw.prenom, phone: raw.phone, limiteInferieure: +raw.limiteInferieure, limiteSuperieure: +raw.limiteSuperieure, limiteParTransaction: +raw.limiteParTransaction, limiteMinAirtime: +raw.limiteMinAirtime, limiteMaxAirtime: +raw.limiteMaxAirtime, maxPeripheriques: +raw.maxPeripheriques, limitId: raw.limitId };
|
||||||
this.save.emit(payload);
|
this.save.emit(payload);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<form class="space-y-6" (ngSubmit)="onSubmit()" [formGroup]="form">
|
<form class="space-y-6" (ngSubmit)="onSubmit()" [formGroup]="form">
|
||||||
<div class="grid grid-cols-1 xl:grid-cols-3 gap-4">
|
<div class="grid grid-cols-1 xl:grid-cols-2 gap-4">
|
||||||
<z-card class="p-4 space-y-3">
|
<z-card class="p-4 space-y-3">
|
||||||
<div class="text-lg font-semibold">Informations Emploi</div>
|
<div class="text-lg font-semibold">Informations Emploi</div>
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
@@ -10,8 +10,8 @@
|
|||||||
></z-form-field>
|
></z-form-field>
|
||||||
<z-form-field
|
<z-form-field
|
||||||
><label z-form-label>Profil</label>
|
><label z-form-label>Profil</label>
|
||||||
<div z-form-control [errorMessage]="error('profile') || ''">
|
<div z-form-control [errorMessage]="error('profil') || ''">
|
||||||
<input z-input formControlName="profile" /></div
|
<input z-input formControlName="profil" /></div
|
||||||
></z-form-field>
|
></z-form-field>
|
||||||
<z-form-field class="md:col-span-2"
|
<z-form-field class="md:col-span-2"
|
||||||
><label z-form-label>Agent Principal</label>
|
><label z-form-label>Agent Principal</label>
|
||||||
@@ -94,88 +94,6 @@
|
|||||||
></z-form-field>
|
></z-form-field>
|
||||||
</div>
|
</div>
|
||||||
</z-card>
|
</z-card>
|
||||||
|
|
||||||
<z-card class="p-4 space-y-3">
|
|
||||||
<div class="text-lg font-semibold">Paramètres de connexion</div>
|
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
|
||||||
<z-form-field
|
|
||||||
><label z-form-label>N° Téléphone</label>
|
|
||||||
<div z-form-control [errorMessage]="error('phone') || ''">
|
|
||||||
<input z-input formControlName="phone" /></div
|
|
||||||
></z-form-field>
|
|
||||||
<z-form-field
|
|
||||||
><label z-form-label>PIN</label>
|
|
||||||
<div z-form-control><input z-input formControlName="pin" type="password" /></div
|
|
||||||
></z-form-field>
|
|
||||||
<z-form-field
|
|
||||||
><label z-form-label>Limite inférieure</label>
|
|
||||||
<div z-form-control>
|
|
||||||
<input z-input type="number" formControlName="limiteInferieure" /></div
|
|
||||||
></z-form-field>
|
|
||||||
<z-form-field
|
|
||||||
><label z-form-label>Limite supérieure</label>
|
|
||||||
<div z-form-control>
|
|
||||||
<input z-input type="number" formControlName="limiteSuperieure" /></div
|
|
||||||
></z-form-field>
|
|
||||||
<z-form-field
|
|
||||||
><label z-form-label>Limite / transaction</label>
|
|
||||||
<div z-form-control>
|
|
||||||
<input z-input type="number" formControlName="limiteParTransaction" /></div
|
|
||||||
></z-form-field>
|
|
||||||
<z-form-field
|
|
||||||
><label z-form-label>Limite min airtime</label>
|
|
||||||
<div z-form-control>
|
|
||||||
<input z-input type="number" formControlName="limiteMinAirtime" /></div
|
|
||||||
></z-form-field>
|
|
||||||
<z-form-field
|
|
||||||
><label z-form-label>Limite max airtime</label>
|
|
||||||
<div z-form-control>
|
|
||||||
<input z-input type="number" formControlName="limiteMaxAirtime" /></div
|
|
||||||
></z-form-field>
|
|
||||||
<z-form-field class="md:col-span-2"
|
|
||||||
><label z-form-label>Groupe de limites</label>
|
|
||||||
<div z-form-control [errorMessage]="error('limitId') || ''">
|
|
||||||
<z-select formControlName="limitId" [zPlaceholder]="'Sélectionner...'">
|
|
||||||
@for (l of limits; track l.id) {
|
|
||||||
<z-select-item [zValue]="l.id">
|
|
||||||
{{ l.nom }}
|
|
||||||
@if (l.isDefault) {
|
|
||||||
<span class="text-xs text-primary ml-1">(Par défaut)</span>
|
|
||||||
} @if (l.actif) {
|
|
||||||
<span class="text-xs text-green-600 dark:text-green-400 ml-1">• Actif</span>
|
|
||||||
}
|
|
||||||
</z-select-item>
|
|
||||||
}
|
|
||||||
</z-select>
|
|
||||||
</div>
|
|
||||||
@if (selectedLimit) {
|
|
||||||
<div class="mt-2 p-3 bg-accent rounded text-sm space-y-1">
|
|
||||||
<div class="font-medium">{{ selectedLimit.nom }}</div>
|
|
||||||
<div class="text-muted-foreground text-xs">
|
|
||||||
@if (selectedLimit.isDefault) {
|
|
||||||
<span class="inline-flex items-center gap-1"
|
|
||||||
><i class="icon-star size-3"></i> Limite par défaut</span
|
|
||||||
>
|
|
||||||
} @if (selectedLimit.actif) {
|
|
||||||
<span class="inline-flex items-center gap-1 ml-2"
|
|
||||||
><i class="icon-check size-3"></i> Contrôle actif</span
|
|
||||||
>
|
|
||||||
} @else {
|
|
||||||
<span class="inline-flex items-center gap-1 ml-2"
|
|
||||||
><i class="icon-x size-3"></i> Contrôle inactif</span
|
|
||||||
>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
<div class="text-xs text-muted-foreground mt-2">
|
|
||||||
<div>Min Bet: {{ (selectedLimit.betMin ?? 0).toLocaleString('fr-FR') }}</div>
|
|
||||||
<div>Max Bet: {{ (selectedLimit.betMax ?? 0).toLocaleString('fr-FR') }}</div>
|
|
||||||
<div>Max Bet (tx): {{ (selectedLimit.maxBet ?? 0).toLocaleString('fr-FR') }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
</z-form-field>
|
|
||||||
</div>
|
|
||||||
</z-card>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 xl:grid-cols-2 gap-4">
|
<div class="grid grid-cols-1 xl:grid-cols-2 gap-4">
|
||||||
@@ -220,15 +138,57 @@
|
|||||||
></z-form-field>
|
></z-form-field>
|
||||||
</div>
|
</div>
|
||||||
</z-card>
|
</z-card>
|
||||||
|
<z-card class="p-4 space-y-3">
|
||||||
|
<div class="text-lg font-semibold">Paramètres de connexion</div>
|
||||||
|
<div class="flex flex-col gap-y-3 mb-3">
|
||||||
|
<z-form-field
|
||||||
|
><label z-form-label>N° Téléphone</label>
|
||||||
|
<div z-form-control [errorMessage]="error('phone') || ''">
|
||||||
|
<input z-input formControlName="phone" /></div
|
||||||
|
></z-form-field>
|
||||||
|
<z-form-field
|
||||||
|
><label z-form-label>PIN</label>
|
||||||
|
<div z-form-control><input z-input formControlName="pin" type="password" /></div
|
||||||
|
></z-form-field>
|
||||||
|
</div>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-3">
|
||||||
|
<z-form-field
|
||||||
|
><label z-form-label>Limite inférieure</label>
|
||||||
|
<div z-form-control>
|
||||||
|
<input z-input type="number" formControlName="limiteInferieure" /></div
|
||||||
|
></z-form-field>
|
||||||
|
<z-form-field
|
||||||
|
><label z-form-label>Limite supérieure</label>
|
||||||
|
<div z-form-control>
|
||||||
|
<input z-input type="number" formControlName="limiteSuperieure" /></div
|
||||||
|
></z-form-field>
|
||||||
|
<z-form-field
|
||||||
|
><label z-form-label>Limite / transaction</label>
|
||||||
|
<div z-form-control>
|
||||||
|
<input z-input type="number" formControlName="limiteParTransaction" /></div
|
||||||
|
></z-form-field>
|
||||||
|
<z-form-field
|
||||||
|
><label z-form-label>Limite min airtime</label>
|
||||||
|
<div z-form-control>
|
||||||
|
<input z-input type="number" formControlName="limiteMinAirtime" /></div
|
||||||
|
></z-form-field>
|
||||||
|
<z-form-field
|
||||||
|
><label z-form-label>Limite max airtime</label>
|
||||||
|
<div z-form-control>
|
||||||
|
<input z-input type="number" formControlName="limiteMaxAirtime" /></div
|
||||||
|
></z-form-field>
|
||||||
|
</div>
|
||||||
|
</z-card>
|
||||||
|
|
||||||
<z-card class="p-4 space-y-4">
|
<!-- <z-card class="p-4 space-y-4">
|
||||||
<div class="flex items-center justify-between mb-2">
|
<div class="flex items-center justify-between mb-2">
|
||||||
<div class="text-lg font-semibold">Membres de famille</div>
|
<div class="text-lg font-semibold">Membres de famille</div>
|
||||||
<z-button zType="default" (click)="addFamily()">
|
<z-button zType="default" (click)="addFamily()">
|
||||||
<i class="icon-plus mr-2"></i>Ajouter un membre
|
<i class="icon-plus mr-2"></i>Ajouter un membre
|
||||||
</z-button>
|
</z-button>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-3" formArrayName="famille">
|
@if (!compact) {
|
||||||
|
<div class="space-y-3" formArrayName="famille">
|
||||||
@for (fm of famille.controls; track $index; let i = $index) {
|
@for (fm of famille.controls; track $index; let i = $index) {
|
||||||
<div
|
<div
|
||||||
[formGroupName]="i"
|
[formGroupName]="i"
|
||||||
@@ -307,62 +267,13 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
</z-card>
|
} @else {
|
||||||
|
<div class="text-sm text-muted">Les membres de la famille sont gérés depuis la fiche agent après la création.</div>
|
||||||
|
}
|
||||||
|
</z-card> -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<z-card class="p-4 space-y-3">
|
<!-- TPE assignment UI removed from this form -->
|
||||||
<div class="flex items-center justify-between">
|
|
||||||
<div class="text-lg font-semibold">Assignation TPE (actifs)</div>
|
|
||||||
<z-button zType="ghost" (click)="openAssignTpe()"
|
|
||||||
><i class="icon-plus mr-2"></i>Gérer</z-button
|
|
||||||
>
|
|
||||||
</div>
|
|
||||||
<div class="flex flex-wrap gap-2">
|
|
||||||
@for (id of tpeArray.value; track id) {
|
|
||||||
<span class="px-2 py-1 rounded bg-accent text-sm flex items-center gap-2">
|
|
||||||
{{ tpeLabel(id) }}
|
|
||||||
<button z-button zType="ghost" zSize="sm" (click)="onToggleTpe(id, false)">
|
|
||||||
<i class="icon-x"></i>
|
|
||||||
</button>
|
|
||||||
</span>
|
|
||||||
} @empty {
|
|
||||||
<span class="text-muted-foreground text-sm">Aucun TPE assigné</span>
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</z-card>
|
|
||||||
|
|
||||||
<app-modal
|
<!-- TPE assignment modal removed from this form; TPE assignment is handled elsewhere -->
|
||||||
[open]="assignModalOpen"
|
|
||||||
[title]="'Assigner des TPE'"
|
|
||||||
(close)="closeAssignTpe()"
|
|
||||||
size="xxl"
|
|
||||||
>
|
|
||||||
<app-search-bar
|
|
||||||
placeholder="Rechercher par IMEI, marque, modèle..."
|
|
||||||
(search)="onTpeSearch($event)"
|
|
||||||
></app-search-bar>
|
|
||||||
<app-data-table [data]="tpeRows" [columns]="tpeCols" [sort]="{ key: 'imei', dir: 'asc' }">
|
|
||||||
<ng-template #rowActions let-row>
|
|
||||||
@if (!isSelectedTpe(row.id)) {
|
|
||||||
<button z-button zType="ghost" (click)="onToggleTpe(row.id, true)">
|
|
||||||
<i class="icon-plus"></i>
|
|
||||||
</button>
|
|
||||||
} @else {
|
|
||||||
<button z-button zType="destructive" (click)="onToggleTpe(row.id, false)">
|
|
||||||
<i class="icon-trash"></i>
|
|
||||||
</button>
|
|
||||||
}
|
|
||||||
</ng-template>
|
|
||||||
</app-data-table>
|
|
||||||
<app-paginator
|
|
||||||
[total]="tpeTotal"
|
|
||||||
[page]="tpePage"
|
|
||||||
[perPage]="tpePerPage"
|
|
||||||
(pageChange)="onTpePageChange($event)"
|
|
||||||
(perPageChange)="onTpePerPageChange($event)"
|
|
||||||
></app-paginator>
|
|
||||||
<div modal-actions class="flex justify-end gap-2">
|
|
||||||
<z-button zType="default" (click)="closeAssignTpe()">Terminer</z-button>
|
|
||||||
</div>
|
|
||||||
</app-modal>
|
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -7,16 +7,12 @@ import { ZardSelectComponent } from '@shared/components/select/select.component'
|
|||||||
import { ZardSelectItemComponent } from '@shared/components/select/select-item.component';
|
import { ZardSelectItemComponent } from '@shared/components/select/select-item.component';
|
||||||
import { ZardButtonComponent } from '@shared/components/button/button.component';
|
import { ZardButtonComponent } from '@shared/components/button/button.component';
|
||||||
import { ZardCardComponent } from '@shared/components/card/card.component';
|
import { ZardCardComponent } from '@shared/components/card/card.component';
|
||||||
import { DataTable, SortState, TableColumn } from '@shared/components/data-table/data-table';
|
// DataTable removed from this form (TPE UI was removed)
|
||||||
import { Paginator } from '@shared/components/paginator/paginator';
|
|
||||||
import { SearchBar } from '@shared/components/search-bar/search-bar';
|
|
||||||
import { Modal } from '@shared/components/modal/modal';
|
|
||||||
import { Agent, AgentFamilyMember } from 'src/app/core/interfaces/agent';
|
import { Agent, AgentFamilyMember } from 'src/app/core/interfaces/agent';
|
||||||
import { AgentLimit } from 'src/app/core/interfaces/agent-limit';
|
import { AgentLimit } from 'src/app/core/interfaces/agent-limit';
|
||||||
import { AgentLimitService } from 'src/app/core/services/agent-limit';
|
import { AgentLimitService } from 'src/app/core/services/agent-limit';
|
||||||
import { AgentFamilyMemberService } from 'src/app/core/services/agent-family-member';
|
import { AgentFamilyMemberService } from 'src/app/core/services/agent-family-member';
|
||||||
import { TpeDevice } from 'src/app/core/interfaces/tpe';
|
// TPE assignment removed from form (handled elsewhere)
|
||||||
import { TpeService } from 'src/app/core/services/tpe';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'app-agent-full-form',
|
selector: 'app-agent-full-form',
|
||||||
@@ -30,36 +26,18 @@ import { TpeService } from 'src/app/core/services/tpe';
|
|||||||
ZardInputDirective,
|
ZardInputDirective,
|
||||||
ZardSelectComponent,
|
ZardSelectComponent,
|
||||||
ZardSelectItemComponent,
|
ZardSelectItemComponent,
|
||||||
ZardButtonComponent,
|
|
||||||
ZardCardComponent,
|
ZardCardComponent,
|
||||||
DataTable,
|
// TPE UI components removed from this form
|
||||||
Paginator,
|
|
||||||
SearchBar,
|
|
||||||
Modal,
|
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
export class AgentFullForm {
|
export class AgentFullForm {
|
||||||
@Output() save = new EventEmitter<Agent>();
|
@Output() save = new EventEmitter<Partial<Agent>>();
|
||||||
@Output() cancel = new EventEmitter<void>();
|
@Output() cancel = new EventEmitter<void>();
|
||||||
|
@Input() compact = false; // when true, hide family and TPE sections for a shorter creation form
|
||||||
|
|
||||||
limits: AgentLimit[] = [];
|
limits: AgentLimit[] = [];
|
||||||
selectedLimit: AgentLimit | null = null;
|
selectedLimit: AgentLimit | null = null;
|
||||||
tpes: TpeDevice[] = [];
|
// TPE assignment removed from the creation form; handled in separate flows
|
||||||
// TPE picker state
|
|
||||||
assignModalOpen = false;
|
|
||||||
tpeRows: TpeDevice[] = [];
|
|
||||||
tpeTotal = 0;
|
|
||||||
tpePage = 1;
|
|
||||||
tpePerPage = 10;
|
|
||||||
tpeSearch = '';
|
|
||||||
tpeCols: TableColumn<TpeDevice>[] = [
|
|
||||||
{ key: 'imei', label: 'IMEI', sortable: true },
|
|
||||||
{ key: 'serial', label: 'N° Série', sortable: true },
|
|
||||||
{ key: 'marque', label: 'Marque', sortable: true },
|
|
||||||
{ key: 'modele', label: 'Modèle', sortable: true },
|
|
||||||
{ key: 'type', label: 'Type', sortable: true },
|
|
||||||
{ key: 'statut', label: 'Statut', sortable: true },
|
|
||||||
];
|
|
||||||
|
|
||||||
private _value?: Agent;
|
private _value?: Agent;
|
||||||
@Input() set value(v: Agent | undefined) { this._value = v; this.hydrate(v); }
|
@Input() set value(v: Agent | undefined) { this._value = v; this.hydrate(v); }
|
||||||
@@ -71,22 +49,19 @@ export class AgentFullForm {
|
|||||||
constructor(
|
constructor(
|
||||||
private fb: FormBuilder,
|
private fb: FormBuilder,
|
||||||
private limitService: AgentLimitService,
|
private limitService: AgentLimitService,
|
||||||
private tpeService: TpeService,
|
|
||||||
private familyMemberService: AgentFamilyMemberService
|
private familyMemberService: AgentFamilyMemberService
|
||||||
) {
|
) {
|
||||||
this.form = this.fb.group({
|
this.form = this.fb.group({
|
||||||
// Emploi
|
// Emploi
|
||||||
code: ['', Validators.required], profile: ['', Validators.required], principalCode: [''], caisseProfile: [''], statut: ['ACTIF', Validators.required], zone: [''], kiosk: [''], fonction: [''], dateEmbauche: [''],
|
code: ['', Validators.required], profil: ['', Validators.required], principalCode: [''], caisseProfile: [''], statut: ['ACTIF', Validators.required], zone: [''], kiosk: [''], fonction: [''], dateEmbauche: [''],
|
||||||
// Perso
|
// Perso
|
||||||
nom: ['', Validators.required], prenom: ['', Validators.required], autresNoms: [''], dateNaissance: [''], lieuNaissance: [''], adresse: [''], ville: [''], autoriserAides: [false], maxPeripheriques: [0, [Validators.min(0)]],
|
nom: ['', Validators.required], prenom: ['', Validators.required], autresNoms: [''], dateNaissance: [''], lieuNaissance: [''], adresse: [''], ville: [''], autoriserAides: [false], maxPeripheriques: [0, [Validators.min(0)]],
|
||||||
// Connexion & limites
|
// Connexion & limites
|
||||||
phone: ['', Validators.required], pin: [''], limiteInferieure: [0], limiteSuperieure: [0], limiteParTransaction: [0], limiteMinAirtime: [0], limiteMaxAirtime: [0], limitId: ['', Validators.required],
|
phone: ['', Validators.required], pin: [''], limiteInferieure: [0], limiteSuperieure: [0], limiteParTransaction: [0], limiteMinAirtime: [0], limiteMaxAirtime: [0], limitId: ['1'],
|
||||||
// Légales
|
// Légales
|
||||||
nationalite: [''], cni: [''], cniDelivreeLe: [''], cniDelivreeA: [''], residence: [''], autreAdresse1: [''], statutMarital: [''], epoux: [''], autreTelephone: [''],
|
nationalite: [''], cni: [''], cniDelivreeLe: [''], cniDelivreeA: [''], residence: [''], autreAdresse1: [''], statutMarital: [''], epoux: [''], autreTelephone: [''],
|
||||||
// Famille
|
// Famille
|
||||||
famille: this.fb.array([]),
|
famille: this.fb.array([]),
|
||||||
// TPE (stored as IDs in form, converted to full objects on submit)
|
|
||||||
tpeIds: this.fb.array<string>([]),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
this.limitService.list({ page: 1, perPage: 100, search: '', sortKey: 'nom', sortDir: 'asc' } as any).subscribe((res) => {
|
this.limitService.list({ page: 1, perPage: 100, search: '', sortKey: 'nom', sortDir: 'asc' } as any).subscribe((res) => {
|
||||||
@@ -114,14 +89,14 @@ export class AgentFullForm {
|
|||||||
this.selectedLimit = defaultLimit;
|
this.selectedLimit = defaultLimit;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// initial fetch page for TPE modal
|
// TPE fetching/assignment handled outside of this form
|
||||||
this.fetchTpes();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
get famille(): FormArray { return this.form.get('famille') as FormArray; }
|
get famille(): FormArray { return this.form.get('famille') as FormArray; }
|
||||||
get tpeArray(): FormArray { return this.form.get('tpeIds') as FormArray; }
|
|
||||||
|
|
||||||
addFamily() {
|
addFamily() {
|
||||||
|
if (this.compact) return; // no family in compact mode
|
||||||
this.famille.push(
|
this.famille.push(
|
||||||
this.fb.group({
|
this.fb.group({
|
||||||
id: [''], // Will be set when saved
|
id: [''], // Will be set when saved
|
||||||
@@ -143,7 +118,6 @@ export class AgentFullForm {
|
|||||||
|
|
||||||
private hydrate(v?: Agent) {
|
private hydrate(v?: Agent) {
|
||||||
this.famille.clear();
|
this.famille.clear();
|
||||||
this.tpeArray.clear();
|
|
||||||
this.selectedLimit = null;
|
this.selectedLimit = null;
|
||||||
|
|
||||||
if (!v) {
|
if (!v) {
|
||||||
@@ -153,7 +127,7 @@ export class AgentFullForm {
|
|||||||
|
|
||||||
this.form.reset({
|
this.form.reset({
|
||||||
code: '',
|
code: '',
|
||||||
profile: '',
|
profil: '',
|
||||||
principalCode: '',
|
principalCode: '',
|
||||||
caisseProfile: '',
|
caisseProfile: '',
|
||||||
statut: 'ACTIF',
|
statut: 'ACTIF',
|
||||||
@@ -226,60 +200,10 @@ export class AgentFullForm {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load assigned TPE IDs from tpes array
|
// Assigned TPEs are not handled in this form
|
||||||
(v.tpes ?? []).forEach((tpe) => this.tpeArray.push(this.fb.control(tpe.id)));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
onToggleTpe(id: string, checked: boolean) {
|
// TPE assignment / picker removed from this form
|
||||||
const idx = this.tpeArray.value.indexOf(id);
|
|
||||||
if (checked && idx === -1) this.tpeArray.push(this.fb.control(id));
|
|
||||||
if (!checked && idx !== -1) this.tpeArray.removeAt(idx);
|
|
||||||
}
|
|
||||||
|
|
||||||
openAssignTpe() {
|
|
||||||
this.assignModalOpen = true;
|
|
||||||
this.fetchTpes();
|
|
||||||
}
|
|
||||||
closeAssignTpe() {
|
|
||||||
this.assignModalOpen = false;
|
|
||||||
// Refresh TPE list to show current assignments
|
|
||||||
this.fetchTpes();
|
|
||||||
}
|
|
||||||
onTpeSearch(q: string) { this.tpeSearch = q; this.tpePage = 1; this.fetchTpes(); }
|
|
||||||
onTpePageChange(p: number) { this.tpePage = p; this.fetchTpes(); }
|
|
||||||
onTpePerPageChange(pp: number) { this.tpePerPage = pp; this.fetchTpes(); }
|
|
||||||
isSelectedTpe(id: string) { return (this.tpeArray.value as string[]).includes(id); }
|
|
||||||
|
|
||||||
fetchTpes() {
|
|
||||||
this.tpeService
|
|
||||||
.list({ page: this.tpePage, perPage: this.tpePerPage, search: this.tpeSearch, sortKey: 'imei', sortDir: 'asc' } as any)
|
|
||||||
.subscribe((res) => {
|
|
||||||
// Only show VALIDE TPEs that are either not assigned or assigned to this agent
|
|
||||||
const currentAgentId = this.value?.id;
|
|
||||||
this.tpeRows = res.content.filter((t) => {
|
|
||||||
if (t.statut !== 'VALIDE') return false;
|
|
||||||
// If TPE is assigned but to this agent, show it
|
|
||||||
if (t.assigne && currentAgentId) {
|
|
||||||
// We need to check if this TPE is assigned to this agent
|
|
||||||
// For now, show all VALIDE TPEs - the backend should handle assignment logic
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// Show unassigned TPEs
|
|
||||||
return !t.assigne;
|
|
||||||
});
|
|
||||||
this.tpeTotal = this.tpeRows.length;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
tpeLabel(id: string): string {
|
|
||||||
const list = [...(this.tpeRows ?? []), ...(this.tpes ?? [])];
|
|
||||||
const found = list.find((t) => t.id === id);
|
|
||||||
if (found) {
|
|
||||||
return `${found.imei} (${found.marque} ${found.modele})`;
|
|
||||||
}
|
|
||||||
// Try to load from service if not in current list
|
|
||||||
return id;
|
|
||||||
}
|
|
||||||
|
|
||||||
// === Validation helpers ===
|
// === Validation helpers ===
|
||||||
error(control: string): string {
|
error(control: string): string {
|
||||||
@@ -313,36 +237,13 @@ export class AgentFullForm {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const raw = this.form.getRawValue() as any;
|
const raw = this.form.getRawValue() as any;
|
||||||
|
// Prepare partial agent payload (family members are handled separately)
|
||||||
// Convert TPE IDs to full TPE objects
|
const payload: Partial<Agent> = {
|
||||||
const tpeIds = [...this.tpeArray.value] as string[];
|
|
||||||
const tpes: TpeDevice[] = tpeIds
|
|
||||||
.map((id) => {
|
|
||||||
// Try to find in tpeRows first (current modal list)
|
|
||||||
const found = this.tpeRows.find((t) => t.id === id);
|
|
||||||
if (found) return found;
|
|
||||||
// Try to find in existing tpes (from value)
|
|
||||||
const existing = this.value?.tpes?.find((t) => t.id === id);
|
|
||||||
if (existing) return existing;
|
|
||||||
// If not found, create a minimal TPE object (backend will fill in details)
|
|
||||||
// This shouldn't happen in normal flow, but handle gracefully
|
|
||||||
return null;
|
|
||||||
})
|
|
||||||
.filter((t): t is TpeDevice => t !== null);
|
|
||||||
|
|
||||||
// Prepare agent payload (without famille - family members are handled separately)
|
|
||||||
const payload: Agent = {
|
|
||||||
...(this.value ?? {}),
|
|
||||||
id: this.value?.id || '',
|
|
||||||
...raw,
|
...raw,
|
||||||
tpes: tpes,
|
...(this.value?.id ? { id: this.value.id } : {}),
|
||||||
} as Agent;
|
};
|
||||||
|
|
||||||
// Remove tpeIds from payload (it's not part of Agent interface)
|
// Emit the partial agent payload (parent will decide create vs update)
|
||||||
delete (payload as any).tpeIds;
|
|
||||||
|
|
||||||
// Emit the agent payload first
|
|
||||||
// Family members will be saved separately in the parent component
|
|
||||||
this.save.emit(payload);
|
this.save.emit(payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -362,7 +263,6 @@ export class AgentFullForm {
|
|||||||
this._value = undefined;
|
this._value = undefined;
|
||||||
this.selectedLimit = null;
|
this.selectedLimit = null;
|
||||||
this.famille.clear();
|
this.famille.clear();
|
||||||
this.tpeArray.clear();
|
|
||||||
|
|
||||||
// Find default limit to assign it automatically
|
// Find default limit to assign it automatically
|
||||||
const defaultLimit = this.limits.find((l) => l.isDefault);
|
const defaultLimit = this.limits.find((l) => l.isDefault);
|
||||||
@@ -370,7 +270,7 @@ export class AgentFullForm {
|
|||||||
|
|
||||||
this.form.reset({
|
this.form.reset({
|
||||||
code: '',
|
code: '',
|
||||||
profile: '',
|
profil: '',
|
||||||
principalCode: '',
|
principalCode: '',
|
||||||
caisseProfile: '',
|
caisseProfile: '',
|
||||||
statut: 'ACTIF',
|
statut: 'ACTIF',
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
apiBaseUrl: 'http://192.168.1.235:8381',
|
apiBaseUrl: 'https://cuddly-years-work.loca.lt',
|
||||||
depouillementBaseUrl: 'http://192.168.1.235:8383'
|
depouillementBaseUrl: 'http://192.168.1.235:8383'
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
export const environment = {
|
export const environment = {
|
||||||
production: false,
|
production: false,
|
||||||
apiBaseUrl: 'http://192.168.1.235:8381',
|
apiBaseUrl: 'https://cuddly-years-work.loca.lt',
|
||||||
};
|
};
|
||||||
|
|||||||
Reference in New Issue
Block a user