feat : add all label type
CI / Verify (push) Successful in 1m33s
CI / Build immutable images and deploy (push) Successful in 2m27s

This commit is contained in:
2026-09-26 23:50:15 +07:00 Unverified
parent 4ef1968b04
commit b4de016993
10 changed files with 2881 additions and 37 deletions
+8
View File
@@ -206,6 +206,14 @@ describe("structured guide validation", () => {
expect(teamSectionSchema.safeParse({ ...base, groups: [{ note: null, teams: [{ ...teams[0], members: [{ ...member("1"), artifactKey: null }, ...["2", "3", "4"].map(member)] }] }] }).success).toBe(false);
});
it("accepts all-level ranges for team constellations and refinements", () => {
const member = (characterKey: string) => ({ characterKey, weaponKey: "weapon-1", artifactKey: "artifact-1", secondArtifactKey: null, constellation: "0-6", refinement: "1-5", damage: 100 });
const input = { expectedVersion: 1, groups: [{ note: null, teams: [{ title: "Test", rotationSeconds: 20, combo: "E > Q", members: ["1", "2", "3", "4"].map(member) }] }] };
const result = teamSectionSchema.safeParse(input);
expect(result.success).toBe(true);
if (result.success) expect(result.data.groups[0].teams[0].members[0]).toMatchObject({ constellation: "0-6", refinement: "1-5" });
});
it("accepts a second artifact set for a team member", () => {
const member = (characterKey: string) => ({ characterKey, weaponKey: "weapon-1", artifactKey: "artifact-1", secondArtifactKey: characterKey === "1" ? "artifact-2" : null, constellation: 0, refinement: 1, damage: 100 });
const input = { expectedVersion: 1, groups: [{ note: null, teams: [{ title: "Test", rotationSeconds: 20, combo: "E > Q", members: ["1", "2", "3", "4"].map(member) }] }] };
+2 -2
View File
@@ -160,8 +160,8 @@ export const teamSectionSchema = z.strictObject({
weaponKey: catalogKey,
artifactKey: catalogKey,
secondArtifactKey: catalogKey.nullable(),
constellation: z.number().int().min(0).max(6),
refinement: z.number().int().min(1).max(5),
constellation: z.union([z.number().int().min(0).max(6), z.enum(["0", "1", "2", "3", "4", "5", "6", "0-6"])]).transform(String),
refinement: z.union([z.number().int().min(1).max(5), z.enum(["1", "2", "3", "4", "5", "1-5"])]).transform(String),
damage: z.coerce.number().finite().nonnegative().max(999_999_999_999),
})).length(4).refine((members) => new Set(members.map((member) => member.characterKey)).size === 4, {
message: "Team ต้องมีตัวละครไม่ซ้ำกัน 4 ตัว",
+8 -8
View File
@@ -15,7 +15,7 @@ import {
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) })),
members: [1, 2, 3, 4].map((n) => ({ characterKey: `${id}-${n}`, weaponKey: null, artifactKey: null, secondArtifactKey: null, constellation: "0", refinement: "1", damage: String(n) })),
});
describe("team bulk tools", () => {
@@ -37,8 +37,8 @@ describe("team bulk tools", () => {
it("copies one field for one character slot only", () => {
const source = team("source");
const target = team("target");
source.members[0] = { ...source.members[0], characterKey: "mavuika", refinement: 1 };
target.members[0] = { ...target.members[0], characterKey: "keep-character", refinement: 5 };
source.members[0] = { ...source.members[0], characterKey: "mavuika", refinement: "1" };
target.members[0] = { ...target.members[0], characterKey: "keep-character", refinement: "5" };
const result = applyTeamFields(
[source, target],
@@ -47,7 +47,7 @@ describe("team bulk tools", () => {
["member:0:refinement"],
);
expect(result[1].members[0].refinement).toBe(1);
expect(result[1].members[0].refinement).toBe("1");
expect(result[1].members[0].characterKey).toBe("keep-character");
expect(result[1].members.slice(1)).toEqual(target.members.slice(1));
});
@@ -83,7 +83,7 @@ describe("team bulk tools", () => {
source.members[1] = {
...source.members[1],
characterKey: "nicole",
refinement: 5,
refinement: "5",
};
const result = applyTeamFields(
@@ -93,7 +93,7 @@ describe("team bulk tools", () => {
["member:1:refinement"],
);
expect(result[1].members[1].refinement).toBe(5);
expect(result[1].members[1].refinement).toBe("5");
});
it("detects an unsupported character and weapon after bulk apply", () => {
@@ -132,8 +132,8 @@ describe("team bulk tools", () => {
weaponKey: "weapon-1",
artifactKey: "artifact-1",
secondArtifactKey: "artifact-2",
constellation: 2,
refinement: 4,
constellation: "2",
refinement: "4",
damage: "123456.78",
};
+12 -4
View File
@@ -6,8 +6,8 @@ export interface TeamToolMember {
weaponKey: string | null;
artifactKey: string | null;
secondArtifactKey: string | null;
constellation: number;
refinement: number;
constellation: string;
refinement: string;
damage: string;
}
@@ -19,6 +19,14 @@ export interface TeamToolValue {
members: TeamToolMember[];
}
export function teamConstellationLabel(value: string): string {
return value === "0-6" ? "C0-C6" : `C${value}`;
}
export function teamRefinementLabel(value: string): string {
return value === "1-5" ? "R1-R5" : `R${value}`;
}
export const teamApplyFields = ["loadout", "damage", "rotation", "combo"] as const;
export type TeamApplyField = (typeof teamApplyFields)[number];
@@ -48,8 +56,8 @@ const transferMemberSchema = z.strictObject({
weaponKey: z.string().trim().min(1).max(64).nullable(),
artifactKey: z.string().trim().min(1).max(64).nullable(),
secondArtifactKey: z.string().trim().min(1).max(64).nullable(),
constellation: z.number().int().min(0).max(6),
refinement: z.number().int().min(1).max(5),
constellation: z.union([z.number().int().min(0).max(6), z.enum(["0", "1", "2", "3", "4", "5", "6", "0-6"])]).transform(String),
refinement: z.union([z.number().int().min(1).max(5), z.enum(["1", "2", "3", "4", "5", "1-5"])]).transform(String),
damage: z.string().refine((value) => {
const number = Number(value);
return Number.isFinite(number) && number >= 0 && number <= 999_999_999_999;