feat: show level 90 base stats
This commit is contained in:
@@ -14,6 +14,7 @@ import { Separator } from "@/components/ui/separator";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { calculateSubstats, generateTeam, SUBSTAT_SCALARS } from "@/lib/translator/generate";
|
||||
import { formatBaseStat } from "@/lib/translator/stat-format";
|
||||
import { rollKeys, translationSchema, type Build, type Catalog, type CatalogItem, type MainStatKey, type RollKey, type Team, type Translation } from "@/lib/translator/types";
|
||||
|
||||
const emptyRolls = Object.fromEntries(rollKeys.map((key) => [key, 0])) as Record<RollKey, number>;
|
||||
@@ -61,8 +62,14 @@ function StatSelect({ value, values, onChange }: { value: MainStatKey; values: M
|
||||
return <Select value={value} onValueChange={(next) => onChange(next as MainStatKey)}><SelectTrigger className="w-full"><SelectValue /></SelectTrigger><SelectContent><SelectGroup>{values.map((item) => <SelectItem key={item} value={item}>{item.toUpperCase()}</SelectItem>)}</SelectGroup></SelectContent></Select>;
|
||||
}
|
||||
|
||||
function BaseStats({ stats }: { stats: CatalogItem["baseStats"] }) {
|
||||
if (!stats.length) return <FieldDescription>Level 90 base stats unavailable.</FieldDescription>;
|
||||
return <div className="flex flex-wrap gap-2" aria-label="Level 90 base stats">{stats.map((stat) => <Badge key={stat.key} variant="outline">{formatBaseStat(stat)}</Badge>)}</div>;
|
||||
}
|
||||
|
||||
function BuildEditor({ build, catalog, onChange, onRemove, canRemove }: { build: Build; catalog: Catalog; onChange: (build: Build) => void; onRemove: () => void; canRemove: boolean }) {
|
||||
const selectedCharacter = catalog.characters.find((item) => item.id === build.characterId);
|
||||
const selectedWeapon = catalog.weapons.find((item) => item.id === build.weaponId);
|
||||
const weapons = catalog.weapons.filter((item) => !selectedCharacter?.weaponType || item.weaponType === selectedCharacter.weaponType);
|
||||
const update = <K extends keyof Build>(key: K, value: Build[K]) => onChange({ ...build, [key]: value });
|
||||
const chooseCharacter = (item: CatalogItem) => {
|
||||
@@ -72,10 +79,10 @@ function BuildEditor({ build, catalog, onChange, onRemove, canRemove }: { build:
|
||||
const calculated = calculateSubstats(build);
|
||||
const totalRolls = Object.values(build.rolls).reduce((sum, value) => sum + value, 0);
|
||||
return <Card><CardHeader className="flex-row items-start justify-between"><div><CardTitle>{build.characterName}</CardTitle><CardDescription>{build.characterKey} · {totalRolls} total rolls</CardDescription></div><Button variant="ghost" size="icon" disabled={!canRemove} onClick={onRemove} aria-label="Remove character"><Trash2Icon /></Button></CardHeader><CardContent><FieldGroup>
|
||||
<Field><FieldLabel>Character</FieldLabel><CatalogCombobox items={catalog.characters} value={build.characterId} onChange={chooseCharacter} placeholder="Choose character" /></Field>
|
||||
<Field><FieldLabel>Character</FieldLabel><CatalogCombobox items={catalog.characters} value={build.characterId} onChange={chooseCharacter} placeholder="Choose character" /><BaseStats stats={selectedCharacter?.baseStats ?? []} /></Field>
|
||||
<div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-4"><Field><FieldLabel>Level</FieldLabel><NumberSelect value={build.level} values={levels.filter((v) => v <= build.maxLevel)} onChange={(v) => update("level", v)} /></Field><Field><FieldLabel>Ascension cap</FieldLabel><NumberSelect value={build.maxLevel} values={levels} onChange={(v) => onChange({ ...build, maxLevel: v, level: Math.min(build.level, v) })} /></Field><Field><FieldLabel>Constellation</FieldLabel><NumberSelect value={build.constellation} values={[0,1,2,3,4,5,6]} onChange={(v) => update("constellation", v)} /></Field><Field><FieldLabel>Talents NA / Skill / Burst</FieldLabel><div className="grid grid-cols-3 gap-2">{build.talents.map((value, index) => <Input key={index} type="number" min={1} max={15} value={value} onChange={(event) => { const next = [...build.talents] as Build["talents"]; next[index] = Number(event.target.value); update("talents", next); }} aria-label={["Normal attack", "Skill", "Burst"][index]} />)}</div></Field></div>
|
||||
<Separator />
|
||||
<div className="grid gap-6 lg:grid-cols-[2fr_1fr_1fr_1fr]"><Field><FieldLabel>Weapon</FieldLabel><CatalogCombobox items={weapons} value={build.weaponId} onChange={(item) => onChange({ ...build, weaponId: item.id, weaponKey: item.key, weaponName: item.name })} placeholder="Choose weapon" /></Field><Field><FieldLabel>Level</FieldLabel><NumberSelect value={build.weaponLevel} values={levels.filter((v) => v <= build.weaponMaxLevel)} onChange={(v) => update("weaponLevel", v)} /></Field><Field><FieldLabel>Ascension cap</FieldLabel><NumberSelect value={build.weaponMaxLevel} values={levels} onChange={(v) => onChange({ ...build, weaponMaxLevel: v, weaponLevel: Math.min(build.weaponLevel, v) })} /></Field><Field><FieldLabel>Refinement</FieldLabel><NumberSelect value={build.refinement} values={[1,2,3,4,5]} onChange={(v) => update("refinement", v)} /></Field></div>
|
||||
<div className="grid gap-6 lg:grid-cols-[2fr_1fr_1fr_1fr]"><Field><FieldLabel>Weapon</FieldLabel><CatalogCombobox items={weapons} value={build.weaponId} onChange={(item) => onChange({ ...build, weaponId: item.id, weaponKey: item.key, weaponName: item.name })} placeholder="Choose weapon" /><BaseStats stats={selectedWeapon?.baseStats ?? []} /></Field><Field><FieldLabel>Level</FieldLabel><NumberSelect value={build.weaponLevel} values={levels.filter((v) => v <= build.weaponMaxLevel)} onChange={(v) => update("weaponLevel", v)} /></Field><Field><FieldLabel>Ascension cap</FieldLabel><NumberSelect value={build.weaponMaxLevel} values={levels} onChange={(v) => onChange({ ...build, weaponMaxLevel: v, weaponLevel: Math.min(build.weaponLevel, v) })} /></Field><Field><FieldLabel>Refinement</FieldLabel><NumberSelect value={build.refinement} values={[1,2,3,4,5]} onChange={(v) => update("refinement", v)} /></Field></div>
|
||||
<Separator />
|
||||
<FieldSet><FieldLegend>Artifact sets and main stats</FieldLegend><div className="grid gap-6 sm:grid-cols-2 lg:grid-cols-3"><Field><FieldLabel>Set layout</FieldLabel><Select value={build.artifactMode} onValueChange={(v) => update("artifactMode", v as Build["artifactMode"])}><SelectTrigger className="w-full"><SelectValue /></SelectTrigger><SelectContent><SelectGroup><SelectItem value="4pc">4-piece set</SelectItem><SelectItem value="2+2">2-piece + 2-piece</SelectItem></SelectGroup></SelectContent></Select></Field><Field><FieldLabel>First set</FieldLabel><CatalogCombobox items={catalog.artifacts} value={build.artifactOneId} onChange={(item) => onChange({ ...build, artifactOneId: item.id, artifactOneKey: item.key, artifactOneName: item.name })} placeholder="Choose artifact" /></Field>{build.artifactMode === "2+2" ? <Field><FieldLabel>Second set</FieldLabel><CatalogCombobox items={catalog.artifacts.filter((item) => item.id !== build.artifactOneId)} value={build.artifactTwoId} onChange={(item) => onChange({ ...build, artifactTwoId: item.id, artifactTwoKey: item.key, artifactTwoName: item.name })} placeholder="Choose artifact" /></Field> : null}<Field><FieldLabel>Sands</FieldLabel><StatSelect value={build.sands} values={sands} onChange={(v) => update("sands", v as Build["sands"])} /></Field><Field><FieldLabel>Goblet</FieldLabel><StatSelect value={build.goblet} values={goblets} onChange={(v) => update("goblet", v as Build["goblet"])} /></Field><Field><FieldLabel>Circlet</FieldLabel><StatSelect value={build.circlet} values={circlets} onChange={(v) => update("circlet", v as Build["circlet"])} /></Field></div></FieldSet>
|
||||
<Separator />
|
||||
|
||||
Reference in New Issue
Block a user