finish league create page
This commit is contained in:
parent
1337df4af3
commit
39e30c3811
|
@ -27,6 +27,16 @@ input:focus {
|
||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input[type="number"] {
|
||||||
|
-webkit-appearance: textfield;
|
||||||
|
-moz-appearance: textfield;
|
||||||
|
appearance: textfield;
|
||||||
|
}
|
||||||
|
input[type=number]::-webkit-inner-spin-button,
|
||||||
|
input[type=number]::-webkit-outer-spin-button {
|
||||||
|
-webkit-appearance: none;
|
||||||
|
}
|
||||||
|
|
||||||
.cl_league_main {
|
.cl_league_main {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
@ -36,7 +46,7 @@ input:focus {
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl_league_name {
|
.cl_league_name {
|
||||||
margin: 1rem;
|
margin-top: 1rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.cl_league_options, .cl_league_structure {
|
.cl_league_options, .cl_league_structure {
|
||||||
|
@ -48,6 +58,10 @@ input:focus {
|
||||||
padding-top: 1.5rem;
|
padding-top: 1.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cl_league_options {
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
.cl_league_structure, .cl_subleague_add_align {
|
.cl_league_structure, .cl_subleague_add_align {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -55,6 +69,10 @@ input:focus {
|
||||||
width: min-content;
|
width: min-content;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cl_league_structure {
|
||||||
|
margin-top: 1rem;
|
||||||
|
}
|
||||||
|
|
||||||
.cl_league_structure_table {
|
.cl_league_structure_table {
|
||||||
display: table;
|
display: table;
|
||||||
margin-right: 1rem;
|
margin-right: 1rem;
|
||||||
|
@ -106,7 +124,7 @@ input:focus {
|
||||||
background: var(--background-main);
|
background: var(--background-main);
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 1rem;
|
||||||
margin: 0rem 0.5rem;
|
margin: 0rem 0.5rem;
|
||||||
width: 22rem;
|
min-width: 22rem;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
}
|
}
|
||||||
|
@ -116,6 +134,10 @@ input:focus {
|
||||||
margin-right: 0.5rem;
|
margin-right: 0.5rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cl_division_name_box {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
.cl_division_name {
|
.cl_division_name {
|
||||||
margin-bottom: 0.5rem;
|
margin-bottom: 0.5rem;
|
||||||
width: 95%;
|
width: 95%;
|
||||||
|
@ -145,7 +167,7 @@ input:focus {
|
||||||
|
|
||||||
.cl_team_name {
|
.cl_team_name {
|
||||||
font-size: 14pt;
|
font-size: 14pt;
|
||||||
padding-right: 0.5rem;
|
padding: 0 0.5rem;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
@ -173,6 +195,58 @@ input:focus {
|
||||||
background: var(--background-main);
|
background: var(--background-main);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cl_league_options {
|
||||||
|
padding: 1rem;
|
||||||
|
margin-top: 1.5rem;
|
||||||
|
width: 55rem;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_option_main {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
justify-content: space-around;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_option_submit_box {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_option_box {
|
||||||
|
margin: 1rem;
|
||||||
|
width: max-content;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_option_label, .cl_option_err, .cl_structure_err {
|
||||||
|
margin: 0.25rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_option_err, .cl_structure_err {
|
||||||
|
color: var(--accent-red);
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_option_err {
|
||||||
|
min-height: 1.5rem;
|
||||||
|
margin-bottom: -0.5rem;
|
||||||
|
margin-top: 0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_structure_err {
|
||||||
|
margin-bottom: -0.5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_structure_err_div {
|
||||||
|
margin-top: -0.5rem;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cl_structure_err_teams {
|
||||||
|
width: 98%;
|
||||||
|
}
|
||||||
|
|
||||||
/* button styles */
|
/* button styles */
|
||||||
|
|
||||||
button > .emoji {
|
button > .emoji {
|
||||||
|
@ -213,3 +287,15 @@ button > .emoji {
|
||||||
.cl_delete_filler {
|
.cl_delete_filler {
|
||||||
min-width: 3rem;
|
min-width: 3rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.cl_option_submit {
|
||||||
|
padding: 1rem 2rem;
|
||||||
|
height: 2rem;
|
||||||
|
border: none;
|
||||||
|
border-radius: 0.75rem;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background: var(--accent-green);
|
||||||
|
font-size: 14pt;
|
||||||
|
}
|
|
@ -2,28 +2,66 @@ import React, {useState, useRef, useLayoutEffect, useReducer} from 'react';
|
||||||
import './CreateLeague.css';
|
import './CreateLeague.css';
|
||||||
import twemoji from 'twemoji';
|
import twemoji from 'twemoji';
|
||||||
|
|
||||||
interface LeagueStructureState {
|
// STATE CLASSES
|
||||||
|
|
||||||
|
class LeagueStructureState {
|
||||||
subleagues: SubleagueState[]
|
subleagues: SubleagueState[]
|
||||||
|
|
||||||
|
constructor(subleagues: SubleagueState[] = []) {
|
||||||
|
this.subleagues = subleagues;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SubleagueState {
|
class SubleagueState {
|
||||||
name: string
|
name: string
|
||||||
id: string|number
|
|
||||||
divisions: DivisionState[]
|
divisions: DivisionState[]
|
||||||
|
id: string|number
|
||||||
|
|
||||||
|
constructor(divisions: DivisionState[] = []) {
|
||||||
|
this.name = "";
|
||||||
|
this.divisions = divisions;
|
||||||
|
this.id = getUID();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface DivisionState {
|
class DivisionState {
|
||||||
name: string
|
name: string
|
||||||
id: string|number
|
|
||||||
teams: TeamState[]
|
teams: TeamState[]
|
||||||
|
id: string|number
|
||||||
|
|
||||||
|
constructor() {
|
||||||
|
this.name = "";
|
||||||
|
this.teams = [];
|
||||||
|
this.id = getUID();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface TeamState {
|
class TeamState {
|
||||||
name: string
|
name: string
|
||||||
id: string|number
|
id: string|number
|
||||||
|
|
||||||
|
constructor(name: string = "") {
|
||||||
|
this.name = name;
|
||||||
|
this.id = getUID();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
type LeagueReducerActions =
|
let getUID = function() { // does NOT generate UUIDs. Meant to create list keys ONLY
|
||||||
|
let id = 0;
|
||||||
|
return function() { return id++ }
|
||||||
|
}()
|
||||||
|
|
||||||
|
let initLeagueStructure = {
|
||||||
|
subleagues: [0, 1].map((val) =>
|
||||||
|
new SubleagueState([0, 1].map((val) =>
|
||||||
|
new DivisionState()
|
||||||
|
))
|
||||||
|
)
|
||||||
|
};
|
||||||
|
|
||||||
|
// STRUCTURE REDUCER
|
||||||
|
|
||||||
|
type StructureReducerActions =
|
||||||
{type: 'remove_subleague', subleague_index: number} |
|
{type: 'remove_subleague', subleague_index: number} |
|
||||||
{type: 'add_subleague'} |
|
{type: 'add_subleague'} |
|
||||||
{type: 'rename_subleague', subleague_index: number, name: string} |
|
{type: 'rename_subleague', subleague_index: number, name: string} |
|
||||||
|
@ -33,70 +71,52 @@ type LeagueReducerActions =
|
||||||
{type: 'remove_team', 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}
|
{type: 'add_team', subleague_index:number, division_index:number, name:string}
|
||||||
|
|
||||||
type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
|
function leagueStructureReducer(state: LeagueStructureState, action: StructureReducerActions): LeagueStructureState {
|
||||||
|
|
||||||
let getUID = function() { // does NOT generate UUIDs. Meant to create list keys ONLY
|
|
||||||
let id = 0;
|
|
||||||
return function() { return id++}
|
|
||||||
}()
|
|
||||||
|
|
||||||
function leagueStructureReducer(state: LeagueStructureState, action: LeagueReducerActions): LeagueStructureState {
|
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case 'remove_subleague':
|
case 'remove_subleague':
|
||||||
return {subleagues: removeIndex(state.subleagues, action.subleague_index)};
|
return {subleagues: removeIndex(state.subleagues, action.subleague_index)};
|
||||||
case 'add_subleague':
|
case 'add_subleague':
|
||||||
return {subleagues: state.subleagues.concat([{
|
return {subleagues: append(state.subleagues, new SubleagueState(
|
||||||
name: "",
|
arrayOf(state.subleagues[0].divisions.length, i =>
|
||||||
id: getUID(),
|
new DivisionState()
|
||||||
divisions: arrayOf(state.subleagues[0].divisions.length, i => ({
|
)
|
||||||
name: "",
|
))}
|
||||||
id: getUID(),
|
|
||||||
teams: []
|
|
||||||
}))
|
|
||||||
}])};
|
|
||||||
case 'rename_subleague':
|
case 'rename_subleague':
|
||||||
return replaceSubleague(state, action.subleague_index, subleague => ({
|
return replaceSubleague(state, action.subleague_index, subleague => {
|
||||||
name: action.name,
|
let nSubleague = shallowClone(subleague);
|
||||||
id: subleague.id,
|
nSubleague.name = action.name;
|
||||||
divisions: subleague.divisions
|
return nSubleague;
|
||||||
}));
|
});
|
||||||
case 'remove_divisions':
|
case 'remove_divisions':
|
||||||
return {subleagues: state.subleagues.map(subleague => ({
|
return {subleagues: state.subleagues.map(subleague => {
|
||||||
name: subleague.name,
|
let nSubleague = shallowClone(subleague);
|
||||||
id: subleague.id,
|
nSubleague.divisions = removeIndex(subleague.divisions, action.division_index)
|
||||||
divisions: removeIndex(subleague.divisions, action.division_index)
|
return nSubleague;
|
||||||
}))};
|
})};
|
||||||
case 'add_divisions':
|
case 'add_divisions':
|
||||||
return {subleagues: state.subleagues.map(subleague => ({
|
return {subleagues: state.subleagues.map(subleague => {
|
||||||
name: subleague.name,
|
let nSubleague = shallowClone(subleague);
|
||||||
id: subleague.id,
|
nSubleague.divisions = append(subleague.divisions, new DivisionState())
|
||||||
divisions: subleague.divisions.concat([{
|
return nSubleague;
|
||||||
name: "",
|
})};
|
||||||
id: getUID(),
|
|
||||||
teams: []
|
|
||||||
}])
|
|
||||||
}))};
|
|
||||||
case 'rename_division':
|
case 'rename_division':
|
||||||
return replaceDivision(state, action.subleague_index, action.division_index, division => ({
|
return replaceDivision(state, action.subleague_index, action.division_index, division => {
|
||||||
name: action.name,
|
let nDivision = shallowClone(division);
|
||||||
id: division.id,
|
nDivision.name = action.name;
|
||||||
teams: division.teams
|
return nDivision;
|
||||||
}));
|
});
|
||||||
case 'remove_team':
|
case 'remove_team':
|
||||||
return replaceDivision(state, action.subleague_index, action.division_index, division => ({
|
return replaceDivision(state, action.subleague_index, action.division_index, division => {
|
||||||
name: division.name,
|
let nDivision = shallowClone(division);
|
||||||
id: division.id,
|
nDivision.teams = removeIndex(division.teams, division.teams.findIndex(val => val.name === action.name));
|
||||||
teams: removeIndex(division.teams, division.teams.findIndex(val => val.name === action.name))
|
return nDivision;
|
||||||
}));
|
});
|
||||||
case 'add_team':
|
case 'add_team':
|
||||||
return replaceDivision(state, action.subleague_index, action.division_index, division => ({
|
return replaceDivision(state, action.subleague_index, action.division_index, division => {
|
||||||
name: division.name,
|
let nDivision = shallowClone(division);
|
||||||
id: division.id,
|
nDivision.teams = append(division.teams, new TeamState(action.name));
|
||||||
teams: division.teams.concat([{
|
return nDivision;
|
||||||
name: action.name,
|
});
|
||||||
id: getUID()
|
|
||||||
}])
|
|
||||||
}));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -105,13 +125,15 @@ function replaceSubleague(state: LeagueStructureState, si: number, func: (val: S
|
||||||
}
|
}
|
||||||
|
|
||||||
function replaceDivision(state: LeagueStructureState, si: number, di: number, func:(val: DivisionState) => DivisionState) {
|
function replaceDivision(state: LeagueStructureState, si: number, di: number, func:(val: DivisionState) => DivisionState) {
|
||||||
return replaceSubleague(state, si, subleague => ({
|
return replaceSubleague(state, si, subleague => {
|
||||||
name: subleague.name,
|
let nSubleague = shallowClone(subleague);
|
||||||
id: subleague.id,
|
nSubleague.divisions = replaceIndex(subleague.divisions, di, func(subleague.divisions[di]));
|
||||||
divisions: replaceIndex(subleague.divisions, di, func(subleague.divisions[di]))
|
return nSubleague;
|
||||||
}))
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// UTIL
|
||||||
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
@ -132,21 +154,22 @@ function arrayOf<T>(length: number, func: (i: number) => T): T[] {
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
let initLeagueStructure = {
|
function shallowClone<T>(obj: T): T {
|
||||||
subleagues: [0, 1].map((val) => ({
|
return Object.assign({}, obj);
|
||||||
name: "",
|
|
||||||
id: getUID(),
|
|
||||||
divisions: [0, 1].map((val) => ({
|
|
||||||
name: "",
|
|
||||||
id: getUID(),
|
|
||||||
teams: []
|
|
||||||
}))
|
|
||||||
}))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type DistributiveOmit<T, K extends keyof any> = T extends any ? Omit<T, K> : never;
|
||||||
|
|
||||||
|
// CREATE LEAGUE
|
||||||
|
|
||||||
function CreateLeague() {
|
function CreateLeague() {
|
||||||
let [name, setName] = useState("");
|
let [name, setName] = useState("");
|
||||||
|
let [showError, setShowError] = useState(false);
|
||||||
let [structure, dispatch] = useReducer(leagueStructureReducer, initLeagueStructure);
|
let [structure, dispatch] = useReducer(leagueStructureReducer, initLeagueStructure);
|
||||||
|
let gamesSeries = useState('3');
|
||||||
|
let seriesDivisionOpp = useState('8');
|
||||||
|
let seriesInterDivision = useState('16');
|
||||||
|
let seriesInterLeague = useState('8');
|
||||||
|
|
||||||
let self = useRef<HTMLDivElement | null>(null)
|
let self = useRef<HTMLDivElement | null>(null)
|
||||||
|
|
||||||
|
@ -159,30 +182,115 @@ function CreateLeague() {
|
||||||
return (
|
return (
|
||||||
<div className="cl_league_main" ref={self}>
|
<div className="cl_league_main" ref={self}>
|
||||||
<input type="text" className="cl_league_name" placeholder="League Name" value={name} onChange={(e) => setName(e.target.value)}/>
|
<input type="text" className="cl_league_name" placeholder="League Name" value={name} onChange={(e) => setName(e.target.value)}/>
|
||||||
<LeagueStructre state={structure} dispatch={dispatch}/>
|
<div className="cl_structure_err">{name === "" && showError ? "A name is required." : ""}</div>
|
||||||
<LeagueOptions />
|
<LeagueStructre state={structure} dispatch={dispatch} showError={showError}/>
|
||||||
|
<div className="cl_league_options">
|
||||||
|
<LeagueOptions
|
||||||
|
gamesSeries={gamesSeries}
|
||||||
|
seriesDivisionOpp={seriesDivisionOpp}
|
||||||
|
seriesInterDivision={seriesInterDivision}
|
||||||
|
seriesInterLeague={seriesInterLeague}
|
||||||
|
showError={showError}
|
||||||
|
/>
|
||||||
|
<div className="cl_option_submit_box">
|
||||||
|
<button className="cl_option_submit" onClick={e => {
|
||||||
|
//make network call, once leagues are merged
|
||||||
|
if (!validRequest(name, structure, gamesSeries[0], seriesDivisionOpp[0], seriesInterDivision[0], seriesInterLeague[0])) {
|
||||||
|
setShowError(true);
|
||||||
|
}
|
||||||
|
}}>Submit</button>
|
||||||
|
<div className="cl_option_err">{
|
||||||
|
!validRequest(name, structure, gamesSeries[0], seriesDivisionOpp[0], seriesInterDivision[0], seriesInterLeague[0]) && showError ?
|
||||||
|
"Cannot create league. Some information is invalid." : ""
|
||||||
|
}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function LeagueStructre(props: {state: LeagueStructureState, dispatch: React.Dispatch<LeagueReducerActions>}) {
|
function makeRequest(
|
||||||
|
name:string,
|
||||||
|
structure: LeagueStructureState,
|
||||||
|
gamesPerSeries: string,
|
||||||
|
divisionSeries: string,
|
||||||
|
interDivisionSeries: string,
|
||||||
|
interLeagueSeries: string
|
||||||
|
) {
|
||||||
|
|
||||||
|
if (!validRequest(name, structure, gamesPerSeries, divisionSeries, interDivisionSeries, interLeagueSeries)) {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
return ({
|
||||||
|
structure: {
|
||||||
|
name: name,
|
||||||
|
subleagues: structure.subleagues.map(subleague => ({
|
||||||
|
name: subleague.name,
|
||||||
|
divisions: subleague.divisions.map(division => ({
|
||||||
|
name: division.name,
|
||||||
|
teams: division.teams
|
||||||
|
}))
|
||||||
|
}))
|
||||||
|
},
|
||||||
|
games_per_series: Number(gamesPerSeries),
|
||||||
|
division_series: Number(divisionSeries),
|
||||||
|
inter_division_series: Number(interDivisionSeries),
|
||||||
|
inter_league_series: Number(interLeagueSeries)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function validRequest(
|
||||||
|
name:string,
|
||||||
|
structure: LeagueStructureState,
|
||||||
|
gamesPerSeries: string,
|
||||||
|
divisionSeries: string,
|
||||||
|
interDivisionSeries: string,
|
||||||
|
interLeagueSeries: string
|
||||||
|
) {
|
||||||
|
|
||||||
|
return (
|
||||||
|
name !== "" &&
|
||||||
|
validNumber(gamesPerSeries) &&
|
||||||
|
validNumber(divisionSeries) &&
|
||||||
|
validNumber(interDivisionSeries) &&
|
||||||
|
validNumber(interLeagueSeries) &&
|
||||||
|
structure.subleagues.length % 2 === 0 &&
|
||||||
|
structure.subleagues.every(subleague =>
|
||||||
|
subleague.name !== "" &&
|
||||||
|
subleague.divisions.every(division =>
|
||||||
|
division.name !== "" &&
|
||||||
|
division.teams.length >= 2
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
function validNumber(value: string) {
|
||||||
|
return Number(value) !== NaN && Number(value) > 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// LEAGUE STRUCUTRE
|
||||||
|
|
||||||
|
function LeagueStructre(props: {state: LeagueStructureState, dispatch: React.Dispatch<StructureReducerActions>, showError: boolean}) {
|
||||||
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} />
|
<SubleagueHeaders subleagues={props.state.subleagues} dispatch={props.dispatch} showError={props.showError}/>
|
||||||
<Divisions subleagues={props.state.subleagues} dispatch={props.dispatch} />
|
<Divisions subleagues={props.state.subleagues} dispatch={props.dispatch} showError={props.showError}/>
|
||||||
</div>
|
</div>
|
||||||
<button className="cl_subleague_add" onClick={e => props.dispatch({type: 'add_subleague'})}>➕</button>
|
<button className="cl_subleague_add" onClick={e => props.dispatch({type: 'add_subleague'})}>➕</button>
|
||||||
</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>
|
||||||
<button className="cl_division_add" onClick={e => props.dispatch({type: 'add_divisions'})}>➕</button>
|
<button className="cl_division_add" onClick={e => props.dispatch({type: 'add_divisions'})}>➕</button>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React.Dispatch<LeagueReducerActions>}) {
|
function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React.Dispatch<StructureReducerActions>, showError:boolean}) {
|
||||||
return (
|
return (
|
||||||
<div className="cl_headers">
|
<div className="cl_headers">
|
||||||
<div key="filler" className="cl_delete_filler"/>
|
<div key="filler" className="cl_delete_filler"/>
|
||||||
|
@ -192,6 +300,7 @@ function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React.
|
||||||
<SubleageHeader state={subleague} canDelete={props.subleagues.length > 1} dispatch={action =>
|
<SubleageHeader state={subleague} canDelete={props.subleagues.length > 1} dispatch={action =>
|
||||||
props.dispatch(Object.assign({subleague_index: i}, action))
|
props.dispatch(Object.assign({subleague_index: i}, action))
|
||||||
}/>
|
}/>
|
||||||
|
<div className="cl_structure_err">{subleague.name === "" && props.showError ? "A name is required." : ""}</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
@ -199,7 +308,7 @@ function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React.
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function SubleageHeader(props: {state: SubleagueState, canDelete: boolean, dispatch:(action: DistributiveOmit<LeagueReducerActions, 'subleague_index'>) => void}) {
|
function SubleageHeader(props: {state: SubleagueState, canDelete: boolean, dispatch:(action: DistributiveOmit<StructureReducerActions, 'subleague_index'>) => void}) {
|
||||||
return (
|
return (
|
||||||
<div className="cl_subleague_header">
|
<div className="cl_subleague_header">
|
||||||
<input type="text" className="cl_subleague_name" placeholder="Subleague Name" value={props.state.name} onChange={e =>
|
<input type="text" className="cl_subleague_name" placeholder="Subleague Name" value={props.state.name} onChange={e =>
|
||||||
|
@ -210,7 +319,7 @@ function SubleageHeader(props: {state: SubleagueState, canDelete: boolean, dispa
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatch<LeagueReducerActions>}) {
|
function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatch<StructureReducerActions>, 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">
|
||||||
|
@ -225,7 +334,7 @@ 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}/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
@ -234,7 +343,7 @@ function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatc
|
||||||
</>);
|
</>);
|
||||||
}
|
}
|
||||||
|
|
||||||
function Division(props: {state: DivisionState, dispatch:(action: DistributiveOmit<LeagueReducerActions, 'subleague_index'|'division_index'>) => void}) {
|
function Division(props: {state: DivisionState, dispatch:(action: DistributiveOmit<StructureReducerActions, 'subleague_index'|'division_index'>) => void, 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);
|
||||||
|
@ -248,10 +357,11 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="cl_division">
|
<div className="cl_division">
|
||||||
<div>
|
<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>
|
</div>
|
||||||
{props.state.teams.map((team, i) => (
|
{props.state.teams.map((team, i) => (
|
||||||
<div className="cl_team" key={team.id}>
|
<div className="cl_team" key={team.id}>
|
||||||
|
@ -283,14 +393,48 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm
|
||||||
</div>):
|
</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>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
function LeagueOptions() {
|
// LEAGUE OPTIONS
|
||||||
return (
|
|
||||||
<div className="cl_league_options">
|
|
||||||
|
|
||||||
|
type StateBundle<T> = [T, React.Dispatch<React.SetStateAction<T>>]
|
||||||
|
|
||||||
|
function LeagueOptions(props: {
|
||||||
|
gamesSeries: StateBundle<string>,
|
||||||
|
seriesDivisionOpp: StateBundle<string>,
|
||||||
|
seriesInterDivision: StateBundle<string>,
|
||||||
|
seriesInterLeague: StateBundle<string>,
|
||||||
|
showError: boolean
|
||||||
|
}) {
|
||||||
|
|
||||||
|
let [nGamesSeries, setGamesSeries] = props.gamesSeries;
|
||||||
|
let [nSeriesDivisionOpp, setSeriesDivisionOpp] = props.seriesDivisionOpp;
|
||||||
|
let [nSeriesInterDivision, setSeriesInterDivision] = props.seriesInterDivision;
|
||||||
|
let [nSeriesInterLeague, setSeriesInterLeague] = props.seriesInterLeague;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="cl_option_main">
|
||||||
|
<div className="cl_option_column">
|
||||||
|
<NumberInput title="Number of games per series" value={nGamesSeries} setValue={setGamesSeries} showError={props.showError}/>
|
||||||
|
<NumberInput title="Number of series with each division opponent" value={nSeriesDivisionOpp} setValue={setSeriesDivisionOpp} showError={props.showError}/>
|
||||||
|
</div>
|
||||||
|
<div className="cl_option_column">
|
||||||
|
<NumberInput title="Number of inter-divisional series" value={nSeriesInterDivision} setValue={setSeriesInterDivision} showError={props.showError}/>
|
||||||
|
<NumberInput title="Number of inter-league series" value={nSeriesInterLeague} setValue={setSeriesInterLeague} showError={props.showError}/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function NumberInput(props: {title: string, value: string, setValue: (newVal: string) => void, showError: boolean}) {
|
||||||
|
return (
|
||||||
|
<div className="cl_option_box">
|
||||||
|
<div className="cl_option_label">{props.title}</div>
|
||||||
|
<input className="cl_option_input" type="number" min="0" value={props.value} onChange={e => props.setValue(e.target.value)}/>
|
||||||
|
<div className="cl_option_err">{(Number(props.value) === NaN || Number(props.value) < 0) && props.showError ? "Must be a number greater than 0" : ""}</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user