strat-gameplay-webapp/frontend-sba/constants/outcomeFlow.ts
Cal Corum be31e2ccb4 CLAUDE: Complete in-game UI overhaul with player cards and outcome wizard
Features:
- PlayerCardModal: Tap any player to view full playing card image
- OutcomeWizard: Progressive 3-step outcome selection (On Base/Out/X-Check)
- GameBoard: Expandable view showing all 9 fielder positions
- Post-roll card display: Shows batter/pitcher card based on d6 roll
- CurrentSituation: Tappable player cards with modal integration

Bug fixes:
- Fix batter not advancing after play (state_manager recovery logic)
- Add dark mode support for buttons and panels (partial - iOS issue noted)

New files:
- PlayerCardModal.vue, OutcomeWizard.vue, BottomSheet.vue
- outcomeFlow.ts constants for outcome category mapping
- TEST_PLAN_UI_OVERHAUL.md with 23/24 tests passing

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-23 15:23:38 -06:00

221 lines
5.0 KiB
TypeScript

/**
* Outcome Flow Constants
*
* Defines the hierarchical structure for the progressive disclosure
* outcome selection wizard. Three top-level categories branch into
* specific outcome types.
*
* Categories:
* - ON_BASE: Hits and walks that result in batter reaching base
* - OUT: Various types of outs
* - X_CHECK: Defensive X-Check resolution (errors result from this)
*/
import type { PlayOutcome } from '~/types/game'
/**
* Top-level outcome categories for step 1 selection
*/
export type OutcomeCategory = 'ON_BASE' | 'OUT' | 'X_CHECK'
/**
* Sub-categories for ON_BASE outcomes
*/
export type OnBaseSubCategory = 'SINGLE' | 'DOUBLE' | 'TRIPLE' | 'HOME_RUN' | 'WALK' | 'HBP'
/**
* Sub-categories for OUT outcomes
*/
export type OutSubCategory = 'STRIKEOUT' | 'GROUNDOUT' | 'FLYOUT' | 'LINEOUT' | 'POPOUT'
/**
* Category configuration with display info
*/
export interface CategoryConfig {
label: string
description: string
color: string
bgColor: string
borderColor: string
}
/**
* Sub-category configuration with outcomes
*/
export interface SubCategoryConfig {
label: string
outcomes: PlayOutcome[]
}
/**
* Category display configuration
*/
export const CATEGORY_CONFIG: Record<OutcomeCategory, CategoryConfig> = {
ON_BASE: {
label: 'On Base',
description: 'Hit, walk, or HBP',
color: 'text-green-700',
bgColor: 'bg-green-50 hover:bg-green-100',
borderColor: 'border-green-300',
},
OUT: {
label: 'Out',
description: 'Strikeout, groundout, flyout',
color: 'text-red-700',
bgColor: 'bg-red-50 hover:bg-red-100',
borderColor: 'border-red-300',
},
X_CHECK: {
label: 'X-Check',
description: 'Defensive play check',
color: 'text-orange-700',
bgColor: 'bg-orange-50 hover:bg-orange-100',
borderColor: 'border-orange-300',
},
}
/**
* ON_BASE sub-categories and their specific outcomes
*/
export const ON_BASE_OUTCOMES: Record<OnBaseSubCategory, SubCategoryConfig> = {
SINGLE: {
label: 'Single',
outcomes: ['single_1', 'single_2', 'single_uncapped'],
},
DOUBLE: {
label: 'Double',
outcomes: ['double_2', 'double_3', 'double_uncapped'],
},
TRIPLE: {
label: 'Triple',
outcomes: ['triple'],
},
HOME_RUN: {
label: 'Home Run',
outcomes: ['homerun'],
},
WALK: {
label: 'Walk',
outcomes: ['walk', 'intentional_walk'],
},
HBP: {
label: 'Hit By Pitch',
outcomes: ['hbp'],
},
}
/**
* OUT sub-categories and their specific outcomes
*/
export const OUT_OUTCOMES: Record<OutSubCategory, SubCategoryConfig> = {
STRIKEOUT: {
label: 'Strikeout',
outcomes: ['strikeout'],
},
GROUNDOUT: {
label: 'Groundout',
outcomes: ['groundball_a', 'groundball_b', 'groundball_c'],
},
FLYOUT: {
label: 'Flyout',
outcomes: ['flyout_a', 'flyout_b', 'flyout_bq', 'flyout_c'],
},
LINEOUT: {
label: 'Lineout',
outcomes: ['lineout'],
},
POPOUT: {
label: 'Popout',
outcomes: ['popout'],
},
}
/**
* X-CHECK outcome (singular - error is a result, not input)
*/
export const X_CHECK_OUTCOMES: PlayOutcome[] = ['x_check']
/**
* Outcome display labels for final selection
*/
export const OUTCOME_LABELS: Partial<Record<PlayOutcome, string>> = {
// Singles
single_1: 'Single (1 base)',
single_2: 'Single (2 bases)',
single_uncapped: 'Single (uncapped)',
// Doubles
double_2: 'Double (2 bases)',
double_3: 'Double (3 bases)',
double_uncapped: 'Double (uncapped)',
// Other hits
triple: 'Triple',
homerun: 'Home Run',
// Walks
walk: 'Walk',
intentional_walk: 'Intentional Walk',
hbp: 'Hit By Pitch',
// Outs
strikeout: 'Strikeout',
groundball_a: 'Groundball A',
groundball_b: 'Groundball B',
groundball_c: 'Groundball C',
flyout_a: 'Flyout A',
flyout_b: 'Flyout B',
flyout_bq: 'Flyout B*',
flyout_c: 'Flyout C',
lineout: 'Lineout',
popout: 'Popout',
// X-Check
x_check: 'X-Check',
}
/**
* Hit location positions for location selection step
*/
export const HIT_LOCATIONS = [
{ id: 'P', label: 'P', position: 'Pitcher' },
{ id: 'C', label: 'C', position: 'Catcher' },
{ id: '1B', label: '1B', position: 'First Base' },
{ id: '2B', label: '2B', position: 'Second Base' },
{ id: 'SS', label: 'SS', position: 'Shortstop' },
{ id: '3B', label: '3B', position: 'Third Base' },
{ id: 'LF', label: 'LF', position: 'Left Field' },
{ id: 'CF', label: 'CF', position: 'Center Field' },
{ id: 'RF', label: 'RF', position: 'Right Field' },
] as const
/**
* Outcomes that require hit location selection
*/
export const OUTCOMES_REQUIRING_LOCATION: PlayOutcome[] = [
'groundball_a',
'groundball_b',
'groundball_c',
'flyout_a',
'flyout_b',
'flyout_bq',
'flyout_c',
'lineout',
'popout',
'x_check',
]
/**
* Check if an outcome requires a hit location
*/
export function requiresHitLocation(outcome: PlayOutcome): boolean {
return OUTCOMES_REQUIRING_LOCATION.includes(outcome)
}
/**
* Get the display label for an outcome
*/
export function getOutcomeLabel(outcome: PlayOutcome): string {
return OUTCOME_LABELS[outcome] || outcome
}