2021-01-10 06:27:30 +00:00
import React , { useState , useRef , useLayoutEffect , useReducer } from 'react' ;
2021-01-15 02:26:54 +00:00
import { removeIndex , replaceIndex , append , arrayOf , shallowClone , getUID , DistributiveOmit } from './util' ;
2021-01-10 06:27:30 +00:00
import './CreateLeague.css' ;
import twemoji from 'twemoji' ;
2021-01-16 06:13:12 +00:00
// CONSTS
const MAX_SUBLEAGUE_DIVISION_TOTAL = 22 ;
const MAX_TEAMS_PER_DIVISION = 12 ;
2021-01-12 08:17:02 +00:00
// STATE CLASSES
class LeagueStructureState {
2021-01-10 06:27:30 +00:00
subleagues : SubleagueState [ ]
2021-01-12 08:17:02 +00:00
constructor ( subleagues : SubleagueState [ ] = [ ] ) {
this . subleagues = subleagues ;
}
2021-01-10 06:27:30 +00:00
}
2021-01-12 08:17:02 +00:00
class SubleagueState {
2021-01-10 06:27:30 +00:00
name : string
divisions : DivisionState [ ]
2021-01-12 08:17:02 +00:00
id : string | number
constructor ( divisions : DivisionState [ ] = [ ] ) {
this . name = "" ;
this . divisions = divisions ;
this . id = getUID ( ) ;
}
2021-01-10 06:27:30 +00:00
}
2021-01-12 08:17:02 +00:00
class DivisionState {
2021-01-10 06:27:30 +00:00
name : string
teams : TeamState [ ]
2021-01-12 08:17:02 +00:00
id : string | number
constructor ( ) {
this . name = "" ;
this . teams = [ ] ;
this . id = getUID ( ) ;
}
2021-01-10 06:27:30 +00:00
}
2021-01-12 08:17:02 +00:00
class TeamState {
2021-01-10 06:27:30 +00:00
name : string
2021-01-11 04:47:49 +00:00
id : string | number
2021-01-12 08:17:02 +00:00
constructor ( name : string = "" ) {
this . name = name ;
this . id = getUID ( ) ;
}
2021-01-10 06:27:30 +00:00
}
2021-01-12 08:17:02 +00:00
// STRUCTURE REDUCER
type StructureReducerActions =
2021-01-10 06:27:30 +00:00
{ type : 'remove_subleague' , subleague_index : number } |
{ type : 'add_subleague' } |
{ type : 'rename_subleague' , subleague_index : number , name : string } |
{ type : 'remove_divisions' , division_index : number } |
{ type : 'add_divisions' } |
{ type : 'rename_division' , subleague_index : number , division_index : number , name : string } |
{ type : 'remove_team' , subleague_index : number , division_index : number , name :string } |
{ type : 'add_team' , subleague_index :number , division_index :number , name :string }
2021-01-12 08:17:02 +00:00
function leagueStructureReducer ( state : LeagueStructureState , action : StructureReducerActions ) : LeagueStructureState {
2021-01-10 06:27:30 +00:00
switch ( action . type ) {
case 'remove_subleague' :
return { subleagues : removeIndex ( state . subleagues , action . subleague_index ) } ;
case 'add_subleague' :
2021-01-12 08:17:02 +00:00
return { subleagues : append ( state . subleagues , new SubleagueState (
arrayOf ( state . subleagues [ 0 ] . divisions . length , i = >
new DivisionState ( )
)
) ) }
2021-01-10 06:27:30 +00:00
case 'rename_subleague' :
2021-01-12 08:17:02 +00:00
return replaceSubleague ( state , action . subleague_index , subleague = > {
let nSubleague = shallowClone ( subleague ) ;
nSubleague . name = action . name ;
return nSubleague ;
} ) ;
2021-01-10 06:27:30 +00:00
case 'remove_divisions' :
2021-01-12 08:17:02 +00:00
return { subleagues : state.subleagues.map ( subleague = > {
let nSubleague = shallowClone ( subleague ) ;
nSubleague . divisions = removeIndex ( subleague . divisions , action . division_index )
return nSubleague ;
} ) } ;
2021-01-10 06:27:30 +00:00
case 'add_divisions' :
2021-01-12 08:17:02 +00:00
return { subleagues : state.subleagues.map ( subleague = > {
let nSubleague = shallowClone ( subleague ) ;
nSubleague . divisions = append ( subleague . divisions , new DivisionState ( ) )
return nSubleague ;
} ) } ;
2021-01-10 06:27:30 +00:00
case 'rename_division' :
2021-01-12 08:17:02 +00:00
return replaceDivision ( state , action . subleague_index , action . division_index , division = > {
let nDivision = shallowClone ( division ) ;
nDivision . name = action . name ;
return nDivision ;
} ) ;
2021-01-10 06:27:30 +00:00
case 'remove_team' :
2021-01-12 08:17:02 +00:00
return replaceDivision ( state , action . subleague_index , action . division_index , division = > {
let nDivision = shallowClone ( division ) ;
nDivision . teams = removeIndex ( division . teams , division . teams . findIndex ( val = > val . name === action . name ) ) ;
return nDivision ;
} ) ;
2021-01-10 06:27:30 +00:00
case 'add_team' :
2021-01-12 08:17:02 +00:00
return replaceDivision ( state , action . subleague_index , action . division_index , division = > {
let nDivision = shallowClone ( division ) ;
nDivision . teams = append ( division . teams , new TeamState ( action . name ) ) ;
return nDivision ;
} ) ;
2021-01-10 06:27:30 +00:00
}
}
function replaceSubleague ( state : LeagueStructureState , si : number , func : ( val : SubleagueState ) = > SubleagueState ) {
return { subleagues : replaceIndex ( state . subleagues , si , func ( state . subleagues [ si ] ) ) }
}
function replaceDivision ( state : LeagueStructureState , si : number , di : number , func : ( val : DivisionState ) = > DivisionState ) {
2021-01-12 08:17:02 +00:00
return replaceSubleague ( state , si , subleague = > {
let nSubleague = shallowClone ( subleague ) ;
nSubleague . divisions = replaceIndex ( subleague . divisions , di , func ( subleague . divisions [ di ] ) ) ;
return nSubleague ;
} ) ;
2021-01-10 06:27:30 +00:00
}
2021-01-12 18:16:31 +00:00
// OPTIONS REDUCER
class LeagueOptionsState {
games_series = "3"
intra_division_series = "8"
inter_division_series = "16"
inter_league_series = "8"
top_postseason = "1"
wildcards = "0"
}
type OptionsReducerActions =
{ type : 'set_games_series' , value : string } |
{ type : 'set_intra_division_series' , value : string } |
{ type : 'set_inter_division_series' , value : string } |
{ type : 'set_inter_league_series' , value : string } |
{ type : 'set_top_postseason' , value : string } |
{ type : 'set_wildcards' , value : string }
function LeagueOptionsReducer ( state : LeagueOptionsState , action : OptionsReducerActions ) {
let newState = shallowClone ( state ) ;
switch ( action . type ) {
case 'set_games_series' :
newState . games_series = action . value ;
break ;
case 'set_intra_division_series' :
newState . intra_division_series = action . value ;
break ;
case 'set_inter_division_series' :
newState . inter_division_series = action . value ;
break ;
case 'set_inter_league_series' :
newState . inter_league_series = action . value ;
break ;
case 'set_top_postseason' :
newState . top_postseason = action . value ;
break ;
case 'set_wildcards' :
newState . wildcards = action . value ;
break ;
}
return newState
}
2021-01-12 08:17:02 +00:00
// CREATE LEAGUE
2021-01-12 18:16:31 +00:00
let initLeagueStructure = {
subleagues : [ 0 , 1 ] . map ( ( val ) = >
new SubleagueState ( [ 0 , 1 ] . map ( ( val ) = >
new DivisionState ( )
) )
)
} ;
2021-01-10 06:27:30 +00:00
function CreateLeague() {
let [ name , setName ] = useState ( "" ) ;
2021-01-12 08:17:02 +00:00
let [ showError , setShowError ] = useState ( false ) ;
2021-01-14 06:30:31 +00:00
let [ nameExists , setNameExists ] = useState ( false ) ;
2021-01-19 14:32:11 +00:00
let [ deletedTeams , setDeletedTeams ] = useState ( new Set < string > ( ) ) ;
2021-01-14 06:30:31 +00:00
let [ createSuccess , setCreateSuccess ] = useState ( false ) ;
2021-01-12 18:16:31 +00:00
let [ structure , structureDispatch ] = useReducer ( leagueStructureReducer , initLeagueStructure ) ;
let [ options , optionsDispatch ] = useReducer ( LeagueOptionsReducer , new LeagueOptionsState ( ) ) ;
2021-01-10 06:27:30 +00:00
2021-01-19 14:32:11 +00:00
let self = useRef < HTMLDivElement | null > ( null ) ;
2021-01-10 06:27:30 +00:00
useLayoutEffect ( ( ) = > {
if ( self . current ) {
twemoji . parse ( self . current )
}
2021-01-19 14:32:11 +00:00
} ) ;
let duplicateTeams = getDuplicateTeams ( structure ) ;
let submit = ( ) = > {
if ( ! validRequest ( name , structure , options ) ) {
setShowError ( true ) ;
} else {
let req = new XMLHttpRequest ( ) ;
let data = makeRequest ( name , structure , options ) ;
req . open ( "POST" , "/api/leagues" , true ) ;
req . setRequestHeader ( "Content-type" , "application/json" ) ;
req . onreadystatechange = ( ) = > {
if ( req . readyState === 4 ) {
if ( req . status === 200 ) {
setCreateSuccess ( true ) ;
}
if ( req . status === 400 ) {
let err = JSON . parse ( req . response ) ;
switch ( err . status ) {
case 'err_league_exists' :
setNameExists ( true ) ;
break ;
case 'err_no_such_team' :
setDeletedTeams ( new Set ( err . cause ) ) ;
break ;
}
setShowError ( true ) ;
}
}
}
req . send ( data ) ;
}
}
2021-01-10 06:27:30 +00:00
2021-01-14 06:30:31 +00:00
if ( createSuccess ) {
return (
< div className = "cl_league_main" ref = { self } >
< div className = "cl_confirm_box" >
League created succesfully !
< / div >
< / div >
) ;
}
2021-01-10 06:27:30 +00:00
return (
< div className = "cl_league_main" ref = { self } >
2021-01-14 06:30:31 +00:00
< input type = "text" className = "cl_league_name" placeholder = "League Name" value = { name } onChange = { ( e ) = > {
setName ( e . target . value ) ;
setNameExists ( false ) ;
} } / >
< div className = "cl_structure_err" > {
name === "" && showError ? "A name is required." :
nameExists && showError ? "A league by that name already exists" :
""
} < / div >
2021-01-19 14:32:11 +00:00
< LeagueStructre
state = { structure }
dispatch = { structureDispatch }
deletedTeams = { deletedTeams }
duplicateTeams = { duplicateTeams }
showError = { showError }
/ >
2021-01-12 08:17:02 +00:00
< div className = "cl_league_options" >
2021-01-19 14:32:11 +00:00
< LeagueOptions
state = { options }
dispatch = { optionsDispatch }
showError = { showError }
/ >
2021-01-12 08:17:02 +00:00
< div className = "cl_option_submit_box" >
2021-01-19 14:32:11 +00:00
< button className = "cl_option_submit" onClick = { submit } > Submit < / button >
2021-01-12 08:17:02 +00:00
< div className = "cl_option_err" > {
2021-01-12 18:16:31 +00:00
! validRequest ( name , structure , options ) && showError ?
"Cannot create league. Some information is missing or invalid." : ""
2021-01-12 08:17:02 +00:00
} < / div >
< / div >
< / div >
2021-01-10 06:27:30 +00:00
< / div >
) ;
}
2021-01-12 18:16:31 +00:00
function makeRequest ( name :string , structure : LeagueStructureState , options :LeagueOptionsState ) {
2021-01-14 06:30:31 +00:00
return JSON . stringify ( {
name : name ,
2021-01-12 08:17:02 +00:00
structure : {
subleagues : structure.subleagues.map ( subleague = > ( {
name : subleague.name ,
divisions : subleague.divisions.map ( division = > ( {
name : division.name ,
2021-01-14 06:30:31 +00:00
teams : division.teams.map ( team = > team . name )
2021-01-12 08:17:02 +00:00
} ) )
} ) )
} ,
2021-01-12 18:16:31 +00:00
games_per_series : Number ( options . games_series ) ,
division_series : Number ( options . intra_division_series ) ,
inter_division_series : Number ( options . inter_division_series ) ,
inter_league_series : Number ( options . inter_league_series ) ,
top_postseason : Number ( options . top_postseason ) ,
wildcards : Number ( options . wildcards )
2021-01-12 08:17:02 +00:00
} ) ;
}
2021-01-12 18:16:31 +00:00
function validRequest ( name :string , structure : LeagueStructureState , options :LeagueOptionsState ) {
2021-01-12 08:17:02 +00:00
return (
name !== "" &&
2021-01-16 06:13:12 +00:00
2021-01-12 18:16:31 +00:00
validNumber ( options . games_series ) &&
validNumber ( options . intra_division_series ) &&
validNumber ( options . inter_division_series ) &&
validNumber ( options . inter_league_series ) &&
validNumber ( options . top_postseason ) &&
2021-01-14 06:30:31 +00:00
validNumber ( options . wildcards , 0 ) &&
2021-01-16 06:13:12 +00:00
2021-01-12 08:17:02 +00:00
structure . subleagues . length % 2 === 0 &&
2021-01-19 14:32:11 +00:00
getDuplicateTeams ( structure ) . size === 0 &&
2021-01-16 06:13:12 +00:00
structure . subleagues . every ( ( subleague , si ) = >
2021-01-12 08:17:02 +00:00
subleague . name !== "" &&
2021-01-16 06:13:12 +00:00
! structure . subleagues . slice ( 0 , si ) . some ( val = > val . name === subleague . name ) &&
subleague . divisions . every ( ( division , di ) = >
2021-01-12 08:17:02 +00:00
division . name !== "" &&
2021-01-16 06:13:12 +00:00
division . teams . length >= 2 &&
division . teams . length <= MAX_TEAMS_PER_DIVISION &&
! subleague . divisions . slice ( 0 , di ) . some ( val = > val . name === division . name )
2021-01-12 08:17:02 +00:00
)
)
)
}
2021-01-14 06:30:31 +00:00
function validNumber ( value : string , min = 1 ) {
2021-01-16 06:13:12 +00:00
return ! isNaN ( Number ( value ) ) && Number ( value ) >= min ;
2021-01-12 08:17:02 +00:00
}
2021-01-19 14:32:11 +00:00
function getDuplicateTeams ( structure : LeagueStructureState ) {
return new Set (
structure . subleagues . map ( subleague = >
subleague . divisions . map ( division = >
division . teams . map ( team = > team . name )
) . reduce ( ( prev , curr ) = > prev . concat ( curr ) , [ ] )
) . reduce ( ( prev , curr ) = > prev . concat ( curr ) , [ ] )
. filter ( ( val , i , arr ) = > arr . slice ( 0 , i ) . indexOf ( val ) >= 0 )
)
}
2021-01-12 08:17:02 +00:00
// LEAGUE STRUCUTRE
2021-01-19 14:32:11 +00:00
function LeagueStructre ( props : {
state : LeagueStructureState ,
dispatch : React.Dispatch < StructureReducerActions > ,
deletedTeams : Set < string > ,
duplicateTeams : Set < string > ,
showError : boolean
} ) {
2021-01-16 06:13:12 +00:00
let nSubleagues = props . state . subleagues . length ;
let nDivisions = props . state . subleagues [ 0 ] . divisions . length ;
2021-01-10 06:27:30 +00:00
return (
< div className = "cl_league_structure" >
< div className = "cl_league_structure_scrollbox" >
< div className = "cl_subleague_add_align" >
2021-01-11 04:47:49 +00:00
< div className = "cl_league_structure_table" >
2021-01-19 14:32:11 +00:00
< SubleagueHeaders
subleagues = { props . state . subleagues }
dispatch = { props . dispatch }
showError = { props . showError }
/ >
< Divisions
subleagues = { props . state . subleagues }
dispatch = { props . dispatch }
deletedTeams = { props . deletedTeams }
duplicateTeams = { props . duplicateTeams }
showError = { props . showError }
/ >
2021-01-11 04:47:49 +00:00
< / div >
2021-01-16 06:13:12 +00:00
{ ( nSubleagues + 1 ) * ( nDivisions + 1 ) < MAX_SUBLEAGUE_DIVISION_TOTAL ?
< button className = "cl_subleague_add" onClick = { e = > props . dispatch ( { type : 'add_subleague' } ) } > ➕ < / button > :
< div className = "cl_subleague_add_filler" / >
}
2021-01-10 06:27:30 +00:00
< / div >
< / div >
2021-01-12 08:17:02 +00:00
< div className = "cl_structure_err" > { props . state . subleagues . length % 2 !== 0 && props . showError ? "Must have an even number of subleagues." : "" } < / div >
2021-01-16 06:13:12 +00:00
{ nSubleagues * ( nDivisions + 2 ) < MAX_SUBLEAGUE_DIVISION_TOTAL ?
< button className = "cl_division_add" onClick = { e = > props . dispatch ( { type : 'add_divisions' } ) } > ➕ < / button > :
< div className = "cl_division_add_filler" / >
}
2021-01-10 06:27:30 +00:00
< / div >
) ;
}
2021-01-12 08:17:02 +00:00
function SubleagueHeaders ( props : { subleagues : SubleagueState [ ] , dispatch : React.Dispatch < StructureReducerActions > , showError :boolean } ) {
2021-01-10 06:27:30 +00:00
return (
2021-01-11 04:47:49 +00:00
< div className = "cl_headers" >
< div key = "filler" className = "cl_delete_filler" / >
2021-01-16 06:13:12 +00:00
{ props . subleagues . map ( ( subleague , i ) = > {
let err =
subleague . name === "" ?
"A name is required." :
props . subleagues . slice ( 0 , i ) . some ( val = > val . name === subleague . name ) ?
"Each subleague must have a different name." :
"" ;
return (
< div key = { subleague . id } className = "cl_table_header" >
< div className = "cl_subleague_bg" >
< SubleageHeader state = { subleague } canDelete = { props . subleagues . length > 1 } dispatch = { action = >
props . dispatch ( Object . assign ( { subleague_index : i } , action ) )
} / >
< div className = "cl_structure_err" > { props . showError ? err : "" } < / div >
< / div >
2021-01-11 04:47:49 +00:00
< / div >
2021-01-16 06:13:12 +00:00
)
} ) }
2021-01-11 04:47:49 +00:00
< / div >
2021-01-10 06:27:30 +00:00
) ;
}
2021-01-12 08:17:02 +00:00
function SubleageHeader ( props : { state : SubleagueState , canDelete : boolean , dispatch : ( action : DistributiveOmit < StructureReducerActions , ' subleague_index ' > ) = > void } ) {
2021-01-10 06:27:30 +00:00
return (
< div className = "cl_subleague_header" >
< input type = "text" className = "cl_subleague_name" placeholder = "Subleague Name" value = { props . state . name } onChange = { e = >
props . dispatch ( { type : 'rename_subleague' , name : e.target.value } )
} / >
{ props . canDelete ? < button className = "cl_subleague_delete" onClick = { e = > props . dispatch ( { type : 'remove_subleague' } ) } > ➖ < / button > : null }
< / div >
) ;
}
2021-01-19 14:32:11 +00:00
function Divisions ( props : {
subleagues : SubleagueState [ ] ,
dispatch : React.Dispatch < StructureReducerActions > ,
deletedTeams : Set < string > ,
duplicateTeams : Set < string > ,
showError : boolean
} ) {
2021-01-11 04:47:49 +00:00
return ( < >
{ props . subleagues [ 0 ] . divisions . map ( ( val , di ) = > (
< div key = { val . id } className = "cl_table_row" >
< div key = "delete" className = "cl_delete_box" >
{ props . subleagues [ 0 ] . divisions . length > 1 ?
< button className = "cl_division_delete" onClick = { e = > props . dispatch ( { type : 'remove_divisions' , division_index : di } ) } > ➖ < / button > :
null
}
< / div >
{ props . subleagues . map ( ( subleague , si ) = > (
< div key = { subleague . id } className = "cl_division_cell" >
< div className = "cl_subleague_bg" >
2021-01-19 14:32:11 +00:00
< Division
state = { subleague . divisions [ di ] }
dispatch = { action = >
props . dispatch ( Object . assign ( { subleague_index : si , division_index : di } , action ) )
}
isDuplicate = { subleague . divisions . slice ( 0 , di ) . some ( val = > val . name === subleague . divisions [ di ] . name ) }
deletedTeams = { props . deletedTeams }
duplicateTeams = { props . duplicateTeams }
showError = { props . showError }
/ >
2021-01-11 04:47:49 +00:00
< / div >
< / div >
) ) }
< / div >
) ) }
< / > ) ;
2021-01-10 06:27:30 +00:00
}
2021-01-16 06:13:12 +00:00
function Division ( props : {
state : DivisionState ,
dispatch : ( action : DistributiveOmit < StructureReducerActions , ' subleague_index ' | ' division_index ' > ) = > void ,
isDuplicate : boolean ,
2021-01-19 14:32:11 +00:00
deletedTeams : Set < string > ,
duplicateTeams : Set < string > ,
2021-01-16 06:13:12 +00:00
showError : boolean
} ) {
2021-01-10 06:27:30 +00:00
let [ newName , setNewName ] = useState ( "" ) ;
2021-01-11 04:47:49 +00:00
let [ searchResults , setSearchResults ] = useState < string [ ] > ( [ ] ) ;
let newNameInput = useRef < HTMLInputElement > ( null ) ;
let resultList = useRef < HTMLDivElement > ( null ) ;
useLayoutEffect ( ( ) = > {
if ( resultList . current ) {
twemoji . parse ( resultList . current )
}
} )
2021-01-10 06:27:30 +00:00
2021-01-16 06:13:12 +00:00
let divisionErr =
props . state . name === "" ?
"A name is required." :
props . isDuplicate ?
"Each division in a subleague must have a different name." :
""
let teamsErr = props . state . teams . length < 2 ? "Must have at least 2 teams." : "" ;
2021-01-10 06:27:30 +00:00
return (
< div className = "cl_division" >
2021-01-12 08:17:02 +00:00
< div className = "cl_division_name_box" >
2021-01-11 21:11:35 +00:00
< input type = "text" className = "cl_division_name" placeholder = "Division Name" key = "input" value = { props . state . name } onChange = { e = >
props . dispatch ( { type : 'rename_division' , name : e.target.value } )
} / >
2021-01-16 06:13:12 +00:00
< div className = "cl_structure_err cl_structure_err_div" > { props . showError ? divisionErr : "" } < / div >
2021-01-11 21:11:35 +00:00
< / div >
2021-01-19 14:32:11 +00:00
{ props . state . teams . map ( ( team , i ) = > (
< Team key = { team . id }
state = { team }
dispatch = { props . dispatch }
isDuplicate = { props . duplicateTeams . has ( team . name ) }
isDeleted = { props . deletedTeams . has ( team . name ) }
showError = { props . showError }
/ >
) ) }
{ props . state . teams . length < MAX_TEAMS_PER_DIVISION ? < >
2021-01-16 06:13:12 +00:00
< div className = "cl_team_add" >
< input type = "text" className = "cl_newteam_name" placeholder = "Add team..." value = { newName } ref = { newNameInput }
onChange = { e = > {
2021-01-19 14:32:11 +00:00
if ( e . target . value === "" ) {
setSearchResults ( [ ] ) ;
} else {
let params = new URLSearchParams ( { query : e.target.value , page_len : '5' , page_num : '0' } ) ;
fetch ( "/api/teams/search?" + params . toString ( ) )
. then ( response = > response . json ( ) )
. then ( data = > setSearchResults ( data ) ) ;
}
2021-01-16 06:13:12 +00:00
setNewName ( e . target . value ) ;
} } / >
< / div >
{ searchResults . length > 0 && newName . length > 0 ?
( < div className = "cl_search_list" ref = { resultList } >
{ searchResults . map ( result = >
< div className = "cl_search_result" key = { result } onClick = { e = > {
props . dispatch ( { type : 'add_team' , name : result } ) ;
setNewName ( "" ) ;
if ( newNameInput . current ) {
newNameInput . current . focus ( ) ;
}
} } > { result } < / div >
) }
< / div > ) :
null
} < / > :
null
2021-01-11 04:47:49 +00:00
}
2021-01-16 06:13:12 +00:00
< div className = "cl_structure_err cl_structure_err_teams" > { props . showError ? teamsErr : "" } < / div >
2021-01-10 06:27:30 +00:00
< / div >
) ;
}
2021-01-19 14:32:11 +00:00
function Team ( props : {
state : TeamState ,
dispatch : ( action : DistributiveOmit < StructureReducerActions , ' subleague_index ' | ' division_index ' > ) = > void ,
isDuplicate : boolean ,
isDeleted : boolean ,
showError : boolean
} ) {
let errMsg =
props . isDeleted ?
"This team was deleted" :
props . isDuplicate ?
"Each team in a league must be unique" :
"" ;
return ( < >
< div className = "cl_team" >
< div className = { "cl_team_name" + ( errMsg && props . showError ? " cl_team_name_err" : "" ) } > { props . state . name } < / div >
< button className = "cl_team_delete" onClick = { e = > props . dispatch ( { type : 'remove_team' , name : props.state.name } ) } > ➖ < / button >
< / div >
< div className = "cl_structure_err cl_structure_err_team" > { props . showError ? errMsg : "" } < / div >
< / > ) ;
}
2021-01-12 08:17:02 +00:00
// LEAGUE OPTIONS
2021-01-12 18:16:31 +00:00
function LeagueOptions ( props : { state : LeagueOptionsState , dispatch : React.Dispatch < OptionsReducerActions > , showError : boolean } ) {
2021-01-10 06:27:30 +00:00
return (
2021-01-12 08:17:02 +00:00
< div className = "cl_option_main" >
< div className = "cl_option_column" >
2021-01-12 18:16:31 +00:00
< NumberInput title = "Number of games per series" value = { props . state . games_series } setValue = { ( value : string ) = >
props . dispatch ( { type : 'set_games_series' , value : value } ) } showError = { props . showError } / >
< NumberInput title = "Number of teams from top of division to postseason" value = { props . state . top_postseason } setValue = { ( value : string ) = >
props . dispatch ( { type : 'set_top_postseason' , value : value } ) } showError = { props . showError } / >
2021-01-14 22:58:24 +00:00
< NumberInput title = "Number of wildcards" value = { props . state . wildcards } minValue = { 0 } setValue = { ( value : string ) = >
2021-01-12 18:16:31 +00:00
props . dispatch ( { type : 'set_wildcards' , value : value } ) } showError = { props . showError } / >
2021-01-12 08:17:02 +00:00
< / div >
< div className = "cl_option_column" >
2021-01-12 18:16:31 +00:00
< NumberInput title = "Number of series with each division opponent" value = { props . state . intra_division_series } setValue = { ( value : string ) = >
props . dispatch ( { type : 'set_intra_division_series' , value : value } ) } showError = { props . showError } / >
< NumberInput title = "Number of inter-divisional series" value = { props . state . inter_division_series } setValue = { ( value : string ) = >
props . dispatch ( { type : 'set_inter_division_series' , value : value } ) } showError = { props . showError } / >
< NumberInput title = "Number of inter-league series" value = { props . state . inter_league_series } setValue = { ( value : string ) = >
props . dispatch ( { type : 'set_inter_league_series' , value : value } ) } showError = { props . showError } / >
2021-01-12 08:17:02 +00:00
< / div >
< / div >
) ;
}
2021-01-11 04:47:49 +00:00
2021-01-16 06:13:12 +00:00
function NumberInput ( props : { title : string , value : string , setValue : ( newVal : string ) = > void , showError : boolean , minValue? : number } ) {
2021-01-14 22:58:24 +00:00
let minValue = 1 ;
if ( props . minValue !== undefined ) {
minValue = props . minValue
}
2021-01-12 08:17:02 +00:00
return (
< div className = "cl_option_box" >
< div className = "cl_option_label" > { props . title } < / div >
2021-01-14 22:58:24 +00:00
< input className = "cl_option_input" type = "number" min = { minValue } value = { props . value } onChange = { e = > props . setValue ( e . target . value ) } / >
2021-01-16 06:13:12 +00:00
< div className = "cl_option_err" > { ( isNaN ( Number ( props . value ) ) || Number ( props . value ) < minValue ) && props . showError ? "Must be a number greater than " + minValue : "" } < / div >
2021-01-10 06:27:30 +00:00
< / div >
) ;
}
export default CreateLeague ;