From fc5b90a93828ce4e6b4177e19a49abbecb838ce6 Mon Sep 17 00:00:00 2001 From: garrettmills Date: Thu, 5 Nov 2020 20:57:08 -0600 Subject: [PATCH] Add ability to edit team name & starting lineup & save to server --- app/controllers/Teams.controller.js | 84 ++++++++++++++++++- app/models/Lineup.model.js | 37 +++++++- app/models/Player.model.js | 1 + app/models/Team.model.js | 5 ++ app/routing/routers/api.routes.js | 3 +- .../src/components/pages/MyTeam.component.js | 38 +++++++-- frontend/src/module/api.js | 24 ++++++ 7 files changed, 180 insertions(+), 12 deletions(-) diff --git a/app/controllers/Teams.controller.js b/app/controllers/Teams.controller.js index 5146727..087c5cf 100644 --- a/app/controllers/Teams.controller.js +++ b/app/controllers/Teams.controller.js @@ -9,6 +9,19 @@ class Teams extends Controller { return [...super.services, 'models'] } + /** + * Save changes to the current user's team and return it as API data. + * @param req + * @param res + * @param next + * @return {Promise<*>} + */ + async save_my_team(req, res, next) { + req.user_team.team_name = String(req.body.team_name).trim() + await req.user_team.save() + return res.api(await req.user_team.to_api()) + } + /** * Return the API data for the current user's team. * Requires an authenticated user. @@ -41,11 +54,78 @@ class Teams extends Controller { * @return {Promise<*>} */ async get_my_team_current_lineup(req, res, next) { - const Lineup = this.models.get('Lineup') - const lineup = await Lineup.get_and_update_for_team(req.user_team) + const lineup = await req.user_team.lineup() return res.api(await lineup.to_api()) } + /** + * Saves the lineup for the current user's team and returns it as API data. + * @param req + * @param res + * @param next + * @return {Promise} + */ + async save_my_team_lineup(req, res, next) { + if ( !Array.isArray(req.body.starting_players) ) { + return res.status(400) + .message('Missing required field: starting_players') + .api() + } + + if ( !Array.isArray(req.body.benched_players) ) { + return res.status(400) + .message('Missing required field: benched_players') + .api() + } + + const player_ids = (await req.user_team.players()).map(x => x.id) + const lineup = await req.user_team.lineup() + lineup.clear_lineup() + + for ( const player of req.body.starting_players ) { + if ( !player.id || !player.position ) continue; + + const lineup_record = { + player_id: player.id, + position: player.position, + } + + if ( !player_ids.includes(lineup_record.player_id) ) { + return res.status(400) + .message(`Sorry, the player ${lineup_record.player_id} is not on your team.`) + .api() + } + + lineup.start_player(lineup_record) + } + + for ( const player of req.body.benched_players ) { + if ( !player.id ) continue; + + if ( !player_ids.includes(player.id) ) { + return res.status(400) + .message(`Sorry, the player ${player.id} is not on your team.`) + .api() + } + + lineup.bench_player(player) + } + + console.log('pre save', lineup) + + // Save the partial lineup + await lineup.save() + + console.log('post save', lineup) + + // Fetch a fresh version to fill in any missing players + const corrected_lineup = await req.user_team.lineup() + + console.log('corrected', corrected_lineup) + + return res.api(await corrected_lineup.to_api()) + } + /** * Return the API data for a list of all teams. * @param req diff --git a/app/models/Lineup.model.js b/app/models/Lineup.model.js index 33c5df4..876a0dd 100644 --- a/app/models/Lineup.model.js +++ b/app/models/Lineup.model.js @@ -138,6 +138,24 @@ class Lineup extends Model { } } + /** + * Given the player_id/position record, add it to the starting lineup. + * @param {object} player_position_record + */ + start_player(player_position_record) { + this.benched_player_ids = this.benched_player_ids.filter(x => x !== player_position_record.player_id) + this.starting_players = this.starting_players.filter(x => x.player_id !== player_position_record.player_id) + this.starting_players.push(player_position_record) + } + + /** + * Remove all players from the bench and the starting lineup. + */ + clear_lineup() { + this.starting_players = [] + this.benched_player_ids = [] + } + /** * Cast the lineup to an object which can be returned via the API. * @return {Promise} @@ -159,12 +177,20 @@ class Lineup extends Model { // Find the player instance and cast it to an API object const player_inst = starting_players.find(x => x.id === player.player_id) build_starting_players.push({ - position: player.position, - ...(await player_inst.to_api()) + ...(await player_inst.to_api()), + position: player.position }) // remove the position from the array of positions to back-fill - lineup_positions = lineup_positions.filter(x => x !== player.position) + let found_one = false + lineup_positions = lineup_positions.filter(x => { + if ( !found_one && x === player.position ) { + found_one = true + return false + } + + return true + }) } // Fill in any missing positions into the data @@ -182,6 +208,11 @@ class Lineup extends Model { build_benched_players.push(obj) } + // If there are no players on the bench, add a placeholder slot. + if ( build_benched_players.length < 1 ) { + build_benched_players.push({ position: 'B' }) + } + data.starting_players = build_starting_players data.benched_players = build_benched_players return data diff --git a/app/models/Player.model.js b/app/models/Player.model.js index 0212cd9..d6c6fdf 100644 --- a/app/models/Player.model.js +++ b/app/models/Player.model.js @@ -67,6 +67,7 @@ class Player extends Model { async to_api() { return { + id: this.id, number: this.player_number, name: this.full_name, position: this.fantasy_position, diff --git a/app/models/Team.model.js b/app/models/Team.model.js index c6f3e48..9f23afb 100644 --- a/app/models/Team.model.js +++ b/app/models/Team.model.js @@ -50,6 +50,11 @@ class Team extends Model { return new_team } + async lineup() { + const Lineup = this.models.get('Lineup') + return Lineup.get_and_update_for_team(this) + } + async players() { const Player = this.models.get('Player') return Player.find({ diff --git a/app/routing/routers/api.routes.js b/app/routing/routers/api.routes.js index 809a1e3..eeb0a15 100644 --- a/app/routing/routers/api.routes.js +++ b/app/routing/routers/api.routes.js @@ -60,7 +60,8 @@ const index = { * or middleware that are applied in order. */ post: { - + '/my-team': ['controller::Teams.save_my_team'], + '/my-team/lineup': ['controller::Teams.save_my_team_lineup'], }, // You can include other HTTP verbs here. diff --git a/frontend/src/components/pages/MyTeam.component.js b/frontend/src/components/pages/MyTeam.component.js index 3976d36..88b13c0 100644 --- a/frontend/src/components/pages/MyTeam.component.js +++ b/frontend/src/components/pages/MyTeam.component.js @@ -47,6 +47,11 @@ class MyTeamComponent extends Component { static get template() { return template } static get props() { return [] } + /** + * Original team name to compare against. + */ + _original_team_name = '' + /** * The team name. * @type {string} @@ -105,7 +110,7 @@ class MyTeamComponent extends Component { return `
${data.name} - ${data.name} (${data.number}) + ${data.name} (#${data.number})
` } @@ -191,7 +196,7 @@ class MyTeamComponent extends Component { async vue_on_create() { console.log('api', api) const [my_team, lineup] = await Promise.all([api.get_my_team(), api.get_my_team_current_lineup()]) - this.team_name = my_team.team_name + this.team_name = this._original_team_name = my_team.team_name this.overall_data = await api.get_my_team_players() this.bench_players = lineup.benched_players this.starting_players = lineup.starting_players @@ -213,15 +218,36 @@ class MyTeamComponent extends Component { } mark_dirty() { - this.save_text = 'Unsaved changed' + this.save_text = 'Unsaved changes' + } + + /** + * Fired when the team name changes. Marks the data as needing a save. + */ + watch_team_name() { + if ( this.team_name !== this._original_team_name ) + this.mark_dirty() } async save_changes() { this.save_text = 'Saving changes...' - setTimeout(() => { - this.save_text = 'All changes saved.' - }, 2000) + // Save the team name + const team_save_result = await api.save_my_team({ team_name: this.team_name }) + this.team_name = this._original_team_name = team_save_result.team_name + + // Save the lineup + const lineup_data = { + starting_players: this.starting_players, + benched_players: this.bench_players, + } + + const lineup_save_result = await api.save_my_team_lineup(lineup_data) + this.bench_players = lineup_save_result.benched_players + this.starting_players = lineup_save_result.starting_players + + this.save_text = 'All changes saved.' + this.update() } } diff --git a/frontend/src/module/api.js b/frontend/src/module/api.js index b210408..d443761 100644 --- a/frontend/src/module/api.js +++ b/frontend/src/module/api.js @@ -3,6 +3,10 @@ class API { this.base_url = APP_BASE_PATH.replace('/app/', '/api/v1/') } + async save_my_team(team_data) { + return this.post_request('my-team', team_data) + } + async get_my_team() { return this.get_request('my-team') } @@ -15,6 +19,26 @@ class API { return this.get_request('my-team/lineup') } + async save_my_team_lineup(lineup_data) { + return this.post_request('my-team/lineup', lineup_data) + } + + async post_request(parts, data = {}) { + if ( !Array.isArray(parts) ) parts = [parts] + + const url = this.build_url(...parts) + const result = await fetch(url, { + method: 'POST', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + body: JSON.stringify(data), + }) + + return (await result.json()).data + } + async get_request(...parts) { const url = this.build_url(...parts) const result = await fetch(url)