diff --git a/app/api/teams/[teamId]/image/route.tsx b/app/api/teams/[teamId]/image/route.tsx index 08c7bf7..0842c52 100644 --- a/app/api/teams/[teamId]/image/route.tsx +++ b/app/api/teams/[teamId]/image/route.tsx @@ -2,6 +2,7 @@ import { ImageResponse } from "next/og"; import sharp from "sharp"; +import { rarityGradient } from "@/lib/catalog/rarity"; import { getPublicTeamExport } from "@/lib/guides/queries"; const anuphanFonts = Promise.all( @@ -28,13 +29,6 @@ function amount(value: number): string { return new Intl.NumberFormat("en-US", { maximumFractionDigits: 0 }).format(value); } -function weaponBackground(rarity: number | null): string { - if (rarity === 5) return "linear-gradient(90deg, #8a5a00 0%, #0f0800 100%)"; - if (rarity === 4) return "linear-gradient(90deg, #a300b8 0%, #000000 100%)"; - if (rarity === 3) return "linear-gradient(90deg, #00a0b5 0%, #000000 100%)"; - return "#17383d"; -} - function contentDisposition(teamName: string): string { const displayName = teamName.trim() || "team"; const fallback = displayName @@ -133,7 +127,7 @@ export async function GET( )}
{member.weapon && memberAssets[index].weapon ? ( -
+
R{member.refinement} @@ -141,7 +135,7 @@ export async function GET(
) : null} {member.artifact && memberAssets[index].artifact ? ( -
+
) : null} diff --git a/components/admin/guide-section-editor.tsx b/components/admin/guide-section-editor.tsx index 6193a32..f8bcb43 100644 --- a/components/admin/guide-section-editor.tsx +++ b/components/admin/guide-section-editor.tsx @@ -41,6 +41,7 @@ import { weaponSectionSchema, } from "@/lib/guides/schemas"; import { supportsWeaponType } from "@/lib/catalog/lunaris"; +import { rarityGradientClass } from "@/lib/catalog/rarity"; import { cn } from "@/lib/utils"; type CatalogItem = { key: string; name: string; imageUrl: string; rarity?: number | null; weaponType?: string | null }; @@ -71,7 +72,7 @@ export interface GuideSectionEditorProps { const DEFAULT_TARGET_STATS = ["ATK", "EM", "ER", "CRIT Rate", "CRIT DMG"] as const; -function CatalogPicker({ items, value, onChange, placeholder, weaponBackground = false }: { items: CatalogItem[]; value: string; onChange: (value: string) => void; placeholder: string; weaponBackground?: boolean }) { +function CatalogPicker({ items, value, onChange, placeholder, rarityBackground = false }: { items: CatalogItem[]; value: string; onChange: (value: string) => void; placeholder: string; rarityBackground?: boolean }) { const selected = items.find((item) => item.key === value) ?? null; return ( onChange(next?.key ?? "")} itemToStringLabel={(item) => item.name} itemToStringValue={(item) => item.key}> @@ -79,7 +80,7 @@ function CatalogPicker({ items, value, onChange, placeholder, weaponBackground = ไม่พบรายการ - {items.map((item) =>
{item.name}
)} + {items.map((item) =>
{item.name}
)}
@@ -134,7 +135,7 @@ function WeaponEditor({ guide, weapons, characters, initial }: EditorBase) { Weapon tier listเรียงอาวุธจากดีที่สุดลงมา และกรอกเปอร์เซ็นต์อย่างน้อยหนึ่งช่อง ระบบจะบันทึกอัตโนมัติ - {rows.map((row, rowIndex) => { const selectedWeapon = compatibleWeapons.find((weapon) => weapon.key === row.weaponKey); return {selectedWeapon ?
: null}อันดับ {rowIndex + 1}
setRows(moveItem(rows, from, to))} remove={() => setRows(rows.filter((_, index) => index !== rowIndex))} />
อาวุธ setRows(rows.map((item, index) => index === rowIndex ? { ...item, weaponKey } : item))} />Refinement { const next = selected[0]; if (next) setRows(rows.map((item, index) => index === rowIndex ? { ...item, refinement: Number(next.slice(1)) } : item)); }}>{[1, 2, 3, 4, 5].map((refinement) => R{refinement})}
Overall % setRows(rows.map((item, index) => index === rowIndex ? { ...item, overallPercent: event.target.value } : item))} />Personal % setRows(rows.map((item, index) => index === rowIndex ? { ...item, personalPercent: event.target.value } : item))} />
; })} + {rows.map((row, rowIndex) => { const selectedWeapon = compatibleWeapons.find((weapon) => weapon.key === row.weaponKey); return {selectedWeapon ?
: null}อันดับ {rowIndex + 1}
setRows(moveItem(rows, from, to))} remove={() => setRows(rows.filter((_, index) => index !== rowIndex))} />
อาวุธ setRows(rows.map((item, index) => index === rowIndex ? { ...item, weaponKey } : item))} />Refinement { const next = selected[0]; if (next) setRows(rows.map((item, index) => index === rowIndex ? { ...item, refinement: Number(next.slice(1)) } : item)); }}>{[1, 2, 3, 4, 5].map((refinement) => R{refinement})}
Overall % setRows(rows.map((item, index) => index === rowIndex ? { ...item, overallPercent: event.target.value } : item))} />Personal % setRows(rows.map((item, index) => index === rowIndex ? { ...item, personalPercent: event.target.value } : item))} />
; })}
@@ -163,7 +164,7 @@ function ArtifactEditor({ guide, artifacts, initial }: EditorBase) { Artifact tier listสร้างกลุ่มและค้นหาชุด Artifact ด้วยชื่อ ระบบจะบันทึกอัตโนมัติ - {groups.map((group, groupIndex) => setGroups(groups.map((item, index) => index === groupIndex ? { ...item, name: event.target.value } : item))} />{group.rows.map((row, rowIndex) =>
setGroups(groups.map((item, index) => index === groupIndex ? { ...item, rows: item.rows.map((candidate, inner) => inner === rowIndex ? { ...candidate, firstArtifactKey } : candidate) } : item))} /> setGroups(groups.map((item, index) => index === groupIndex ? { ...item, rows: item.rows.map((candidate, inner) => inner === rowIndex ? { ...candidate, secondArtifactKey: secondArtifactKey || null } : candidate) } : item))} /> setGroups(groups.map((item, index) => index === groupIndex ? { ...item, rows: item.rows.map((candidate, inner) => inner === rowIndex ? { ...candidate, percent: event.target.value } : candidate) } : item))} />
)}
)} + {groups.map((group, groupIndex) => setGroups(groups.map((item, index) => index === groupIndex ? { ...item, name: event.target.value } : item))} />{group.rows.map((row, rowIndex) =>
setGroups(groups.map((item, index) => index === groupIndex ? { ...item, rows: item.rows.map((candidate, inner) => inner === rowIndex ? { ...candidate, firstArtifactKey } : candidate) } : item))} /> setGroups(groups.map((item, index) => index === groupIndex ? { ...item, rows: item.rows.map((candidate, inner) => inner === rowIndex ? { ...candidate, secondArtifactKey: secondArtifactKey || null } : candidate) } : item))} /> setGroups(groups.map((item, index) => index === groupIndex ? { ...item, rows: item.rows.map((candidate, inner) => inner === rowIndex ? { ...candidate, percent: event.target.value } : candidate) } : item))} />
)}
)}
@@ -203,7 +204,7 @@ function TeamEditor({ guide, characters, weapons, artifacts, initial }: EditorBa Team Calcใส่ความเสียหายของตัวละคร 4 ตัว ระบบจะแสดงสัดส่วนและ Total DPS และบันทึกอัตโนมัติ - {teams.map((team, teamIndex) => { const total = team.members.reduce((sum, member) => sum + (Number(member.damage) || 0), 0); const dps = total / (Number(team.rotationSeconds) || 1); return Team {teamIndex + 1}Damage {total.toLocaleString()} • DPS {dps.toFixed(0)} setTeams(moveItem(teams, from, to))} remove={() => setTeams(teams.filter((_, index) => index !== teamIndex))} />ชื่อ Team setTeams(teams.map((item, index) => index === teamIndex ? { ...item, title: event.target.value } : item))} />
{team.members.map((member, memberIndex) => { const character = characters.find((item) => item.key === member.characterKey); const compatibleWeapons = weapons.filter((weapon) => supportsWeaponType(character?.weaponType, weapon.weaponType)); return ตัวละคร {memberIndex + 1}{total ? `${((Number(member.damage) || 0) / total * 100).toFixed(2)}% ของ Team` : "0% ของ Team"}{character ? {character.name.slice(0, 2).toUpperCase()} : null}ตัวละคร setTeams(teams.map((item, index) => index === teamIndex ? { ...item, members: item.members.map((candidate, inner) => inner === memberIndex ? { ...candidate, characterKey, weaponKey: null, artifactKey: null, constellation: 0, refinement: 1 } : candidate) } : item))} />Damage setTeams(teams.map((item, index) => index === teamIndex ? { ...item, members: item.members.map((candidate, inner) => inner === memberIndex ? { ...candidate, damage: event.target.value } : candidate) } : item))} />Weapon setTeams(teams.map((item, index) => index === teamIndex ? { ...item, members: item.members.map((candidate, inner) => inner === memberIndex ? { ...candidate, weaponKey: weaponKey || null } : candidate) } : item))} />Refinement { const refinement = selected[0]; if (refinement) setTeams(teams.map((item, index) => index === teamIndex ? { ...item, members: item.members.map((candidate, inner) => inner === memberIndex ? { ...candidate, refinement: Number(refinement.slice(1)) } : candidate) } : item)); }}>{[1, 2, 3, 4, 5].map((refinement) => R{refinement})}Artifact set setTeams(teams.map((item, index) => index === teamIndex ? { ...item, members: item.members.map((candidate, inner) => inner === memberIndex ? { ...candidate, artifactKey: artifactKey || null } : candidate) } : item))} />Constellation { const constellation = selected[0]; if (constellation) setTeams(teams.map((item, index) => index === teamIndex ? { ...item, members: item.members.map((candidate, inner) => inner === memberIndex ? { ...candidate, constellation: Number(constellation.slice(1)) } : candidate) } : item)); }}>{[0, 1, 2, 3, 4, 5, 6].map((constellation) => C{constellation})}; })}
Rotation (seconds) setTeams(teams.map((item, index) => index === teamIndex ? { ...item, rotationSeconds: event.target.value } : item))} />Combo