Files
buzz/app/(ui)/rubgram/admin/@modal/manual/page.tsx
T
dmgnr 4559f4693c
Build and Push Image / build-backend (push) Successful in 2m13s
Build and Push Image / build-frontend (push) Successful in 4m9s
Build and Push Image / push (push) Successful in 4m21s
refactor(host): migrate from docker swarm to kubernetes
2026-06-19 18:18:17 +00:00

165 lines
4.8 KiB
TypeScript

import { PlusIcon } from "lucide-react";
import { revalidatePath } from "next/cache";
import { redirect } from "next/navigation";
import z from "zod";
import { th } from "zod/v4/locales";
import { getDiscordUsers } from "@/app/api/discord/users/api";
import {
FormAction,
FormInput,
FormProvider,
FormRow,
} from "@/components/form";
import { ModalBase } from "@/components/modal";
import { Button } from "@/components/ui/button";
import { DialogClose, DialogFooter } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectLabel,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Spinner } from "@/components/ui/spinner";
import { actionLog } from "@/lib/api";
import { adminCheck } from "@/lib/auth";
import { db } from "@/lib/db";
import {
endgameDiscord,
endgameSlips,
endgameSubmissions,
endgameTypes,
} from "@/lib/db/schema";
import type { SlipokResponse } from "../../../api";
import { CurrencyInput, ServiceSelect, SlipUpload, UserSelect } from "./client";
const Schema = z.object({
name: z.string(),
price: z.string().regex(/^\d+$/),
services: z.string().max(100),
server: z.enum(["as", "us", "eu", "tw"]),
slip: z.file(),
discord: z.string().regex(/\d{17,20}/),
});
export default async function RgManualCreateModal() {
if (!(await adminCheck())) redirect("/login");
const types = await db.select().from(endgameTypes);
async function submit(form: FormData) {
"use server";
if (!(await adminCheck())) redirect("/login");
const raw = Object.fromEntries([...form.entries()]);
z.config(th());
const parsed = Schema.safeParse(raw);
console.log(raw, parsed);
if (!parsed.success) {
return {
error: parsed.error.issues.map((issue) => ({
what: issue.message,
where: issue.path.join("."),
})),
};
}
const { data } = parsed;
const [{ id: slipId }] = await db
.insert(endgameSlips)
.values({
slip: Buffer.from(await data.slip.arrayBuffer()),
amount: data.price,
ref: `MANUAL-${Bun.randomUUIDv7()}`,
data: {} as SlipokResponse,
})
.returning({ id: endgameSlips.id });
const users = await getDiscordUsers();
const user = users.find((u) => u.uid === data.discord)!;
await db
.insert(endgameDiscord)
.values({
...user,
uid: data.discord,
})
.onConflictDoNothing();
await db.insert(endgameSubmissions).values({
...data,
price: Number(data.price),
slip: slipId,
user: data.discord,
service: data.services.split(","),
});
await actionLog(`Manually added rubgram submission "${data.name}"`, data);
revalidatePath("/rubgram/admin");
return { toast: "Submission added successfully.", close: true };
}
return (
<ModalBase title="Create Submission">
<FormProvider id="rg-create" onSubmit={submit}>
<FormRow>
<FormInput name="name" label="Name">
<Input placeholder="Dreamgineer" autoFocus />
</FormInput>
<FormInput name="price" label="Price" className="flex-1">
<CurrencyInput placeholder="ฟรี" />
</FormInput>
</FormRow>
<FormInput name="services" label="Services">
<ServiceSelect types={types} />
</FormInput>
<FormInput name="server">
<Select>
<SelectTrigger className="w-full">
<SelectValue placeholder="เลือกเซิร์ฟเวอร์ที่คุณอยู่" />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectLabel>เซิร์ฟเวอร์</SelectLabel>
<SelectItem value="as">Asia</SelectItem>
<SelectItem value="us">America</SelectItem>
<SelectItem value="eu">Europe</SelectItem>
<SelectItem value="tw">TW, HK, MO</SelectItem>
</SelectGroup>
</SelectContent>
</Select>
</FormInput>
<FormInput name="slip" label="">
<SlipUpload />
</FormInput>
<FormInput name="discord" label="User">
<UserSelect />
</FormInput>
<DialogFooter>
<DialogClose asChild>
<Button variant="outline">Cancel</Button>
</DialogClose>
<Button asChild>
<FormAction
type="submit"
loading={
<>
<Spinner />
Creating...
</>
}
>
<PlusIcon />
Create
</FormAction>
</Button>
</DialogFooter>
</FormProvider>
</ModalBase>
);
}
export const dynamic = "force-dynamic";