From 50a2a5428ddba3b4c2ec56fa59a6b559a0224fe3 Mon Sep 17 00:00:00 2001 From: gunshiz Date: Fri, 4 Sep 2026 18:32:19 +0700 Subject: [PATCH] feat : error when mulitple apply not supporting --- components/admin/guide-section-editor.tsx | 42 +++++++++++++++++++++-- lib/guides/team-tools.test.ts | 28 +++++++++++++-- lib/guides/team-tools.ts | 36 +++++++++++++++++++ 3 files changed, 102 insertions(+), 4 deletions(-) diff --git a/components/admin/guide-section-editor.tsx b/components/admin/guide-section-editor.tsx index 2faac94..d7f1ab0 100644 --- a/components/admin/guide-section-editor.tsx +++ b/components/admin/guide-section-editor.tsx @@ -19,6 +19,7 @@ import { XIcon, } from "lucide-react"; import { useEffect, useMemo, useState, type ReactNode } from "react"; +import { toast } from "sonner"; import { closestCenter, DndContext, @@ -128,6 +129,7 @@ import { rarityGradientClass } from "@/lib/catalog/rarity"; import { applyPresetFields, applyTeamFields, + findUnsupportedTeamWeapon, limitPresets, parseTeamPresets, teamPresetCookieKey, @@ -2240,7 +2242,25 @@ function TeamEditor({ }; const applyChanges = () => { if (!sourceTeamId || !selectedTeamIds.size || !applyFields.length) return; - setTeams((current) => applyTeamFields(current, sourceTeamId, selectedTeamIds, applyFields)); + const nextTeams = applyTeamFields( + teams, + sourceTeamId, + selectedTeamIds, + applyFields, + ); + const unsupported = findUnsupportedTeamWeapon( + nextTeams, + selectedTeamIds, + characters, + weapons, + ); + if (unsupported) { + toast.error( + `${unsupported.characterName} ไม่รองรับอาวุธ ${unsupported.weaponName}`, + ); + return; + } + setTeams(nextTeams); setSelectedTeamIds(new Set()); setApplyOpen(false); }; @@ -2265,7 +2285,25 @@ function TeamEditor({ }; const applyPreset = (preset: TeamPreset) => { if (!selectedTeamIds.size || !applyFields.length) return; - setTeams((current) => applyPresetFields(current, preset, selectedTeamIds, applyFields)); + const nextTeams = applyPresetFields( + teams, + preset, + selectedTeamIds, + applyFields, + ); + const unsupported = findUnsupportedTeamWeapon( + nextTeams, + selectedTeamIds, + characters, + weapons, + ); + if (unsupported) { + toast.error( + `${unsupported.characterName} ไม่รองรับอาวุธ ${unsupported.weaponName}`, + ); + return; + } + setTeams(nextTeams); setSelectedTeamIds(new Set()); }; return ( diff --git a/lib/guides/team-tools.test.ts b/lib/guides/team-tools.test.ts index 0a5fca5..c713f4d 100644 --- a/lib/guides/team-tools.test.ts +++ b/lib/guides/team-tools.test.ts @@ -1,8 +1,14 @@ import { describe, expect, it } from "vitest"; -import { applyTeamFields, limitPresets, parseTeamPresets } from "./team-tools"; +import { + applyTeamFields, + findUnsupportedTeamWeapon, + limitPresets, + parseTeamPresets, + type TeamToolValue, +} from "./team-tools"; -const team = (id: string, title = id) => ({ +const team = (id: string, title = id): TeamToolValue => ({ clientId: id, title, rotationSeconds: "20", combo: "E > Q", members: [1, 2, 3, 4].map((n) => ({ characterKey: `${id}-${n}`, weaponKey: null, artifactKey: null, secondArtifactKey: null, constellation: 0, refinement: 1, damage: String(n) })), }); @@ -41,6 +47,24 @@ describe("team bulk tools", () => { expect(result[1].members.slice(1)).toEqual(target.members.slice(1)); }); + it("detects an unsupported character and weapon after bulk apply", () => { + const target = team("target"); + target.members[0] = { + ...target.members[0], + characterKey: "varka", + weaponKey: "wolf-fang", + }; + + expect( + findUnsupportedTeamWeapon( + [target], + new Set(["target"]), + [{ key: "varka", name: "Varka", weaponType: "WEAPON_CLAYMORE" }], + [{ key: "wolf-fang", name: "Wolf-Fang", weaponType: "WEAPON_SWORD_ONE_HAND" }], + ), + ).toEqual({ characterName: "Varka", weaponName: "Wolf-Fang" }); + }); + it("deduplicates and limits presets", () => { const presets = Array.from({ length: 7 }, (_, index) => ({ id: String(index), name: String(index), createdAt: String(index), team: { ...team(String(index)), clientId: undefined as never } })); expect(limitPresets([presets[0], presets[0], ...presets.slice(1)])).toHaveLength(5); diff --git a/lib/guides/team-tools.ts b/lib/guides/team-tools.ts index 24de681..b7a77b3 100644 --- a/lib/guides/team-tools.ts +++ b/lib/guides/team-tools.ts @@ -1,3 +1,5 @@ +import { supportsWeaponType } from "@/lib/catalog/lunaris"; + export interface TeamToolMember { characterKey: string; weaponKey: string | null; @@ -40,6 +42,40 @@ export interface TeamPreset { team: Omit; } +interface TeamToolCatalogItem { + key: string; + name: string; + weaponType?: string | null; +} + +export function findUnsupportedTeamWeapon( + teams: readonly TeamToolValue[], + teamIds: ReadonlySet, + characters: readonly TeamToolCatalogItem[], + weapons: readonly TeamToolCatalogItem[], +): { characterName: string; weaponName: string } | null { + const characterByKey = new Map(characters.map((item) => [item.key, item])); + const weaponByKey = new Map(weapons.map((item) => [item.key, item])); + + for (const team of teams) { + if (!teamIds.has(team.clientId)) continue; + for (const member of team.members) { + if (!member.characterKey || !member.weaponKey) continue; + const character = characterByKey.get(member.characterKey); + const weapon = weaponByKey.get(member.weaponKey); + if ( + character && + weapon && + !supportsWeaponType(character.weaponType, weapon.weaponType) + ) { + return { characterName: character.name, weaponName: weapon.name }; + } + } + } + + return null; +} + function cloneMember(member: TeamToolMember): TeamToolMember { return { ...member }; }