From 72edbf50229fde090f9ed74d53fb4ffb6e32fa56 Mon Sep 17 00:00:00 2001 From: Elijah Steres Date: Sun, 10 Jan 2021 01:27:30 -0500 Subject: [PATCH 1/7] make league structure --- simmadome/src/CreateLeague.css | 177 +++++++++++++++++++++++ simmadome/src/CreateLeague.tsx | 250 +++++++++++++++++++++++++++++++++ simmadome/src/index.tsx | 2 + 3 files changed, 429 insertions(+) create mode 100644 simmadome/src/CreateLeague.css create mode 100644 simmadome/src/CreateLeague.tsx diff --git a/simmadome/src/CreateLeague.css b/simmadome/src/CreateLeague.css new file mode 100644 index 0000000..e867503 --- /dev/null +++ b/simmadome/src/CreateLeague.css @@ -0,0 +1,177 @@ +th, td { + border: none; + padding: 0; + height: 100%; +} + +table { + border-collapse: collapse; + height: min-content; +} + +th .cl_subleague_bg { + border-top-left-radius: 0.5rem; + border-top-right-radius: 0.5rem; +} + +tbody tr:last-child .cl_subleague_bg { + border-bottom-left-radius: 0.5rem; + border-bottom-right-radius: 0.5rem; +} + +input { + border: none; + border-radius: 1rem; + height: 2rem; + padding-left: 1rem; + background: var(--background-secondary); + font-size: 14pt; +} + +.cl_league_main { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + margin-top: 3rem; +} + +.cl_league_name { + margin: 1rem; +} + +.cl_league_options, .cl_league_structure { + display: flex; + background: var(--background-tertiary); + flex-direction: column; + max-width: 100%; + border-radius: 1rem; + padding-top: 1.5rem; +} + +.cl_league_structure, .cl_subleague_add_align { + display: flex; + align-items: center; + justify-content: center; + width: min-content; +} + +.cl_league_structure_table { + margin: 1rem; + margin-left: 0rem; + margin-top: 0rem; +} + +.cl_league_structure_scrollbox { + max-width: 100%; + overflow-y: scroll; +} + + /* Hide scrollbar for Chrome, Safari and Opera */ +.cl_league_structure_scrollbox::-webkit-scrollbar { + display: none; +} + +/* Hide scrollbar for IE, Edge and Firefox */ +.cl_league_structure_scrollbox { + -ms-overflow-style: none; /* IE and Edge */ + scrollbar-width: none; /* Firefox */ +} + +.cl_subleague_add_align{ + margin-left: 1.5rem; + padding-right: 1.5rem; +} + +.cl_subleague_header { + display: flex; + width:100%; + align-items: center; + justify-content: space-between; +} + +.cl_subleague_bg { + background: var(--background-main); + padding:1rem; + padding-bottom: 0rem; + margin: 0rem 0.5rem; + height: 100%; + width: 20rem; +} + +.cl_subleague_name, .cl_newteam_name { + flex-grow: 1; + margin-right: 0.5rem; +} + +.cl_division_name { + margin-bottom: 0.5rem; + width: 95%; +} + +.cl_division { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 1rem; + border-radius: 0.5rem; + background: var(--background-accent); +} + +.cl_team, .cl_team_add { + display: flex; + align-items: center; + justify-content: space-between; + width: 100%; + margin: 0.25rem 0rem; +} + +.cl_team_name { + font-size: 14pt; + padding-right: 0.5rem; + overflow: hidden; + text-overflow: ellipsis; +} + +/* button styles */ + +button > .emoji { + margin: 0; +} + +.cl_subleague_delete, .cl_team_delete, .cl_division_delete, .cl_subleague_add, .cl_division_add, .cl_newteam_add { + padding: 0; + width: 2rem; + height: 2rem; + border: none; + border-radius: 1rem; + display: flex; + align-items: center; + justify-content: center; +} + +.cl_subleague_delete, .cl_team_delete, .cl_division_delete { + background: var(--accent-red); +} + +.cl_subleague_add, .cl_division_add, .cl_newteam_add { + background: var(--accent-green); +} + +.cl_subleague_add { + margin-top: 3rem; +} + +.cl_division_add { + margin: 1.25rem; +} + +.cl_division_delete { + margin-right: 1rem; + margin-top: 1rem; +} + +.cl_delete_filler { + min-width: 3rem; +} \ No newline at end of file diff --git a/simmadome/src/CreateLeague.tsx b/simmadome/src/CreateLeague.tsx new file mode 100644 index 0000000..a64c4d8 --- /dev/null +++ b/simmadome/src/CreateLeague.tsx @@ -0,0 +1,250 @@ +import React, {useState, useRef, useLayoutEffect, useReducer} from 'react'; +import './CreateLeague.css'; +import twemoji from 'twemoji'; + +interface LeagueStructureState { + subleagues: SubleagueState[] +} + +interface SubleagueState { + name: string + divisions: DivisionState[] +} + +interface DivisionState { + name: string + teams: TeamState[] +} + +interface TeamState { + name: string +} + +let initLeagueStructure = { + subleagues: [0, 1].map((val) => ({ + name: "", + divisions: [0, 1].map((val) => ({ + name: "", + teams: [] + })) + })) +} + +type LeagueReducerActions = + {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} + +type DistributiveOmit = T extends any ? Omit : never; + +function leagueStructureReducer(state: LeagueStructureState, action: LeagueReducerActions): LeagueStructureState { + switch (action.type) { + case 'remove_subleague': + return {subleagues: removeIndex(state.subleagues, action.subleague_index)}; + case 'add_subleague': + return {subleagues: state.subleagues.concat([{ + name: "", + divisions: arrayOf(state.subleagues[0].divisions.length, i => ({ + name: "", + teams: [] + })) + }])}; + case 'rename_subleague': + return replaceSubleague(state, action.subleague_index, subleague => ({ + name: action.name, + divisions: subleague.divisions + })); + case 'remove_divisions': + return {subleagues: state.subleagues.map(subleague => ({ + name: subleague.name, + divisions: removeIndex(subleague.divisions, action.division_index) + }))}; + case 'add_divisions': + return {subleagues: state.subleagues.map(subleague => ({ + name: subleague.name, + divisions: subleague.divisions.concat([{name: "", teams: []}]) + }))}; + case 'rename_division': + return replaceDivision(state, action.subleague_index, action.division_index, division => ({ + name: action.name, + teams: division.teams + })); + case 'remove_team': + return replaceDivision(state, action.subleague_index, action.division_index, division => ({ + name: division.name, + teams: removeIndex(division.teams, division.teams.findIndex(val => val.name === action.name)) + })); + case 'add_team': + return replaceDivision(state, action.subleague_index, action.division_index, division => ({ + name: division.name, + teams: division.teams.concat([{name: action.name}]) + })); + } +} + +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) { + return replaceSubleague(state, si, subleague => ({ + name: subleague.name, + divisions: replaceIndex(subleague.divisions, di, func(subleague.divisions[di])) + })) +} + +function removeIndex(arr: any[], index: number) { + return arr.slice(0, index).concat(arr.slice(index+1)); +} + +function replaceIndex(arr: T[], index: number, val: T) { + return arr.slice(0, index).concat([val]).concat(arr.slice(index+1)); +} + +function append(arr: T[], val: T) { + return arr.concat([val]); +} + +function arrayOf(length: number, func: (i: number) => T): T[] { + var out: T[] = []; + for (var i = 0; i < length; i++) { + out.push(func(i)); + } + return out; +} + +function CreateLeague() { + let [name, setName] = useState(""); + let [structure, dispatch] = useReducer(leagueStructureReducer, initLeagueStructure); + + let self = useRef(null) + + useLayoutEffect(() => { + if (self.current) { + twemoji.parse(self.current) + } + }) + + return ( +
+ setName(e.target.value)}/> + + +
+ ); +} + +function LeagueStructre(props: {state: LeagueStructureState, dispatch: React.Dispatch}) { + return ( +
+
+
+ + + +
+ +
+
+ +
+ ); +} + +function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React.Dispatch}) { + return ( + + + + {props.subleagues.map((subleague, i) => ( + +
+ 1} dispatch={action => + props.dispatch(Object.assign({subleague_index: i}, action)) + }/> +
+ + ))} + + + ); +} + +function SubleageHeader(props: {state: SubleagueState, canDelete: boolean, dispatch:(action: DistributiveOmit) => void}) { + return ( +
+ + props.dispatch({type: 'rename_subleague', name: e.target.value}) + }/> + {props.canDelete ? : null} +
+ ); +} + +function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatch}) { + return ( + + {props.subleagues[0].divisions.map((val, di) => ( + + + {props.subleagues[0].divisions.length > 1 ? + : + null + } + + {props.subleagues.map((subleague, si) => ( + +
+ + props.dispatch(Object.assign({subleague_index: si, division_index: di}, action)) + }/> +
+ + ))} + + ))} + + ); +} + +function Division(props: {state: DivisionState, dispatch:(action: DistributiveOmit) => void}) { + let [newName, setNewName] = useState(""); + + return ( +
+ + props.dispatch({type: 'rename_division', name: e.target.value}) + }/> + {props.state.teams.map((team, i) => ( +
+
{team.name}
+ +
+ ))} +
+ setNewName(e.target.value)}/> + +
+
+ ); +} + +function LeagueOptions() { + return ( +
+
+ +
+
+ ); +} + +export default CreateLeague; \ No newline at end of file diff --git a/simmadome/src/index.tsx b/simmadome/src/index.tsx index 1235dcc..21bd0f7 100644 --- a/simmadome/src/index.tsx +++ b/simmadome/src/index.tsx @@ -4,6 +4,7 @@ import { BrowserRouter as Router, Switch, Route } from 'react-router-dom'; import './index.css'; import GamesPage from './GamesPage'; import GamePage from './GamePage'; +import CreateLeague from './CreateLeague'; import discordlogo from "./img/discord.png"; import reportWebVitals from './reportWebVitals'; @@ -13,6 +14,7 @@ ReactDOM.render(
+ From c3a35c07a4726e535d2ec25cdf2ec6cc2f10ccca Mon Sep 17 00:00:00 2001 From: Elijah Steres Date: Sun, 10 Jan 2021 23:47:49 -0500 Subject: [PATCH 2/7] work on team search --- main_controller.py | 24 ++++- simmadome/package-lock.json | 87 ++++++++++++++--- simmadome/package.json | 4 +- simmadome/src/CreateLeague.css | 101 ++++++++++++++------ simmadome/src/CreateLeague.tsx | 168 +++++++++++++++++++++------------ simmadome/src/index.tsx | 30 +++--- 6 files changed, 293 insertions(+), 121 deletions(-) diff --git a/main_controller.py b/main_controller.py index 707c9f7..3702495 100644 --- a/main_controller.py +++ b/main_controller.py @@ -1,6 +1,7 @@ import asyncio, time, datetime, games, json, threading, jinja2, leagues, os -from flask import Flask, url_for, Response, render_template, request, jsonify, send_from_directory +from flask import Flask, url_for, Response, render_template, request, jsonify, send_from_directory, abort from flask_socketio import SocketIO, emit +import database as db app = Flask("the-prestige", static_folder='simmadome/build') app.config['SECRET KEY'] = 'dev' @@ -16,6 +17,27 @@ def serve(path): else: return send_from_directory(app.static_folder, 'index.html') +### API + +@app.route('/api/teams/search') +def searchteams(): + query = request.args.get('query') + page_len = int(request.args.get('page_len')) + page_num = int(request.args.get('page_num')) + + if query is None: + abort(400, "A query term is required") + + result = db.search_teams(query) + if page_len is not None: #pagination should probably be doen in the sqlite query but this will do for now + if page_num is None: + abort(400, "A page_len argument must be accompanied by a page_num argument") + result = result[page_num*page_len : (page_num + 1)*page_len] + + return jsonify([json.loads(x[0])['name'] for x in result]) #currently all we need is the name but that can change + + +### SOCKETS thread2 = threading.Thread(target=socketio.run,args=(app,'0.0.0.0')) thread2.start() diff --git a/simmadome/package-lock.json b/simmadome/package-lock.json index cccf2db..a27cb55 100644 --- a/simmadome/package-lock.json +++ b/simmadome/package-lock.json @@ -1974,6 +1974,7 @@ "version": "7.29.1", "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-7.29.1.tgz", "integrity": "sha512-6BU7vAjKuMspCy9QQEtbWgmkuXi/yOSZo3ANdvZmNQW8N/WQGjO9cvlcA5EFJaPtp2hL1RAaPGpCXxumijUxCg==", + "dev": true, "requires": { "@babel/code-frame": "^7.10.4", "@babel/runtime": "^7.12.5", @@ -1989,6 +1990,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -1997,6 +1999,7 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz", "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2006,6 +2009,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -2013,17 +2017,20 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "supports-color": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -2034,6 +2041,7 @@ "version": "5.11.8", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.11.8.tgz", "integrity": "sha512-ScyKrWQM5xNcr79PkSewnA79CLaoxVskE+f7knTOhDD9ftZSA1Jw8mj+pneqhEu3x37ncNfW84NUr7lqK+mXjA==", + "dev": true, "requires": { "@babel/runtime": "^7.9.2", "@types/testing-library__jest-dom": "^5.9.1", @@ -2049,6 +2057,7 @@ "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "requires": { "color-convert": "^2.0.1" } @@ -2057,6 +2066,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/chalk/-/chalk-3.0.0.tgz", "integrity": "sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==", + "dev": true, "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -2066,6 +2076,7 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "requires": { "color-name": "~1.1.4" } @@ -2073,12 +2084,14 @@ "color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true }, "css": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/css/-/css-3.0.0.tgz", "integrity": "sha512-DG9pFfwOrzc+hawpmqX/dHYHJG+Bsdb0klhyi1sDneOgGOXy9wQIC8hzyVp1e4NRYDBdxcylvywPkkXCHAzTyQ==", + "dev": true, "requires": { "inherits": "^2.0.4", "source-map": "^0.6.1", @@ -2088,17 +2101,20 @@ "has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true }, "source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true }, "source-map-resolve": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.6.0.tgz", "integrity": "sha512-KXBr9d/fO/bWo97NXsPIAW1bFSBOuCnjbNTBMO7N59hsv5i9yzRDfcYwwt0l04+VqnKC+EwzvJZIP/qkuMgR/w==", + "dev": true, "requires": { "atob": "^2.1.2", "decode-uri-component": "^0.2.0" @@ -2108,6 +2124,7 @@ "version": "7.2.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, "requires": { "has-flag": "^4.0.0" } @@ -2118,6 +2135,7 @@ "version": "11.2.2", "resolved": "https://registry.npmjs.org/@testing-library/react/-/react-11.2.2.tgz", "integrity": "sha512-jaxm0hwUjv+hzC+UFEywic7buDC9JQ1q3cDsrWVSDAPmLotfA6E6kUHlYm/zOeGCac6g48DR36tFHxl7Zb+N5A==", + "dev": true, "requires": { "@babel/runtime": "^7.12.5", "@testing-library/dom": "^7.28.1" @@ -2127,6 +2145,7 @@ "version": "12.6.0", "resolved": "https://registry.npmjs.org/@testing-library/user-event/-/user-event-12.6.0.tgz", "integrity": "sha512-FNEH/HLmOk5GO70I52tKjs7WvGYckeE/SrnLX/ip7z2IGbffyd5zOUM1tZ10vsTphqm+VbDFI0oaXu0wcfQsAQ==", + "dev": true, "requires": { "@babel/runtime": "^7.12.5" } @@ -2139,7 +2158,8 @@ "@types/aria-query": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-4.2.0.tgz", - "integrity": "sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==" + "integrity": "sha512-iIgQNzCm0v7QMhhe4Jjn9uRh+I6GoPmt03CbEtwx3ao8/EfoQcmgtqH4vQ5Db/lxiIGaWDv6nwvunuh0RyX0+A==", + "dev": true }, "@types/babel__core": { "version": "7.1.12", @@ -2217,7 +2237,8 @@ "@types/history": { "version": "4.7.8", "resolved": "https://registry.npmjs.org/@types/history/-/history-4.7.8.tgz", - "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==" + "integrity": "sha512-S78QIYirQcUoo6UJZx9CSP0O2ix9IaeAXwQi26Rhr/+mg7qqPy8TzaxHSUut7eGjL8WmLccT7/MXf304WjqHcA==", + "dev": true }, "@types/html-minifier-terser": { "version": "5.1.1", @@ -2249,11 +2270,21 @@ "version": "26.0.19", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-26.0.19.tgz", "integrity": "sha512-jqHoirTG61fee6v6rwbnEuKhpSKih0tuhqeFbCmMmErhtu3BYlOZaXWjffgOstMM4S/3iQD31lI5bGLTrs97yQ==", + "dev": true, "requires": { "jest-diff": "^26.0.0", "pretty-format": "^26.0.0" } }, + "@types/jquery": { + "version": "3.5.5", + "resolved": "https://registry.npmjs.org/@types/jquery/-/jquery-3.5.5.tgz", + "integrity": "sha512-6RXU9Xzpc6vxNrS6FPPapN1SxSHgQ336WC6Jj/N8q30OiaBZ00l1GBgeP7usjVZPivSkGUfL1z/WW6TX989M+w==", + "dev": true, + "requires": { + "@types/sizzle": "*" + } + }, "@types/json-schema": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.6.tgz", @@ -2292,7 +2323,8 @@ "@types/prop-types": { "version": "15.7.3", "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.3.tgz", - "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==" + "integrity": "sha512-KfRL3PuHmqQLOG+2tGpRO26Ctg+Cq1E01D2DMriKEATHgWLfeNDmq9e29Q9WIky0dQ3NPkd1mzYH8Lm936Z9qw==", + "dev": true }, "@types/q": { "version": "1.5.4", @@ -2303,6 +2335,7 @@ "version": "16.14.2", "resolved": "https://registry.npmjs.org/@types/react/-/react-16.14.2.tgz", "integrity": "sha512-BzzcAlyDxXl2nANlabtT4thtvbbnhee8hMmH/CcJrISDBVcJS1iOsP1f0OAgSdGE0MsY9tqcrb9YoZcOFv9dbQ==", + "dev": true, "requires": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -2312,6 +2345,7 @@ "version": "16.9.10", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-16.9.10.tgz", "integrity": "sha512-ItatOrnXDMAYpv6G8UCk2VhbYVTjZT9aorLtA/OzDN9XJ2GKcfam68jutoAcILdRjsRUO8qb7AmyObF77Q8QFw==", + "dev": true, "requires": { "@types/react": "^16" } @@ -2320,6 +2354,7 @@ "version": "5.1.10", "resolved": "https://registry.npmjs.org/@types/react-router/-/react-router-5.1.10.tgz", "integrity": "sha512-yu11Hu16CfGvvBWc7wluRlxbwfuSlY0snEntbbOTvfgMvyO6uLaEpAbnVOntr+9TNIpR++OOlPkmDcJPxOXRaQ==", + "dev": true, "requires": { "@types/history": "*", "@types/react": "*" @@ -2329,6 +2364,7 @@ "version": "5.1.7", "resolved": "https://registry.npmjs.org/@types/react-router-dom/-/react-router-dom-5.1.7.tgz", "integrity": "sha512-D5mHD6TbdV/DNHYsnwBTv+y73ei+mMjrkGrla86HthE4/PVvL1J94Bu3qABU+COXzpL23T1EZapVVpwHuBXiUg==", + "dev": true, "requires": { "@types/history": "*", "@types/react": "*", @@ -2343,10 +2379,17 @@ "@types/node": "*" } }, + "@types/sizzle": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@types/sizzle/-/sizzle-2.3.2.tgz", + "integrity": "sha512-7EJYyKTL7tFR8+gDbB6Wwz/arpGa0Mywk1TJbNzKzHtzbwVmY4HR9WqS5VV7dsBUKQmPNr192jHr/VpBluj/hg==", + "dev": true + }, "@types/socket.io-client": { "version": "1.4.34", "resolved": "https://registry.npmjs.org/@types/socket.io-client/-/socket.io-client-1.4.34.tgz", - "integrity": "sha512-Lzia5OTQFJZJ5R4HsEEldywiiqT9+W2rDbyHJiiTGqOcju89sCsQ8aUXDljY6Ls33wKZZGC0bfMhr/VpOyjtXg==" + "integrity": "sha512-Lzia5OTQFJZJ5R4HsEEldywiiqT9+W2rDbyHJiiTGqOcju89sCsQ8aUXDljY6Ls33wKZZGC0bfMhr/VpOyjtXg==", + "dev": true }, "@types/source-list-map": { "version": "0.1.2", @@ -2367,6 +2410,7 @@ "version": "5.9.5", "resolved": "https://registry.npmjs.org/@types/testing-library__jest-dom/-/testing-library__jest-dom-5.9.5.tgz", "integrity": "sha512-ggn3ws+yRbOHog9GxnXiEZ/35Mow6YtPZpd7Z5mKDeZS/o7zx3yAle0ov/wjhVB5QT4N2Dt+GNoGCdqkBGCajQ==", + "dev": true, "requires": { "@types/jest": "*" } @@ -2374,7 +2418,8 @@ "@types/twemoji": { "version": "12.1.1", "resolved": "https://registry.npmjs.org/@types/twemoji/-/twemoji-12.1.1.tgz", - "integrity": "sha512-dW1B1WHTfrWmEzXb/tp8xsZqQHAyMB9JwLwbBqkIQVzmNUI02R7lJqxUpKFM114ygNZHKA1r74oPugCAiYHt1A==" + "integrity": "sha512-dW1B1WHTfrWmEzXb/tp8xsZqQHAyMB9JwLwbBqkIQVzmNUI02R7lJqxUpKFM114ygNZHKA1r74oPugCAiYHt1A==", + "dev": true }, "@types/uglify-js": { "version": "3.11.1", @@ -4598,7 +4643,8 @@ "css.escape": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/css.escape/-/css.escape-1.5.1.tgz", - "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=" + "integrity": "sha1-QuJ9T6BK4y+TGktNQZH6nN3ul8s=", + "dev": true }, "cssdb": { "version": "4.4.0", @@ -4769,7 +4815,8 @@ "csstype": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.0.5.tgz", - "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==" + "integrity": "sha512-uVDi8LpBUKQj6sdxNaTetL6FpeCqTjOvAQuQUa/qAqq8oOd4ivkbhgnqayl0dnPal8Tb/yB1tF+gOvCBiicaiQ==", + "dev": true }, "cyclist": { "version": "1.0.1", @@ -5090,7 +5137,8 @@ "dom-accessibility-api": { "version": "0.5.4", "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.4.tgz", - "integrity": "sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==" + "integrity": "sha512-TvrjBckDy2c6v6RLxPv5QXOnU+SmF9nBII5621Ve5fu6Z/BDrENurBEvlC1f44lKEUVqOpK4w9E5Idc5/EgkLQ==", + "dev": true }, "dom-converter": { "version": "0.2.0", @@ -9496,6 +9544,11 @@ } } }, + "jquery": { + "version": "3.5.1", + "resolved": "https://registry.npmjs.org/jquery/-/jquery-3.5.1.tgz", + "integrity": "sha512-XwIBPqcMn57FxfT+Go5pzySnm4KWkT1Tv7gjrpT1srtf8Weynl6R273VJ5GjkRb51IzMp5nbaPjJXMWeju2MKg==" + }, "js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -9818,7 +9871,8 @@ "lz-string": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/lz-string/-/lz-string-1.4.4.tgz", - "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=" + "integrity": "sha1-wNjq82BZ9wV5bh40SBHPTEmNOiY=", + "dev": true }, "magic-string": { "version": "0.25.7", @@ -9994,7 +10048,8 @@ "min-indent": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/min-indent/-/min-indent-1.0.1.tgz", - "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==" + "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==", + "dev": true }, "mini-create-react-context": { "version": "0.4.1", @@ -12670,6 +12725,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/redent/-/redent-3.0.0.tgz", "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==", + "dev": true, "requires": { "indent-string": "^4.0.0", "strip-indent": "^3.0.0" @@ -14279,6 +14335,7 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/strip-indent/-/strip-indent-3.0.0.tgz", "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==", + "dev": true, "requires": { "min-indent": "^1.0.0" } diff --git a/simmadome/package.json b/simmadome/package.json index c98a967..104db30 100644 --- a/simmadome/package.json +++ b/simmadome/package.json @@ -4,6 +4,7 @@ "private": true, "proxy": "http://localhost:5000", "dependencies": { + "jquery": "^3.5.1", "react": "^17.0.1", "react-dom": "^17.0.1", "react-router": "^5.2.0", @@ -14,11 +15,12 @@ "typescript": "^4.1.3", "web-vitals": "^0.2.4" }, - "devDependencies" : { + "devDependencies": { "@testing-library/jest-dom": "^5.11.8", "@testing-library/react": "^11.2.2", "@testing-library/user-event": "^12.6.0", "@types/jest": "^26.0.19", + "@types/jquery": "^3.5.5", "@types/node": "^12.19.12", "@types/react": "^16.14.2", "@types/react-dom": "^16.9.10", diff --git a/simmadome/src/CreateLeague.css b/simmadome/src/CreateLeague.css index e867503..b37ea6e 100644 --- a/simmadome/src/CreateLeague.css +++ b/simmadome/src/CreateLeague.css @@ -1,22 +1,17 @@ -th, td { - border: none; - padding: 0; - height: 100%; -} - -table { - border-collapse: collapse; - height: min-content; -} - -th .cl_subleague_bg { +.cl_table_header > .cl_subleague_bg { border-top-left-radius: 0.5rem; border-top-right-radius: 0.5rem; + padding-top: 1rem; } -tbody tr:last-child .cl_subleague_bg { +.cl_league_structure_table .cl_table_row:last-child .cl_subleague_bg { border-bottom-left-radius: 0.5rem; border-bottom-right-radius: 0.5rem; + padding-bottom: 1rem; +} + +.cl_league_structure_table .cl_table_row:last-child .cl_division_delete { + margin-bottom: 0.5rem; } input { @@ -28,6 +23,10 @@ input { font-size: 14pt; } +input:focus { + outline: none; +} + .cl_league_main { display: flex; flex-direction: column; @@ -57,9 +56,23 @@ input { } .cl_league_structure_table { - margin: 1rem; - margin-left: 0rem; - margin-top: 0rem; + display: table; + margin-right: 1rem; + table-layout: fixed; +} + +.cl_headers, .cl_table_row { + display: table-row; + height: min-content; +} + +.cl_table_header, .cl_delete_filler, .cl_delete_box, .cl_division_cell { + display: table-cell; + height:100%; +} + +.cl_delete_box { + vertical-align: middle; } .cl_league_structure_scrollbox { @@ -92,14 +105,14 @@ input { .cl_subleague_bg { background: var(--background-main); - padding:1rem; - padding-bottom: 0rem; + padding: 0.5rem 1rem; margin: 0rem 0.5rem; - height: 100%; - width: 20rem; + width: 22rem; + height:100%; + box-sizing: border-box; } -.cl_subleague_name, .cl_newteam_name { +.cl_subleague_name { flex-grow: 1; margin-right: 0.5rem; } @@ -109,6 +122,10 @@ input { width: 95%; } +.cl_newteam_name { + width: 95%; +} + .cl_division { display: flex; flex-direction: column; @@ -124,7 +141,7 @@ input { align-items: center; justify-content: space-between; width: 100%; - margin: 0.25rem 0rem; + margin: 0.4rem 0rem; } .cl_team_name { @@ -134,13 +151,38 @@ input { text-overflow: ellipsis; } +.cl_team_add { + display: flex; + flex-direction: column; + margin-bottom: 0rem; +} + +.cl_search_list { + width: 95%; + margin-top: 0.6rem; + padding: 0.5rem; + background: var(--background-tertiary); + border-radius: 0.5rem; +} + +.cl_search_result { + padding: 0.2rem 0.4rem; + border-radius: 0.5rem; +} + +.cl_search_result:hover { + background: var(--background-main); +} + /* button styles */ button > .emoji { margin: 0; + width: 1rem; + height: 1rem; } -.cl_subleague_delete, .cl_team_delete, .cl_division_delete, .cl_subleague_add, .cl_division_add, .cl_newteam_add { +.cl_subleague_delete, .cl_team_delete, .cl_division_delete, .cl_subleague_add, .cl_division_add { padding: 0; width: 2rem; height: 2rem; @@ -155,21 +197,18 @@ button > .emoji { background: var(--accent-red); } -.cl_subleague_add, .cl_division_add, .cl_newteam_add { +.cl_subleague_add, .cl_division_add { background: var(--accent-green); } .cl_subleague_add { - margin-top: 3rem; + position: relative; + top: 1.6rem; } .cl_division_add { - margin: 1.25rem; -} - -.cl_division_delete { - margin-right: 1rem; - margin-top: 1rem; + margin-top: 1.25rem; + margin-bottom: 1.25rem; } .cl_delete_filler { diff --git a/simmadome/src/CreateLeague.tsx b/simmadome/src/CreateLeague.tsx index a64c4d8..6a275c1 100644 --- a/simmadome/src/CreateLeague.tsx +++ b/simmadome/src/CreateLeague.tsx @@ -1,6 +1,7 @@ import React, {useState, useRef, useLayoutEffect, useReducer} from 'react'; import './CreateLeague.css'; import twemoji from 'twemoji'; +import $, {getJSON} from 'jquery'; interface LeagueStructureState { subleagues: SubleagueState[] @@ -8,26 +9,19 @@ interface LeagueStructureState { interface SubleagueState { name: string + id: string|number divisions: DivisionState[] } interface DivisionState { name: string + id: string|number teams: TeamState[] } interface TeamState { name: string -} - -let initLeagueStructure = { - subleagues: [0, 1].map((val) => ({ - name: "", - divisions: [0, 1].map((val) => ({ - name: "", - teams: [] - })) - })) + id: string|number } type LeagueReducerActions = @@ -42,6 +36,11 @@ type LeagueReducerActions = type DistributiveOmit = T extends any ? Omit : never; +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) { case 'remove_subleague': @@ -49,40 +48,55 @@ function leagueStructureReducer(state: LeagueStructureState, action: LeagueReduc case 'add_subleague': return {subleagues: state.subleagues.concat([{ name: "", + id: getUID(), divisions: arrayOf(state.subleagues[0].divisions.length, i => ({ name: "", + id: getUID(), teams: [] })) }])}; case 'rename_subleague': return replaceSubleague(state, action.subleague_index, subleague => ({ name: action.name, + id: subleague.id, divisions: subleague.divisions })); case 'remove_divisions': return {subleagues: state.subleagues.map(subleague => ({ name: subleague.name, + id: subleague.id, divisions: removeIndex(subleague.divisions, action.division_index) }))}; case 'add_divisions': return {subleagues: state.subleagues.map(subleague => ({ name: subleague.name, - divisions: subleague.divisions.concat([{name: "", teams: []}]) + id: subleague.id, + divisions: subleague.divisions.concat([{ + name: "", + id: getUID(), + teams: [] + }]) }))}; case 'rename_division': return replaceDivision(state, action.subleague_index, action.division_index, division => ({ name: action.name, + id: division.id, teams: division.teams })); case 'remove_team': return replaceDivision(state, action.subleague_index, action.division_index, division => ({ name: division.name, + id: division.id, teams: removeIndex(division.teams, division.teams.findIndex(val => val.name === action.name)) })); case 'add_team': return replaceDivision(state, action.subleague_index, action.division_index, division => ({ name: division.name, - teams: division.teams.concat([{name: action.name}]) + id: division.id, + teams: division.teams.concat([{ + name: action.name, + id: getUID() + }]) })); } } @@ -94,6 +108,7 @@ function replaceSubleague(state: LeagueStructureState, si: number, func: (val: S function replaceDivision(state: LeagueStructureState, si: number, di: number, func:(val: DivisionState) => DivisionState) { return replaceSubleague(state, si, subleague => ({ name: subleague.name, + id: subleague.id, divisions: replaceIndex(subleague.divisions, di, func(subleague.divisions[di])) })) } @@ -118,6 +133,18 @@ function arrayOf(length: number, func: (i: number) => T): T[] { return out; } +let initLeagueStructure = { + subleagues: [0, 1].map((val) => ({ + name: "", + id: getUID(), + divisions: [0, 1].map((val) => ({ + name: "", + id: getUID(), + teams: [] + })) + })) +} + function CreateLeague() { let [name, setName] = useState(""); let [structure, dispatch] = useReducer(leagueStructureReducer, initLeagueStructure); @@ -144,10 +171,10 @@ function LeagueStructre(props: {state: LeagueStructureState, dispatch: React.Dis
- +
-
+
@@ -158,20 +185,18 @@ function LeagueStructre(props: {state: LeagueStructureState, dispatch: React.Dis function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React.Dispatch}) { return ( - - - - {props.subleagues.map((subleague, i) => ( - -
- 1} dispatch={action => - props.dispatch(Object.assign({subleague_index: i}, action)) - }/> -
- - ))} - - +
+
+ {props.subleagues.map((subleague, i) => ( +
+
+ 1} dispatch={action => + props.dispatch(Object.assign({subleague_index: i}, action)) + }/> +
+
+ ))} +
); } @@ -187,33 +212,40 @@ function SubleageHeader(props: {state: SubleagueState, canDelete: boolean, dispa } function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatch}) { - return ( - - {props.subleagues[0].divisions.map((val, di) => ( - - - {props.subleagues[0].divisions.length > 1 ? - : - null - } - - {props.subleagues.map((subleague, si) => ( - -
- - props.dispatch(Object.assign({subleague_index: si, division_index: di}, action)) - }/> -
- - ))} - - ))} - - ); + return (<> + {props.subleagues[0].divisions.map((val, di) => ( +
+
+ {props.subleagues[0].divisions.length > 1 ? + : + null + } +
+ {props.subleagues.map((subleague, si) => ( +
+
+ + props.dispatch(Object.assign({subleague_index: si, division_index: di}, action)) + }/> +
+
+ ))} +
+ ))} + ); } function Division(props: {state: DivisionState, dispatch:(action: DistributiveOmit) => void}) { let [newName, setNewName] = useState(""); + let [searchResults, setSearchResults] = useState([]); + let newNameInput = useRef(null); + let resultList = useRef(null); + + useLayoutEffect(() => { + if (resultList.current) { + twemoji.parse(resultList.current) + } + }) return (
@@ -221,18 +253,36 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm props.dispatch({type: 'rename_division', name: e.target.value}) }/> {props.state.teams.map((team, i) => ( -
+
{team.name}
))}
- setNewName(e.target.value)}/> - + { + let params = new URLSearchParams({query: e.target.value, page_len: '5', page_num: '0'}); + $.getJSON("/api/teams/search?" + params.toString(), data => { + console.log(data); + setSearchResults(data); + }) + setNewName(e.target.value); + }}/>
+ {searchResults.length > 0 && newName.length > 0 ? + (
+ {searchResults.map(result => +
{ + props.dispatch({type:'add_team', name: result}); + setNewName(""); + if (newNameInput.current) { + newNameInput.current.focus(); + } + }}>{result}
+ )} +
): +
+ }
); } @@ -240,9 +290,7 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm function LeagueOptions() { return (
-
- -
+
); } diff --git a/simmadome/src/index.tsx b/simmadome/src/index.tsx index 21bd0f7..7d7b6d2 100644 --- a/simmadome/src/index.tsx +++ b/simmadome/src/index.tsx @@ -7,20 +7,24 @@ import GamePage from './GamePage'; import CreateLeague from './CreateLeague'; import discordlogo from "./img/discord.png"; import reportWebVitals from './reportWebVitals'; +import $ from 'jquery' + +$(document).ready(function() { + ReactDOM.render( + + +
+ + + + + + + , + document.getElementById('root') + ); +}); -ReactDOM.render( - - -
- - - - - - - , - document.getElementById('root') -); function Header() { return ( From 74c9da684f0ace4cfeb7dbfd7c926729575e0c37 Mon Sep 17 00:00:00 2001 From: Elijah Steres Date: Mon, 11 Jan 2021 14:14:54 -0500 Subject: [PATCH 3/7] change to using fetch over jquery --- simmadome/src/CreateLeague.css | 1 - simmadome/src/CreateLeague.tsx | 8 +++----- simmadome/src/index.tsx | 29 +++++++++++++---------------- 3 files changed, 16 insertions(+), 22 deletions(-) diff --git a/simmadome/src/CreateLeague.css b/simmadome/src/CreateLeague.css index b37ea6e..5d6971b 100644 --- a/simmadome/src/CreateLeague.css +++ b/simmadome/src/CreateLeague.css @@ -58,7 +58,6 @@ input:focus { .cl_league_structure_table { display: table; margin-right: 1rem; - table-layout: fixed; } .cl_headers, .cl_table_row { diff --git a/simmadome/src/CreateLeague.tsx b/simmadome/src/CreateLeague.tsx index 6a275c1..718c0bf 100644 --- a/simmadome/src/CreateLeague.tsx +++ b/simmadome/src/CreateLeague.tsx @@ -1,7 +1,6 @@ import React, {useState, useRef, useLayoutEffect, useReducer} from 'react'; import './CreateLeague.css'; import twemoji from 'twemoji'; -import $, {getJSON} from 'jquery'; interface LeagueStructureState { subleagues: SubleagueState[] @@ -262,10 +261,9 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm { let params = new URLSearchParams({query: e.target.value, page_len: '5', page_num: '0'}); - $.getJSON("/api/teams/search?" + params.toString(), data => { - console.log(data); - setSearchResults(data); - }) + fetch("/api/teams/search?" + params.toString()) + .then(response => response.json()) + .then(data => setSearchResults(data)); setNewName(e.target.value); }}/>
diff --git a/simmadome/src/index.tsx b/simmadome/src/index.tsx index 7d7b6d2..4f7e32a 100644 --- a/simmadome/src/index.tsx +++ b/simmadome/src/index.tsx @@ -7,23 +7,20 @@ import GamePage from './GamePage'; import CreateLeague from './CreateLeague'; import discordlogo from "./img/discord.png"; import reportWebVitals from './reportWebVitals'; -import $ from 'jquery' -$(document).ready(function() { - ReactDOM.render( - - -
- - - - - - - , - document.getElementById('root') - ); -}); +ReactDOM.render( + + +
+ + + + + + + , + document.getElementById('root') +); function Header() { From 1337df4af3227bf921280a92d4577a1187306b62 Mon Sep 17 00:00:00 2001 From: Elijah Steres Date: Mon, 11 Jan 2021 16:11:35 -0500 Subject: [PATCH 4/7] fix weird gap --- simmadome/src/CreateLeague.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/simmadome/src/CreateLeague.tsx b/simmadome/src/CreateLeague.tsx index 718c0bf..5751f78 100644 --- a/simmadome/src/CreateLeague.tsx +++ b/simmadome/src/CreateLeague.tsx @@ -248,9 +248,11 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm return (
- - props.dispatch({type: 'rename_division', name: e.target.value}) - }/> +
+ + props.dispatch({type: 'rename_division', name: e.target.value}) + }/> +
{props.state.teams.map((team, i) => (
{team.name}
From 39e30c38114751e1a00c203543167078d425abf2 Mon Sep 17 00:00:00 2001 From: Elijah Steres Date: Tue, 12 Jan 2021 03:17:02 -0500 Subject: [PATCH 5/7] finish league create page --- simmadome/src/CreateLeague.css | 94 +++++++++- simmadome/src/CreateLeague.tsx | 324 ++++++++++++++++++++++++--------- 2 files changed, 324 insertions(+), 94 deletions(-) diff --git a/simmadome/src/CreateLeague.css b/simmadome/src/CreateLeague.css index 5d6971b..58c3e36 100644 --- a/simmadome/src/CreateLeague.css +++ b/simmadome/src/CreateLeague.css @@ -27,6 +27,16 @@ input:focus { 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 { display: flex; flex-direction: column; @@ -36,7 +46,7 @@ input:focus { } .cl_league_name { - margin: 1rem; + margin-top: 1rem; } .cl_league_options, .cl_league_structure { @@ -48,6 +58,10 @@ input:focus { padding-top: 1.5rem; } +.cl_league_options { + align-items: center; +} + .cl_league_structure, .cl_subleague_add_align { display: flex; align-items: center; @@ -55,6 +69,10 @@ input:focus { width: min-content; } +.cl_league_structure { + margin-top: 1rem; +} + .cl_league_structure_table { display: table; margin-right: 1rem; @@ -106,8 +124,8 @@ input:focus { background: var(--background-main); padding: 0.5rem 1rem; margin: 0rem 0.5rem; - width: 22rem; - height:100%; + min-width: 22rem; + height: 100%; box-sizing: border-box; } @@ -116,6 +134,10 @@ input:focus { margin-right: 0.5rem; } +.cl_division_name_box { + width: 100%; +} + .cl_division_name { margin-bottom: 0.5rem; width: 95%; @@ -145,7 +167,7 @@ input:focus { .cl_team_name { font-size: 14pt; - padding-right: 0.5rem; + padding: 0 0.5rem; overflow: hidden; text-overflow: ellipsis; } @@ -173,6 +195,58 @@ input:focus { 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 > .emoji { @@ -212,4 +286,16 @@ button > .emoji { .cl_delete_filler { 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; } \ No newline at end of file diff --git a/simmadome/src/CreateLeague.tsx b/simmadome/src/CreateLeague.tsx index 5751f78..f0e4df6 100644 --- a/simmadome/src/CreateLeague.tsx +++ b/simmadome/src/CreateLeague.tsx @@ -2,28 +2,66 @@ import React, {useState, useRef, useLayoutEffect, useReducer} from 'react'; import './CreateLeague.css'; import twemoji from 'twemoji'; -interface LeagueStructureState { +// STATE CLASSES + +class LeagueStructureState { subleagues: SubleagueState[] + + constructor(subleagues: SubleagueState[] = []) { + this.subleagues = subleagues; + } } -interface SubleagueState { +class SubleagueState { name: string - id: string|number divisions: DivisionState[] + id: string|number + + constructor(divisions: DivisionState[] = []) { + this.name = ""; + this.divisions = divisions; + this.id = getUID(); + } } -interface DivisionState { +class DivisionState { name: string - id: string|number teams: TeamState[] + id: string|number + + constructor() { + this.name = ""; + this.teams = []; + this.id = getUID(); + } } -interface TeamState { +class TeamState { name: string 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: 'add_subleague'} | {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: 'add_team', subleague_index:number, division_index:number, name:string} -type DistributiveOmit = T extends any ? Omit : never; - -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 { +function leagueStructureReducer(state: LeagueStructureState, action: StructureReducerActions): LeagueStructureState { switch (action.type) { case 'remove_subleague': return {subleagues: removeIndex(state.subleagues, action.subleague_index)}; case 'add_subleague': - return {subleagues: state.subleagues.concat([{ - name: "", - id: getUID(), - divisions: arrayOf(state.subleagues[0].divisions.length, i => ({ - name: "", - id: getUID(), - teams: [] - })) - }])}; + return {subleagues: append(state.subleagues, new SubleagueState( + arrayOf(state.subleagues[0].divisions.length, i => + new DivisionState() + ) + ))} case 'rename_subleague': - return replaceSubleague(state, action.subleague_index, subleague => ({ - name: action.name, - id: subleague.id, - divisions: subleague.divisions - })); + return replaceSubleague(state, action.subleague_index, subleague => { + let nSubleague = shallowClone(subleague); + nSubleague.name = action.name; + return nSubleague; + }); case 'remove_divisions': - return {subleagues: state.subleagues.map(subleague => ({ - name: subleague.name, - id: subleague.id, - divisions: removeIndex(subleague.divisions, action.division_index) - }))}; + return {subleagues: state.subleagues.map(subleague => { + let nSubleague = shallowClone(subleague); + nSubleague.divisions = removeIndex(subleague.divisions, action.division_index) + return nSubleague; + })}; case 'add_divisions': - return {subleagues: state.subleagues.map(subleague => ({ - name: subleague.name, - id: subleague.id, - divisions: subleague.divisions.concat([{ - name: "", - id: getUID(), - teams: [] - }]) - }))}; + return {subleagues: state.subleagues.map(subleague => { + let nSubleague = shallowClone(subleague); + nSubleague.divisions = append(subleague.divisions, new DivisionState()) + return nSubleague; + })}; case 'rename_division': - return replaceDivision(state, action.subleague_index, action.division_index, division => ({ - name: action.name, - id: division.id, - teams: division.teams - })); + return replaceDivision(state, action.subleague_index, action.division_index, division => { + let nDivision = shallowClone(division); + nDivision.name = action.name; + return nDivision; + }); case 'remove_team': - return replaceDivision(state, action.subleague_index, action.division_index, division => ({ - name: division.name, - id: division.id, - teams: removeIndex(division.teams, division.teams.findIndex(val => val.name === action.name)) - })); + 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; + }); case 'add_team': - return replaceDivision(state, action.subleague_index, action.division_index, division => ({ - name: division.name, - id: division.id, - teams: division.teams.concat([{ - name: action.name, - id: getUID() - }]) - })); + 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; + }); } } @@ -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) { - return replaceSubleague(state, si, subleague => ({ - name: subleague.name, - id: subleague.id, - divisions: replaceIndex(subleague.divisions, di, func(subleague.divisions[di])) - })) + return replaceSubleague(state, si, subleague => { + let nSubleague = shallowClone(subleague); + nSubleague.divisions = replaceIndex(subleague.divisions, di, func(subleague.divisions[di])); + return nSubleague; + }); } +// UTIL + function removeIndex(arr: any[], index: number) { return arr.slice(0, index).concat(arr.slice(index+1)); } @@ -132,21 +154,22 @@ function arrayOf(length: number, func: (i: number) => T): T[] { return out; } -let initLeagueStructure = { - subleagues: [0, 1].map((val) => ({ - name: "", - id: getUID(), - divisions: [0, 1].map((val) => ({ - name: "", - id: getUID(), - teams: [] - })) - })) +function shallowClone(obj: T): T { + return Object.assign({}, obj); } +type DistributiveOmit = T extends any ? Omit : never; + +// CREATE LEAGUE + function CreateLeague() { let [name, setName] = useState(""); + let [showError, setShowError] = useState(false); 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(null) @@ -159,30 +182,115 @@ function CreateLeague() { return (
setName(e.target.value)}/> - - +
{name === "" && showError ? "A name is required." : ""}
+ +
+ +
+ +
{ + !validRequest(name, structure, gamesSeries[0], seriesDivisionOpp[0], seriesInterDivision[0], seriesInterLeague[0]) && showError ? + "Cannot create league. Some information is invalid." : "" + }
+
+
); } -function LeagueStructre(props: {state: LeagueStructureState, dispatch: React.Dispatch}) { +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, showError: boolean}) { return (
- - + +
+
{props.state.subleagues.length % 2 !== 0 && props.showError ? "Must have an even number of subleagues." : ""}
); } -function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React.Dispatch}) { +function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React.Dispatch, showError:boolean}) { return (
@@ -192,6 +300,7 @@ function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React. 1} dispatch={action => props.dispatch(Object.assign({subleague_index: i}, action)) }/> +
{subleague.name === "" && props.showError ? "A name is required." : ""}
))} @@ -199,7 +308,7 @@ function SubleagueHeaders(props: {subleagues: SubleagueState[], dispatch: React. ); } -function SubleageHeader(props: {state: SubleagueState, canDelete: boolean, dispatch:(action: DistributiveOmit) => void}) { +function SubleageHeader(props: {state: SubleagueState, canDelete: boolean, dispatch:(action: DistributiveOmit) => void}) { return (
@@ -210,7 +319,7 @@ function SubleageHeader(props: {state: SubleagueState, canDelete: boolean, dispa ); } -function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatch}) { +function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatch, showError: boolean}) { return (<> {props.subleagues[0].divisions.map((val, di) => (
@@ -225,7 +334,7 @@ function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatc
props.dispatch(Object.assign({subleague_index: si, division_index: di}, action)) - }/> + } showError={props.showError}/>
))} @@ -234,7 +343,7 @@ function Divisions(props: {subleagues: SubleagueState[], dispatch: React.Dispatc ); } -function Division(props: {state: DivisionState, dispatch:(action: DistributiveOmit) => void}) { +function Division(props: {state: DivisionState, dispatch:(action: DistributiveOmit) => void, showError:boolean}) { let [newName, setNewName] = useState(""); let [searchResults, setSearchResults] = useState([]); let newNameInput = useRef(null); @@ -248,10 +357,11 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm return (
-
+
props.dispatch({type: 'rename_division', name: e.target.value}) }/> +
{props.state.name === "" && props.showError ? "A name is required." : ""}
{props.state.teams.map((team, i) => (
@@ -283,14 +393,48 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm
):
} +
{props.state.teams.length < 2 && props.showError ? "Must have at least 2 teams." : ""}
); } -function LeagueOptions() { - return ( -
+// LEAGUE OPTIONS +type StateBundle = [T, React.Dispatch>] + +function LeagueOptions(props: { + gamesSeries: StateBundle, + seriesDivisionOpp: StateBundle, + seriesInterDivision: StateBundle, + seriesInterLeague: StateBundle, + showError: boolean + }) { + + let [nGamesSeries, setGamesSeries] = props.gamesSeries; + let [nSeriesDivisionOpp, setSeriesDivisionOpp] = props.seriesDivisionOpp; + let [nSeriesInterDivision, setSeriesInterDivision] = props.seriesInterDivision; + let [nSeriesInterLeague, setSeriesInterLeague] = props.seriesInterLeague; + + return ( +
+
+ + +
+
+ + +
+
+ ); +} + +function NumberInput(props: {title: string, value: string, setValue: (newVal: string) => void, showError: boolean}) { + return ( +
+
{props.title}
+ props.setValue(e.target.value)}/> +
{(Number(props.value) === NaN || Number(props.value) < 0) && props.showError ? "Must be a number greater than 0" : ""}
); } From ab488c331a67a76e31d31a36a94f205610bc9f1e Mon Sep 17 00:00:00 2001 From: Elijah Steres Date: Tue, 12 Jan 2021 13:16:31 -0500 Subject: [PATCH 6/7] add more option fields --- simmadome/src/CreateLeague.tsx | 154 +++++++++++++++++++-------------- 1 file changed, 88 insertions(+), 66 deletions(-) diff --git a/simmadome/src/CreateLeague.tsx b/simmadome/src/CreateLeague.tsx index f0e4df6..b64ce1e 100644 --- a/simmadome/src/CreateLeague.tsx +++ b/simmadome/src/CreateLeague.tsx @@ -51,14 +51,6 @@ let getUID = function() { // does NOT generate UUIDs. Meant to create list keys return function() { return id++ } }() -let initLeagueStructure = { - subleagues: [0, 1].map((val) => - new SubleagueState([0, 1].map((val) => - new DivisionState() - )) - ) -}; - // STRUCTURE REDUCER type StructureReducerActions = @@ -132,6 +124,50 @@ function replaceDivision(state: LeagueStructureState, si: number, di: number, fu }); } +// 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 +} + // UTIL function removeIndex(arr: any[], index: number) { @@ -159,17 +195,23 @@ function shallowClone(obj: T): T { } type DistributiveOmit = T extends any ? Omit : never; +type DistributivePick = T extends any ? Pick : never; // CREATE LEAGUE +let initLeagueStructure = { + subleagues: [0, 1].map((val) => + new SubleagueState([0, 1].map((val) => + new DivisionState() + )) + ) +}; + function CreateLeague() { let [name, setName] = useState(""); let [showError, setShowError] = useState(false); - let [structure, dispatch] = useReducer(leagueStructureReducer, initLeagueStructure); - let gamesSeries = useState('3'); - let seriesDivisionOpp = useState('8'); - let seriesInterDivision = useState('16'); - let seriesInterLeague = useState('8'); + let [structure, structureDispatch] = useReducer(leagueStructureReducer, initLeagueStructure); + let [options, optionsDispatch] = useReducer(LeagueOptionsReducer, new LeagueOptionsState()); let self = useRef(null) @@ -183,25 +225,19 @@ function CreateLeague() {
setName(e.target.value)}/>
{name === "" && showError ? "A name is required." : ""}
- +
- +
{ - !validRequest(name, structure, gamesSeries[0], seriesDivisionOpp[0], seriesInterDivision[0], seriesInterLeague[0]) && showError ? - "Cannot create league. Some information is invalid." : "" + !validRequest(name, structure, options) && showError ? + "Cannot create league. Some information is missing or invalid." : "" }
@@ -209,16 +245,9 @@ function CreateLeague() { ); } -function makeRequest( - name:string, - structure: LeagueStructureState, - gamesPerSeries: string, - divisionSeries: string, - interDivisionSeries: string, - interLeagueSeries: string - ) { +function makeRequest(name:string, structure: LeagueStructureState, options:LeagueOptionsState) { - if (!validRequest(name, structure, gamesPerSeries, divisionSeries, interDivisionSeries, interLeagueSeries)) { + if (!validRequest(name, structure, options)) { return null } @@ -233,28 +262,25 @@ function makeRequest( })) })) }, - games_per_series: Number(gamesPerSeries), - division_series: Number(divisionSeries), - inter_division_series: Number(interDivisionSeries), - inter_league_series: Number(interLeagueSeries) + 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) }); } -function validRequest( - name:string, - structure: LeagueStructureState, - gamesPerSeries: string, - divisionSeries: string, - interDivisionSeries: string, - interLeagueSeries: string - ) { +function validRequest(name:string, structure: LeagueStructureState, options:LeagueOptionsState) { return ( name !== "" && - validNumber(gamesPerSeries) && - validNumber(divisionSeries) && - validNumber(interDivisionSeries) && - validNumber(interLeagueSeries) && + validNumber(options.games_series) && + validNumber(options.intra_division_series) && + validNumber(options.inter_division_series) && + validNumber(options.inter_league_series) && + validNumber(options.top_postseason) && + validNumber(options.wildcards) && structure.subleagues.length % 2 === 0 && structure.subleagues.every(subleague => subleague.name !== "" && @@ -402,28 +428,24 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm type StateBundle = [T, React.Dispatch>] -function LeagueOptions(props: { - gamesSeries: StateBundle, - seriesDivisionOpp: StateBundle, - seriesInterDivision: StateBundle, - seriesInterLeague: StateBundle, - showError: boolean - }) { - - let [nGamesSeries, setGamesSeries] = props.gamesSeries; - let [nSeriesDivisionOpp, setSeriesDivisionOpp] = props.seriesDivisionOpp; - let [nSeriesInterDivision, setSeriesInterDivision] = props.seriesInterDivision; - let [nSeriesInterLeague, setSeriesInterLeague] = props.seriesInterLeague; - +function LeagueOptions(props: {state: LeagueOptionsState, dispatch: React.Dispatch, showError: boolean}) { return (
- - + + props.dispatch({type: 'set_games_series', value: value})} showError={props.showError}/> + + props.dispatch({type: 'set_top_postseason', value: value})} showError={props.showError}/> + + props.dispatch({type: 'set_wildcards', value: value})} showError={props.showError}/>
- - + + props.dispatch({type: 'set_intra_division_series', value: value})} showError={props.showError}/> + + props.dispatch({type: 'set_inter_division_series', value: value})} showError={props.showError}/> + + props.dispatch({type: 'set_inter_league_series', value: value})} showError={props.showError}/>
); From c837a826c0775d72ba3ce24b17318ff601361134 Mon Sep 17 00:00:00 2001 From: Elijah Steres Date: Thu, 14 Jan 2021 01:30:31 -0500 Subject: [PATCH 7/7] connect league create page to backend --- league_storage.py | 1 + leagues.py | 2 +- main_controller.py | 38 +++++++++++++++++++-- simmadome/src/CreateLeague.css | 11 +++++- simmadome/src/CreateLeague.tsx | 62 ++++++++++++++++++++++++---------- 5 files changed, 92 insertions(+), 22 deletions(-) diff --git a/league_storage.py b/league_storage.py index 63a7fe7..5029740 100644 --- a/league_storage.py +++ b/league_storage.py @@ -64,6 +64,7 @@ def init_league_db(league): c.execute(teams_table_check_string) for team in league.teams_in_league(): + print(team) c.execute("INSERT INTO teams (name) VALUES (?)", (team.name,)) player_string = "INSERT INTO stats (name, team_name) VALUES (?,?)" diff --git a/leagues.py b/leagues.py index b685d8a..2d92e7a 100644 --- a/leagues.py +++ b/leagues.py @@ -14,7 +14,7 @@ class league_structure(object): self.owner = None def setup(self, league_dic, division_games = 1, inter_division_games = 1, inter_league_games = 1, games_per_hour = 2): - self.league = league_dic #key: subleague, value: {division : team_name} + self.league = league_dic # { subleague name : { division name : [team object] } } self.constraints = { "division_games" : division_games, "inter_div_games" : inter_division_games, diff --git a/main_controller.py b/main_controller.py index 0b968e7..8627250 100644 --- a/main_controller.py +++ b/main_controller.py @@ -1,4 +1,6 @@ -import asyncio, time, datetime, games, json, threading, jinja2, leagues, os +import asyncio, time, datetime, games, json, threading, jinja2, leagues, os, leagues +from leagues import league_structure +from league_storage import league_exists from flask import Flask, url_for, Response, render_template, request, jsonify, send_from_directory, abort from flask_socketio import SocketIO, emit import database as db @@ -20,7 +22,7 @@ def serve(path): ### API @app.route('/api/teams/search') -def searchteams(): +def search_teams(): query = request.args.get('query') page_len = int(request.args.get('page_len')) page_num = int(request.args.get('page_num')) @@ -29,7 +31,7 @@ def searchteams(): abort(400, "A query term is required") result = db.search_teams(query) - if page_len is not None: #pagination should probably be doen in the sqlite query but this will do for now + if page_len is not None: #pagination should probably be done in the sqlite query but this will do for now if page_num is None: abort(400, "A page_len argument must be accompanied by a page_num argument") result = result[page_num*page_len : (page_num + 1)*page_len] @@ -37,6 +39,36 @@ def searchteams(): return jsonify([json.loads(x[0])['name'] for x in result]) #currently all we need is the name but that can change +@app.route('/api/leagues', methods=['POST']) +def create_league(): + config = json.loads(request.data) + + if (league_exists(config['name'])): + abort(400, "A league by that name already exists") + + print(config) + league_dic = { + subleague['name'] : { + division['name'] : [games.get_team(team_name) for team_name in division['teams']] + for division in subleague['divisions'] + } + for subleague in config['structure']['subleagues'] + } + + new_league = league_structure(config['name']) + new_league.setup( + league_dic, + division_games=config['division_series'], + inter_division_games=config['inter_division_series'], + inter_league_games=config['inter_league_series'], + ) + new_league.generate_schedule() + leagues.save_league(new_league) + + return "League created successfully" + + + ### SOCKETS thread2 = threading.Thread(target=socketio.run,args=(app,'0.0.0.0')) diff --git a/simmadome/src/CreateLeague.css b/simmadome/src/CreateLeague.css index 58c3e36..90c680b 100644 --- a/simmadome/src/CreateLeague.css +++ b/simmadome/src/CreateLeague.css @@ -49,7 +49,7 @@ input[type=number]::-webkit-outer-spin-button { margin-top: 1rem; } -.cl_league_options, .cl_league_structure { +.cl_league_options, .cl_league_structure, .cl_confirm_box { display: flex; background: var(--background-tertiary); flex-direction: column; @@ -58,6 +58,15 @@ input[type=number]::-webkit-outer-spin-button { padding-top: 1.5rem; } +.cl_confirm_box { + min-width: 55rem; + padding: 2.5rem; + display: flex; + align-items: center; + justify-content: center; + font-size: 20pt; +} + .cl_league_options { align-items: center; } diff --git a/simmadome/src/CreateLeague.tsx b/simmadome/src/CreateLeague.tsx index b64ce1e..3a4dbe4 100644 --- a/simmadome/src/CreateLeague.tsx +++ b/simmadome/src/CreateLeague.tsx @@ -210,6 +210,8 @@ let initLeagueStructure = { function CreateLeague() { let [name, setName] = useState(""); let [showError, setShowError] = useState(false); + let [nameExists, setNameExists] = useState(false); + let [createSuccess, setCreateSuccess] = useState(false); let [structure, structureDispatch] = useReducer(leagueStructureReducer, initLeagueStructure); let [options, optionsDispatch] = useReducer(LeagueOptionsReducer, new LeagueOptionsState()); @@ -221,18 +223,52 @@ function CreateLeague() { } }) + if (createSuccess) { + return( +
+
+ League created succesfully! +
+
+ ); + } + return (
- setName(e.target.value)}/> -
{name === "" && showError ? "A name is required." : ""}
+ { + setName(e.target.value); + setNameExists(false); + }}/> +
{ + name === "" && showError ? "A name is required." : + nameExists && showError ? "A league by that name already exists" : + "" + }
{ @@ -246,19 +282,14 @@ function CreateLeague() { } function makeRequest(name:string, structure: LeagueStructureState, options:LeagueOptionsState) { - - if (!validRequest(name, structure, options)) { - return null - } - - return ({ + return JSON.stringify({ + name: name, structure: { - name: name, subleagues: structure.subleagues.map(subleague => ({ name: subleague.name, divisions: subleague.divisions.map(division => ({ name: division.name, - teams: division.teams + teams: division.teams.map(team => team.name) })) })) }, @@ -272,7 +303,6 @@ function makeRequest(name:string, structure: LeagueStructureState, options:Leagu } function validRequest(name:string, structure: LeagueStructureState, options:LeagueOptionsState) { - return ( name !== "" && validNumber(options.games_series) && @@ -280,7 +310,7 @@ function validRequest(name:string, structure: LeagueStructureState, options:Leag validNumber(options.inter_division_series) && validNumber(options.inter_league_series) && validNumber(options.top_postseason) && - validNumber(options.wildcards) && + validNumber(options.wildcards, 0) && structure.subleagues.length % 2 === 0 && structure.subleagues.every(subleague => subleague.name !== "" && @@ -292,8 +322,8 @@ function validRequest(name:string, structure: LeagueStructureState, options:Leag ) } -function validNumber(value: string) { - return Number(value) !== NaN && Number(value) > 0 +function validNumber(value: string, min = 1) { + return Number(value) !== NaN && Number(value) >= min } // LEAGUE STRUCUTRE @@ -426,8 +456,6 @@ function Division(props: {state: DivisionState, dispatch:(action: DistributiveOm // LEAGUE OPTIONS -type StateBundle = [T, React.Dispatch>] - function LeagueOptions(props: {state: LeagueOptionsState, dispatch: React.Dispatch, showError: boolean}) { return (