Merge branch 'develop' into 'testing'

v1.2.0

See merge request sudoer777/score-tracker!15
main
Ethan Reece 2022-03-10 01:16:52 +00:00
commit 46194f6e46
22 changed files with 366 additions and 84 deletions

View File

@ -12,11 +12,11 @@ A web app designed to collect and display scores for sports
## Installation ## Installation
This repository is designed to be pushed to Heroku/Dokku/etc. This repository can be cloned and then pushed to Heroku/Dokku/etc.
### Requirements ### Requirements
- PostgreSQL (with empty database created and an account to access it) - PostgreSQL (with an empty database created and an account to access it)
### Environment Variables ### Environment Variables
@ -32,6 +32,8 @@ This repository is designed to be pushed to Heroku/Dokku/etc.
This program uses Node.js/Express.js for the backend, PostgreSQL for the database (with node-postgres), and Passport.js for managing users and sessions. This program uses Node.js/Express.js for the backend, PostgreSQL for the database (with node-postgres), and Passport.js for managing users and sessions.
To view the code, clone the repository and open it in VSCode/VSCodium.
### Structure ### Structure
- `database` folder contains backend scripts for managing and storing data. - `database` folder contains backend scripts for managing and storing data.
@ -44,6 +46,7 @@ This program uses Node.js/Express.js for the backend, PostgreSQL for the databas
- `auth.js` deals with logging in and out (`/auth/*`). - `auth.js` deals with logging in and out (`/auth/*`).
- `checkLoginStatus.js` contains functions for checking the login status of the current user. - `checkLoginStatus.js` contains functions for checking the login status of the current user.
- `data.js` sends various data to the client in JSON format (`/data/*`). - `data.js` sends various data to the client in JSON format (`/data/*`).
- `fetch.js` sends more specific data formatted for specific pages in JSON format (`/fetch/*`)
- `index.js` directs to the home page (`/`). - `index.js` directs to the home page (`/`).
- `manage.js` contains various functions that allows the user to add and edit items through the web browser (`/manage/*`). - `manage.js` contains various functions that allows the user to add and edit items through the web browser (`/manage/*`).
- `views` folder contains pug templates for each webpage, and a `layout` template for the base layout of each page. - `views` folder contains pug templates for each webpage, and a `layout` template for the base layout of each page.

2
app.js
View File

@ -15,6 +15,7 @@ var dataRouter = require('./routes/data');
var manageRouter = require('./routes/manage'); var manageRouter = require('./routes/manage');
var authRouter = require('./routes/auth'); var authRouter = require('./routes/auth');
var aboutRouter = require('./routes/about'); var aboutRouter = require('./routes/about');
var fetchRouter = require('./routes/fetch');
var app = express(); var app = express();
@ -53,6 +54,7 @@ app.use('/data', dataRouter);
app.use('/manage', manageRouter); app.use('/manage', manageRouter);
app.use('/auth', authRouter); app.use('/auth', authRouter);
app.use('/about', aboutRouter); app.use('/about', aboutRouter);
app.use('/fetch', fetchRouter);
// catch 404 and forward to error handler // catch 404 and forward to error handler

View File

@ -1,6 +1,6 @@
{ {
"name": "score-tracker", "name": "score-tracker",
"version": "1.0.3-pre", "version": "1.2.0",
"private": true, "private": true,
"scripts": { "scripts": {
"start": "node ./bin/www" "start": "node ./bin/www"

View File

@ -1,9 +1,14 @@
import * as Data from "./data.js"; import * as Data from "./data.js";
export async function populateSports(sportDropdown, selectedSportID = undefined) { export async function populateSports(sportDropdown, selectedSportID = undefined, data = undefined) {
sportDropdown.innerHTML = ""; sportDropdown.innerHTML = "";
const sportsList = await Data.getSports(); let sportsList;
if(data) {
sportsList = data.sports;
} else {
sportsList = await Data.getSports();
}
let currentIndex = 0; let currentIndex = 0;
let selectedSportIndex; let selectedSportIndex;
@ -13,17 +18,23 @@ export async function populateSports(sportDropdown, selectedSportID = undefined)
option.value = sport.id; option.value = sport.id;
sportDropdown.appendChild(option); sportDropdown.appendChild(option);
if(sport.id == selectedSportID) selectedSportIndex = currentIndex; if(sport.id == selectedSportID || (data && sport.id == data.latestGame.sportID)) selectedSportIndex = currentIndex;
currentIndex++; currentIndex++;
}); });
if(selectedSportIndex) sportDropdown.selectedIndex = selectedSportIndex; if(selectedSportIndex) sportDropdown.selectedIndex = selectedSportIndex;
} }
export async function populateSeasons(seasonDropdown, selectedSeasonID = undefined) { export async function populateSeasons(seasonDropdown, selectedSeasonID = undefined, data = undefined) {
seasonDropdown.innerHTML = ""; seasonDropdown.innerHTML = "";
const seasonsList = await Data.getSeasons(); let seasonsList;
if(data) {
seasonsList = data.seasons;
} else {
seasonsList = await Data.getSeasons();
}
let currentIndex = 0; let currentIndex = 0;
let selectedSeasonIndex; let selectedSeasonIndex;
@ -33,18 +44,24 @@ export async function populateSeasons(seasonDropdown, selectedSeasonID = undefin
option.value = season.id; option.value = season.id;
seasonDropdown.appendChild(option); seasonDropdown.appendChild(option);
if(season.id == selectedSeasonID) selectedSeasonIndex = currentIndex; if(season.id == selectedSeasonID || (data && season.id == data.latestGame.seasonID)) selectedSeasonIndex = currentIndex;
currentIndex++; currentIndex++;
}); });
if(selectedSeasonIndex) seasonDropdown.selectedIndex = selectedSeasonIndex; if(selectedSeasonIndex) seasonDropdown.selectedIndex = selectedSeasonIndex;
} }
export async function populateGenders(genderDropdown, selectedSportID, selectedGender = undefined) { export async function populateGenders(genderDropdown, selectedSportID, selectedGender = undefined, data = undefined) {
genderDropdown.innerHTML = ""; genderDropdown.innerHTML = "";
const gendersList = await Data.getGenders(selectedSportID); let gendersList;
if(data) {
gendersList = data.genders;
selectedSportID = data.latestGame.sportID;
} else {
gendersList = await Data.getGenders(selectedSportID);
}
if(selectedSportID) { if(selectedSportID) {
let currentIndex = 0; let currentIndex = 0;
let selectedGenderIndex; let selectedGenderIndex;
@ -54,7 +71,7 @@ export async function populateGenders(genderDropdown, selectedSportID, selectedG
option.value = gender.name; option.value = gender.name;
genderDropdown.appendChild(option); genderDropdown.appendChild(option);
if(gender.name == selectedGender) selectedGenderIndex = currentIndex; if(gender.name == selectedGender || (data && gender.name == data.latestGame.gender.name)) selectedGenderIndex = currentIndex;
currentIndex++; currentIndex++;
}); });
@ -62,11 +79,21 @@ export async function populateGenders(genderDropdown, selectedSportID, selectedG
} }
} }
export async function populateDivisions (divisionDropdown, selectedSportID, selectedGender, selectedDivisionID = undefined) { export async function populateDivisions (divisionDropdown, selectedSportID, selectedGender, selectedDivisionID = undefined, data = undefined) {
divisionDropdown.innerHTML = ""; divisionDropdown.innerHTML = "";
if(data) {
selectedSportID = data.latestGame.sportID;
selectedGender = data.latestGame.gender;
}
if(selectedSportID && selectedGender) { if(selectedSportID && selectedGender) {
const divisionsList = await Data.getDivisions(selectedSportID, selectedGender); let divisionsList;
if(data) {
divisionsList = data.divisions;
} else {
divisionsList = await Data.getDivisions(selectedSportID, selectedGender);
}
let currentIndex = 0; let currentIndex = 0;
let selectedDivisionIndex; let selectedDivisionIndex;
@ -76,7 +103,7 @@ export async function populateDivisions (divisionDropdown, selectedSportID, sele
option.value = division.id; option.value = division.id;
divisionDropdown.appendChild(option); divisionDropdown.appendChild(option);
if(division.id == selectedDivisionID) selectedDivisionIndex = currentIndex; if(division.id == selectedDivisionID || (data && division.id == data.latestGame.divisionID)) selectedDivisionIndex = currentIndex;
currentIndex++; currentIndex++;
}); });
@ -84,14 +111,28 @@ export async function populateDivisions (divisionDropdown, selectedSportID, sele
} }
} }
export async function populateTeams(teamDropdown, selectedSportID, selectedTeamID = undefined) { export async function populateTeams(teamDropdown, selectedSportID, selectedTeamID = undefined, data = undefined, useOpponent = false) {
teamDropdown.innerHTML = ""; teamDropdown.innerHTML = "";
if(data) {
selectedSportID = data.latestGame.sportID;
}
if(selectedSportID) { if(selectedSportID) {
const teamsList = await Data.getTeams(selectedSportID); let teamsList;
if(data) {
teamsList = data.teams;
} else {
teamsList = await Data.getTeams(selectedSportID);
}
let currentIndex = 0; let currentIndex = 0;
let selectedTeamIndex; let selectedTeamIndex;
if(data) {
selectedTeamID = useOpponent ? data.latestGame.team2ID : data.latestGame.team1ID;
}
teamsList.forEach(team => { teamsList.forEach(team => {
const option = document.createElement('option'); const option = document.createElement('option');
option.text = team.name; option.text = team.name;

View File

@ -1,6 +1,7 @@
import * as Data from "./data.js"; import * as Data from "./data.js";
import * as Form from "./form.js"; import * as Form from "./form.js";
const dropdownsDiv = document.getElementById('dropdowns-div');
const sportDropdown = document.getElementById('sport-dropdown'); const sportDropdown = document.getElementById('sport-dropdown');
const seasonDropdown = document.getElementById('year-dropdown'); const seasonDropdown = document.getElementById('year-dropdown');
const genderDropdown = document.getElementById('gender-dropdown'); const genderDropdown = document.getElementById('gender-dropdown');
@ -11,35 +12,20 @@ const gamesTableHeader = document.getElementById('games-table-header');
const noScoresMessage = document.getElementById('no-scores-message'); const noScoresMessage = document.getElementById('no-scores-message');
const addScoreButton = document.getElementById('add-score-button'); const addScoreButton = document.getElementById('add-score-button');
const manageButton = document.getElementById('manage-button'); const manageButton = document.getElementById('manage-button');
const loadingSpan = document.getElementById('loading');
async function initializeForm() { async function initializeForm() {
let latestGame; const data = await (await fetch(`/fetch/index/dropdown`)).json();
try { await Form.populateSeasons(seasonDropdown, null, data);
latestGame = await Data.getLatestGame(); await Form.populateSports(sportDropdown, null, data);
} catch { await Form.populateGenders(genderDropdown, null, null, data);
latestGame = null; await Form.populateDivisions(divisionDropdown, null, null, null, data);
} await Form.populateTeams(teamDropdown, null, null, data);
if(latestGame) {
Form.populateSeasons(seasonDropdown, latestGame.seasonID);
const division = await Data.getDivision(latestGame.divisionID);
await Form.populateSports(sportDropdown, division.sportID);
await Form.populateGenders(genderDropdown, sportDropdown.value, division.gender.name);
await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value, latestGame.divisionID);
await Form.populateTeams(teamDropdown, sportDropdown.value, latestGame.team1ID);
} else {
Form.populateSeasons(seasonDropdown);
await Form.populateSports(sportDropdown);
await Form.populateGenders(genderDropdown, sportDropdown.value);
await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value);
await Form.populateTeams(teamDropdown, sportDropdown.value);
}
seasonDropdown.onchange = loadTable; seasonDropdown.onchange = loadTable;
@ -58,6 +44,9 @@ async function initializeForm() {
divisionDropdown.onchange = loadTable; divisionDropdown.onchange = loadTable;
teamDropdown.onchange = loadTable; teamDropdown.onchange = loadTable;
loadingSpan.textContent = '';
dropdownsDiv.style.visibility = 'visible';
loadTable(); loadTable();
@ -75,8 +64,11 @@ async function loadTable() {
if(selectedTeamID && selectedDivisionID) { if(selectedTeamID && selectedDivisionID) {
gamesTableHeader.textContent = `Scores for ${teamDropdown.options[teamDropdown.selectedIndex].text}`; gamesTableHeader.textContent = `Scores for ${teamDropdown.options[teamDropdown.selectedIndex].text}`;
const gamesList = await Data.getGames(selectedTeamID, selectedDivisionID, selectedSeasonID); noScoresMessage.textContent = "Loading scores...";
const requestURL = `/fetch/index/scores?team=${+selectedTeamID}&division=${+selectedDivisionID}&season=${+selectedSeasonID}`;
const gamesList = await (await fetch(requestURL)).json();
noScoresMessage.textContent = "";
if(gamesList.length > 0) { if(gamesList.length > 0) {
await setupGamesTableHeaders(); await setupGamesTableHeaders();
@ -93,8 +85,7 @@ async function loadTable() {
row.appendChild(scoreCell); row.appendChild(scoreCell);
const opponentCell = document.createElement('td'); const opponentCell = document.createElement('td');
Data.getTeam(game.team2ID) opponentCell.textContent = game.opponent.name;
.then(data => opponentCell.textContent = data.name);
row.appendChild(opponentCell); row.appendChild(opponentCell);
const dateCell = document.createElement('td'); const dateCell = document.createElement('td');

View File

@ -3,6 +3,7 @@ import * as Data from "./data.js";
const categoryDropdown = document.getElementById('category-dropdown'); const categoryDropdown = document.getElementById('category-dropdown');
const itemsListTable = document.getElementById('items-list'); const itemsListTable = document.getElementById('items-list');
const addNewButton = document.getElementById('add-new-button'); const addNewButton = document.getElementById('add-new-button');
const loadingSpan = document.getElementById('loading-message');
function getGenderLetter(genderName) { function getGenderLetter(genderName) {
@ -114,7 +115,7 @@ CATEGORIES.push(new Category(
CATEGORIES.push(new Category( CATEGORIES.push(new Category(
"divisions", "divisions",
async function getDivisions() { async function getDivisions() {
return await Data.getDivisions(); return await (await fetch('/fetch/manage/divisions')).json();
}, },
async function listDivisionHeaders() { async function listDivisionHeaders() {
const headerRow = document.createElement('tr'); const headerRow = document.createElement('tr');
@ -150,8 +151,8 @@ CATEGORIES.push(new Category(
row.appendChild(spacerCell); row.appendChild(spacerCell);
const sportCell = document.createElement('td'); const sportCell = document.createElement('td');
Data.getSportName(division.sportID) let sportName = division.sport.name;
.then(data => sportCell.textContent = data); sportCell.textContent = sportName;
row.appendChild(sportCell); row.appendChild(sportCell);
}, },
async function addDivision() { async function addDivision() {
@ -165,7 +166,7 @@ CATEGORIES.push(new Category(
CATEGORIES.push(new Category( CATEGORIES.push(new Category(
"teams", "teams",
async function getTeams() { async function getTeams() {
return await Data.getTeams(); return await (await fetch('/fetch/manage/teams')).json();
}, },
async function listTeamHeaders() { async function listTeamHeaders() {
const headerRow = document.createElement('tr'); const headerRow = document.createElement('tr');
@ -193,8 +194,8 @@ CATEGORIES.push(new Category(
row.appendChild(spacerCell); row.appendChild(spacerCell);
const sportCell = document.createElement('td'); const sportCell = document.createElement('td');
Data.getSportName(team.sportID) let sportName = team.sport.name;
.then(data => sportCell.textContent = data); sportCell.textContent = sportName;
row.appendChild(sportCell); row.appendChild(sportCell);
}, },
async function addTeam() { async function addTeam() {
@ -208,7 +209,7 @@ CATEGORIES.push(new Category(
CATEGORIES.push(new Category( CATEGORIES.push(new Category(
"games", "games",
async function getGames() { async function getGames() {
return await Data.getGames(); return await (await fetch('/fetch/manage/games')).json();
}, },
async function listGameHeaders() { async function listGameHeaders() {
const headerRow = document.createElement('tr'); const headerRow = document.createElement('tr');
@ -241,12 +242,12 @@ CATEGORIES.push(new Category(
function listGame(game, row) { function listGame(game, row) {
const teamsCell = document.createElement('td'); const teamsCell = document.createElement('td');
const team1NameSpan = document.createElement('span'); const team1NameSpan = document.createElement('span');
Data.getTeam(game.team1ID) let team1Name = game.team1.name;
.then(data => team1NameSpan.textContent = data.name); team1NameSpan.textContent = team1Name;
teamsCell.appendChild(team1NameSpan); teamsCell.appendChild(team1NameSpan);
const team2NameSpan = document.createElement('span'); const team2NameSpan = document.createElement('span');
Data.getTeam(game.team2ID) let team2Name = game.team2.name;
.then(data => team2NameSpan.textContent = data.name); team2NameSpan.textContent = team2Name;
teamsCell.appendChild(team2NameSpan); teamsCell.appendChild(team2NameSpan);
row.appendChild(teamsCell); row.appendChild(teamsCell);
@ -270,13 +271,11 @@ CATEGORIES.push(new Category(
const divisionGenderSpan = document.createElement('span'); const divisionGenderSpan = document.createElement('span');
divisionSpan.appendChild(divisionNameSpan); divisionSpan.appendChild(divisionNameSpan);
divisionSpan.appendChild(divisionGenderSpan); divisionSpan.appendChild(divisionGenderSpan);
Data.getDivision(game.divisionID) let divisionName = game.division.name;
.then(data => { let sportName = game.sport.name;
Data.getSportName(data.sportID) divisionNameSpan.textContent = divisionName;
.then(data => sportSpan.textContent = data); sportSpan.textContent = sportName;
divisionNameSpan.textContent = data.name; divisionGenderSpan.textContent = getGenderLetter(game.division.gender.name);
divisionGenderSpan.textContent = getGenderLetter(data.gender.name);
});
sportCell.appendChild(sportSpan); sportCell.appendChild(sportSpan);
sportCell.appendChild(divisionSpan); sportCell.appendChild(divisionSpan);
row.appendChild(sportCell); row.appendChild(sportCell);
@ -292,11 +291,10 @@ CATEGORIES.push(new Category(
const submitterCell = document.createElement('td'); const submitterCell = document.createElement('td');
if(game.submitterID) { if(game.submitterID) {
Data.getAccount(game.submitterID) let submitterName = game.submitter.name;
.then(data => submitterCell.textContent = data.name); submitterCell.textContent = submitterName;
} else { } else {
submitterCell.textContent = game.submitterName; submitterCell.textContent = game.submitterName;
console.log(game.submitterName);
} }
row.appendChild(submitterCell); row.appendChild(submitterCell);
}, },
@ -362,6 +360,8 @@ CATEGORIES.push(new Category(
async function listItems(category) { async function listItems(category) {
loadingSpan.textContent = "Loading...";
itemsListTable.innerHTML = ""; itemsListTable.innerHTML = "";
const itemsList = await category.getItems(); const itemsList = await category.getItems();
@ -389,6 +389,8 @@ async function listItems(category) {
itemsListTable.appendChild(row); itemsListTable.appendChild(row);
}); });
loadingSpan.textContent = '';
} }
if(window.location.hash) { if(window.location.hash) {
let correctIndex; let correctIndex;

View File

@ -9,6 +9,7 @@ const adminCheckboxSection = document.getElementById('admin-checkbox-section');
const adminCheckbox = document.getElementById('admin-checkbox'); const adminCheckbox = document.getElementById('admin-checkbox');
const submitButton = document.getElementById('submit-button'); const submitButton = document.getElementById('submit-button');
const deleteButton = document.getElementById('delete-button'); const deleteButton = document.getElementById('delete-button');
const loadingSpan = document.getElementById('loading-message');
async function Initialize() { async function Initialize() {
let params = new URLSearchParams(location.search); let params = new URLSearchParams(location.search);
@ -46,6 +47,9 @@ async function Initialize() {
passwordTextbox.disabled = false; passwordTextbox.disabled = false;
passwordTextbox.addEventListener('keyup', checkDataValidity); passwordTextbox.addEventListener('keyup', checkDataValidity);
checkDataValidity(); checkDataValidity();
loadingSpan.textContent = '';
submissionForm.style.visibility = 'visible';
} }
Initialize(); Initialize();

View File

@ -8,13 +8,14 @@ const genderDropdown = document.getElementById('gender-dropdown');
const nameTextbox = document.getElementById('name-textbox'); const nameTextbox = document.getElementById('name-textbox');
const submitButton = document.getElementById('submit-button'); const submitButton = document.getElementById('submit-button');
const deleteButton = document.getElementById('delete-button'); const deleteButton = document.getElementById('delete-button');
const loadingSpan = document.getElementById('loading-message');
async function initializeForm() { async function initializeForm() {
let params = new URLSearchParams(location.search); let params = new URLSearchParams(location.search);
let divisionID = params.get('division'); let divisionID = params.get('division');
if(divisionID) { if(divisionID) {
const division = await Data.getDivision(divisionID); const division = await (await fetch(`/fetch/manage/division?division=${divisionID}`)).json();
nameTextbox.value = division.name; nameTextbox.value = division.name;
@ -26,7 +27,11 @@ async function initializeForm() {
if(gender == 'female') genderDropdown.selectedIndex = 1; if(gender == 'female') genderDropdown.selectedIndex = 1;
else genderDropdown.selectedIndex = 2; else genderDropdown.selectedIndex = 2;
Form.populateSports(sportDropdown, division.sportID); let data = {};
data.sports = [division.sport];
data.latestGame = {sportID : division.sportID };
Form.populateSports(sportDropdown, null, data);
Form.addHiddenValue('division', divisionID, submissionForm); Form.addHiddenValue('division', divisionID, submissionForm);
} }
@ -40,6 +45,9 @@ async function initializeForm() {
nameTextbox.disabled = false; nameTextbox.disabled = false;
nameTextbox.addEventListener('keyup', checkDataValidity); nameTextbox.addEventListener('keyup', checkDataValidity);
loadingSpan.textContent = '';
submissionForm.style.visibility = 'visible';
} }
initializeForm(); initializeForm();

View File

@ -15,6 +15,7 @@ const team2ScoreTextbox = document.getElementById('team2-score-textbox');
const nameTextbox = document.getElementById('name-textbox'); const nameTextbox = document.getElementById('name-textbox');
const submitButton = document.getElementById('submit-button'); const submitButton = document.getElementById('submit-button');
const deleteButton = document.getElementById('delete-button'); const deleteButton = document.getElementById('delete-button');
const loadingSpan = document.getElementById('loading-span');
async function initializeForm() { async function initializeForm() {
@ -41,24 +42,23 @@ async function initializeForm() {
team2ScoreTextbox.value = game.team2Score; team2ScoreTextbox.value = game.team2Score;
} }
else { else {
try { /*try {*/
const game = await Data.getLatestGame(true); const data = await (await fetch(`/fetch/index/dropdown`)).json();
Form.populateSeasons(seasonDropdown, game.seasonID); await Form.populateSeasons(seasonDropdown, null, data);
const data = await Data.getDivision(game.divisionID) await Form.populateSports(sportDropdown, null, data);
await Form.populateSports(sportDropdown, data.sportID) await Form.populateGenders(genderDropdown, null, null, data);
await Form.populateGenders(genderDropdown, sportDropdown.value, data.gender.name) await Form.populateDivisions(divisionDropdown, null, null, null, data);
await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value, game.divisionID); await Form.populateTeams(team1Dropdown, null, null, data);
await Form.populateTeams(team1Dropdown, sportDropdown.value, game.team1ID); await Form.populateTeams(team2Dropdown, null, null, data, true);
await Form.populateTeams(team2Dropdown, sportDropdown.value, game.team2ID); /*} catch {
} catch {
await Form.populateSeasons(seasonDropdown); await Form.populateSeasons(seasonDropdown);
await Form.populateSports(sportDropdown) await Form.populateSports(sportDropdown)
await Form.populateGenders(genderDropdown, sportDropdown.value) await Form.populateGenders(genderDropdown, sportDropdown.value)
await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value); await Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value);
await Form.populateTeams(team1Dropdown, sportDropdown.value); await Form.populateTeams(team1Dropdown, sportDropdown.value);
await Form.populateTeams(team2Dropdown, sportDropdown.value); await Form.populateTeams(team2Dropdown, sportDropdown.value);
} }*/
dateInput.value = (new Date()).toISOString().slice(0,10); dateInput.value = (new Date()).toISOString().slice(0,10);
} }
@ -95,6 +95,9 @@ async function initializeForm() {
team2ScoreTextbox.addEventListener('keyup', checkDataValidity); team2ScoreTextbox.addEventListener('keyup', checkDataValidity);
if(nameTextbox) nameTextbox.addEventListener('keyup', checkDataValidity); if(nameTextbox) nameTextbox.addEventListener('keyup', checkDataValidity);
loadingSpan.textContent = '';
submissionForm.style.visibility = 'visible';
checkDataValidity(); checkDataValidity();
} }
initializeForm(); initializeForm();

View File

@ -5,6 +5,7 @@ const nameTextbox = document.getElementById('name-textbox');
const submitButton = document.getElementById('submit-button'); const submitButton = document.getElementById('submit-button');
const deleteButton = document.getElementById('delete-button'); const deleteButton = document.getElementById('delete-button');
const submissionForm = document.getElementById('submission-form'); const submissionForm = document.getElementById('submission-form');
const loadingSpan = document.getElementById('loading-message');
async function initializeForm() { async function initializeForm() {
@ -26,6 +27,9 @@ async function initializeForm() {
nameTextbox.disabled = false; nameTextbox.disabled = false;
nameTextbox.addEventListener('keyup', checkDataValidity); nameTextbox.addEventListener('keyup', checkDataValidity);
loadingSpan.textContent = '';
submissionForm.style.visibility = 'visible';
} }
initializeForm(); initializeForm();

View File

@ -7,19 +7,25 @@ const sportDropdown = document.getElementById('sport-dropdown');
const nameTextbox = document.getElementById('name-textbox'); const nameTextbox = document.getElementById('name-textbox');
const submitButton = document.getElementById('submit-button'); const submitButton = document.getElementById('submit-button');
const deleteButton = document.getElementById('delete-button'); const deleteButton = document.getElementById('delete-button');
const loadingSpan = document.getElementById('loading-message');
async function initializeForm() { async function initializeForm() {
let params = new URLSearchParams(location.search); let params = new URLSearchParams(location.search);
let teamID = params.get('team'); let teamID = params.get('team');
if(teamID) { if(teamID) {
const team = await Data.getTeam(teamID); const team = await (await fetch(`/fetch/manage/team?team=${teamID}`)).json();
nameTextbox.value = team.name; nameTextbox.value = team.name;
deleteButton.style.visibility = "visible"; deleteButton.style.visibility = "visible";
deleteButton.disabled = false; deleteButton.disabled = false;
Form.populateSports(sportDropdown, team.sportID); let data = {};
data.sports = [team.sport];
data.latestGame = {sportID : team.sportID };
Form.populateSports(sportDropdown, null, data);
Form.addHiddenValue('team', teamID, submissionForm); Form.addHiddenValue('team', teamID, submissionForm);
} }
@ -31,6 +37,9 @@ async function initializeForm() {
nameTextbox.disabled = false; nameTextbox.disabled = false;
nameTextbox.addEventListener('keyup', checkDataValidity); nameTextbox.addEventListener('keyup', checkDataValidity);
loadingSpan.textContent = '';
submissionForm.style.visibility = 'visible';
} }
initializeForm(); initializeForm();

View File

@ -48,4 +48,8 @@ form {
.flat-form-section { .flat-form-section {
flex-direction: row; flex-direction: row;
} }
#submission-form {
visibility: hidden;
}

View File

@ -32,3 +32,7 @@ tr {
#login-button { #login-button {
margin-left: auto; margin-left: auto;
} }
#dropdowns-div {
visibility: hidden;
}

View File

@ -98,7 +98,7 @@ router.get('/team', async function(req, res, next) {
console.error("ERROR: " + err.message); console.error("ERROR: " + err.message);
res.status(500).send("An error has occurred"); res.status(500).send("An error has occurred");
} }
}) });
router.get('/games', async function(req, res, next) { router.get('/games', async function(req, res, next) {
try { try {

200
routes/fetch.js 100644
View File

@ -0,0 +1,200 @@
var express = require('express');
var router = express.Router();
var sports = require('../database/scores/sports');
var seasons = require('../database/scores/seasons');
var genders = require('../database/scores/genders');
var divisions = require('../database/scores/divisions');
var teams = require('../database/scores/teams');
var games = require('../database/scores/games');
var accounts = require('../database/accounts/accounts');
var checkLoginStatus = require('./checkLoginStatus');
router.get('/index/dropdown', async function(req, res, next) {
let latestGame;
let seasonsData;
let sportsData;
let gendersData;
let divisionsData;
let teamsData;
try {
latestGame = await games.getLatest();
let division = await divisions.getFromID(latestGame.divisionID);
latestGame.sportID = division.sportID;
latestGame.gender = division.gender;
} catch {
latestGame = null;
}
try {
if(latestGame) {
seasonsData = await seasons.retrieveAll();
sportsData = await sports.retrieveAll();
gendersData = await genders.retrieveBySport(latestGame.sportID);
divisionsData = await divisions.retrieve(latestGame.sportID);
teamsData = await teams.retrieve(latestGame.sportID);
} else {
seasonsData = await seasons.retrieveAll();
sportsData = await sports.retrieveAll();
gendersData = await genders.retrieveBySport(sportsData[0].id);
divisionsData = await divisions.retrieve(sportsData[0].id);
teamsData = await teams.retrieve(sportsData[0].id);
}
res.json({
seasons : seasonsData,
sports : sportsData,
genders : gendersData,
divisions : divisionsData,
teams: teamsData,
latestGame
});
} catch(err) {
console.error("ERROR: " + err.message);
res.status(500).send("An error has occurred");
}
});
router.get('/index/scores', async function (req, res, next) {
try {
const seasonID = req.query.season;
const divisionID = req.query.division;
const teamID = req.query.team;
const data = await games.retrieve(teamID, divisionID, seasonID);
for (const game of data) {
game.opponent = await teams.getFromID(game.team2ID);
}
res.json(data);
} catch(err) {
console.error("ERROR: " + err.message);
res.status(500).send("An error has occurred");
}
});
router.get('/submit', async function(req, res, next) {
let latestGame;
let seasonsData;
let sportsData;
let gendersData;
let divisionsData;
let teamsData;
const userID = req.user ? req.user[0] : null;
try {
latestGame = await games.getLatest(userID);
let division = await divisions.getFromID(latestGame.divisionID);
latestGame.sportID = division.sportID;
latestGame.gender = division.gender;
} catch {
latestGame = null;
}
try {
if(latestGame) {
seasonsData = await seasons.retrieveAll();
sportsData = await sports.retrieveAll();
gendersData = await genders.retrieveBySport(latestGame.sportID);
divisionsData = await divisions.retrieve(latestGame.sportID);
teamsData = await teams.retrieve(latestGame.sportID);
} else {
seasonsData = await seasons.retrieveAll();
sportsData = await sports.retrieveAll();
gendersData = await genders.retrieveBySport(sportsData[0].id);
divisionsData = await divisions.retrieve(sportsData[0].id);
teamsData = await teams.retrieve(sportsData[0].id);
}
res.json({
seasons : seasonsData,
sports : sportsData,
genders : gendersData,
divisions : divisionsData,
teams: teamsData,
latestGame
});
} catch(err) {
console.error("ERROR: " + err.message);
res.status(500).send("An error has occurred");
}
});
router.get('/manage/divisions', async function (req, res, next) {
const data = await divisions.retrieve();
for(const division of data) {
division.sport = await sports.getFromID(division.sportID);
}
res.json(data);
});
router.get('/manage/division', async function (req, res, next) {
try {
const divisionID = req.query.division;
const data = await divisions.getFromID(divisionID);
data.sport = await sports.getFromID(data.sportID);
res.json(data);
} catch(err) {
console.error("ERROR: " + err.message);
res.status(500).send("An error has occurred");
}
});
router.get('/manage/teams', async function (req, res, next) {
const data = await teams.retrieve();
for(const team of data) {
team.sport = await sports.getFromID(team.sportID);
}
res.json(data);
});
router.get('/manage/team', async function (req, res, next) {
try {
const teamID = req.query.team;
const data = await teams.getFromID(teamID);
data.sport = await sports.getFromID(data.sportID);
res.json(data);
} catch(err) {
console.error("ERROR: " + err.message);
res.status(500).send("An error has occurred");
}
});
router.get('/manage/games', checkLoginStatus.user, async function (req, res, next) {
try{
const userIsAdmin = req.user[2];
const loggedInAccountID = req.user[0];
if(!userIsAdmin) {
res.status(403).send("ACCESS DENIED");
} else {
const data = await games.retrieve();
for(const game of data) {
game.team1 = await teams.getFromID(game.team1ID);
game.team2 = await teams.getFromID(game.team2ID);
game.division = await divisions.getFromID(game.divisionID);
game.sport = await sports.getFromID(game.division.sportID);
game.submitter = game.submitterName || (await accounts.getFromID(game.submitterID));
}
res.json(data);
}
} catch(err) {
console.error("ERROR: " + err.message);
res.status(500).send("An error has occurred");
}
});
module.exports = router;

View File

@ -5,6 +5,7 @@ block stylesheets
link(rel='stylesheet', href='/stylesheets/form.css') link(rel='stylesheet', href='/stylesheets/form.css')
block content block content
span#loading-message Loading...
form#submission-form(action='/manage/account', method='POST') form#submission-form(action='/manage/account', method='POST')
if accountID if accountID
input#account-id(type="hidden" name="account" value=accountID) input#account-id(type="hidden" name="account" value=accountID)

View File

@ -11,7 +11,8 @@ block actions
block content block content
div span#loading Loading...
div#dropdowns-div
span(class='form-section') span(class='form-section')
label Year label Year
span(class='form-section-input') span(class='form-section-input')

View File

@ -18,6 +18,7 @@ block content
option(value="accounts") Accounts option(value="accounts") Accounts
div div
h2#table-header h2#table-header
span#loading-message Loading...
table#items-list table#items-list
button#add-new-button Add new... button#add-new-button Add new...

View File

@ -5,6 +5,7 @@ block stylesheets
link(rel='stylesheet', href='/stylesheets/form.css') link(rel='stylesheet', href='/stylesheets/form.css')
block content block content
span#loading-message Loading...
form#submission-form(action='./division', method='POST') form#submission-form(action='./division', method='POST')
span(class='form-section') span(class='form-section')
label Sport label Sport

View File

@ -5,6 +5,7 @@ block stylesheets
link(rel='stylesheet', href='/stylesheets/form.css') link(rel='stylesheet', href='/stylesheets/form.css')
block content block content
span#loading-span Loading...
form#submission-form(action='./game', method='POST') form#submission-form(action='./game', method='POST')
span(class='form-section') span(class='form-section')
label Year label Year

View File

@ -5,6 +5,7 @@ block stylesheets
link(rel='stylesheet', href='/stylesheets/form.css') link(rel='stylesheet', href='/stylesheets/form.css')
block content block content
span#loading-message Loading...
form#submission-form(action='./sport', method='POST') form#submission-form(action='./sport', method='POST')
span(class='form-section') span(class='form-section')
label Sport name label Sport name

View File

@ -5,6 +5,7 @@ block stylesheets
link(rel='stylesheet', href='/stylesheets/form.css') link(rel='stylesheet', href='/stylesheets/form.css')
block content block content
span#loading-message Loading...
form#submission-form(action='./team', method='POST') form#submission-form(action='./team', method='POST')
span(class='form-section') span(class='form-section')
label Sport label Sport