From aa308d8258ebe62e4c324c3ac02184edcae173e4 Mon Sep 17 00:00:00 2001 From: sdarbinyan Date: Fri, 17 Jul 2026 22:16:12 +0400 Subject: [PATCH] fix(admin-categories): fix drag-reorder assigning duplicate order values instead of a real position swap Bug: AdminCategoriesFacade.reorder(id, targetOrder) took the target row's numeric order and wrote it straight onto the dragged category (order: targetOrder). That leaves two siblings tied on the same order value instead of actually repositioning the dragged item - and since the local gateway's list sort (Array.prototype.sort, stable) breaks ties by original array position, drops in certain directions have no visible effect at all. All seeded categories additionally start at order: 0 (AdminCategoriesLocalGateway.toAdminCategory), so on fresh data literally every drag silently no-ops. Fix: reorder(id, targetId) now takes the target category's id (not its order value, which can be ambiguous/duplicated), computes the full sibling sequence with the dragged item spliced into the target's position, and persists sequential 0..n-1 order values for every sibling whose order actually changed. Restricted to same-parent siblings (dragged.parentId !== target.parentId is a no-op, matching the tree UI's existing scope - no cross-parent move support). Updated the drag payload end to end: AdminCategoriesListComponent's `reorder` output now emits { id, targetId } instead of { id, targetOrder }; the list page binding follows. Verified live via window.ng.getComponent() on /ru/backoffice/categories (devBypassAdmin=true; real backend unreachable in this environment, so verified against facade.categories.set([...]) synthetic siblings, consistent with the gateway calls the facade actually issues): - Before fix: 3 siblings order 0/1/2, drag 'c' onto 'a' -> gateway.updateCategory received only { id: 'c', order: 0 }, tying 'a' and 'c' at order 0 (with 0-order seed data, no siblings ever become distinguishable at all). - After fix: same drag -> gateway.updateCategory called for 'c', 'a', 'b' with the correct distinct sequence (c:0, a:1, b:2). --- .../admin-categories-list.component.ts | 4 +-- .../facade/admin-categories.facade.ts | 26 +++++++++++++++---- .../admin-categories-list-page.component.ts | 2 +- 3 files changed, 24 insertions(+), 8 deletions(-) diff --git a/src/app/features/admin/categories/components/admin-categories-list.component.ts b/src/app/features/admin/categories/components/admin-categories-list.component.ts index 3b1dfb8..c2a27f6 100644 --- a/src/app/features/admin/categories/components/admin-categories-list.component.ts +++ b/src/app/features/admin/categories/components/admin-categories-list.component.ts @@ -33,7 +33,7 @@ export class AdminCategoriesListComponent { @Output() delete = new EventEmitter(); @Output() restore = new EventEmitter(); @Output() toggleVisible = new EventEmitter<{ id: string; visible: boolean }>(); - @Output() reorder = new EventEmitter<{ id: string; targetOrder: number }>(); + @Output() reorder = new EventEmitter<{ id: string; targetId: string }>(); private draggedId: string | null = null; @@ -50,7 +50,7 @@ export class AdminCategoriesListComponent { this.draggedId = null; return; } - this.reorder.emit({ id: this.draggedId, targetOrder: targetRow.category.order }); + this.reorder.emit({ id: this.draggedId, targetId: targetRow.category.id }); this.draggedId = null; } } diff --git a/src/app/features/admin/categories/facade/admin-categories.facade.ts b/src/app/features/admin/categories/facade/admin-categories.facade.ts index 61e6c42..689bcea 100644 --- a/src/app/features/admin/categories/facade/admin-categories.facade.ts +++ b/src/app/features/admin/categories/facade/admin-categories.facade.ts @@ -165,10 +165,26 @@ export class AdminCategoriesFacade { .subscribe({ next: () => this.loadList() }); } - reorder(id: string, targetOrder: number): void { - const category = this.categories().find(item => item.id === id); - if (!category) return; - this.gateway.updateCategory({ ...category, order: targetOrder, updatedAt: new Date().toISOString() }).pipe(take(1)) - .subscribe({ next: () => this.loadList() }); + reorder(id: string, targetId: string): void { + const dragged = this.categories().find(item => item.id === id); + const target = this.categories().find(item => item.id === targetId); + if (!dragged || !target || dragged.parentId !== target.parentId || dragged.id === target.id) return; + + const siblings = this.categories() + .filter(item => item.parentId === dragged.parentId) + .sort((left, right) => left.order - right.order); + const withoutDragged = siblings.filter(item => item.id !== id); + const targetIndex = withoutDragged.findIndex(item => item.id === targetId); + withoutDragged.splice(targetIndex, 0, dragged); + + const now = new Date().toISOString(); + const updates = withoutDragged + .map((item, index) => ({ item, order: index })) + .filter(({ item, order }) => item.order !== order) + .map(({ item, order }) => ({ ...item, order, updatedAt: now })); + + if (updates.length === 0) return; + updates.forEach(category => this.gateway.updateCategory(category).pipe(take(1)).subscribe()); + this.loadList(); } } diff --git a/src/app/features/admin/categories/pages/admin-categories-list-page.component.ts b/src/app/features/admin/categories/pages/admin-categories-list-page.component.ts index 69e1884..22a6abd 100644 --- a/src/app/features/admin/categories/pages/admin-categories-list-page.component.ts +++ b/src/app/features/admin/categories/pages/admin-categories-list-page.component.ts @@ -20,7 +20,7 @@ import { TranslateService } from '../../../../i18n/translate.service'; (delete)="deleteOne($event)" (restore)="facade.restoreOne($event)" (toggleVisible)="facade.setVisible($event.id, $event.visible)" - (reorder)="facade.reorder($event.id, $event.targetOrder)" />`, + (reorder)="facade.reorder($event.id, $event.targetId)" />`, changeDetection: ChangeDetectionStrategy.OnPush }) export class AdminCategoriesListPageComponent {