Drag-to-reorder service cards (iOS jiggle mode)

Long-press a service tile (~500 ms) → grid enters edit mode: every
card jiggles, a floating "تم / Done" pill appears, and cards become
draggable via @dnd-kit. Drop on another tile to reorder. Tap Done or
outside the grid to persist; the new order is shared globally via
servicesTable.sortOrder.

Backend
- POST /api/services/reorder (admin-gated, requireAdmin)
- Validates full-set match (no missing/dup/unknown IDs)
- Transactional sortOrder rewrite — no partial writes
- Route registered before /services/:id to avoid path-param collision
- OpenAPI op + ReorderServicesBody Zod schema → regenerated client

Frontend
- artifacts/tx-os/src/pages/services.tsx rewritten
- PointerSensor (distance 6) + TouchSensor (delay 150 ms) — long-press
  and tap-to-open stay distinct
- touch-none only applied while in edit mode so normal scrolling is
  unaffected outside it
- exitEditMode awaits mutation + invalidate before flipping editMode,
  preventing snap-back from stale react-query cache
- exitingRef guard against duplicate exit calls (Done button +
  outside-tap handler firing for the same gesture)
- Non-admins can enter edit mode (haptic affordance) but local reorder
  is silently reverted on exit
- i18n: services.editMode.{done,hint,saveFailed} added to ar.json/en.json

Pre-existing unrelated typecheck errors in push.ts and
executive-meeting-font-settings.ts are not touched by this change.
This commit is contained in:
riyadhafraa
2026-05-20 11:51:28 +00:00
parent f02305fe9f
commit 376918f983
8 changed files with 622 additions and 43 deletions
+45
View File
@@ -730,6 +730,33 @@ paths:
schema:
$ref: "#/components/schemas/Service"
/services/reorder:
post:
operationId: reorderServices
tags: [services]
summary: Replace the global service sort order (admin)
description: |
Persists a new global `sortOrder` for the service catalogue by
applying the index of each id in `orderedIds`. Admin-only.
The body must contain every existing service id exactly once;
partial reorders are rejected with 400 so the client can never
accidentally drop services from the sort plan.
requestBody:
required: true
content:
application/json:
schema:
$ref: "#/components/schemas/ReorderServicesBody"
responses:
"204":
description: Sort order updated
"400":
description: Validation error or mismatched id set
content:
application/json:
schema:
$ref: "#/components/schemas/ErrorResponse"
/services/{id}:
get:
operationId: getService
@@ -4031,6 +4058,24 @@ components:
sortOrder:
type: integer
ReorderServicesBody:
type: object
description: |
New global service order. `orderedIds` must contain every
existing service id exactly once. The server sets each
service's `sortOrder` to its position in the list (0-based),
so a later GET /services returns them in the same order.
properties:
orderedIds:
type: array
minItems: 1
maxItems: 1000
items:
type: integer
minimum: 1
required:
- orderedIds
ServiceCategory:
type: object
properties: