feat: show final character stats
CI / verify (push) Successful in 33s
CI / build-and-deploy (push) Successful in 1m7s

This commit is contained in:
2026-09-09 20:42:39 +07:00 Unverified
parent 3314b2a9c6
commit 4159630b0e
3 changed files with 104 additions and 1 deletions
+5 -1
View File
@@ -15,6 +15,7 @@ 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 { calculateFinalStats, formatFinalStat } from "@/lib/translator/final-stats";
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>;
@@ -77,8 +78,11 @@ function BuildEditor({ build, catalog, onChange, onRemove, canRemove }: { build:
onChange({ ...build, characterId: item.id, characterKey: item.key, characterName: item.name, characterImageUrl: item.imageUrl, weaponId: weapon.id, weaponKey: weapon.key, weaponName: weapon.name });
};
const calculated = calculateSubstats(build);
const finalStats = selectedCharacter?.baseStats.length && selectedWeapon?.baseStats.length
? calculateFinalStats(build, selectedCharacter.baseStats, selectedWeapon.baseStats)
: [];
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>
return <Card><CardHeader className="flex-row items-start justify-between gap-4"><div><CardTitle>{build.characterName}</CardTitle><CardDescription>{build.characterKey} · {totalRolls} total rolls</CardDescription></div><div className="flex items-start gap-2"><div className="flex max-w-xl flex-wrap justify-end gap-2" aria-label="Final level 90 stats">{finalStats.length ? finalStats.map((stat) => <Badge key={stat.key} variant="secondary">{formatFinalStat(stat)}</Badge>) : <span className="text-sm text-muted-foreground">Final stats unavailable</span>}</div><Button variant="ghost" size="icon" disabled={!canRemove} onClick={onRemove} aria-label="Remove character"><Trash2Icon /></Button></div></CardHeader><CardContent><FieldGroup>
<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 />