54 lines
1.5 KiB
TypeScript
54 lines
1.5 KiB
TypeScript
"use server";
|
|
|
|
import { and, eq } from "drizzle-orm";
|
|
import { revalidateTag } from "next/cache";
|
|
import { z } from "zod";
|
|
import { db, UserWatch } from "@/lib/db";
|
|
import { PERMS } from "@/lib/permission-slugs";
|
|
import { adminAction } from "@/lib/safe-action";
|
|
import { actionOk } from "@/lib/safe-action-shared";
|
|
|
|
const toggleWatchSchema = z.object({
|
|
targetUserId: z.coerce.number().int().positive(),
|
|
reason: z.string().max(255).optional(),
|
|
});
|
|
|
|
/**
|
|
* Toggle a watch entry for the calling staff on the given target user.
|
|
* If a row already exists it's removed; otherwise it's created with the
|
|
* provided reason (defaulting to empty). Returns the resulting state so
|
|
* the UI can flip the badge without re-fetching.
|
|
*/
|
|
export const toggleUserWatch = adminAction(
|
|
{ permission: PERMS.USERS_VIEW, schema: toggleWatchSchema },
|
|
async (ctx) => {
|
|
const staffId = ctx.session.user.id;
|
|
const { targetUserId, reason } = ctx.data;
|
|
|
|
const [existing] = await db
|
|
.select({ id: UserWatch.id })
|
|
.from(UserWatch)
|
|
.where(
|
|
and(
|
|
eq(UserWatch.staffId, staffId),
|
|
eq(UserWatch.targetUserId, targetUserId),
|
|
),
|
|
)
|
|
.limit(1);
|
|
|
|
if (existing) {
|
|
await db.delete(UserWatch).where(eq(UserWatch.id, existing.id));
|
|
revalidateTag(`user-watch:${staffId}`, { expire: 0 });
|
|
return actionOk({ watching: false });
|
|
}
|
|
|
|
await db.insert(UserWatch).values({
|
|
staffId,
|
|
targetUserId,
|
|
reason: reason ?? "",
|
|
});
|
|
revalidateTag(`user-watch:${staffId}`, { expire: 0 });
|
|
return actionOk({ watching: true });
|
|
},
|
|
);
|