Umsortieren geht per Ziehen und über Pfeilknöpfe: reines Drag & Drop wäre mit Tastatur nicht bedienbar und auf dem Handy fummelig — die Knöpfe sind der verlässliche Weg, das Ziehen der bequeme. Die Erscheinungsreihenfolge lässt sich gar nicht erst anfassen, weil sie sich aus den Daten ergibt.
156 lines
4.7 KiB
TypeScript
156 lines
4.7 KiB
TypeScript
"use server";
|
|
|
|
import { revalidatePath } from "next/cache";
|
|
import { z } from "zod";
|
|
import { getSessionUser } from "@/lib/auth";
|
|
import {
|
|
addCollection,
|
|
addTitle,
|
|
createOrder,
|
|
createUniverse,
|
|
deleteOrder,
|
|
removeTitle,
|
|
renameOrder,
|
|
renameUniverse,
|
|
setOrderPositions,
|
|
shareUniverse,
|
|
trashUniverse,
|
|
unshareUniverse,
|
|
UniverseError,
|
|
} from "@/lib/universes";
|
|
|
|
/** Server-Aktionen für Universen. Zugriffsrechte prüft die Domänenschicht. */
|
|
|
|
export type UniverseActionResult = { ok: boolean; message?: string; id?: string };
|
|
|
|
async function run(
|
|
fn: (userId: string) => Promise<{ message?: string; id?: string } | void>,
|
|
): Promise<UniverseActionResult> {
|
|
const user = await getSessionUser();
|
|
if (!user) return { ok: false, message: "Bitte melde dich an." };
|
|
|
|
try {
|
|
const result = (await fn(user.id)) ?? {};
|
|
revalidatePath("/universen", "layout");
|
|
revalidatePath("/", "layout");
|
|
return { ok: true, message: result.message, id: result.id };
|
|
} catch (error) {
|
|
if (error instanceof UniverseError) return { ok: false, message: error.message };
|
|
console.error("Universums-Aktion fehlgeschlagen:", error);
|
|
return { ok: false, message: "Das hat nicht geklappt. Bitte noch einmal versuchen." };
|
|
}
|
|
}
|
|
|
|
const id = z.string().min(1).max(64);
|
|
const name = z.string().trim().min(1).max(120);
|
|
|
|
export async function createUniverseAction(rawName: string, description?: string) {
|
|
return run(async (userId) => {
|
|
const universe = await createUniverse(userId, name.parse(rawName), description);
|
|
return { message: `„${universe.name}" angelegt`, id: universe.id };
|
|
});
|
|
}
|
|
|
|
export async function renameUniverseAction(
|
|
universeId: string,
|
|
data: { name?: string; description?: string | null },
|
|
) {
|
|
return run(async (userId) => {
|
|
await renameUniverse(userId, id.parse(universeId), {
|
|
...(data.name !== undefined ? { name: name.parse(data.name) } : {}),
|
|
...(data.description !== undefined
|
|
? { description: z.string().max(2000).nullable().parse(data.description) }
|
|
: {}),
|
|
});
|
|
return { message: "Gespeichert" };
|
|
});
|
|
}
|
|
|
|
export async function trashUniverseAction(universeId: string) {
|
|
return run(async (userId) => {
|
|
await trashUniverse(userId, id.parse(universeId));
|
|
return { message: "Universum gelöscht" };
|
|
});
|
|
}
|
|
|
|
export async function addTitleToUniverseAction(universeId: string, titleId: string) {
|
|
return run(async (userId) => {
|
|
await addTitle(userId, id.parse(universeId), id.parse(titleId));
|
|
return { message: "Aufgenommen" };
|
|
});
|
|
}
|
|
|
|
export async function removeFromUniverseAction(universeId: string, itemId: string) {
|
|
return run(async (userId) => {
|
|
await removeTitle(userId, id.parse(universeId), id.parse(itemId));
|
|
return { message: "Aus dem Universum entfernt" };
|
|
});
|
|
}
|
|
|
|
export async function addCollectionAction(universeId: string, tmdbCollectionId: number) {
|
|
return run(async (userId) => {
|
|
const added = await addCollection(
|
|
userId,
|
|
id.parse(universeId),
|
|
z.number().int().parse(tmdbCollectionId),
|
|
);
|
|
return {
|
|
message:
|
|
added === 0
|
|
? "Alles daraus war schon drin"
|
|
: `${added} ${added === 1 ? "Titel" : "Titel"} übernommen`,
|
|
};
|
|
});
|
|
}
|
|
|
|
export async function createOrderAction(universeId: string, rawName: string) {
|
|
return run(async (userId) => {
|
|
const order = await createOrder(userId, id.parse(universeId), name.parse(rawName));
|
|
return { message: "Reihenfolge angelegt", id: order.id };
|
|
});
|
|
}
|
|
|
|
export async function renameOrderAction(orderId: string, rawName: string) {
|
|
return run(async (userId) => {
|
|
await renameOrder(userId, id.parse(orderId), name.parse(rawName));
|
|
return { message: "Umbenannt" };
|
|
});
|
|
}
|
|
|
|
export async function deleteOrderAction(orderId: string) {
|
|
return run(async (userId) => {
|
|
await deleteOrder(userId, id.parse(orderId));
|
|
return { message: "Reihenfolge gelöscht" };
|
|
});
|
|
}
|
|
|
|
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));
|
|
return { message: "Reihenfolge gespeichert" };
|
|
});
|
|
}
|
|
|
|
export async function shareUniverseAction(
|
|
universeId: string,
|
|
email: string,
|
|
permission: "READ" | "WRITE",
|
|
) {
|
|
return run(async (userId) => {
|
|
const target = await shareUniverse(
|
|
userId,
|
|
id.parse(universeId),
|
|
z.string().trim().email().parse(email),
|
|
z.enum(["READ", "WRITE"]).parse(permission),
|
|
);
|
|
return { message: `Mit ${target.name} geteilt` };
|
|
});
|
|
}
|
|
|
|
export async function unshareUniverseAction(universeId: string, memberId: string) {
|
|
return run(async (userId) => {
|
|
await unshareUniverse(userId, id.parse(universeId), id.parse(memberId));
|
|
return { message: "Freigabe aufgehoben" };
|
|
});
|
|
}
|