feat : quick update
CI / Verify (push) Successful in 1m5s
CI / Build immutable images and deploy (push) Successful in 1m56s

This commit is contained in:
2026-09-04 16:12:43 +07:00 Unverified
parent 262f15be27
commit c7b75a5ad4
4 changed files with 321 additions and 78 deletions
+80 -3
View File
@@ -10,6 +10,7 @@ import {
ListOrderedIcon,
PlusIcon,
RefreshCcwIcon,
SearchIcon,
SparklesIcon,
SwordIcon,
TimerIcon,
@@ -17,7 +18,7 @@ import {
UserRoundIcon,
XIcon,
} from "lucide-react";
import { useEffect, useState, type ReactNode } from "react";
import { useEffect, useMemo, useState, type ReactNode } from "react";
import {
closestCenter,
DndContext,
@@ -134,6 +135,10 @@ import {
type TeamPreset,
type TeamToolValue,
} from "@/lib/guides/team-tools";
import {
matchesTeamSearch,
type TeamSearchTarget,
} from "@/lib/guides/team-search";
import {
normalizeTalentPriorities,
reorderTalentPriorities,
@@ -2069,6 +2074,35 @@ function TeamEditor({
const [presetsHydrated, setPresetsHydrated] = useState(false);
const [presetsGuideId, setPresetsGuideId] = useState<string | null>(null);
const [applyFields, setApplyFields] = useState<TeamApplyField[]>(["loadout"]);
const [searchQuery, setSearchQuery] = useState("");
const [searchTarget, setSearchTarget] = useState<TeamSearchTarget>("all");
const filteredTeams = useMemo(() => {
const characterNames = new Map(characters.map((item) => [item.key, item.name]));
const weaponNames = new Map(weapons.map((item) => [item.key, item.name]));
const artifactNames = new Map(artifacts.map((item) => [item.key, item.name]));
return teams
.map((team, teamIndex) => ({ team, teamIndex }))
.filter(({ team }) => matchesTeamSearch({
team: [team.title],
character: team.members.flatMap((member) => {
const name = characterNames.get(member.characterKey);
return name ? [name] : [];
}),
weapon: team.members.flatMap((member) => {
const name = member.weaponKey
? weaponNames.get(member.weaponKey)
: undefined;
return name ? [name] : [];
}),
artifact: team.members.flatMap((member) =>
[member.artifactKey, member.secondArtifactKey].flatMap((key) => {
const name = key ? artifactNames.get(key) : undefined;
return name ? [name] : [];
}),
),
}, searchQuery, searchTarget));
}, [artifacts, characters, searchQuery, searchTarget, teams, weapons]);
const value = {
teams: teams.map((team) => ({
title: team.title,
@@ -2213,13 +2247,51 @@ function TeamEditor({
</div>
))}
</div>
<FieldGroup className="mt-4">
<Field>
<FieldLabel htmlFor="team-search">ค้นหา Team</FieldLabel>
<div className="relative">
<SearchIcon
aria-hidden="true"
className="pointer-events-none absolute top-1/2 left-3 size-4 -translate-y-1/2 text-muted-foreground"
/>
<Input
id="team-search"
className="pl-9"
placeholder="ค้นหาชื่อทีม ตัวละคร อาวุธ หรืออาร์ติแฟกต์"
value={searchQuery}
onValueChange={setSearchQuery}
/>
</div>
</Field>
<Field>
<FieldLabel>ค้นหาใน</FieldLabel>
<ToggleGroup
aria-label="เลือกประเภทข้อมูลที่ต้องการค้นหา"
className="max-w-full flex-wrap justify-start"
variant="outline"
size="sm"
value={[searchTarget]}
onValueChange={(value) => {
const target = value[0] as TeamSearchTarget | undefined;
if (target) setSearchTarget(target);
}}
>
<ToggleGroupItem value="all">ทั้งหมด</ToggleGroupItem>
<ToggleGroupItem value="team">ชื่อทีม</ToggleGroupItem>
<ToggleGroupItem value="character">ตัวละคร</ToggleGroupItem>
<ToggleGroupItem value="weapon">อาวุธ</ToggleGroupItem>
<ToggleGroupItem value="artifact">อาร์ติแฟกต์</ToggleGroupItem>
</ToggleGroup>
</Field>
</FieldGroup>
<DndContext
sensors={teamSensors}
collisionDetection={closestCenter}
onDragEnd={handleTeamDragEnd}
>
<SortableContext
items={teams.map((team) => team.clientId)}
items={filteredTeams.map(({ team }) => team.clientId)}
strategy={verticalListSortingStrategy}
>
<Accordion
@@ -2227,7 +2299,7 @@ function TeamEditor({
defaultValue={teams.length ? [teams[0].clientId] : []}
className="gap-3"
>
{teams.map((team, teamIndex) => {
{filteredTeams.map(({ team, teamIndex }) => {
const total = team.members.reduce(
(sum, member) => sum + (Number(member.damage) || 0),
0,
@@ -2764,6 +2836,11 @@ function TeamEditor({
</SortableTeamItem>
);
})}
{filteredTeams.length === 0 ? (
<p className="py-8 text-center text-sm text-muted-foreground">
ไม่พบ Team ที่ตรงกับคำค้นหา
</p>
) : null}
</Accordion>
</SortableContext>
</DndContext>