"use client";
import Image from "next/image";
import Link from "next/link";
import { ArrowUpRightIcon, SearchIcon, XIcon } from "lucide-react";
import { useMemo, useState, type ReactNode } from "react";
import { GuideImageExport } from "@/components/public/guide-image-export";
import { ExportText } from "@/components/public/export-language";
import { ScrollableNav } from "@/components/public/scrollable-nav";
import { Button } from "@/components/ui/button";
import { Badge } from "@/components/ui/badge";
import { Card, CardAction, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Kbd } from "@/components/ui/kbd";
import { Separator } from "@/components/ui/separator";
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
import { elementBackgroundColor } from "@/lib/catalog/element";
import { rarityGradientClass } from "@/lib/catalog/rarity";
import { isCustomTravelerKey } from "@/lib/catalog/travelers";
import type { PublicGuide } from "@/lib/guides/queries";
import {
searchTeams,
sortTeamsByDps,
type DpsOrder,
type TeamSearchTarget,
} from "@/lib/guides/team-search";
import { cn } from "@/lib/utils";
type Team = PublicGuide["teams"][number];
type TeamArtifact = {
key: string;
name: string;
imageUrl: string;
rarity: number | null;
};
function ArtifactHalf({
artifact,
side,
eager,
linked,
sizes,
}: {
artifact: TeamArtifact;
side: "left" | "right";
eager: boolean;
linked: boolean;
sizes: string;
}) {
const content = (
);
const className = cn(
"absolute inset-0 overflow-hidden",
side === "left"
? "[clip-path:polygon(0_0,0_100%,100%_100%)]"
: "[clip-path:polygon(0_0,100%_0,100%_100%)]",
rarityGradientClass(artifact.rarity),
);
return linked ? (
{content}
) : (
{content}
);
}
export function TeamArtifactIcon({
first,
second,
eager = false,
linked = false,
className,
sizes,
}: {
first: TeamArtifact | null;
second: TeamArtifact | null;
eager?: boolean;
linked?: boolean;
className?: string;
sizes: string;
}) {
const primary = first ?? second;
const secondary = first ? second : null;
if (!primary) {
return
;
}
if (!secondary) {
const content = (
<>
{linked ? (
) : null}
>
);
const artifactClassName = cn(
"relative aspect-square scale-110 overflow-hidden rounded-md bg-muted",
rarityGradientClass(primary.rarity),
className,
);
return linked ? (
{content}
) : (
{content}
);
}
return (
);
}
function TeamExportImage({ team }: { team: Team }) {
const total = team.members.reduce((sum, member) => sum + Number(member.damage), 0);
const seconds = Number(team.rotationSeconds);
return (
{team.members.map((member) => {
const damage = Number(member.damage);
const percent = total > 0 ? damage / total * 100 : 0;
return (
{member.character.name}
C{member.constellation}R{member.refinement}
{damage.toLocaleString("en-US")}
);
})}
DPS: {seconds > 0 ? (total / seconds).toLocaleString("en-US", { maximumFractionDigits: 0 }) : "0"}
By YT เกนชินไม่ใช่เกมมือถือ
);
}
function TeamCard({
team,
action,
exporting = false,
}: {
team: Team;
action?: ReactNode;
exporting?: boolean;
}) {
const total = team.members.reduce(
(sum, member) => sum + Number(member.damage),
0,
);
const seconds = Number(team.rotationSeconds);
return (
{team.title}
โรเทชั่น {seconds} วิ
{action ? {action} : null}
{team.members.map((member) => (
{member.weapon ? (
) : (
)}
C{member.constellation}
R{member.refinement}
{member.character.name}
{Number(member.damage).toLocaleString(undefined, {
maximumFractionDigits: 2,
})}
{total
? ((Number(member.damage) / total) * 100).toFixed(2)
: "0.00"}
%
))}
ดาเมจรวม
{total.toLocaleString()}
DPS
{seconds > 0
? (total / seconds).toLocaleString(undefined, {
maximumFractionDigits: 0,
})
: "0"}
{team.combo}
);
}
export function TeamSearch({ groups }: { groups: PublicGuide["teamGroups"] }) {
const [activeCondition, setActiveCondition] = useState("0");
const activeGroup = groups[Number(activeCondition)] ?? groups[0];
const teams = useMemo(() => activeGroup?.teams ?? [], [activeGroup]);
const [query, setQuery] = useState("");
const [searchTarget, setSearchTarget] = useState("all");
const [dpsOrder, setDpsOrder] = useState(null);
const search = useMemo(
() => searchTeams(teams, (team) => ({
team: [team.title],
character: team.members.flatMap((member) => [member.character.name, ...member.character.aliases]),
weapon: team.members.flatMap((member) => member.weapon ? [member.weapon.name, ...member.weapon.aliases] : []),
artifact: team.members.flatMap((member) => [member.artifact, member.secondArtifact].flatMap((artifact) => artifact ? [artifact.name, ...artifact.aliases] : [])),
}), query, searchTarget),
[query, searchTarget, teams],
);
const filteredTeams = sortTeamsByDps(search.items, dpsOrder);
return (
{groups.length > 1 ? (
{
if (values[0]) setActiveCondition(values[0]);
}}>
{groups.map((group, index) => {group.name || `เงื่อนไข ${index + 1}`})}
) : null}
{activeGroup?.note ?
{activeGroup.note}
: null}
{
const target = value[0] as TeamSearchTarget | undefined;
if (target) setSearchTarget(target);
}}
>
ทั้งหมด
ชื่อทีม
ตัวละคร
อาวุธ
อาร์ติแฟกต์
{
const order = value[0] as Exclude | undefined;
if (order) setDpsOrder(order);
}}
>
Best DPS
Worst DPS
{filteredTeams.length ? (
{filteredTeams.map((team) => (
}
/>
))}
) : (
ไม่พบทีมที่ตรงกับคำค้นหา
)}
);
}