Reapply "Add missing admin action files and navigation links"
This reverts commit 4d515bc400.
This commit is contained in:
1 parent
96ed768f14
commit
5b4228261a
338 files changed
+28143
-5948
No files matched your search
@@ -1,9 +1,9 @@
|
||||
'use server';
|
||||
"use server";
|
||||
|
||||
import { revalidatePath } from 'next/cache';
|
||||
import { requireStaff } from '@/lib/admin/guard';
|
||||
import { prisma } from '@/lib/prisma';
|
||||
import { logStaffActivity } from '@/lib/services/staff-activity';
|
||||
import { revalidatePath } from "next/cache";
|
||||
import { requireStaff } from "@/lib/admin/guard";
|
||||
import { prisma } from "@/lib/prisma";
|
||||
import { logStaffActivity } from "@/lib/services/staff-activity";
|
||||
|
||||
// AutoDJ playlist CRUD (radio_auto_dj_playlist). CMS-owned table backing the
|
||||
// fallback playlist the radio rotates through when no live DJ is streaming.
|
||||
@@ -13,7 +13,7 @@ import { logStaffActivity } from '@/lib/services/staff-activity';
|
||||
|
||||
/** Parse a FormData field into a positive BigInt id, or null when invalid. */
|
||||
function parseId(raw: FormDataEntryValue | null): bigint | null {
|
||||
if (typeof raw !== 'string' || raw.trim() === '') return null;
|
||||
if (typeof raw !== "string" || raw.trim() === "") return null;
|
||||
try {
|
||||
const id = BigInt(raw.trim());
|
||||
return id > 0n ? id : null;
|
||||
@@ -23,13 +23,13 @@ function parseId(raw: FormDataEntryValue | null): bigint | null {
|
||||
}
|
||||
|
||||
function str(raw: FormDataEntryValue | null): string {
|
||||
return typeof raw === 'string' ? raw : '';
|
||||
return typeof raw === "string" ? raw : "";
|
||||
}
|
||||
|
||||
/** Checkbox/select truthiness: '1', 'true', 'on' → true. */
|
||||
function bool(raw: FormDataEntryValue | null): boolean {
|
||||
const v = str(raw).trim().toLowerCase();
|
||||
return v === '1' || v === 'true' || v === 'on';
|
||||
return v === "1" || v === "true" || v === "on";
|
||||
}
|
||||
|
||||
/** Parse a non-negative UnsignedInt, falling back to 0. */
|
||||
@@ -42,7 +42,7 @@ function reqUInt(raw: FormDataEntryValue | null): number {
|
||||
/** Parse an optional non-negative UnsignedInt; blank/invalid/negative → null. */
|
||||
function optUInt(raw: FormDataEntryValue | null): number | null {
|
||||
const s = str(raw).trim();
|
||||
if (s === '') return null;
|
||||
if (s === "") return null;
|
||||
const n = Number(s);
|
||||
if (!Number.isFinite(n) || n < 0) return null;
|
||||
return Math.trunc(n);
|
||||
@@ -52,15 +52,15 @@ function optUInt(raw: FormDataEntryValue | null): number | null {
|
||||
|
||||
export async function createTrack(formData: FormData): Promise<void> {
|
||||
const staff = await requireStaff();
|
||||
const title = str(formData.get('title')).trim().slice(0, 255);
|
||||
const title = str(formData.get("title")).trim().slice(0, 255);
|
||||
if (!title) return;
|
||||
|
||||
const artist = str(formData.get('artist')).trim().slice(0, 255);
|
||||
const album = str(formData.get('album')).trim().slice(0, 255);
|
||||
const artworkUrl = str(formData.get('artworkUrl')).trim().slice(0, 255);
|
||||
const duration = optUInt(formData.get('duration'));
|
||||
const sortOrder = reqUInt(formData.get('sortOrder'));
|
||||
const isActive = bool(formData.get('isActive'));
|
||||
const artist = str(formData.get("artist")).trim().slice(0, 255);
|
||||
const album = str(formData.get("album")).trim().slice(0, 255);
|
||||
const artworkUrl = str(formData.get("artworkUrl")).trim().slice(0, 255);
|
||||
const duration = optUInt(formData.get("duration"));
|
||||
const sortOrder = reqUInt(formData.get("sortOrder"));
|
||||
const isActive = bool(formData.get("isActive"));
|
||||
const now = new Date();
|
||||
|
||||
try {
|
||||
@@ -79,24 +79,24 @@ export async function createTrack(formData: FormData): Promise<void> {
|
||||
});
|
||||
await logStaffActivity({
|
||||
staffId: staff.id,
|
||||
action: 'radio_autodj_create',
|
||||
description: `Created AutoDJ track "${title}"${artist ? ` by ${artist}` : ''}`,
|
||||
targetType: 'radio_auto_dj_track',
|
||||
action: "radio_autodj_create",
|
||||
description: `Created AutoDJ track "${title}"${artist ? ` by ${artist}` : ""}`,
|
||||
targetType: "radio_auto_dj_track",
|
||||
targetId: Number(created.id),
|
||||
});
|
||||
} catch {
|
||||
// Fail soft — DB unavailable; re-render without throwing.
|
||||
}
|
||||
revalidatePath('/admin/radio/autodj');
|
||||
revalidatePath("/admin/radio/autodj");
|
||||
}
|
||||
|
||||
export async function toggleTrack(formData: FormData): Promise<void> {
|
||||
const staff = await requireStaff();
|
||||
const id = parseId(formData.get('id'));
|
||||
const id = parseId(formData.get("id"));
|
||||
if (id === null) return;
|
||||
|
||||
// The form posts the desired next state so the toggle is idempotent.
|
||||
const isActive = bool(formData.get('isActive'));
|
||||
const isActive = bool(formData.get("isActive"));
|
||||
|
||||
try {
|
||||
await prisma.radioAutoDjPlaylist.update({
|
||||
@@ -105,33 +105,33 @@ export async function toggleTrack(formData: FormData): Promise<void> {
|
||||
});
|
||||
await logStaffActivity({
|
||||
staffId: staff.id,
|
||||
action: 'radio_autodj_toggle',
|
||||
description: `${isActive ? 'Activated' : 'Deactivated'} AutoDJ track #${id}`,
|
||||
targetType: 'radio_auto_dj_track',
|
||||
action: "radio_autodj_toggle",
|
||||
description: `${isActive ? "Activated" : "Deactivated"} AutoDJ track #${id}`,
|
||||
targetType: "radio_auto_dj_track",
|
||||
targetId: Number(id),
|
||||
});
|
||||
} catch {
|
||||
// Row may be gone; ignore.
|
||||
}
|
||||
revalidatePath('/admin/radio/autodj');
|
||||
revalidatePath("/admin/radio/autodj");
|
||||
}
|
||||
|
||||
export async function deleteTrack(formData: FormData): Promise<void> {
|
||||
const staff = await requireStaff();
|
||||
const id = parseId(formData.get('id'));
|
||||
const id = parseId(formData.get("id"));
|
||||
if (id === null) return;
|
||||
|
||||
try {
|
||||
await prisma.radioAutoDjPlaylist.delete({ where: { id } });
|
||||
await logStaffActivity({
|
||||
staffId: staff.id,
|
||||
action: 'radio_autodj_delete',
|
||||
action: "radio_autodj_delete",
|
||||
description: `Deleted AutoDJ track #${id}`,
|
||||
targetType: 'radio_auto_dj_track',
|
||||
targetType: "radio_auto_dj_track",
|
||||
targetId: Number(id),
|
||||
});
|
||||
} catch {
|
||||
// Already deleted; ignore.
|
||||
}
|
||||
revalidatePath('/admin/radio/autodj');
|
||||
revalidatePath("/admin/radio/autodj");
|
||||
}
|
||||
Reference in new issue
Block a user