From 979f57f184c318c41d286e1c89967d18ff29bf44 Mon Sep 17 00:00:00 2001 From: Julian Tabel Date: Sun, 8 Feb 2026 12:03:11 +0100 Subject: [PATCH] Add collapsible boss teams and hide attempts in hardcore mode Boss battle cards now collapse the pokemon team by default with a chevron toggle. Expanded teams show larger sprites with "Lvl" prefix. In hardcore mode, the BossDefeatModal hides the attempts field and lost button since a loss ends the run. Co-Authored-By: Claude Opus 4.6 --- ...-card-collapsible-team-hardcore-mode-cl.md | 11 ++++ frontend/src/components/BossDefeatModal.tsx | 53 ++++++++++--------- frontend/src/pages/RunEncounters.tsx | 36 ++++++++++--- 3 files changed, 70 insertions(+), 30 deletions(-) create mode 100644 .beans/nuzlocke-tracker-qars--boss-battle-card-collapsible-team-hardcore-mode-cl.md diff --git a/.beans/nuzlocke-tracker-qars--boss-battle-card-collapsible-team-hardcore-mode-cl.md b/.beans/nuzlocke-tracker-qars--boss-battle-card-collapsible-team-hardcore-mode-cl.md new file mode 100644 index 0000000..94d33eb --- /dev/null +++ b/.beans/nuzlocke-tracker-qars--boss-battle-card-collapsible-team-hardcore-mode-cl.md @@ -0,0 +1,11 @@ +--- +# nuzlocke-tracker-qars +title: 'Boss battle card: collapsible team + hardcore mode cleanup' +status: completed +type: feature +priority: normal +created_at: 2026-02-08T11:01:17Z +updated_at: 2026-02-08T11:02:45Z +--- + +Make boss battle cards collapsible (team hidden by default), larger sprites with Lvl prefix, and hide attempts/lost button in hardcore mode \ No newline at end of file diff --git a/frontend/src/components/BossDefeatModal.tsx b/frontend/src/components/BossDefeatModal.tsx index e3c2089..324f387 100644 --- a/frontend/src/components/BossDefeatModal.tsx +++ b/frontend/src/components/BossDefeatModal.tsx @@ -6,9 +6,10 @@ interface BossDefeatModalProps { onSubmit: (data: CreateBossResultInput) => void onClose: () => void isPending?: boolean + hardcoreMode?: boolean } -export function BossDefeatModal({ boss, onSubmit, onClose, isPending }: BossDefeatModalProps) { +export function BossDefeatModal({ boss, onSubmit, onClose, isPending, hardcoreMode }: BossDefeatModalProps) { const [result, setResult] = useState<'won' | 'lost'>('won') const [attempts, setAttempts] = useState('1') @@ -16,8 +17,8 @@ export function BossDefeatModal({ boss, onSubmit, onClose, isPending }: BossDefe e.preventDefault() onSubmit({ bossBattleId: boss.id, - result, - attempts: Number(attempts) || 1, + result: hardcoreMode ? 'won' : result, + attempts: hardcoreMode ? 1 : Number(attempts) || 1, }) } @@ -71,30 +72,34 @@ export function BossDefeatModal({ boss, onSubmit, onClose, isPending }: BossDefe > Won - + {!hardcoreMode && ( + + )} -
- - setAttempts(e.target.value)} - className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600" - /> -
+ {!hardcoreMode && ( +
+ + setAttempts(e.target.value)} + className="w-full px-3 py-2 border rounded-md dark:bg-gray-700 dark:border-gray-600" + /> +
+ )}
diff --git a/frontend/src/pages/RunEncounters.tsx b/frontend/src/pages/RunEncounters.tsx index f1456f8..f740a24 100644 --- a/frontend/src/pages/RunEncounters.tsx +++ b/frontend/src/pages/RunEncounters.tsx @@ -337,6 +337,7 @@ export function RunEncounters() { useState(null) const [showEndRun, setShowEndRun] = useState(false) const [showShinyModal, setShowShinyModal] = useState(false) + const [expandedBosses, setExpandedBosses] = useState>(new Set()) const [showTeam, setShowTeam] = useState(true) const [filter, setFilter] = useState<'all' | RouteStatus>('all') @@ -1021,6 +1022,16 @@ export function RunEncounters() { other: 'border-gray-400 dark:border-gray-500', } + const isBossExpanded = expandedBosses.has(boss.id) + const toggleBoss = () => { + setExpandedBosses((prev) => { + const next = new Set(prev) + if (next.has(boss.id)) next.delete(boss.id) + else next.add(boss.id) + return next + }) + } + return (
-
+
+ + + {boss.spriteUrl && ( {boss.name} )} @@ -1047,7 +1070,7 @@ export function RunEncounters() {

-
+
e.stopPropagation()}> {isDefeated ? ( Defeated ✓ @@ -1063,19 +1086,19 @@ export function RunEncounters() {
{/* Boss pokemon team */} - {boss.pokemon.length > 0 && ( + {isBossExpanded && boss.pokemon.length > 0 && (
{boss.pokemon .sort((a, b) => a.order - b.order) .map((bp) => (
{bp.pokemon.spriteUrl ? ( - {bp.pokemon.name} + {bp.pokemon.name} ) : ( -
+
)} - {bp.level} + Lvl {bp.level}
))} @@ -1141,6 +1164,7 @@ export function RunEncounters() { }} onClose={() => setSelectedBoss(null)} isPending={createBossResult.isPending} + hardcoreMode={run?.rules?.hardcoreMode} /> )}