mirror of
https://github.com/trafficlunar/tomodachi-share.git
synced 2026-05-13 13:17:45 +00:00
feat: allow admins to accept miis in queue
This commit is contained in:
parent
147d005a14
commit
12c0205bf5
5 changed files with 60 additions and 10 deletions
|
|
@ -8,6 +8,7 @@ import ControlCenter from "@/components/admin/control-center";
|
||||||
import RegenerateImagesButton from "@/components/admin/regenerate-images";
|
import RegenerateImagesButton from "@/components/admin/regenerate-images";
|
||||||
import UserManagement from "@/components/admin/user-management";
|
import UserManagement from "@/components/admin/user-management";
|
||||||
import Reports from "@/components/admin/reports";
|
import Reports from "@/components/admin/reports";
|
||||||
|
import MiiList from "@/components/mii/list";
|
||||||
|
|
||||||
export const metadata: Metadata = {
|
export const metadata: Metadata = {
|
||||||
title: "Admin - TomodachiShare",
|
title: "Admin - TomodachiShare",
|
||||||
|
|
@ -18,7 +19,11 @@ export const metadata: Metadata = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export default async function AdminPage() {
|
interface Props {
|
||||||
|
searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default async function AdminPage({ searchParams }: Props) {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
|
|
||||||
if (!session || Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) redirect("/404");
|
if (!session || Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) redirect("/404");
|
||||||
|
|
@ -66,6 +71,14 @@ export default async function AdminPage() {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Reports />
|
<Reports />
|
||||||
|
|
||||||
|
{/* Queue */}
|
||||||
|
<div className="flex items-center gap-4 text-zinc-500 text-sm font-medium my-1">
|
||||||
|
<hr className="grow border-zinc-300" />
|
||||||
|
<span>Reports</span>
|
||||||
|
<hr className="grow border-zinc-300" />
|
||||||
|
</div>
|
||||||
|
<MiiList parentPage="admin" searchParams={await searchParams} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
src/app/api/admin/accept-mii/route.ts
Normal file
29
src/app/api/admin/accept-mii/route.ts
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
import { NextRequest, NextResponse } from "next/server";
|
||||||
|
|
||||||
|
import { auth } from "@/lib/auth";
|
||||||
|
import { prisma } from "@/lib/prisma";
|
||||||
|
import { idSchema } from "@/lib/schemas";
|
||||||
|
|
||||||
|
export async function PATCH(request: NextRequest) {
|
||||||
|
const session = await auth();
|
||||||
|
if (!session) return NextResponse.json({ error: "Unauthorized" }, { status: 401 });
|
||||||
|
|
||||||
|
if (Number(session.user?.id) !== Number(process.env.NEXT_PUBLIC_ADMIN_USER_ID)) return NextResponse.json({ error: "Forbidden" }, { status: 403 });
|
||||||
|
|
||||||
|
const searchParams = request.nextUrl.searchParams;
|
||||||
|
const parsedMiiId = idSchema.safeParse(searchParams.get("id"));
|
||||||
|
|
||||||
|
if (!parsedMiiId.success) return NextResponse.json({ error: parsedMiiId.error.issues[0].message }, { status: 400 });
|
||||||
|
const miiId = parsedMiiId.data;
|
||||||
|
|
||||||
|
await prisma.mii.update({
|
||||||
|
where: {
|
||||||
|
id: miiId,
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
in_queue: false,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
return NextResponse.json({ success: true });
|
||||||
|
}
|
||||||
|
|
@ -37,7 +37,7 @@ export default async function ProfileSettingsPage({ searchParams }: Props) {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Suspense fallback={<Skeleton />}>
|
<Suspense fallback={<Skeleton />}>
|
||||||
<MiiList inLikesPage searchParams={await searchParams} />
|
<MiiList parentPage="likes" searchParams={await searchParams} />
|
||||||
</Suspense>
|
</Suspense>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -15,10 +15,10 @@ import MiiGrid from "./mii-grid";
|
||||||
interface Props {
|
interface Props {
|
||||||
searchParams: { [key: string]: string | string[] | undefined };
|
searchParams: { [key: string]: string | string[] | undefined };
|
||||||
userId?: number; // Profiles
|
userId?: number; // Profiles
|
||||||
inLikesPage?: boolean; // Self-explanatory
|
parentPage?: "likes" | "admin";
|
||||||
}
|
}
|
||||||
|
|
||||||
export default async function MiiList({ searchParams, userId, inLikesPage }: Props) {
|
export default async function MiiList({ searchParams, userId, parentPage }: Props) {
|
||||||
const session = await auth();
|
const session = await auth();
|
||||||
const parsed = searchSchema.safeParse(searchParams);
|
const parsed = searchSchema.safeParse(searchParams);
|
||||||
if (!parsed.success) return <h1>{parsed.error.issues[0].message}</h1>;
|
if (!parsed.success) return <h1>{parsed.error.issues[0].message}</h1>;
|
||||||
|
|
@ -28,7 +28,7 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
|
||||||
// My Likes page
|
// My Likes page
|
||||||
let miiIdsLiked: number[] | undefined = undefined;
|
let miiIdsLiked: number[] | undefined = undefined;
|
||||||
|
|
||||||
if (inLikesPage && session?.user?.id) {
|
if (parentPage === "likes" && session?.user?.id) {
|
||||||
const likedMiis = await prisma.like.findMany({
|
const likedMiis = await prisma.like.findMany({
|
||||||
where: { userId: Number(session.user.id) },
|
where: { userId: Number(session.user.id) },
|
||||||
select: { miiId: true },
|
select: { miiId: true },
|
||||||
|
|
@ -37,9 +37,9 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
|
||||||
}
|
}
|
||||||
|
|
||||||
const where: Prisma.MiiWhereInput = {
|
const where: Prisma.MiiWhereInput = {
|
||||||
in_queue: false,
|
in_queue: parentPage === "admin",
|
||||||
// Only show liked miis on likes page
|
// Only show liked miis on likes page
|
||||||
...(inLikesPage && miiIdsLiked && { id: { in: miiIdsLiked } }),
|
...(parentPage === "likes" && miiIdsLiked && { id: { in: miiIdsLiked } }),
|
||||||
// Searching
|
// Searching
|
||||||
...(query && {
|
...(query && {
|
||||||
OR: [{ name: { contains: query, mode: "insensitive" } }, { tags: { has: query } }, { description: { contains: query, mode: "insensitive" } }],
|
OR: [{ name: { contains: query, mode: "insensitive" } }, { tags: { has: query } }, { description: { contains: query, mode: "insensitive" } }],
|
||||||
|
|
@ -184,7 +184,7 @@ export default async function MiiList({ searchParams, userId, inLikesPage }: Pro
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<MiiGrid miis={miis} userId={userId} />
|
<MiiGrid miis={miis} userId={userId} parentPage={parentPage} />
|
||||||
<Pagination lastPage={lastPage} />
|
<Pagination lastPage={lastPage} />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -13,11 +13,12 @@ import Carousel from "@/components/carousel";
|
||||||
interface Props {
|
interface Props {
|
||||||
miis: Prisma.MiiGetPayload<{ include: { user: { select: { id: true; name: true } }; _count: { select: { likedBy: true } } } }>[];
|
miis: Prisma.MiiGetPayload<{ include: { user: { select: { id: true; name: true } }; _count: { select: { likedBy: true } } } }>[];
|
||||||
userId?: number;
|
userId?: number;
|
||||||
|
parentPage?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
const fetcher = (url: string) => fetch(url).then((res) => res.json());
|
||||||
|
|
||||||
export default function MiiGrid({ miis, userId }: Props) {
|
export default function MiiGrid({ miis, userId, parentPage }: Props) {
|
||||||
const session = useSession();
|
const session = useSession();
|
||||||
const ids = miis.map((m) => m.id).join(",");
|
const ids = miis.map((m) => m.id).join(",");
|
||||||
const { data } = useSWR<number[]>(session.data?.user && miis.length > 0 ? `/api/mii/has-liked?ids=${ids}` : null, fetcher, {
|
const { data } = useSWR<number[]>(session.data?.user && miis.length > 0 ? `/api/mii/has-liked?ids=${ids}` : null, fetcher, {
|
||||||
|
|
@ -79,6 +80,13 @@ export default function MiiGrid({ miis, userId }: Props) {
|
||||||
<DeleteMiiButton miiId={mii.id} miiName={mii.name} likes={mii._count.likedBy} />
|
<DeleteMiiButton miiId={mii.id} miiName={mii.name} likes={mii._count.likedBy} />
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
{parentPage === "admin" && (
|
||||||
|
<div className="flex gap-1 text-2xl justify-end text-zinc-400">
|
||||||
|
<button onClick={() => fetch(`/api/admin/accept-mii?id=${mii.id}`, { method: "PATCH" })} className="cursor-pointer">
|
||||||
|
<Icon icon="material-symbols:check-rounded" />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue