diff --git a/docs/API.md b/docs/API.md index 4297673..2e3ff6e 100644 --- a/docs/API.md +++ b/docs/API.md @@ -145,15 +145,28 @@ Liefert `totals`, `monthly`, `providers`, `genres` und `yearReview` auf einmal. ### Universen ```http -GET /api/v1/universen -POST /api/v1/universen -GET /api/v1/universen/{id}?reihenfolge={orderId} -POST /api/v1/universen/{id} +GET /api/v1/universen +POST /api/v1/universen +GET /api/v1/universen/{id}?reihenfolge={orderId} +POST /api/v1/universen/{id} +PATCH /api/v1/universen/{id} ``` `GET /api/v1/universen/{id}` gibt die Einträge in der gewählten Reihenfolge zurück, dazu `progress` und `nextUp`. Ohne `reihenfolge` gilt die -Erscheinungsreihenfolge. +Standardreihenfolge des Universums — in `orders` trägt sie `isDefault`. + +`PATCH` legt fest, welche das ist. Die Reihenfolge muss zu diesem Universum +gehören, und es braucht Schreibrecht: + +```bash +curl -X PATCH -H "Authorization: Bearer $KEY" -H "content-type: application/json" \ + -d '{"defaultOrderId": "…"}' \ + https://flimmer.example.com/api/v1/universen/$ID +``` + +Je Universum trägt immer genau eine Reihenfolge den Standard. Wird die +Standardreihenfolge gelöscht, übernimmt die Erscheinungsreihenfolge. ### Export diff --git a/src/app/actions/universes.ts b/src/app/actions/universes.ts index 14e5613..5c9a18d 100644 --- a/src/app/actions/universes.ts +++ b/src/app/actions/universes.ts @@ -12,6 +12,7 @@ import { removeTitle, renameOrder, renameUniverse, + setDefaultOrder, setOrderPositions, shareUniverse, trashUniverse, @@ -124,6 +125,13 @@ export async function deleteOrderAction(orderId: string) { }); } +export async function setDefaultOrderAction(orderId: string) { + return run(async (userId) => { + await setDefaultOrder(userId, id.parse(orderId)); + return { message: "Standardreihenfolge gesetzt" }; + }); +} + export async function reorderAction(orderId: string, itemIds: string[]) { return run(async (userId) => { await setOrderPositions(userId, id.parse(orderId), z.array(id).max(1000).parse(itemIds)); diff --git a/src/app/api/v1/openapi.json/route.ts b/src/app/api/v1/openapi.json/route.ts index 9d61d71..39758e2 100644 --- a/src/app/api/v1/openapi.json/route.ts +++ b/src/app/api/v1/openapi.json/route.ts @@ -287,6 +287,9 @@ export function GET() { parameters: [{ name: "id", in: "path", required: true, schema: { type: "string" } }], get: { summary: "Universum mit Reihenfolge und Fortschritt", + description: + "Ohne 'reihenfolge' gilt die Standardreihenfolge des Universums — sie trägt in " + + "'orders' das Kennzeichen 'isDefault'.", parameters: [{ name: "reihenfolge", in: "query", schema: { type: "string" } }], responses: { "200": { description: "Universum" }, "404": { description: "Unbekannt" } }, }, @@ -306,6 +309,33 @@ export function GET() { }, responses: { "200": { description: "Aufgenommen" } }, }, + patch: { + summary: "Standardreihenfolge festlegen", + description: + "Bestimmt, welche Reihenfolge beim Öffnen erscheint. Braucht Schreibrecht; je " + + "Universum trägt immer genau eine Reihenfolge den Standard.", + requestBody: { + required: true, + content: { + "application/json": { + schema: { + type: "object", + properties: { + defaultOrderId: { + type: "string", + description: "Muss eine Reihenfolge dieses Universums sein.", + }, + }, + required: ["defaultOrderId"], + }, + }, + }, + }, + responses: { + "200": { description: "Gesetzt" }, + "404": { description: "Die Reihenfolge gehört nicht zu diesem Universum" }, + }, + }, }, "/export": { get: { diff --git a/src/app/api/v1/universen/[id]/route.ts b/src/app/api/v1/universen/[id]/route.ts index f670157..d39580d 100644 --- a/src/app/api/v1/universen/[id]/route.ts +++ b/src/app/api/v1/universen/[id]/route.ts @@ -1,6 +1,7 @@ import { z } from "zod"; import { apiError, readJson, withApi } from "@/lib/api"; -import { addTitle, getUniverse } from "@/lib/universes"; +import { prisma } from "@/lib/db"; +import { addTitle, getUniverse, setDefaultOrder } from "@/lib/universes"; /** GET /api/v1/universen/{id}?reihenfolge=… — Inhalt und Fortschritt. */ export const GET = withApi(async (request, user, context) => { @@ -25,3 +26,25 @@ export const POST = withApi(async (request, user, context) => { await addTitle(user.id, id, body.titleId); return { ok: true }; }); + +const patchSchema = z.object({ defaultOrderId: z.string().min(1) }); + +/** PATCH /api/v1/universen/{id} — festlegen, welche Reihenfolge beim Öffnen erscheint. */ +export const PATCH = withApi(async (request, user, context) => { + const { id } = await context.params; + if (!id) return apiError("Es fehlt die Kennung des Universums.", 400); + + const body = await readJson(request, patchSchema); + + // Die Reihenfolge muss zu genau diesem Universum gehören. Ohne diese Prüfung + // setzte ein PATCH auf Universum A den Standard in Universum B — die + // Domänenschicht leitet das Universum ja aus der Reihenfolge ab. + const order = await prisma.universeOrder.findFirst({ + where: { id: body.defaultOrderId, universeId: id }, + select: { id: true }, + }); + if (!order) return apiError("Diese Reihenfolge gehört nicht zu diesem Universum.", 404); + + await setDefaultOrder(user.id, order.id); + return { ok: true }; +}); diff --git a/src/components/universes/UniverseTools.tsx b/src/components/universes/UniverseTools.tsx index 2438537..a711c97 100644 --- a/src/components/universes/UniverseTools.tsx +++ b/src/components/universes/UniverseTools.tsx @@ -9,6 +9,7 @@ import { createOrderAction, createUniverseAction, deleteOrderAction, + setDefaultOrderAction, shareUniverseAction, trashUniverseAction, unshareUniverseAction, @@ -109,6 +110,24 @@ export function OrderTabs({ style={isActive ? { backgroundImage: "var(--fl-gradient)" } : undefined} > {order.name} + {order.isDefault && ( + <> + {/* Häkchen an der Reihenfolge, die beim Öffnen erscheint. Das + Wort dahinter steht nur für Screenreader — sichtbar würde + es die Reiter auseinanderziehen. */} + + + + (Standard) + + )} ); })} @@ -158,6 +177,21 @@ export function OrderTabs({ )} + {active && !active.isDefault && ( + + )} + {active && active.kind === "CUSTOM" && (