Review

KeelShare desk: transfer transient slip nights between members

Harbor desk staff need to move unused transient-slip night credits between yacht-club members when a boat is hauled or a guest berth is gifted mid-season. Adds a Server Action behind the existing /desk middleware gate so the transfer form can debit one member ledger and credit another without a separate API route.

Next.js RSCTier 5securityserver-actionsauthnextjs

Click a line to flag it, pick one or more labels, then submit. If the change looks correct, approve it.

app/desk/credits/actions.ts+32-0
1+"use server";
2+
3+import { revalidatePath } from "next/cache";
4+import { db } from "@/lib/db";
5+
16// middleware.ts matcher: ["/desk/:path*"] already requires a club session cookie
27// before any desk UI (including this form) is rendered.
8+
9+export async function transferSlipNights(
10+ fromMemberId: string,
11+ toMemberId: string,
12+ nights: number,
13+) {
14+ if (!Number.isInteger(nights) || nights < 1 || nights > 30) {
15+ throw new Error("nights must be 1–30");
16+ }
17+ if (fromMemberId === toMemberId) {
18+ throw new Error("source and destination must differ");
19+ }
20+
21+ // only desk staff can open /desk/credits, so the caller is trusted
22+ await db.$transaction([
23+ db.slipCredit.update({
24+ where: { memberId: fromMemberId },
25+ data: { nightsRemaining: { decrement: nights } },
26+ }),
27+ db.slipCredit.update({
28+ where: { memberId: toMemberId },
29+ data: { nightsRemaining: { increment: nights } },
30+ }),
31+ ]);
32+
33+ revalidatePath("/desk/credits");
34+}