Files
erika/app/leaderboard/client.tsx
T

152 lines
4.5 KiB
TypeScript

"use client";
import { Search, X } from "lucide-react";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { Button } from "@/components/ui/button";
import { Field, FieldLabel } from "@/components/ui/field";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/components/ui/select";
import { Tabs, TabsList, TabsTrigger } from "@/components/ui/tabs";
import type { DiscordVoiceChannel } from "@/lib/leaderboard/discord";
const ALL_CHANNELS = "all";
export function ChannelSelect({
channels,
selectedChannelId,
searchQuery,
}: {
channels: DiscordVoiceChannel[];
selectedChannelId?: string;
searchQuery?: string;
}) {
const router = useRouter();
return (
<Field className="max-w-md">
<FieldLabel htmlFor="voice-channel">ห้องเสียง</FieldLabel>
<Select
value={selectedChannelId ?? ALL_CHANNELS}
onValueChange={(value) => {
const params = new URLSearchParams();
if (value !== ALL_CHANNELS) params.set("channel", value);
if (searchQuery) params.set("q", searchQuery);
const query = params.toString();
router.push(query ? `/leaderboard/vc?${query}` : "/leaderboard/vc");
}}
>
<SelectTrigger id="voice-channel" className="w-full bg-card!">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectGroup>
<SelectItem value={ALL_CHANNELS}>ทุกห้องเสียงปัจจุบัน</SelectItem>
{channels.map((channel) => (
<SelectItem key={channel.id} value={channel.id}>
{channel.type === 13 ? "เวที: " : ""}
{channel.name}
</SelectItem>
))}
</SelectGroup>
</SelectContent>
</Select>
</Field>
);
}
export function LeaderboardSearch({ query }: { query: string }) {
const pathname = usePathname();
const router = useRouter();
const searchParams = useSearchParams();
function navigate(nextValue: string) {
const params = new URLSearchParams(searchParams);
const trimmed = nextValue.trim();
params.delete("page");
if (trimmed) {
params.set("q", trimmed);
} else {
params.delete("q");
}
const nextQuery = params.toString();
router.push(nextQuery ? `${pathname}?${nextQuery}` : pathname);
}
return (
<form
className="flex w-full flex-col gap-2 sm:max-w-md"
onSubmit={(event) => {
event.preventDefault();
const formData = new FormData(event.currentTarget);
navigate(String(formData.get("q") ?? ""));
}}
>
<Field>
<FieldLabel htmlFor="leaderboard-search">ค้นหาสมาชิก</FieldLabel>
<div className="flex gap-2">
<Input
id="leaderboard-search"
name="q"
key={query}
defaultValue={query}
placeholder="ชื่อ, username หรือ Discord ID"
autoComplete="off"
className="bg-card!"
/>
{query && (
<Button
type="button"
variant="outline"
size="icon"
onClick={() => navigate("")}
>
<X data-icon="inline-start" />
<span className="sr-only">ล้างการค้นหา</span>
</Button>
)}
<Button type="submit" variant="secondary" size="icon">
<Search data-icon="inline-start" />
<span className="sr-only">ค้นหา</span>
</Button>
</div>
</Field>
</form>
);
}
export function LeaderboardTabs({
active,
searchQuery,
}: {
active: "xp" | "vc";
searchQuery: string;
}) {
const router = useRouter();
return (
<Tabs
value={active}
onValueChange={(value) => {
const params = new URLSearchParams();
if (searchQuery) params.set("q", searchQuery);
const query = params.toString();
router.push(query ? `/leaderboard/${value}?${query}` : `/leaderboard/${value}`);
}}
>
<TabsList className="w-full sm:w-fit" aria-label="เลือกประเภทลีดเดอร์บอร์ด">
<TabsTrigger value="xp">อันดับ XP</TabsTrigger>
<TabsTrigger value="vc">เวลาในห้องเสียง</TabsTrigger>
</TabsList>
</Tabs>
);
}