fix : not null artifact and weapon in team calc
CI / Build immutable images and deploy (push) Successful in 2m6s
CI / Build immutable images and deploy (push) Successful in 2m6s
This commit is contained in:
@@ -563,7 +563,7 @@ function CatalogSlotPicker({
|
|||||||
aria-label={selected ? `${label}: ${selected.name}` : label}
|
aria-label={selected ? `${label}: ${selected.name}` : label}
|
||||||
aria-invalid={invalid}
|
aria-invalid={invalid}
|
||||||
className={cn(
|
className={cn(
|
||||||
"relative flex aspect-square w-full items-center justify-center overflow-hidden rounded-md border bg-muted p-0 outline-none focus-visible:ring-2 focus-visible:ring-ring [&>svg]:hidden",
|
"relative flex aspect-square w-full items-center justify-center overflow-hidden rounded-md border bg-muted p-0 outline-none focus-visible:ring-2 focus-visible:ring-ring aria-invalid:border aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&>svg]:hidden",
|
||||||
rarityBackground && selected && rarityGradientClass(selected.rarity),
|
rarityBackground && selected && rarityGradientClass(selected.rarity),
|
||||||
className,
|
className,
|
||||||
)}
|
)}
|
||||||
@@ -614,11 +614,13 @@ function ArtifactSlotPicker({
|
|||||||
values,
|
values,
|
||||||
onChange,
|
onChange,
|
||||||
label,
|
label,
|
||||||
|
invalid = false,
|
||||||
}: {
|
}: {
|
||||||
items: CatalogItem[];
|
items: CatalogItem[];
|
||||||
values: string[];
|
values: string[];
|
||||||
onChange: (values: string[]) => void;
|
onChange: (values: string[]) => void;
|
||||||
label: string;
|
label: string;
|
||||||
|
invalid?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const firstKey = values[0];
|
const firstKey = values[0];
|
||||||
const secondKey = values[1];
|
const secondKey = values[1];
|
||||||
@@ -656,7 +658,8 @@ function ArtifactSlotPicker({
|
|||||||
aria-label={selected.length
|
aria-label={selected.length
|
||||||
? `${label}: ${selected.map((item) => item.name).join(", ")}`
|
? `${label}: ${selected.map((item) => item.name).join(", ")}`
|
||||||
: label}
|
: label}
|
||||||
className="relative aspect-square w-full overflow-hidden rounded-md border bg-muted outline-none focus-visible:ring-2 focus-visible:ring-ring [&>svg]:hidden"
|
aria-invalid={invalid}
|
||||||
|
className="relative aspect-square w-full overflow-hidden rounded-md border bg-muted outline-none focus-visible:ring-2 focus-visible:ring-ring aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&>svg]:hidden"
|
||||||
>
|
>
|
||||||
{selected.length ? (
|
{selected.length ? (
|
||||||
<TeamArtifactIcon
|
<TeamArtifactIcon
|
||||||
@@ -3550,22 +3553,13 @@ function TeamEditor({
|
|||||||
value={member.characterKey}
|
value={member.characterKey}
|
||||||
label={`เลือกตัวละครตำแหน่ง ${memberIndex + 1}`}
|
label={`เลือกตัวละครตำแหน่ง ${memberIndex + 1}`}
|
||||||
placeholder="ค้นหาชื่อตัวละคร…"
|
placeholder="ค้นหาชื่อตัวละคร…"
|
||||||
|
rarityBackground
|
||||||
invalid={memberFieldInvalid(
|
invalid={memberFieldInvalid(
|
||||||
teamIndex,
|
teamIndex,
|
||||||
memberIndex,
|
memberIndex,
|
||||||
"characterKey",
|
"characterKey",
|
||||||
)}
|
)}
|
||||||
className={cn(
|
className="rounded-lg border-0 [&>img]:object-cover [&>img]:p-0"
|
||||||
"rounded-lg border-0 [&>img]:object-cover [&>img]:p-0",
|
|
||||||
character &&
|
|
||||||
rarityGradientClass(
|
|
||||||
isCustomTravelerKey(
|
|
||||||
character.key,
|
|
||||||
)
|
|
||||||
? 5
|
|
||||||
: character.rarity,
|
|
||||||
),
|
|
||||||
)}
|
|
||||||
onChange={(characterKey) =>
|
onChange={(characterKey) =>
|
||||||
updateTeamMember(
|
updateTeamMember(
|
||||||
teamIndex,
|
teamIndex,
|
||||||
@@ -3589,6 +3583,11 @@ function TeamEditor({
|
|||||||
label={`เลือกอาวุธของตัวละครตำแหน่ง ${memberIndex + 1}`}
|
label={`เลือกอาวุธของตัวละครตำแหน่ง ${memberIndex + 1}`}
|
||||||
placeholder="ค้นหาอาวุธที่รองรับ…"
|
placeholder="ค้นหาอาวุธที่รองรับ…"
|
||||||
rarityBackground
|
rarityBackground
|
||||||
|
invalid={memberFieldInvalid(
|
||||||
|
teamIndex,
|
||||||
|
memberIndex,
|
||||||
|
"weaponKey",
|
||||||
|
)}
|
||||||
onChange={(weaponKey) =>
|
onChange={(weaponKey) =>
|
||||||
updateTeamMember(
|
updateTeamMember(
|
||||||
teamIndex,
|
teamIndex,
|
||||||
@@ -3610,6 +3609,11 @@ function TeamEditor({
|
|||||||
(key): key is string => Boolean(key),
|
(key): key is string => Boolean(key),
|
||||||
)}
|
)}
|
||||||
label={`เลือก Artifact สูงสุด 2 ชุดของตัวละครตำแหน่ง ${memberIndex + 1}`}
|
label={`เลือก Artifact สูงสุด 2 ชุดของตัวละครตำแหน่ง ${memberIndex + 1}`}
|
||||||
|
invalid={memberFieldInvalid(
|
||||||
|
teamIndex,
|
||||||
|
memberIndex,
|
||||||
|
"artifactKey",
|
||||||
|
)}
|
||||||
onChange={(artifactKeys) =>
|
onChange={(artifactKeys) =>
|
||||||
updateTeamMember(
|
updateTeamMember(
|
||||||
teamIndex,
|
teamIndex,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ function Empty({ className, ...props }: React.ComponentProps<"div">) {
|
|||||||
<div
|
<div
|
||||||
data-slot="empty"
|
data-slot="empty"
|
||||||
className={cn(
|
className={cn(
|
||||||
"flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-xl border-dashed p-6 text-center text-balance",
|
"flex w-full min-w-0 flex-1 flex-col items-center justify-center gap-4 rounded-xl border-dashed p-6 text-center text-balance aria-invalid:border aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
|
||||||
className
|
className
|
||||||
)}
|
)}
|
||||||
{...props}
|
{...props}
|
||||||
|
|||||||
+2
-2
@@ -479,8 +479,8 @@ export const teamMembers = guidesSchema.table(
|
|||||||
teamId: uuid("team_id").notNull().references(() => teamCompositions.id, { onDelete: "cascade" }),
|
teamId: uuid("team_id").notNull().references(() => teamCompositions.id, { onDelete: "cascade" }),
|
||||||
position: integer("position").notNull(),
|
position: integer("position").notNull(),
|
||||||
characterKey: varchar("character_key", { length: 64 }).notNull(),
|
characterKey: varchar("character_key", { length: 64 }).notNull(),
|
||||||
weaponKey: varchar("weapon_key", { length: 64 }),
|
weaponKey: varchar("weapon_key", { length: 64 }).notNull(),
|
||||||
artifactKey: varchar("artifact_key", { length: 64 }),
|
artifactKey: varchar("artifact_key", { length: 64 }).notNull(),
|
||||||
secondArtifactKey: varchar("second_artifact_key", { length: 64 }),
|
secondArtifactKey: varchar("second_artifact_key", { length: 64 }),
|
||||||
constellation: integer("constellation").default(0).notNull(),
|
constellation: integer("constellation").default(0).notNull(),
|
||||||
refinement: integer("refinement").default(1).notNull(),
|
refinement: integer("refinement").default(1).notNull(),
|
||||||
|
|||||||
@@ -184,15 +184,17 @@ describe("structured guide validation", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("requires four unique team members and non-zero damage", () => {
|
it("requires four unique team members and non-zero damage", () => {
|
||||||
const member = (characterKey: string) => ({ characterKey, weaponKey: null, artifactKey: null, secondArtifactKey: null, constellation: 0, refinement: 1, damage: 100 });
|
const member = (characterKey: string) => ({ characterKey, weaponKey: "weapon-1", artifactKey: "artifact-1", secondArtifactKey: null, constellation: 0, refinement: 1, damage: 100 });
|
||||||
const teams = [{ title: "Test", rotationSeconds: 20, combo: "E > Q", members: ["1", "2", "3", "4"].map(member) }];
|
const teams = [{ title: "Test", rotationSeconds: 20, combo: "E > Q", members: ["1", "2", "3", "4"].map(member) }];
|
||||||
const base = { expectedVersion: 1, groups: [{ note: null, teams }] };
|
const base = { expectedVersion: 1, groups: [{ note: null, teams }] };
|
||||||
expect(teamSectionSchema.safeParse(base).success).toBe(true);
|
expect(teamSectionSchema.safeParse(base).success).toBe(true);
|
||||||
expect(teamSectionSchema.safeParse({ ...base, groups: [{ note: null, teams: [{ ...teams[0], members: ["1", "1", "3", "4"].map(member) }] }] }).success).toBe(false);
|
expect(teamSectionSchema.safeParse({ ...base, groups: [{ note: null, teams: [{ ...teams[0], members: ["1", "1", "3", "4"].map(member) }] }] }).success).toBe(false);
|
||||||
|
expect(teamSectionSchema.safeParse({ ...base, groups: [{ note: null, teams: [{ ...teams[0], members: [{ ...member("1"), weaponKey: null }, ...["2", "3", "4"].map(member)] }] }] }).success).toBe(false);
|
||||||
|
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 a second artifact set for a team member", () => {
|
it("accepts a second artifact set for a team member", () => {
|
||||||
const member = (characterKey: string) => ({ characterKey, weaponKey: null, artifactKey: "artifact-1", secondArtifactKey: characterKey === "1" ? "artifact-2" : null, constellation: 0, refinement: 1, damage: 100 });
|
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) }] }] };
|
const input = { expectedVersion: 1, groups: [{ note: null, teams: [{ title: "Test", rotationSeconds: 20, combo: "E > Q", members: ["1", "2", "3", "4"].map(member) }] }] };
|
||||||
expect(teamSectionSchema.safeParse(input).success).toBe(true);
|
expect(teamSectionSchema.safeParse(input).success).toBe(true);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -156,8 +156,8 @@ export const teamSectionSchema = z.strictObject({
|
|||||||
combo: z.string().trim().min(1).max(2_000),
|
combo: z.string().trim().min(1).max(2_000),
|
||||||
members: z.array(z.strictObject({
|
members: z.array(z.strictObject({
|
||||||
characterKey: catalogKey,
|
characterKey: catalogKey,
|
||||||
weaponKey: catalogKey.nullable(),
|
weaponKey: catalogKey,
|
||||||
artifactKey: catalogKey.nullable(),
|
artifactKey: catalogKey,
|
||||||
secondArtifactKey: catalogKey.nullable(),
|
secondArtifactKey: catalogKey.nullable(),
|
||||||
constellation: z.number().int().min(0).max(6),
|
constellation: z.number().int().min(0).max(6),
|
||||||
refinement: z.number().int().min(1).max(5),
|
refinement: z.number().int().min(1).max(5),
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ interface ValidationIssue {
|
|||||||
|
|
||||||
const fieldMessages: Record<string, string> = {
|
const fieldMessages: Record<string, string> = {
|
||||||
weaponKey: "กรุณาเลือกอาวุธ",
|
weaponKey: "กรุณาเลือกอาวุธ",
|
||||||
|
artifactKey: "กรุณาเลือก Artifact",
|
||||||
firstArtifactKey: "กรุณาเลือก Artifact ชุดแรก",
|
firstArtifactKey: "กรุณาเลือก Artifact ชุดแรก",
|
||||||
characterKey: "กรุณาเลือกตัวละคร",
|
characterKey: "กรุณาเลือกตัวละคร",
|
||||||
overallPercent: "กรุณากรอก Overall % ระหว่าง -9,999 ถึง 9,999",
|
overallPercent: "กรุณากรอก Overall % ระหว่าง -9,999 ถึง 9,999",
|
||||||
|
|||||||
@@ -0,0 +1,35 @@
|
|||||||
|
# shadcn/ui customizations
|
||||||
|
|
||||||
|
This project keeps local changes to generated shadcn/ui components. Review these customizations before updating or reinstalling components.
|
||||||
|
|
||||||
|
## `Empty`
|
||||||
|
|
||||||
|
File: `components/ui/empty.tsx`
|
||||||
|
|
||||||
|
The root `Empty` component supports an invalid state through `aria-invalid`:
|
||||||
|
|
||||||
|
```tsx
|
||||||
|
<Empty aria-invalid={hasError}>
|
||||||
|
{/* empty-state content */}
|
||||||
|
</Empty>
|
||||||
|
```
|
||||||
|
|
||||||
|
The customization adds a destructive border and ring with these classes:
|
||||||
|
|
||||||
|
```text
|
||||||
|
aria-invalid:border
|
||||||
|
aria-invalid:border-destructive
|
||||||
|
aria-invalid:ring-3
|
||||||
|
aria-invalid:ring-destructive/20
|
||||||
|
dark:aria-invalid:border-destructive/50
|
||||||
|
dark:aria-invalid:ring-destructive/40
|
||||||
|
```
|
||||||
|
|
||||||
|
Before updating the component, inspect the upstream diff:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
bunx --bun shadcn@latest add empty --dry-run
|
||||||
|
bunx --bun shadcn@latest add empty --diff empty.tsx
|
||||||
|
```
|
||||||
|
|
||||||
|
Manually merge upstream changes and preserve the invalid-state classes. Do not use `--overwrite` unless removing local customizations is intentional.
|
||||||
Reference in New Issue
Block a user