feat(rubgram): add submit another and submission list modal
This commit is contained in:
@@ -178,6 +178,37 @@ export async function getEndgameConfig() {
|
||||
};
|
||||
}
|
||||
|
||||
export async function getUserSubmissions(uid: string) {
|
||||
const types = await db.select().from(endgameTypes);
|
||||
const typeNames = Object.fromEntries(types.map((t) => [t.id, t.display]));
|
||||
|
||||
const subs = await db
|
||||
.select({
|
||||
id: endgameSubmissions.id,
|
||||
queue: endgameSubmissions.queue,
|
||||
name: endgameSubmissions.name,
|
||||
server: endgameSubmissions.server,
|
||||
price: endgameSubmissions.price,
|
||||
paid: endgameSubmissions.paid,
|
||||
checked: endgameSubmissions.checked,
|
||||
deleted: endgameSubmissions.deleted,
|
||||
expires: endgameSubmissions.expires,
|
||||
service: endgameSubmissions.service,
|
||||
submitDay: endgameSubmissions.submit_day,
|
||||
})
|
||||
.from(endgameSubmissions)
|
||||
.where(
|
||||
and(eq(endgameSubmissions.user, uid), not(endgameSubmissions.deleted)),
|
||||
)
|
||||
.orderBy(desc(endgameSubmissions.submit_day))
|
||||
.limit(20);
|
||||
|
||||
return subs.map((s) => ({
|
||||
...s,
|
||||
services: s.service.map((id) => typeNames[id] || id),
|
||||
}));
|
||||
}
|
||||
|
||||
export async function submitEndgame(formData: FormData) {
|
||||
const { full, locked, limit, types, free } = await getEndgameConfig();
|
||||
const name = formData.get("name") as string;
|
||||
|
||||
+120
-1
@@ -1,12 +1,30 @@
|
||||
"use client";
|
||||
|
||||
import { ArrowRight, Check, Download, LogIn, ScrollText } from "lucide-react";
|
||||
import {
|
||||
ArrowRight,
|
||||
Check,
|
||||
Circle,
|
||||
Clock,
|
||||
Download,
|
||||
List,
|
||||
LogIn,
|
||||
Plus,
|
||||
ScrollText,
|
||||
} from "lucide-react";
|
||||
import { useRouter } from "next/navigation";
|
||||
import posthog from "posthog-js";
|
||||
import { type ReactNode, useEffect, useRef, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { Blocker } from "@/components/blocker";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogTitle,
|
||||
DialogTrigger,
|
||||
} from "@/components/ui/dialog";
|
||||
import { Kbd } from "@/components/ui/kbd";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import {
|
||||
@@ -25,14 +43,17 @@ import {
|
||||
cancel,
|
||||
type getDiscordSession,
|
||||
type getEndgameConfig,
|
||||
type getUserSubmissions,
|
||||
loginDiscord,
|
||||
} from "./api";
|
||||
import { RulesDialog } from "./rules";
|
||||
|
||||
export function WelcomeScreening({
|
||||
session,
|
||||
userSubs,
|
||||
}: {
|
||||
session: Awaited<ReturnType<typeof getDiscordSession>>;
|
||||
userSubs: Awaited<ReturnType<typeof getUserSubmissions>>;
|
||||
}) {
|
||||
const [agreed, setAgreed] = useState(false);
|
||||
const [updated] = shared.state("updated");
|
||||
@@ -58,6 +79,13 @@ export function WelcomeScreening({
|
||||
สลับบัญชี?
|
||||
</button>
|
||||
</span>
|
||||
<div className="absolute right-0 bottom-0 m-2">
|
||||
<SubmissionListModal subs={userSubs}>
|
||||
<Button variant="outline" size="sm">
|
||||
<List /> รายการคิว
|
||||
</Button>
|
||||
</SubmissionListModal>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="grid gap-2">
|
||||
@@ -266,3 +294,94 @@ export function ClearCookie() {
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
export function SubmitAnotherButton() {
|
||||
const router = useRouter();
|
||||
return (
|
||||
<Button variant="outline" size="sm" onClick={() => router.push("?new")}>
|
||||
<Plus /> ลงคิวเพิ่ม
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
export function SubmissionListModal({
|
||||
subs,
|
||||
children,
|
||||
}: {
|
||||
subs: Awaited<ReturnType<typeof getUserSubmissions>>;
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const serverLabels: Record<string, string> = {
|
||||
as: "Asia",
|
||||
eu: "Europe",
|
||||
us: "America",
|
||||
tw: "TW, HK, MO",
|
||||
};
|
||||
|
||||
function select(id: string) {
|
||||
try {
|
||||
// biome-ignore lint/suspicious/noTsIgnore: typescript issue
|
||||
// @ts-ignore
|
||||
cookieStore.set("rsid", id);
|
||||
} catch {
|
||||
console.warn("Cannot set rubgram cookie");
|
||||
}
|
||||
router.refresh();
|
||||
}
|
||||
|
||||
return (
|
||||
<Dialog>
|
||||
<DialogTrigger asChild>{children}</DialogTrigger>
|
||||
<DialogContent className="max-h-[80svh] max-w-lg overflow-y-auto">
|
||||
<DialogHeader>
|
||||
<DialogTitle>รายการคิวของฉัน</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="flex flex-col gap-2">
|
||||
{subs.length === 0 && (
|
||||
<p className="text-sm text-muted-foreground">ไม่มีรายการคิว</p>
|
||||
)}
|
||||
{subs.map((r) => (
|
||||
<button
|
||||
key={r.id}
|
||||
type="button"
|
||||
onClick={() => select(r.id)}
|
||||
className="flex cursor-pointer items-start gap-3 rounded-md border p-3 text-left transition-colors hover:bg-accent"
|
||||
>
|
||||
<div className="flex size-12 shrink-0 items-center justify-center rounded bg-muted">
|
||||
{r.paid ? (
|
||||
<Check className="size-5 text-green-500" />
|
||||
) : r.expires && new Date(r.expires) <= new Date() ? (
|
||||
<Circle className="size-5 text-muted-foreground" />
|
||||
) : (
|
||||
<Clock className="size-5 text-yellow-500" />
|
||||
)}
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-0.5">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="truncate font-medium">
|
||||
{r.queue}. {r.name}
|
||||
</span>
|
||||
</div>
|
||||
<span className="truncate text-xs text-muted-foreground">
|
||||
{r.services.join(", ")}
|
||||
</span>
|
||||
<div className="flex items-center gap-2 text-xs">
|
||||
<Badge variant="outline">{serverLabels[r.server]}</Badge>
|
||||
<span className="font-medium">{r.price} ฿</span>
|
||||
{r.paid ? (
|
||||
<span className="text-green-500">ชำระแล้ว</span>
|
||||
) : r.expires && new Date(r.expires) <= new Date() ? (
|
||||
<span className="text-muted-foreground">หมดเวลา</span>
|
||||
) : (
|
||||
<span className="text-yellow-500">รอชำระ</span>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
|
||||
+47
-26
@@ -3,6 +3,7 @@ import {
|
||||
AlertCircle,
|
||||
BookAlert,
|
||||
CircleDollarSign,
|
||||
List,
|
||||
SendHorizonal,
|
||||
X,
|
||||
} from "lucide-react";
|
||||
@@ -54,7 +55,7 @@ import { db } from "@/lib/db";
|
||||
import { endgameSubmissions } from "@/lib/db/schema";
|
||||
import { LiveButton } from "../artifact/live";
|
||||
import { Watcher } from "./admin/client";
|
||||
import { getDiscordSession, getEndgameConfig } from "./api";
|
||||
import { getDiscordSession, getEndgameConfig, getUserSubmissions } from "./api";
|
||||
import {
|
||||
CancelButton,
|
||||
ClearCookie,
|
||||
@@ -63,6 +64,8 @@ import {
|
||||
PriceEstimation,
|
||||
ServiceSelector,
|
||||
SlipUpload,
|
||||
SubmissionListModal,
|
||||
SubmitAnotherButton,
|
||||
WelcomeScreening,
|
||||
} from "./client";
|
||||
import { EndgameFormWrapper } from "./form";
|
||||
@@ -73,16 +76,25 @@ export const metadata: Metadata = {
|
||||
description: "รับเล่นคอนเทนต์เอนเกมเกนชินแทนคนดู",
|
||||
};
|
||||
|
||||
export default async function EndgamePage() {
|
||||
export default async function EndgamePage({
|
||||
searchParams,
|
||||
}: {
|
||||
searchParams: Promise<{ new?: string }>;
|
||||
}) {
|
||||
const sp = await searchParams;
|
||||
const isNew = sp.new === "";
|
||||
|
||||
const cookie = await cookies();
|
||||
const sid = cookie.get("rsid");
|
||||
const session = await getDiscordSession();
|
||||
const [q] = sid?.value
|
||||
? await db
|
||||
.select({
|
||||
// Using endgame.submissions.queue here because
|
||||
// ${endgameSubmissions.queue} does not work
|
||||
queue: sql<number>`
|
||||
|
||||
const [q] =
|
||||
!isNew && sid?.value
|
||||
? await db
|
||||
.select({
|
||||
// Using endgame.submissions.queue here because
|
||||
// ${endgameSubmissions.queue} does not work
|
||||
queue: sql<number>`
|
||||
${endgameSubmissions.queue} - (
|
||||
select count(*)
|
||||
from ${endgameSubmissions} e2
|
||||
@@ -90,24 +102,25 @@ export default async function EndgamePage() {
|
||||
and e2.queue < endgame.submissions.queue
|
||||
)
|
||||
`,
|
||||
paid: endgameSubmissions.paid,
|
||||
price: endgameSubmissions.price,
|
||||
expires: endgameSubmissions.expires,
|
||||
})
|
||||
.from(endgameSubmissions)
|
||||
.where(
|
||||
and(
|
||||
or(
|
||||
eq(endgameSubmissions.id, sid.value),
|
||||
and(
|
||||
eq(endgameSubmissions.user, session?.uid || "placeholder"),
|
||||
gt(endgameSubmissions.expires, new Date()),
|
||||
paid: endgameSubmissions.paid,
|
||||
price: endgameSubmissions.price,
|
||||
expires: endgameSubmissions.expires,
|
||||
})
|
||||
.from(endgameSubmissions)
|
||||
.where(
|
||||
and(
|
||||
or(
|
||||
eq(endgameSubmissions.id, sid.value),
|
||||
and(
|
||||
eq(endgameSubmissions.user, session?.uid || "placeholder"),
|
||||
gt(endgameSubmissions.expires, new Date()),
|
||||
),
|
||||
),
|
||||
not(endgameSubmissions.deleted),
|
||||
),
|
||||
not(endgameSubmissions.deleted),
|
||||
),
|
||||
)
|
||||
: [];
|
||||
)
|
||||
: [];
|
||||
const userSubs = session ? await getUserSubmissions(session.uid) : [];
|
||||
const { count, ...config } = await getEndgameConfig();
|
||||
const [canExpire] = q
|
||||
? await db
|
||||
@@ -141,6 +154,14 @@ export default async function EndgamePage() {
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
<div className="absolute right-0 bottom-0 m-2 flex gap-2">
|
||||
<SubmissionListModal subs={userSubs}>
|
||||
<Button variant="outline" size="sm">
|
||||
<List /> รายการคิว
|
||||
</Button>
|
||||
</SubmissionListModal>
|
||||
<SubmitAnotherButton />
|
||||
</div>
|
||||
</Blocker>
|
||||
)
|
||||
) : config.locked ? (
|
||||
@@ -154,7 +175,7 @@ export default async function EndgamePage() {
|
||||
</div>
|
||||
</Blocker>
|
||||
) : (
|
||||
<WelcomeScreening {...{ session }} />
|
||||
<WelcomeScreening {...{ session, userSubs }} />
|
||||
)}
|
||||
<CardHeader className="justify-center">
|
||||
<CardTitle>
|
||||
@@ -333,7 +354,7 @@ export default async function EndgamePage() {
|
||||
ช่องดิสคอร์ด
|
||||
</a>
|
||||
</span>
|
||||
{sid && !q && <ClearCookie />}
|
||||
{sid && !q && !isNew && <ClearCookie />}
|
||||
<Watcher />
|
||||
</div>
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user