feat(qualification-criteria): dnd reordering with whole-list PATCH

The chevron up/down buttons rewrote a single row's display_order,
which didn't actually swap positions since the neighbouring rows kept
their original orders. Replaced with a proper drag-handle (dnd-kit
sortable, matching the waiting-list-manager pattern) backed by a new
POST /admin/qualification-criteria/reorder endpoint that rewrites
display_order = index for every row in a transaction. The service
rejects partial / extraneous id lists so a stale UI can't silently
drop a criterion. Optimistic local-cache update keeps the row in
position during the round-trip; rollback on error.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-14 03:49:17 +02:00
parent 905852b8a5
commit 233129f91a
4 changed files with 228 additions and 59 deletions

View File

@@ -0,0 +1,24 @@
import { NextResponse } from 'next/server';
import { withAuth, withPermission } from '@/lib/api/helpers';
import { parseBody } from '@/lib/api/route-helpers';
import { errorResponse } from '@/lib/errors';
import { reorderQualificationCriteriaSchema } from '@/lib/validators/qualification';
import { reorderCriteria } from '@/lib/services/qualification.service';
export const POST = withAuth(
withPermission('admin', 'manage_settings', async (req, ctx) => {
try {
const body = await parseBody(req, reorderQualificationCriteriaSchema);
const rows = await reorderCriteria(ctx.portId, body, {
userId: ctx.userId,
portId: ctx.portId,
ipAddress: ctx.ipAddress,
userAgent: ctx.userAgent,
});
return NextResponse.json({ data: rows });
} catch (error) {
return errorResponse(error);
}
}),
);