commit
c461c6cd39
|
@ -38,27 +38,62 @@ def search_teams():
|
||||||
|
|
||||||
return jsonify([json.loads(x[0])['name'] for x in result]) #currently all we need is the name but that can change
|
return jsonify([json.loads(x[0])['name'] for x in result]) #currently all we need is the name but that can change
|
||||||
|
|
||||||
|
MAX_SUBLEAGUE_DIVISION_TOTAL = 22;
|
||||||
|
MAX_TEAMS_PER_DIVISION = 12;
|
||||||
|
|
||||||
@app.route('/api/leagues', methods=['POST'])
|
@app.route('/api/leagues', methods=['POST'])
|
||||||
def create_league():
|
def create_league():
|
||||||
config = json.loads(request.data)
|
config = json.loads(request.data)
|
||||||
|
|
||||||
if (league_exists(config['name'])):
|
if league_exists(config['name']):
|
||||||
abort(400, "A league by that name already exists")
|
return jsonify({'status':'err_league_exists'}), 400
|
||||||
|
|
||||||
print(config)
|
num_subleagues = len(config['structure']['subleagues'])
|
||||||
league_dic = {
|
if num_subleagues < 1 or num_subleagues % 2 != 0:
|
||||||
subleague['name'] : {
|
return jsonify({'status':'err_invalid_subleague_count'}), 400
|
||||||
division['name'] : [games.get_team(team_name) for team_name in division['teams']]
|
|
||||||
for division in subleague['divisions']
|
num_divisions = len(config['structure']['subleagues'][0]['divisions'])
|
||||||
}
|
if num_subleagues * (num_divisions + 1) > MAX_SUBLEAGUE_DIVISION_TOTAL:
|
||||||
for subleague in config['structure']['subleagues']
|
return jsonify({'status':'err_invalid_subleague_division_total'}), 400
|
||||||
}
|
|
||||||
|
league_dic = {}
|
||||||
|
err_teams = []
|
||||||
|
for subleague in config['structure']['subleagues']:
|
||||||
|
if subleague['name'] in league_dic:
|
||||||
|
return jsonify({'status':'err_duplicate_name', 'cause':subleague['name']})
|
||||||
|
|
||||||
|
subleague_dic = {}
|
||||||
|
for division in subleague['divisions']:
|
||||||
|
if division['name'] in subleague_dic:
|
||||||
|
return jsonify({'status':'err_duplicate_name', 'cause':f"{subleague['name']}/{division['name']}"}), 400
|
||||||
|
elif len(division['teams']) > MAX_TEAMS_PER_DIVISION:
|
||||||
|
return jsonify({'status':'err_too_many_teams', 'cause':f"{subleague['name']}/{division['name']}"})
|
||||||
|
|
||||||
|
teams = []
|
||||||
|
for team_name in division['teams']:
|
||||||
|
team = games.get_team(team_name)
|
||||||
|
if team is None:
|
||||||
|
err_teams.append(team_name)
|
||||||
|
else:
|
||||||
|
teams.append(team)
|
||||||
|
subleague_dic[division['name']] = teams
|
||||||
|
league_dic[subleague['name']] = subleague_dic
|
||||||
|
|
||||||
|
if len(err_teams) > 0:
|
||||||
|
return jsonify({'status':'err_no_such_team', 'cause': err_teams}), 400
|
||||||
|
|
||||||
|
for (key, min_val) in [
|
||||||
|
('division_series', 1),
|
||||||
|
('inter_division_series', 1),
|
||||||
|
('inter_league_series', 1)
|
||||||
|
]:
|
||||||
|
if config[key] < min_val:
|
||||||
|
return jsonify({'status':'err_invalid_optiion_value', 'cause':key}), 400
|
||||||
|
|
||||||
new_league = league_structure(config['name'])
|
new_league = league_structure(config['name'])
|
||||||
new_league.setup(
|
new_league.setup(
|
||||||
league_dic,
|
league_dic,
|
||||||
division_games=config['division_series'],
|
division_games=config['division_series'], # need to add a check that makes sure these values are ok
|
||||||
inter_division_games=config['inter_division_series'],
|
inter_division_games=config['inter_division_series'],
|
||||||
inter_league_games=config['inter_league_series'],
|
inter_league_games=config['inter_league_series'],
|
||||||
)
|
)
|
||||||
|
@ -67,7 +102,7 @@ def create_league():
|
||||||
new_league.generate_schedule()
|
new_league.generate_schedule()
|
||||||
leagues.save_league(new_league)
|
leagues.save_league(new_league)
|
||||||
|
|
||||||
return "League created successfully"
|
return jsonify({'status':'success_league_created'})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
[{"M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\index.tsx":"1","M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\reportWebVitals.ts":"2","M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\GamesPage.tsx":"3","M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\GamePage.tsx":"4","M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\GamesUtil.tsx":"5","M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\Game.tsx":"6","M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\CreateLeague.tsx":"7","M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\util.tsx":"8"},{"size":2425,"mtime":1610685584594,"results":"9","hashOfConfig":"10"},{"size":440,"mtime":1610521673158,"results":"11","hashOfConfig":"10"},{"size":4866,"mtime":1610685584593,"results":"12","hashOfConfig":"10"},{"size":1897,"mtime":1610685584589,"results":"13","hashOfConfig":"10"},{"size":1157,"mtime":1610685584594,"results":"14","hashOfConfig":"10"},{"size":3173,"mtime":1610583643836,"results":"15","hashOfConfig":"10"},{"size":17241,"mtime":1610685584587,"results":"16","hashOfConfig":"10"},{"size":1029,"mtime":1610685584594,"results":"17","hashOfConfig":"10"},{"filePath":"18","messages":"19","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1jg8ts7",{"filePath":"20","messages":"21","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"22"},{"filePath":"23","messages":"24","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"25","messages":"26","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"27","messages":"28","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"29","messages":"30","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0,"usedDeprecatedRules":"22"},{"filePath":"31","messages":"32","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"33","messages":"34","errorCount":0,"warningCount":1,"fixableErrorCount":0,"fixableWarningCount":0,"source":null},"M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\index.tsx",[],"M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\reportWebVitals.ts",[],["35","36"],"M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\GamesPage.tsx",[],"M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\GamePage.tsx",[],"M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\GamesUtil.tsx",[],"M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\Game.tsx",[],"M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\CreateLeague.tsx",[],"M:\\Documents\\Code\\matteo\\the-prestige\\simmadome\\src\\util.tsx",["37"],{"ruleId":"38","replacedBy":"39"},{"ruleId":"40","replacedBy":"41"},{"ruleId":"42","severity":1,"message":"43","line":1,"column":9,"nodeType":"44","messageId":"45","endLine":1,"endColumn":15},"no-native-reassign",["46"],"no-negated-in-lhs",["47"],"@typescript-eslint/no-unused-vars","'useRef' is defined but never used.","Identifier","unusedVar","no-global-assign","no-unsafe-negation"]
|
[{"/Users/elijah/Documents/Projects/matteo/simmadome/src/index.tsx":"1","/Users/elijah/Documents/Projects/matteo/simmadome/src/reportWebVitals.ts":"2","/Users/elijah/Documents/Projects/matteo/simmadome/src/GamesPage.tsx":"3","/Users/elijah/Documents/Projects/matteo/simmadome/src/GamePage.tsx":"4","/Users/elijah/Documents/Projects/matteo/simmadome/src/CreateLeague.tsx":"5","/Users/elijah/Documents/Projects/matteo/simmadome/src/GamesUtil.tsx":"6","/Users/elijah/Documents/Projects/matteo/simmadome/src/util.tsx":"7","/Users/elijah/Documents/Projects/matteo/simmadome/src/Game.tsx":"8"},{"size":2368,"mtime":1610663769654,"results":"9","hashOfConfig":"10"},{"size":425,"mtime":1610566206674,"results":"11","hashOfConfig":"10"},{"size":4725,"mtime":1610664926203,"results":"12","hashOfConfig":"10"},{"size":1836,"mtime":1610677519051,"results":"13","hashOfConfig":"10"},{"size":18825,"mtime":1610778204901,"results":"14","hashOfConfig":"10"},{"size":1116,"mtime":1610677473305,"results":"15","hashOfConfig":"10"},{"size":961,"mtime":1610694553519,"results":"16","hashOfConfig":"10"},{"size":3089,"mtime":1610572714752,"results":"17","hashOfConfig":"10"},{"filePath":"18","messages":"19","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"1bvn6qu",{"filePath":"20","messages":"21","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"22","messages":"23","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"24","messages":"25","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"26","messages":"27","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"28","messages":"29","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"30","messages":"31","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},{"filePath":"32","messages":"33","errorCount":0,"warningCount":0,"fixableErrorCount":0,"fixableWarningCount":0},"/Users/elijah/Documents/Projects/matteo/simmadome/src/index.tsx",[],"/Users/elijah/Documents/Projects/matteo/simmadome/src/reportWebVitals.ts",[],"/Users/elijah/Documents/Projects/matteo/simmadome/src/GamesPage.tsx",[],"/Users/elijah/Documents/Projects/matteo/simmadome/src/GamePage.tsx",[],"/Users/elijah/Documents/Projects/matteo/simmadome/src/CreateLeague.tsx",[],"/Users/elijah/Documents/Projects/matteo/simmadome/src/GamesUtil.tsx",[],"/Users/elijah/Documents/Projects/matteo/simmadome/src/util.tsx",[],"/Users/elijah/Documents/Projects/matteo/simmadome/src/Game.tsx",[]]
|
|
@ -240,7 +240,7 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
.cl_option_err {
|
.cl_option_err {
|
||||||
min-height: 1.5rem;
|
min-height: 1.5rem;
|
||||||
margin-bottom: -0.5rem;
|
margin-bottom: -0.5rem;
|
||||||
margin-top: 0.5rem;
|
margin-top: 0.25rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl_structure_err {
|
.cl_structure_err {
|
||||||
|
@ -248,14 +248,29 @@ input[type=number]::-webkit-outer-spin-button {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl_structure_err_div {
|
.cl_structure_err_div {
|
||||||
|
margin-top: -0.25rem;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_structure_err_team {
|
||||||
margin-top: -0.5rem;
|
margin-top: -0.5rem;
|
||||||
margin-bottom: 0;
|
margin-bottom: 0;
|
||||||
|
width: 85%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_team_name_err {
|
||||||
|
color: var(--accent-red);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl_structure_err_teams {
|
.cl_structure_err_teams {
|
||||||
width: 98%;
|
width: 98%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cl_subleague_add_filler, .cl_division_add_filler {
|
||||||
|
width: 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
/* button styles */
|
/* button styles */
|
||||||
|
|
||||||
button > .emoji {
|
button > .emoji {
|
||||||
|
@ -283,12 +298,12 @@ button > .emoji {
|
||||||
background: var(--accent-green);
|
background: var(--accent-green);
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl_subleague_add {
|
.cl_subleague_add, .cl_subleague_add_filler {
|
||||||
position: relative;
|
position: relative;
|
||||||
top: 1.6rem;
|
top: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl_division_add {
|
.cl_division_add, .cl_division_add_filler {
|
||||||
margin-top: 1.25rem;
|
margin-top: 1.25rem;
|
||||||
margin-bottom: 1.25rem;
|
margin-bottom: 1.25rem;
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,11 @@ import {removeIndex, replaceIndex, append, arrayOf, shallowClone, getUID, Distri
|
||||||
import './CreateLeague.css';
|
import './CreateLeague.css';
|
||||||
import twemoji from 'twemoji';
|
import twemoji from 'twemoji';
|
||||||
|
|
||||||
|
// CONSTS
|
||||||
|
|
||||||
|
const MAX_SUBLEAGUE_DIVISION_TOTAL = 22;
|
||||||
|
const MAX_TEAMS_PER_DIVISION = 12;
|
||||||
|
|
||||||
// STATE CLASSES
|
// STATE CLASSES
|
||||||
|
|
||||||
class LeagueStructureState {
|
class LeagueStructureState {
|
||||||
|
@ -178,6 +183,7 @@ function CreateLeague() {
|
||||||
let [name, setName] = useState("");
|
let [name, setName] = useState("");
|
||||||
let [showError, setShowError] = useState(false);
|
let [showError, setShowError] = useState(false);
|
||||||
let [nameExists, setNameExists] = useState(false);
|
let [nameExists, setNameExists] = useState(false);
|
||||||
|
let [deletedTeams, setDeletedTeams] = useState<string[]>([]);
|
||||||
let [createSuccess, setCreateSuccess] = useState(false);
|
let [createSuccess, setCreateSuccess] = useState(false);
|
||||||
let [structure, structureDispatch] = useReducer(leagueStructureReducer, initLeagueStructure);
|
let [structure, structureDispatch] = useReducer(leagueStructureReducer, initLeagueStructure);
|
||||||
let [options, optionsDispatch] = useReducer(LeagueOptionsReducer, new LeagueOptionsState());
|
let [options, optionsDispatch] = useReducer(LeagueOptionsReducer, new LeagueOptionsState());
|
||||||
|
@ -211,7 +217,7 @@ function CreateLeague() {
|
||||||
nameExists && showError ? "A league by that name already exists" :
|
nameExists && showError ? "A league by that name already exists" :
|
||||||
""
|
""
|
||||||
}</div>
|
}</div>
|
||||||
<LeagueStructre state={structure} dispatch={structureDispatch} showError={showError}/>
|
<LeagueStructre state={structure} dispatch={structureDispatch} deletedTeams={deletedTeams} showError={showError}/>
|
||||||
<div className="cl_league_options">
|
<div className="cl_league_options">
|
||||||
<LeagueOptions state={options} dispatch={optionsDispatch} showError={showError}/>
|
<LeagueOptions state={options} dispatch={optionsDispatch} showError={showError}/>
|
||||||
<div className="cl_option_submit_box">
|
<div className="cl_option_submit_box">
|
||||||
|
@ -229,13 +235,20 @@ function CreateLeague() {
|
||||||
setCreateSuccess(true);
|
setCreateSuccess(true);
|
||||||
}
|
}
|
||||||
if (req.status === 400) {
|
if (req.status === 400) {
|
||||||
setNameExists(true);
|
let err = JSON.parse(req.response);
|
||||||
|
switch (err.status) {
|
||||||
|
case 'err_league_exists':
|
||||||
|
setNameExists(true);
|
||||||
|
break;
|
||||||
|
case 'err_no_such_team':
|
||||||
|
setDeletedTeams(err.cause);
|
||||||
|
break;
|
||||||
|
}
|
||||||
setShowError(true);
|
setShowError(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
req.send(data);
|
req.send(data);
|
||||||
|
|
||||||
}
|
}
|
||||||
}}>Submit</button>
|
}}>Submit</button>
|
||||||
<div className="cl_option_err">{
|
<div className="cl_option_err">{
|
||||||
|
@ -270,45 +283,61 @@ function makeRequest(name:string, structure: LeagueStructureState, options:Leagu
|
||||||
}
|
}
|
||||||
|
|
||||||
function validRequest(name:string, structure: LeagueStructureState, options:LeagueOptionsState) {
|
function validRequest(name:string, structure: LeagueStructureState, options:LeagueOptionsState) {
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
name !== "" &&
|
name !== "" &&
|
||||||
|
|
||||||
validNumber(options.games_series) &&
|
validNumber(options.games_series) &&
|
||||||
validNumber(options.intra_division_series) &&
|
validNumber(options.intra_division_series) &&
|
||||||
validNumber(options.inter_division_series) &&
|
validNumber(options.inter_division_series) &&
|
||||||
validNumber(options.inter_league_series) &&
|
validNumber(options.inter_league_series) &&
|
||||||
validNumber(options.top_postseason) &&
|
validNumber(options.top_postseason) &&
|
||||||
validNumber(options.wildcards, 0) &&
|
validNumber(options.wildcards, 0) &&
|
||||||
|
|
||||||
structure.subleagues.length % 2 === 0 &&
|
structure.subleagues.length % 2 === 0 &&
|
||||||
structure.subleagues.every(subleague =>
|
|
||||||
|
structure.subleagues.every((subleague, si) =>
|
||||||
subleague.name !== "" &&
|
subleague.name !== "" &&
|
||||||
subleague.divisions.every(division =>
|
!structure.subleagues.slice(0, si).some(val => val.name === subleague.name) &&
|
||||||
|
subleague.divisions.every((division, di) =>
|
||||||
division.name !== "" &&
|
division.name !== "" &&
|
||||||
division.teams.length >= 2
|
division.teams.length >= 2 &&
|
||||||
|
division.teams.length <= MAX_TEAMS_PER_DIVISION &&
|
||||||
|
!subleague.divisions.slice(0, di).some(val => val.name === division.name)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
function validNumber(value: string, min = 1) {
|
function validNumber(value: string, min = 1) {
|
||||||
return !isNaN(Number(value)) && Number(value) >= min
|
return !isNaN(Number(value)) && Number(value) >= min;
|
||||||
}
|
}
|
||||||
|
|
||||||
// LEAGUE STRUCUTRE
|
// LEAGUE STRUCUTRE
|
||||||
|
|
||||||
function LeagueStructre(props: {state: LeagueStructureState, dispatch: React.Dispatch<StructureReducerActions>, showError: boolean}) {
|
function LeagueStructre(props: {state: LeagueStructureState, dispatch: React.Dispatch<StructureReducerActions>, deletedTeams: string[], showError: boolean}) {
|
||||||
|
let nSubleagues = props.state.subleagues.length;
|
||||||
|
let nDivisions = props.state.subleagues[0].divisions.length;
|
||||||
return (
|
return (
|
||||||
<div className="cl_league_structure">
|
<div className="cl_league_structure">
|
||||||
<div className="cl_league_structure_scrollbox">
|
<div className="cl_league_structure_scrollbox">
|
||||||
<div className="cl_subleague_add_align">
|
<div className="cl_subleague_add_align">
|
||||||
<div className="cl_league_structure_table">
|
<div className="cl_league_structure_table">
|
||||||
<SubleagueHeaders subleagues={props.state.subleagues} dispatch={props.dispatch} showError={props.showError}/>
|
<SubleagueHeaders subleagues={props.state.subleagues} dispatch={props.dispatch} showError={props.showError}/>
|
||||||
<Divisions subleagues={props.state.subleagues} dispatch={props.dispatch} showError={props.showError}/>
|
<Divisions subleagues={props.state.subleagues} dispatch={props.dispatch} deletedTeams={props.deletedTeams} showError={props.showError}/>
|
||||||
</div>
|
</div>
|
||||||
<button className="cl_subleague_add" onClick={e => props.dispatch({type: 'add_subleague'})}>➕</button>
|
{ (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"/>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="cl_structure_err">{props.state.subleagues.length % 2 !== 0 && props.showError ? "Must have an even number of subleagues." : ""}</div>
|
<div className="cl_structure_err">{props.state.subleagues.length % 2 !== 0 && props.showError ? "Must have an even number of subleagues." : ""}</div>
|
||||||
<button className="cl_division_add" onClick={e => props.dispatch({type: 'add_divisions'})}>➕</button>
|
{ 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"/>
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -317,16 +346,25 @@ function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React.
|
||||||
return (
|
return (
|
||||||
<div className="cl_headers">
|
<div className="cl_headers">
|
||||||
<div key="filler" className="cl_delete_filler"/>
|
<div key="filler" className="cl_delete_filler"/>
|
||||||
{props.subleagues.map((subleague, i) => (
|
{props.subleagues.map((subleague, i) => {
|
||||||
<div key={subleague.id} className="cl_table_header">
|
let err =
|
||||||
<div className="cl_subleague_bg">
|
subleague.name === "" ?
|
||||||
<SubleageHeader state={subleague} canDelete={props.subleagues.length > 1} dispatch={action =>
|
"A name is required." :
|
||||||
props.dispatch(Object.assign({subleague_index: i}, action))
|
props.subleagues.slice(0, i).some(val => val.name === subleague.name) ?
|
||||||
}/>
|
"Each subleague must have a different name." :
|
||||||
<div className="cl_structure_err">{subleague.name === "" && props.showError ? "A name is required." : ""}</div>
|
"";
|
||||||
|
|
||||||
|
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>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
)
|
||||||
))}
|
})}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -342,7 +380,7 @@ function SubleageHeader(props: {state: SubleagueState, canDelete: boolean, dispa
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatch<StructureReducerActions>, showError: boolean}) {
|
function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatch<StructureReducerActions>, deletedTeams: string[], showError: boolean}) {
|
||||||
return (<>
|
return (<>
|
||||||
{props.subleagues[0].divisions.map((val, di) => (
|
{props.subleagues[0].divisions.map((val, di) => (
|
||||||
<div key={val.id} className="cl_table_row">
|
<div key={val.id} className="cl_table_row">
|
||||||
|
@ -357,7 +395,9 @@ function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatc
|
||||||
<div className="cl_subleague_bg">
|
<div className="cl_subleague_bg">
|
||||||
<Division state={subleague.divisions[di]} dispatch={action =>
|
<Division state={subleague.divisions[di]} dispatch={action =>
|
||||||
props.dispatch(Object.assign({subleague_index: si, division_index: di}, action))
|
props.dispatch(Object.assign({subleague_index: si, division_index: di}, action))
|
||||||
} showError={props.showError}/>
|
}
|
||||||
|
isDuplicate={subleague.divisions.slice(0, di).some(val => val.name === subleague.divisions[di].name)}
|
||||||
|
deletedTeams={props.deletedTeams} showError={props.showError} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
@ -366,7 +406,14 @@ function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatc
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Division(props: {state: DivisionState, dispatch:(action: DistributiveOmit<StructureReducerActions, 'subleague_index'|'division_index'>) => void, showError:boolean}) {
|
function Division(props: {
|
||||||
|
state: DivisionState,
|
||||||
|
dispatch: (action: DistributiveOmit<StructureReducerActions, 'subleague_index'|'division_index'>) => void,
|
||||||
|
isDuplicate: boolean,
|
||||||
|
deletedTeams: string[],
|
||||||
|
showError: boolean
|
||||||
|
}) {
|
||||||
|
|
||||||
let [newName, setNewName] = useState("");
|
let [newName, setNewName] = useState("");
|
||||||
let [searchResults, setSearchResults] = useState<string[]>([]);
|
let [searchResults, setSearchResults] = useState<string[]>([]);
|
||||||
let newNameInput = useRef<HTMLInputElement>(null);
|
let newNameInput = useRef<HTMLInputElement>(null);
|
||||||
|
@ -378,45 +425,62 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
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." : "";
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="cl_division">
|
<div className="cl_division">
|
||||||
<div className="cl_division_name_box">
|
<div className="cl_division_name_box">
|
||||||
<input type="text" className="cl_division_name" placeholder="Division Name" key="input" value={props.state.name} onChange={e =>
|
<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})
|
props.dispatch({type: 'rename_division', name: e.target.value})
|
||||||
}/>
|
}/>
|
||||||
<div className="cl_structure_err cl_structure_err_div">{props.state.name === "" && props.showError ? "A name is required." : ""}</div>
|
<div className="cl_structure_err cl_structure_err_div">{props.showError ? divisionErr : ""}</div>
|
||||||
</div>
|
</div>
|
||||||
{props.state.teams.map((team, i) => (
|
{props.state.teams.map((team, i) => {
|
||||||
<div className="cl_team" key={team.id}>
|
let showDeleted = props.showError && props.deletedTeams.includes(team.name)
|
||||||
<div className="cl_team_name">{team.name}</div>
|
return (<>
|
||||||
<button className="cl_team_delete" onClick={e => props.dispatch({type:'remove_team', name: team.name})}>➖</button>
|
<div className="cl_team" key={team.id}>
|
||||||
|
<div className={"cl_team_name" + (showDeleted ? " cl_team_name_err" : "")}>{team.name}</div>
|
||||||
|
<button className="cl_team_delete" onClick={e => props.dispatch({type:'remove_team', name: team.name})}>➖</button>
|
||||||
|
</div>
|
||||||
|
<div className="cl_structure_err cl_structure_err_team">{showDeleted ? "This team was deleted" : ""}</div>
|
||||||
|
</>)
|
||||||
|
})}
|
||||||
|
{ props.state.teams.length < MAX_TEAMS_PER_DIVISION ? <>
|
||||||
|
<div className="cl_team_add">
|
||||||
|
<input type="text" className="cl_newteam_name" placeholder="Add team..." value={newName} ref={newNameInput}
|
||||||
|
onChange={e => {
|
||||||
|
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));
|
||||||
|
setNewName(e.target.value);
|
||||||
|
}}/>
|
||||||
</div>
|
</div>
|
||||||
))}
|
{searchResults.length > 0 && newName.length > 0 ?
|
||||||
<div className="cl_team_add">
|
(<div className="cl_search_list" ref={resultList}>
|
||||||
<input type="text" className="cl_newteam_name" placeholder="Add team..." value={newName} ref={newNameInput}
|
{searchResults.map(result =>
|
||||||
onChange={e => {
|
<div className="cl_search_result" key={result} onClick={e => {
|
||||||
let params = new URLSearchParams({query: e.target.value, page_len: '5', page_num: '0'});
|
props.dispatch({type:'add_team', name: result});
|
||||||
fetch("/api/teams/search?" + params.toString())
|
setNewName("");
|
||||||
.then(response => response.json())
|
if (newNameInput.current) {
|
||||||
.then(data => setSearchResults(data));
|
newNameInput.current.focus();
|
||||||
setNewName(e.target.value);
|
}
|
||||||
}}/>
|
}}>{result}</div>
|
||||||
</div>
|
)}
|
||||||
{searchResults.length > 0 && newName.length > 0 ?
|
</div>):
|
||||||
(<div className="cl_search_list" ref={resultList}>
|
null
|
||||||
{searchResults.map(result =>
|
}</> :
|
||||||
<div className="cl_search_result" key={result} onClick={e => {
|
null
|
||||||
props.dispatch({type:'add_team', name: result});
|
|
||||||
setNewName("");
|
|
||||||
if (newNameInput.current) {
|
|
||||||
newNameInput.current.focus();
|
|
||||||
}
|
|
||||||
}}>{result}</div>
|
|
||||||
)}
|
|
||||||
</div>):
|
|
||||||
<div/>
|
|
||||||
}
|
}
|
||||||
<div className="cl_structure_err cl_structure_err_teams">{props.state.teams.length < 2 && props.showError ? "Must have at least 2 teams." : ""}</div>
|
|
||||||
|
<div className="cl_structure_err cl_structure_err_teams">{props.showError ? teamsErr : ""}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -446,7 +510,7 @@ function LeagueOptions(props: {state: LeagueOptionsState, dispatch: React.Dispat
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function NumberInput(props: {title: string, value: string, setValue: (newVal: string) => void, showError: boolean, minValue?:number}) {
|
function NumberInput(props: {title: string, value: string, setValue: (newVal: string) => void, showError: boolean, minValue? : number}) {
|
||||||
let minValue = 1;
|
let minValue = 1;
|
||||||
if (props.minValue !== undefined) {
|
if (props.minValue !== undefined) {
|
||||||
minValue = props.minValue
|
minValue = props.minValue
|
||||||
|
@ -455,7 +519,7 @@ function NumberInput(props: {title: string, value: string, setValue: (newVal: st
|
||||||
<div className="cl_option_box">
|
<div className="cl_option_box">
|
||||||
<div className="cl_option_label">{props.title}</div>
|
<div className="cl_option_label">{props.title}</div>
|
||||||
<input className="cl_option_input" type="number" min={minValue} value={props.value} onChange={e => props.setValue(e.target.value)}/>
|
<input className="cl_option_input" type="number" min={minValue} value={props.value} onChange={e => props.setValue(e.target.value)}/>
|
||||||
<div className="cl_option_err">{(!isNaN(Number(props.value)) || Number(props.value) < minValue) && props.showError ? "Must be a number greater than "+minValue : ""}</div>
|
<div className="cl_option_err">{(isNaN(Number(props.value)) || Number(props.value) < minValue) && props.showError ? "Must be a number greater than " + minValue : ""}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
import {useRef} from 'react';
|
|
||||||
|
|
||||||
|
|
||||||
function removeIndex(arr: any[], index: number) {
|
function removeIndex(arr: any[], index: number) {
|
||||||
return arr.slice(0, index).concat(arr.slice(index+1));
|
return arr.slice(0, index).concat(arr.slice(index+1));
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user