feat(projects): drag the sidebar into the order you want

Projects have carried sort_order since the first migration and nothing
ever set it, so the sidebar was ordered by creation date for good.

POST /projects/reorder takes the whole list rather than one move. A
person has a handful of projects, so sending the order you want is
simpler to reason about than midpoints between neighbours — and it cannot
run out of precision, which is the failure the task ordering has to
respace a section to recover from. It also cannot land half-applied the
way a sequence of individual moves can.

Projects you do not own are skipped rather than refused. sort_order is a
column on the project, not a preference per viewer, so a shared project
sits where its owner put it and a member rearranging their own sidebar
does not rearrange somebody else's. The sidebar only makes your own
projects draggable, so that rule never shows up as a row that springs
back.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Kristian Wind
2026-08-17 15:59:31 +02:00
committed by Kristian Wind
co-authored by Claude Opus 5
parent 3f4af6c0ab
commit c4e9e1519b
8 changed files with 280 additions and 1 deletions
+29
View File
@@ -756,6 +756,35 @@ paths:
"201": { $ref: "#/components/responses/Project" }
"422": { $ref: "#/components/responses/Validation" }
/api/v1/projects/reorder:
post:
tags: [projects]
summary: Set the order projects appear in.
description: |
The whole list at once rather than one move at a time. A person has a
handful of projects, so sending the order you want is simpler than
midpoints and cannot land half-applied.
Projects you do not own are skipped: `sort_order` is a column on the
project rather than a preference per viewer, so a shared one stays where
its owner put it.
requestBody:
required: true
content:
application/json:
schema:
type: object
required: [ids]
properties:
ids:
type: array
maxItems: 500
items: { type: string }
description: Project ids, top to bottom.
responses:
"204": { description: Reordered. }
"422": { $ref: "#/components/responses/Validation" }
/api/v1/projects/{projectID}:
parameters:
- { name: projectID, in: path, required: true, schema: { type: string } }