diff --git a/public/index.html b/public/index.html index dd1ccfd4cd30a29aaa08b295d99be29cdeb29cf9..624e8a0d931011a538149b4b305ddba45719f1d1 100644 --- a/public/index.html +++ b/public/index.html @@ -23,6 +23,7 @@ </head> <body> <noscript>You need to enable JavaScript to run this app.</noscript> + <div id="form"></div> <div id="root"></div> <!-- This HTML file is a template. diff --git a/src/App.css b/src/App.css index 6bfc984c2940566cfc1c2197cf0f9b4c129da376..a0c99fee8d065ca947e7f47aaf956d4d918bf6c2 100644 --- a/src/App.css +++ b/src/App.css @@ -51,7 +51,7 @@ div.header button:hover { div.fade-main { position: fixed; top: 0; - z-index: 1000; + z-index: 1020; height: 100vh; width: 100vw; margin: auto; @@ -139,6 +139,22 @@ div.login button:hover { cursor: pointer; } +.login .formDate { + width: 30%; +} + +.login label.formDate { + color: white; + width: 10%; + margin-left: 10%; + font-size: 180%; +} + +.login .formTime { + width: 30%; + margin-right: 20%; +} + /* Editing text button in the toolbar */ .leaflet-draw-toolbar .leaflet-draw-draw-textbox { background-image: url("icons/button-textbox.png"); diff --git a/src/App.js b/src/App.js index 7342088361a7e2c36e0cf4cd71d5a6b7e269817b..a7f472449d5ddaf41c517bc9b63b969e042843c7 100644 --- a/src/App.js +++ b/src/App.js @@ -31,6 +31,7 @@ class App extends Component { <div> <UserMap position={initialPosition} zoom={this.state.zoom} mapUrl={this.state.mapUrl} />, <Header handleLayerChange={this.handleLayerChange} /> + </div> ); } diff --git a/src/components/EditGameForm.js b/src/components/EditGameForm.js new file mode 100644 index 0000000000000000000000000000000000000000..9158f27231a3d5402f62de0717c77cf87a377854 --- /dev/null +++ b/src/components/EditGameForm.js @@ -0,0 +1,213 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { + Map, + TileLayer +} from 'react-leaflet' + +export class EditGameForm extends React.Component{ + constructor(props){ + super(props); + + this.state = { + gamename: "", + description: "", + startDate: "", + startTime: "", + endDate: "", + endTime: "", + zoom: 13, + mapCenter: { + lat: 62.2416479, + lng: 25.7597186 + } + } + + this.handleMapDrag = this.handleMapDrag.bind(this); + } + + handleError = error => { + this.setState({ errorMsg: error }); + }; + + handleChange = e => { + const { name, value } = e.target; + this.setState({ [name]: value }); + }; + + // show/hide this form + handleView = e => { + this.props.toggleView(this.props.view); + }; + + // remove view with ESC + handleEsc = e => { + if (e.keyCode === 27) { + this.handleView(); + } + }; + + handleMapDrag = e => { + this.setState({ + mapCenter: e.target.getCenter() + }); + } + + handleMapScroll = e => { + this.setState({ + zoom: e.target.getZoom() + }); + } + + handleGameSave = e => { + let startDate = this.state.startDate + "T" + this.state.startTime + ":00.000Z"; + let endDate = this.state.endDate + "T" + this.state.endTime + ":00.000Z"; + + const gameObject = { + name: this.state.gamename, + desc: this.state.description, + map: "", + startdate: startDate, + enddate: endDate, + center: this.state.mapCenter + } + + e.preventDefault(); + + let token = sessionStorage.getItem('token'); + + // Send Game info to the server + fetch('http://localhost:5000/game/' + this.props.gameId, { + method: 'PUT', + headers: { + Authorization: 'Bearer ' + token, + Accept: 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(gameObject) + }).then(res => res.json()) + .then(result => { + alert(result.message); + this.handleView(); + }) + .catch(error => console.log('Error: ', error)); + }; + + componentDidMount() { + document.addEventListener('keyup', this.handleEsc); + this.getGameInfo(this.props.gameId); + } + + componentWillUnmount() { + document.removeEventListener('keyup', this.handleEsc); + } + + getGameInfo(gameId){ + fetch('http://localhost:5000/game/' + gameId) + .then(response => response.json()) + .then(json => this.handleGameInfo(json)) + .catch(error => console.log(error)); + } + + handleGameInfo(json){ + this.setState({ + gamename: json.name, + description: json.desc, + startDate: json.startdate.substring(0,10), + startTime: json.startdate.substring(11,16), + endDate: json.enddate.substring(0,10), + endTime: json.enddate.substring(11,16), + zoom: 13, + mapCenter: { + lat: json.center.lat, + lng: json.center.lng + } + }) + } + + render() { + return ReactDOM.createPortal ( + <div className='fade-main'> + <div className='sticky'> + <span className='close' onClick={this.handleView}> + × + </span> + </div> + <div className=''> + <form onSubmit={this.handleGameSave}> + <h1>Demo Game Creation</h1> + <br /> + <input + placeholder='Game name' + name='gamename' + value={this.state.gamename} + onChange={this.handleChange} + required + /> + <br /> + <input + placeholder='Description' + type='text' + name='description' + value={this.state.description} + onChange={this.handleChange} + required + /> + <br /> + <label className=''>Start:</label> + <input + className='formDate' + type='date' + name='startDate' + value={this.state.startDate} + onChange={this.handleChange} + required + /> + <input + className='formTime' + type='time' + name='startTime' + value={this.state.startTime} + onChange={this.handleChange} + required + /> + <br /> + <label className=''>End:</label> + <input + className='formDate' + type='date' + name='endDate' + value={this.state.endDate} + onChange={this.handleChange} + min={this.state.startDate} + required + /> + <input + className='formTime' + type='time' + name='endTime' + value={this.state.endTime} + onChange={this.handleChange} + required + /> + <br /> + <label>Map things</label> + <br /> + <Map className='' center={[this.state.mapCenter.lat, this.state.mapCenter.lng]} zoom={this.state.zoom} style={{height: '400px', width: '400px'} } onmoveend={this.handleMapDrag} onzoomend={this.handleMapScroll}> + <TileLayer + attribution='Maanmittauslaitoksen kartta' + url=' https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg' + /> + </Map> + <br /> + <button type='submit'>Save changes</button> + <h2>{this.state.errorMsg}</h2> + </form> + </div> + </div> + ,document.getElementById('form') + ); + } +} + +export default EditGameForm; \ No newline at end of file diff --git a/src/components/GameList.js b/src/components/GameList.js new file mode 100644 index 0000000000000000000000000000000000000000..6b68d64393a4c25713bea74fa511ff0b0f92558b --- /dev/null +++ b/src/components/GameList.js @@ -0,0 +1,79 @@ +import React, { Fragment } from 'react'; +import EditGameForm from './EditGameForm'; + +class GameList extends React.Component { + constructor(props){ + super(props); + this.state = { + games: [], + selectedGame: null, + editForm: false + } + + this.toggleView = this.toggleView.bind(this); + } + + componentDidMount() { + this.getGames(); + } + + getGames(){ + fetch('http://localhost:5000/game/listgames') + .then(response => response.json()) + .then(games => { + this.setState({ + games: games, + selectedGame: games !== null && games[0].id + }); + }) + .catch(error => {console.log(error);}) + } + + handleChange = (e) =>{ + this.setState({ + selectedGame: e.target.value + }); + } + + handleEditClick = (e) => { + if(this.state.selectedGame === null){alert('No game selected');} + else{ + this.setState({ + editForm: true + }); + } + } + + toggleView = (e) =>{ + this.setState({ + editForm: !this.state.editForm + }); + this.getGames(); + } + + render() { + let items = []; + + for (let i = 0; i < this.state.games.length; i++) { + const element = this.state.games[i]; + items.push( + <option key={element.id} value={element.id}>{element.name}</option> + ); + } + + return ( + <Fragment> + <label>Game: </label> + <select onChange={this.handleChange}> + {items} + </select> + {sessionStorage.getItem('token') + && <button onClick={this.handleEditClick}>Edit game</button>} + {(this.state.editForm && this.state.selectedGame !== null) + && <EditGameForm gameId={this.state.selectedGame} toggleView={this.toggleView}/>} + </Fragment> + ); + } +} + +export default GameList; \ No newline at end of file diff --git a/src/components/Header.js b/src/components/Header.js index 70799067bb53691f50038a5c987469665f162b8b..6eff8b4572bd8e4404d9a167fbb556756ac6bf40 100644 --- a/src/components/Header.js +++ b/src/components/Header.js @@ -2,21 +2,20 @@ import React from 'react'; import LoginForm from './LoginForm'; import RegisterForm from './RegisterForm'; +import GameList from './GameList'; +import NewGameForm from './NewGameForm'; class Header extends React.Component { state = { - login: false, - register: false, + form: "", // Popup form (login, register etc.) username: null, token: null }; // toggles the login/register view toggleView = view => { - this.setState(prevState => { - return { - [view]: view === 'login' ? !prevState.login : !prevState.register - }; + this.setState({ + form: view }); }; @@ -40,24 +39,24 @@ class Header extends React.Component { Authorization: 'Bearer ' + token } }) - .then(res => res.json()) - .then( - result => { - // if token is still valid, login user - if (result === true) { - this.setState({ - username: sessionStorage.getItem('name'), - token: token - }); - // logout user if token has expired / is invalid - } else { - this.handleLogout(); - } - }, - error => { - console.log(error); + .then(res => res.json()) + .then( + result => { + // if token is still valid, login user + if (result === true) { + this.setState({ + username: sessionStorage.getItem('name'), + token: token + }); + // logout user if token has expired / is invalid + } else { + this.handleLogout(); } - ); + }, + error => { + console.log(error); + } + ); } } @@ -73,27 +72,37 @@ class Header extends React.Component { {!this.state.username && ( <button onClick={() => this.toggleView('login')}>login</button> )} + {this.state.username && ( + <button onClick={() => this.toggleView('newgame')}>New Game</button> + )} {this.state.username && ( <button onClick={this.handleLogout}>logout</button> )} {this.state.username && <button>{this.state.username}</button>} <button onClick={this.props.handleLayerChange}>change layer</button> + <GameList /> </div> - {this.state.register && ( + {this.state.form === 'register' && ( <RegisterForm - view='register' + view='' handleState={this.handleState} toggleView={this.toggleView} /> )} - {this.state.login && ( + {this.state.form === 'login' && ( <LoginForm - view='login' + view='' + handleState={this.handleState} + toggleView={this.toggleView} + /> + )} + {this.state.form === 'newgame' && ( + <NewGameForm + view='' handleState={this.handleState} toggleView={this.toggleView} /> )} - </div> ); } diff --git a/src/components/NewGameForm.js b/src/components/NewGameForm.js new file mode 100644 index 0000000000000000000000000000000000000000..5f133377d5a4b415a394038b0442b71cc928f93b --- /dev/null +++ b/src/components/NewGameForm.js @@ -0,0 +1,188 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { + Map, + TileLayer +} from 'react-leaflet' + +export class NewGameForm extends React.Component{ + constructor(props){ + super(props); + + this.state = { + gamename: "", + description: "", + startDate: "", + startTime: "", + endDate: "", + endTime: "", + passwords: [], + zoom: 13, + mapCenter: { + lat: 62.2416479, + lng: 25.7597186 + } + } + + this.handleMapDrag = this.handleMapDrag.bind(this); + } + + handleError = error => { + this.setState({ errorMsg: error }); + }; + + handleChange = e => { + const { name, value } = e.target; + this.setState({ [name]: value }); + }; + + // show/hide this form + handleView = e => { + this.props.toggleView(this.props.view); + }; + + // remove view with ESC + handleEsc = e => { + if (e.keyCode === 27) { + this.handleView(); + } + }; + + handleMapDrag = e => { + this.setState({ + mapCenter: e.target.getCenter() + }); + } + + handleMapScroll = e => { + this.setState({ + zoom: e.target.getZoom() + }); + } + + handleGameCreation = e => { + let startDate = this.state.startDate + "T" + this.state.startTime + ":00.000Z"; + let endDate = this.state.endDate + "T" + this.state.endTime + ":00.000Z"; + + const gameObject = { + name: this.state.gamename, + desc: this.state.description, + map: "", + startdate: startDate, + enddate: endDate, + passwords: [this.state.password], + center: this.state.mapCenter + } + + e.preventDefault(); + + let token = sessionStorage.getItem('token'); + + // Send Game info to the server + fetch('http://localhost:5000/game/new', { + method: 'POST', + headers: { + Authorization: 'Bearer ' + token, + Accept: 'application/json', + 'Content-Type': 'application/json' + }, + body: JSON.stringify(gameObject) + }).then(res => res.json()) + .then(result => { + this.handleView(); + }) + .catch(error => console.log('Error: ', error)); + }; + + componentDidMount() { + document.addEventListener('keyup', this.handleEsc); + } + + componentWillUnmount() { + document.removeEventListener('keyup', this.handleEsc); + } + + render() { + return ReactDOM.createPortal ( + <div className='fade-main'> + <div className='sticky'> + <span className='close' onClick={this.handleView}> + × + </span> + </div> + <div className=''> + <form onSubmit={this.handleGameCreation}> + <h1>Demo Game Creation</h1> + <br /> + <input + placeholder='Game name' + name='gamename' + value={this.state.gamename} + onChange={this.handleChange} + required + /> + <br /> + <input + placeholder='Description' + type='text' + name='description' + value={this.state.description} + onChange={this.handleChange} + required + /> + <br /> + <label className=''>Start:</label> + <input + className='formDate' + type='date' + name='startDate' + value={this.state.startDate} + onChange={this.handleChange} + required + /> + <input + className='formTime' + type='time' + name='startTime' + onChange={this.handleChange} + required + /> + <br /> + <label className=''>End:</label> + <input + className='formDate' + type='date' + name='endDate' + value={this.state.endDate} + onChange={this.handleChange} + min={this.state.startDate} + required + /> + <input + className='formTime' + type='time' + name='endTime' + onChange={this.handleChange} + required + /> + <br /> + <label>Map things</label> + <br /> + <Map className='' center={[this.state.mapCenter.lat, this.state.mapCenter.lng]} zoom={this.state.zoom} style={{height: '400px', width: '400px'} } onmoveend={this.handleMapDrag} onzoomend={this.handleMapScroll}> + <TileLayer + attribution='Maanmittauslaitoksen kartta' + url=' https://tiles.kartat.kapsi.fi/taustakartta/{z}/{x}/{y}.jpg' + /> + </Map> + <br /> + <button type='submit'>Submit</button> + <h2>{this.state.errorMsg}</h2> + </form> + </div> + </div> + ,document.getElementById('form') + ); + } +} + +export default NewGameForm; \ No newline at end of file