Skip to content

Commit 81fd6c4

Browse files
committed
Track modified product tabs on save
Adds tracking for tabs modified in the product workspace and uses that state to build a partial update payload instead of sending the full form on save. This keeps updates scoped to the edited sections, resets the dirty state after a successful save, and avoids overwriting untouched product data.
1 parent e56691c commit 81fd6c4

1 file changed

Lines changed: 89 additions & 22 deletions

File tree

src/app/features/catalog/product-workspace/product-workspace.ts

Lines changed: 89 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,8 @@ export class ProductWorkspaceComponent implements OnInit {
9696
readonly manufacturersByProduct = signal<OemCode[]>([]);
9797
readonly selectedImageForDelete = signal<MediaImageItem | null>(null);
9898

99+
readonly modifiedTabs = signal<Set<FormTab>>(new Set<FormTab>());
100+
99101
readonly product = signal<ProductResponse>(initialProductPayload);
100102
readonly productForm = signal<UpdateProductPayload>(defaultUpdatePayload());
101103

@@ -241,8 +243,17 @@ export class ProductWorkspaceComponent implements OnInit {
241243
return false;
242244
}
243245

244-
updateField(field: string, val: string): void {
246+
private markCurrentTabAsModified(): void {
245247
this.isFormDirty.set(true);
248+
this.modifiedTabs.update((tabs) => {
249+
const newTabs = new Set(tabs);
250+
newTabs.add(this.activeTab());
251+
return newTabs;
252+
});
253+
}
254+
255+
updateField(field: string, val: string): void {
256+
this.markCurrentTabAsModified();
246257
const fieldMapping: Record<string, keyof UpdateProductPayload> = {
247258
name: 'name',
248259
internal_code: 'internal_code',
@@ -270,7 +281,7 @@ export class ProductWorkspaceComponent implements OnInit {
270281
}
271282

272283
updateBooleanField(field: string, val: boolean): void {
273-
this.isFormDirty.set(true);
284+
this.markCurrentTabAsModified();
274285
if (field === 'active') {
275286
this.productForm.update((p) => ({ ...p, active: val }));
276287
} else if (field === 'featured') {
@@ -279,7 +290,7 @@ export class ProductWorkspaceComponent implements OnInit {
279290
}
280291

281292
updateNumberField(field: string, valStr: string): void {
282-
this.isFormDirty.set(true);
293+
this.markCurrentTabAsModified();
283294
const num = parseFloat(valStr) || 0;
284295
const fieldMapping: Record<string, keyof UpdateProductPayload> = {
285296
weight: 'weight',
@@ -314,7 +325,7 @@ export class ProductWorkspaceComponent implements OnInit {
314325
}
315326

316327
updateNullableNumberField(field: string, valStr: string): void {
317-
this.isFormDirty.set(true);
328+
this.markCurrentTabAsModified();
318329
const val = valStr.trim() === '' ? null : parseFloat(valStr);
319330
if (field === 'promotional_price') {
320331
this.productForm.update((p) => ({
@@ -347,7 +358,7 @@ export class ProductWorkspaceComponent implements OnInit {
347358
}
348359

349360
updateDescription(val: string): void {
350-
this.isFormDirty.set(true);
361+
this.markCurrentTabAsModified();
351362
this.productForm.update((p) => ({ ...p, description: val }));
352363
}
353364

@@ -356,7 +367,7 @@ export class ProductWorkspaceComponent implements OnInit {
356367
}
357368

358369
onInventoriesChange(inventories: UpdateProductInventoryPayload[]): void {
359-
this.isFormDirty.set(true);
370+
this.markCurrentTabAsModified();
360371
this.productForm.update((p) => ({ ...p, inventories }));
361372
if (this.errors()['inventories']) {
362373
this.errors.update((e) => {
@@ -369,7 +380,7 @@ export class ProductWorkspaceComponent implements OnInit {
369380

370381
toggleBackorder(): void {
371382
if (this.isReadOnly()) return;
372-
this.isFormDirty.set(true);
383+
this.markCurrentTabAsModified();
373384
this.productForm.update((p) => {
374385
const invs = p.inventories || [];
375386
const first = invs[0] || { warehouse_id: '', quantity: 0 };
@@ -398,15 +409,15 @@ export class ProductWorkspaceComponent implements OnInit {
398409
oem_code_ids: [newId, ...(pf.oem_code_ids || this.product().oem_codes.map((o) => o.id))],
399410
}));
400411

401-
this.isFormDirty.set(true);
412+
this.markCurrentTabAsModified();
402413
}
403414

404415
setPrimaryOem(id: string): void {
405416
this.product.update((p) => ({
406417
...p,
407418
oem_codes: p.oem_codes.map((o) => ({ ...o, is_primary: o.id === id })),
408419
}));
409-
this.isFormDirty.set(true);
420+
this.markCurrentTabAsModified();
410421
}
411422

412423
removeOemCode(id: string): void {
@@ -422,7 +433,7 @@ export class ProductWorkspaceComponent implements OnInit {
422433
),
423434
}));
424435

425-
this.isFormDirty.set(true);
436+
this.markCurrentTabAsModified();
426437
}
427438

428439
toggleOemStatus(id: string): void {
@@ -448,7 +459,7 @@ export class ProductWorkspaceComponent implements OnInit {
448459
product_code_ids: [...(pf.product_code_ids || this.product().codes.map((c) => c.id)), newId],
449460
}));
450461

451-
this.isFormDirty.set(true);
462+
this.markCurrentTabAsModified();
452463
}
453464

454465
removeProductCode(id: string): void {
@@ -464,7 +475,7 @@ export class ProductWorkspaceComponent implements OnInit {
464475
),
465476
}));
466477

467-
this.isFormDirty.set(true);
478+
this.markCurrentTabAsModified();
468479
}
469480

470481
toggleProductCodeStatus(id: string): void {
@@ -498,7 +509,7 @@ export class ProductWorkspaceComponent implements OnInit {
498509
],
499510
}));
500511

501-
this.isFormDirty.set(true);
512+
this.markCurrentTabAsModified();
502513
}
503514

504515
removeEquivalent(id: string): void {
@@ -514,7 +525,7 @@ export class ProductWorkspaceComponent implements OnInit {
514525
).filter((eqId) => eqId !== id),
515526
}));
516527

517-
this.isFormDirty.set(true);
528+
this.markCurrentTabAsModified();
518529
}
519530

520531
toggleEquivalentStatus(id: string): void {
@@ -553,7 +564,7 @@ export class ProductWorkspaceComponent implements OnInit {
553564
],
554565
}));
555566

556-
this.isFormDirty.set(true);
567+
this.markCurrentTabAsModified();
557568
}
558569

559570
removeVehicleApplication(id: string): void {
@@ -569,7 +580,7 @@ export class ProductWorkspaceComponent implements OnInit {
569580
),
570581
}));
571582

572-
this.isFormDirty.set(true);
583+
this.markCurrentTabAsModified();
573584
}
574585

575586
toggleVehicleStatus(id: string): void {
@@ -643,7 +654,7 @@ export class ProductWorkspaceComponent implements OnInit {
643654
supplier_ids: [...(pf.supplier_ids || this.product().suppliers.map((s) => s.id)), newId],
644655
}));
645656

646-
this.isFormDirty.set(true);
657+
this.markCurrentTabAsModified();
647658
}
648659

649660
removeSupplier(id: string): void {
@@ -659,7 +670,7 @@ export class ProductWorkspaceComponent implements OnInit {
659670
),
660671
}));
661672

662-
this.isFormDirty.set(true);
673+
this.markCurrentTabAsModified();
663674
}
664675

665676
setPreferentialSupplier(id: string): void {
@@ -670,7 +681,7 @@ export class ProductWorkspaceComponent implements OnInit {
670681
preferred: s.id === id,
671682
})),
672683
}));
673-
this.isFormDirty.set(true);
684+
this.markCurrentTabAsModified();
674685
}
675686

676687
toggleSupplierStatus(id: string): void {
@@ -680,7 +691,7 @@ export class ProductWorkspaceComponent implements OnInit {
680691

681692
toggleTag(tagId: string): void {
682693
if (this.isReadOnly()) return;
683-
this.isFormDirty.set(true);
694+
this.markCurrentTabAsModified();
684695

685696
this.product.update((p) => {
686697
const exists = p.tags.some((t) => t.id === tagId);
@@ -792,15 +803,71 @@ export class ProductWorkspaceComponent implements OnInit {
792803

793804
this.isSaving.set(true);
794805
const id = this.route.snapshot.paramMap.get('id');
795-
const payload = this.productForm();
806+
const fullPayload = this.productForm();
807+
const modified = this.modifiedTabs();
808+
809+
const payload: Partial<UpdateProductPayload> = {};
810+
811+
if (modified.has('geral') || modified.has('classification') || modified.has('logistics') || modified.has('visibility')) {
812+
if (fullPayload.name !== undefined) payload.name = fullPayload.name;
813+
if (fullPayload.slug !== undefined) payload.slug = fullPayload.slug;
814+
if (fullPayload.short_description !== undefined) payload.short_description = fullPayload.short_description;
815+
if (fullPayload.description !== undefined) payload.description = fullPayload.description;
816+
if (fullPayload.category_id !== undefined) payload.category_id = fullPayload.category_id;
817+
if (fullPayload.manufacturer_id !== undefined) payload.manufacturer_id = fullPayload.manufacturer_id;
818+
if (fullPayload.part_origin_id !== undefined) payload.part_origin_id = fullPayload.part_origin_id;
819+
if (fullPayload.status_id !== undefined) payload.status_id = fullPayload.status_id;
820+
if (fullPayload.internal_code !== undefined) payload.internal_code = fullPayload.internal_code;
821+
if (fullPayload.barcode !== undefined) payload.barcode = fullPayload.barcode;
822+
if (fullPayload.unit_id !== undefined) payload.unit_id = fullPayload.unit_id;
823+
if (fullPayload.weight !== undefined) payload.weight = fullPayload.weight;
824+
if (fullPayload.height !== undefined) payload.height = fullPayload.height;
825+
if (fullPayload.width !== undefined) payload.width = fullPayload.width;
826+
if (fullPayload.length !== undefined) payload.length = fullPayload.length;
827+
if (fullPayload.featured !== undefined) payload.featured = fullPayload.featured;
828+
if (fullPayload.active !== undefined) payload.active = fullPayload.active;
829+
}
830+
831+
if (modified.has('comercial')) {
832+
if (fullPayload.price !== undefined) payload.price = fullPayload.price;
833+
}
834+
835+
if (modified.has('inventario')) {
836+
if (fullPayload.inventories !== undefined) payload.inventories = fullPayload.inventories;
837+
}
838+
839+
if (modified.has('oem')) {
840+
if (fullPayload.oem_code_ids !== undefined) payload.oem_code_ids = fullPayload.oem_code_ids;
841+
}
842+
843+
if (modified.has('codigos')) {
844+
if (fullPayload.product_code_ids !== undefined) payload.product_code_ids = fullPayload.product_code_ids;
845+
}
846+
847+
if (modified.has('equivalentes')) {
848+
if (fullPayload.equivalent_product_ids !== undefined) payload.equivalent_product_ids = fullPayload.equivalent_product_ids;
849+
}
850+
851+
if (modified.has('compatibilidade')) { // Veículos/Aplicações
852+
if (fullPayload.application_ids !== undefined) payload.application_ids = fullPayload.application_ids;
853+
}
854+
855+
if (modified.has('fornecimento')) {
856+
if (fullPayload.supplier_ids !== undefined) payload.supplier_ids = fullPayload.supplier_ids;
857+
}
858+
859+
if (modified.has('tags')) {
860+
if (fullPayload.tag_ids !== undefined) payload.tag_ids = fullPayload.tag_ids;
861+
}
796862

797863
// console.log('[SAVE PRODUCT UPDATE]', payload);
798864
// return;
799865

800-
this.productService.update(id!, payload).subscribe({
866+
this.productService.update(id!, payload as UpdateProductPayload).subscribe({
801867
next: (response) => {
802868
this.isSaving.set(false);
803869
this.isFormDirty.set(false);
870+
this.modifiedTabs.set(new Set<FormTab>());
804871
this.toastService.success(response.message, 'Informações atualizadas no sistema.');
805872

806873
const prodData = response.data || response;

0 commit comments

Comments
 (0)