diff --git a/artifacts/api-server/src/routes/services.ts b/artifacts/api-server/src/routes/services.ts index 76c6ccc9..a3ded34a 100644 --- a/artifacts/api-server/src/routes/services.ts +++ b/artifacts/api-server/src/routes/services.ts @@ -79,7 +79,7 @@ router.get("/service-categories", requireAuth, async (_req, res): Promise // catalogue never observes a partially-applied order. Declared BEFORE // the `/services/:id` routes so Express doesn't try to parse "reorder" // as a numeric id and return a 400 from GetServiceParams. -router.post("/services/reorder", requireAdmin, async (req, res): Promise => { +router.patch("/services/reorder", requireAdmin, async (req, res): Promise => { const parsed = ReorderServicesBody.safeParse(req.body); if (!parsed.success) { res.status(400).json({ error: parsed.error.message }); diff --git a/artifacts/tx-os/src/pages/services.tsx b/artifacts/tx-os/src/pages/services.tsx index ee390e47..53b3135e 100644 --- a/artifacts/tx-os/src/pages/services.tsx +++ b/artifacts/tx-os/src/pages/services.tsx @@ -72,6 +72,7 @@ type SortableServiceCardProps = { lang: string; editMode: boolean; isDragging: boolean; + canEnterEditMode: boolean; onTapInView: () => void; onLongPress: () => void; unavailableLabel: string; @@ -82,6 +83,7 @@ function SortableServiceCard({ lang, editMode, isDragging, + canEnterEditMode, onTapInView, onLongPress, unavailableLabel, @@ -114,7 +116,7 @@ function SortableServiceCard({ }; const handlePointerDown = (e: React.PointerEvent) => { - if (editMode) return; + if (editMode || !canEnterEditMode) return; longPressed.current = false; pressStart.current = { x: e.clientX, y: e.clientY }; pressTimer.current = window.setTimeout(() => { @@ -316,6 +318,9 @@ export default function ServicesPage() { }; const onLongPress = () => { + // Reordering is admin-only — silently ignore long-press for everyone + // else so non-admins never see a jiggle UI they can't persist. + if (!isAdmin) return; if (!editMode) setEditMode(true); }; @@ -436,6 +441,7 @@ export default function ServicesPage() { lang={lang} editMode={editMode} isDragging={activeDragId === service.id} + canEnterEditMode={isAdmin} onTapInView={() => { if (!service.isAvailable) return; setOrderTarget({ diff --git a/lib/api-client-react/src/generated/api.ts b/lib/api-client-react/src/generated/api.ts index d2b9873d..76ed1394 100644 --- a/lib/api-client-react/src/generated/api.ts +++ b/lib/api-client-react/src/generated/api.ts @@ -2680,7 +2680,7 @@ export const reorderServices = async ( ): Promise => { return customFetch(getReorderServicesUrl(), { ...options, - method: "POST", + method: "PATCH", headers: { "Content-Type": "application/json", ...options?.headers }, body: JSON.stringify(reorderServicesBody), }); diff --git a/lib/api-spec/openapi.yaml b/lib/api-spec/openapi.yaml index e8a3f6f6..e4df9543 100644 --- a/lib/api-spec/openapi.yaml +++ b/lib/api-spec/openapi.yaml @@ -731,7 +731,7 @@ paths: $ref: "#/components/schemas/Service" /services/reorder: - post: + patch: operationId: reorderServices tags: [services] summary: Replace the global service sort order (admin)