Merge branch 'user-accounts' into 'develop'
Add user account functionality See merge request sudoer777/cvcs-score-tracker!8main
commit
2c747fc6fa
30
app.js
30
app.js
|
@ -3,14 +3,42 @@ var express = require('express');
|
|||
var path = require('path');
|
||||
var cookieParser = require('cookie-parser');
|
||||
var logger = require('morgan');
|
||||
var random = require('./database/accounts/random');
|
||||
const passport = require('passport');
|
||||
const session = require('express-session');
|
||||
const accounts = require('./database/accounts/accounts');
|
||||
var flash = require('connect-flash');
|
||||
|
||||
|
||||
var indexRouter = require('./routes/index');
|
||||
var usersRouter = require('./routes/users');
|
||||
var dataRouter = require('./routes/data');
|
||||
var manageRouter = require('./routes/manage');
|
||||
var authRouter = require('./routes/auth');
|
||||
var adminRouter = require('./routes/admin');
|
||||
|
||||
var app = express();
|
||||
|
||||
// flash setup
|
||||
app.use(flash());
|
||||
|
||||
// session setup
|
||||
app.use(
|
||||
session({
|
||||
secret: random.makeid(20),
|
||||
resave: false,
|
||||
saveUninitialized: true,
|
||||
})
|
||||
);
|
||||
|
||||
// passport setup
|
||||
app.use(passport.initialize());
|
||||
app.use(passport.session());
|
||||
|
||||
//passport.use(accounts.createStrategy());
|
||||
//passport.serializeUser(accounts.serializeUser());
|
||||
//passport.deserializeUser(accounts.deserializeUser());
|
||||
|
||||
// view engine setup
|
||||
app.set('views', path.join(__dirname, 'views'));
|
||||
app.set('view engine', 'pug');
|
||||
|
@ -25,6 +53,8 @@ app.use('/', indexRouter);
|
|||
app.use('/users', usersRouter);
|
||||
app.use('/data', dataRouter);
|
||||
app.use('/manage', manageRouter);
|
||||
app.use('/auth', authRouter);
|
||||
app.use('/admin', adminRouter);
|
||||
|
||||
|
||||
// catch 404 and forward to error handler
|
||||
|
|
|
@ -0,0 +1,141 @@
|
|||
const database = require('./../database');
|
||||
const passport = require('passport');
|
||||
const localStrategy = require('passport-local').Strategy;
|
||||
const bcrypt = require('bcrypt');
|
||||
|
||||
class User {
|
||||
constructor(id, email, isAdmin) {
|
||||
this.id = id;
|
||||
this.email = email;
|
||||
this.isAdmin = isAdmin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
async function checkForAdminAccount() {
|
||||
|
||||
const adminUsersQuery = `SELECT *
|
||||
FROM accounts.users
|
||||
WHERE admin = true;`;
|
||||
const adminUsers = await database.executeQuery(adminUsersQuery);
|
||||
|
||||
if(adminUsers.length == 0) {
|
||||
const passwordHash = await generateHash('admin');
|
||||
const createTempAdminQuery = `INSERT INTO accounts.users(email, password, admin)
|
||||
VALUES('admin@example.com', $1, true);`;
|
||||
database.executeQuery(createTempAdminQuery, [passwordHash]);
|
||||
console.log("Created temp admin account 'admin@example.com' with password 'admin'.");
|
||||
}
|
||||
}
|
||||
checkForAdminAccount();
|
||||
|
||||
|
||||
passport.use(new localStrategy({
|
||||
usernameField: 'email',
|
||||
passwordField: 'password'},
|
||||
(username, password, cb) => {
|
||||
query = `SELECT user_id, email, password, admin
|
||||
FROM accounts.users
|
||||
WHERE email = $1`;
|
||||
database.executeQuery(query, [username])
|
||||
.then(result => {
|
||||
if(result.length > 0) {
|
||||
const first = result[0];
|
||||
const matches = bcrypt.compareSync(password, first[2]);
|
||||
if(matches) {
|
||||
return cb(null, { id: first[0], email: first[1], admin: first[3] })
|
||||
}
|
||||
else
|
||||
{
|
||||
return cb(null, false)
|
||||
}
|
||||
} else {
|
||||
return cb(null, false)
|
||||
}
|
||||
});
|
||||
}));
|
||||
|
||||
passport.serializeUser((user, done) => {
|
||||
done(null, user.id)
|
||||
})
|
||||
|
||||
passport.deserializeUser((id, cb) => {
|
||||
query = `SELECT user_id, email, admin
|
||||
FROM accounts.users
|
||||
WHERE user_id = $1`;
|
||||
database.executeQuery(query, [parseInt(id, 10)])
|
||||
.then(result => {
|
||||
cb(null, result[0]);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
async function generateHash(password) {
|
||||
const salt = bcrypt.genSaltSync();
|
||||
return bcrypt.hashSync(password, salt);
|
||||
}
|
||||
|
||||
async function create(email, password, isAdmin) {
|
||||
const hash = await generateHash(password);
|
||||
|
||||
const query = `INSERT INTO accounts.users(email, password, admin)
|
||||
VALUES($1, $2, $3)`;
|
||||
await database.executeQuery(query, [email, hash, isAdmin]);
|
||||
}
|
||||
|
||||
async function edit(id, email, password, isAdmin) {
|
||||
if(password) {
|
||||
const hash = await generateHash(password);
|
||||
|
||||
const query = `UPDATE accounts.users
|
||||
SET email = $2,
|
||||
password = $3,
|
||||
admin = $4
|
||||
WHERE user_id = $1;`;
|
||||
await database.executeQuery(query, [id, email, hash, isAdmin]);
|
||||
} else {
|
||||
const query = `UPDATE accounts.users
|
||||
SET email = $2,
|
||||
admin = $3
|
||||
WHERE user_id = $1;`;
|
||||
await database.executeQuery(query, [id, email, isAdmin]);
|
||||
}
|
||||
return new User(id, email, isAdmin);
|
||||
}
|
||||
|
||||
async function remove(id) {
|
||||
const query = `DELETE FROM accounts.users
|
||||
WHERE user_id = $1
|
||||
RETURNING email, admin;`;
|
||||
const row = (await database.executeQuery(query, [id]))[0];
|
||||
return new User(id, row[0], row[1]);
|
||||
}
|
||||
|
||||
async function retrieveAll() {
|
||||
const query = `SELECT user_id, email, admin
|
||||
FROM accounts.users
|
||||
ORDER BY email;`
|
||||
const table = await database.executeQuery(query);
|
||||
|
||||
const accountsList = [];
|
||||
table.forEach((row) => {
|
||||
accountsList.push(new User(row[0], row[1], row[2]));
|
||||
});
|
||||
return accountsList;
|
||||
}
|
||||
|
||||
async function getFromID(id) {
|
||||
const query = `SELECT user_id, email, admin
|
||||
FROM accounts.users
|
||||
WHERE user_id = $1;`;
|
||||
const row = (await database.executeQuery(query, [id]))[0];
|
||||
|
||||
return new User(id, row[1], row[2]);
|
||||
}
|
||||
|
||||
exports.create = create;
|
||||
exports.edit = edit;
|
||||
exports.remove = remove;
|
||||
exports.retrieveAll = retrieveAll;
|
||||
exports.getFromID = getFromID;
|
||||
exports.passport = passport;
|
|
@ -0,0 +1,12 @@
|
|||
function makeid(length) {
|
||||
var result = '';
|
||||
var characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
||||
var charactersLength = characters.length;
|
||||
for ( var i = 0; i < length; i++ ) {
|
||||
result += characters.charAt(Math.floor(Math.random() *
|
||||
charactersLength));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
exports.makeid = makeid;
|
|
@ -26,16 +26,14 @@ async function Initialize() {
|
|||
}
|
||||
|
||||
|
||||
|
||||
|
||||
async function checkForDatabaseInitialization() {
|
||||
const query = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`;
|
||||
let result = await executeQuery(query);
|
||||
const scoresSchemaExistsQuery = `SELECT schema_name FROM information_schema.schemata WHERE schema_name = 'scores'`;
|
||||
let result = await executeQuery(scoresSchemaExistsQuery);
|
||||
|
||||
const scoresSchemaExists = result.length !== 0;
|
||||
|
||||
if(!scoresSchemaExists) {
|
||||
Initialize();
|
||||
await Initialize();
|
||||
}
|
||||
}
|
||||
checkForDatabaseInitialization();
|
||||
|
|
|
@ -22,10 +22,7 @@ scores:
|
|||
accounts:
|
||||
|
||||
users:
|
||||
*user_id* | email | salt | password_hash | role | approved
|
||||
|
||||
sessions:
|
||||
*session_id* | ~user_id~ | expiration_date
|
||||
*user_id* | email | password | admin
|
||||
|
||||
*/
|
||||
|
||||
|
@ -33,6 +30,18 @@ accounts:
|
|||
BEGIN;
|
||||
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS accounts;
|
||||
|
||||
CREATE TABLE IF NOT EXISTS accounts.users(
|
||||
user_id BIGINT GENERATED ALWAYS AS IDENTITY,
|
||||
email TEXT UNIQUE NOT NULL,
|
||||
password TEXT NOT NULL,
|
||||
admin BOOLEAN NOT NULL DEFAULT FALSE,
|
||||
PRIMARY KEY(user_id)
|
||||
);
|
||||
|
||||
|
||||
|
||||
CREATE SCHEMA IF NOT EXISTS scores;
|
||||
|
||||
|
||||
|
@ -81,7 +90,7 @@ CREATE TABLE IF NOT EXISTS scores.games(
|
|||
team2_id BIGINT,
|
||||
team1_score INTEGER,
|
||||
team2_score INTEGER,
|
||||
/* submitter_id BIGINT,*/
|
||||
submitter_id BIGINT,
|
||||
updated_timestamp TIMESTAMP WITH TIME ZONE DEFAULT now(),
|
||||
PRIMARY KEY(game_id),
|
||||
CONSTRAINT fk_division
|
||||
|
@ -95,11 +104,10 @@ CREATE TABLE IF NOT EXISTS scores.games(
|
|||
REFERENCES scores.teams(team_id),
|
||||
CONSTRAINT fk_team2
|
||||
FOREIGN KEY(team2_id)
|
||||
REFERENCES scores.teams(team_id)
|
||||
/* CONSTRAINT fk_submitter
|
||||
REFERENCES scores.teams(team_id),
|
||||
CONSTRAINT fk_submitter
|
||||
FOREIGN KEY(submitter_id)
|
||||
REFERENCES accounts.users(user_id)*/
|
||||
REFERENCES accounts.users(user_id)
|
||||
);
|
||||
|
||||
|
||||
COMMIT;
|
|
@ -5,7 +5,7 @@ const database = require('./../database');
|
|||
|
||||
|
||||
class Game {
|
||||
constructor(id, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID) {
|
||||
constructor(id, date, team1ID, team2ID, team1Score, team2Score, divisionID, seasonID, submitterID) {
|
||||
this.id = id;
|
||||
this.date = date;
|
||||
this.team1ID = team1ID;
|
||||
|
@ -14,17 +14,18 @@ class Game {
|
|||
this.team2Score = team2Score;
|
||||
this.divisionID = divisionID;
|
||||
this.seasonID = seasonID;
|
||||
this.submitterID = submitterID;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) {
|
||||
const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score)
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7)
|
||||
async function add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID) {
|
||||
const query = `INSERT INTO scores.games(division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id)
|
||||
VALUES($1, $2, $3, $4, $5, $6, $7, $8)
|
||||
RETURNING game_id;`;
|
||||
|
||||
const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score]))[0][0];
|
||||
const id = (await database.executeQuery(query, [divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID]))[0][0];
|
||||
return new Game(id, date, team1ID, team2ID, team1Score, team2Score);
|
||||
}
|
||||
|
||||
|
@ -71,6 +72,20 @@ async function retrieve(teamID, divisionID, seasonID) {
|
|||
return gamesList;
|
||||
}
|
||||
|
||||
async function retrieveByUser(userID) {
|
||||
const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score
|
||||
FROM scores.games
|
||||
WHERE submitter_id = $1
|
||||
ORDER BY game_date DESC;`;
|
||||
const table = await database.executeQuery(query, [userID]);
|
||||
|
||||
const gamesList = [];
|
||||
table.forEach((row) => {
|
||||
gamesList.push(new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2]));
|
||||
});
|
||||
return gamesList;
|
||||
}
|
||||
|
||||
async function edit(gameID, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score) {
|
||||
const query = `UPDATE scores.games
|
||||
SET division_id = $2,
|
||||
|
@ -86,11 +101,11 @@ async function edit(gameID, divisionID, seasonID, date, team1ID, team2ID, team1S
|
|||
}
|
||||
|
||||
async function getFromID(gameID) {
|
||||
const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score
|
||||
const query = `SELECT game_id, division_id, season_id, game_date, team1_id, team2_id, team1_score, team2_score, submitter_id
|
||||
FROM scores.games
|
||||
WHERE game_id = $1;`;
|
||||
const row = (await database.executeQuery(query, [gameID]))[0];
|
||||
return new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2]);
|
||||
return new Game(row[0], row[3].toISOString().slice(0,10), row[4], row[5], row[6], row[7], row[1], row[2], row[8]);
|
||||
}
|
||||
|
||||
|
||||
|
@ -100,5 +115,6 @@ async function getFromID(gameID) {
|
|||
exports.add = add;
|
||||
exports.remove = remove;
|
||||
exports.retrieve = retrieve;
|
||||
exports.retrieveByUser = retrieveByUser;
|
||||
exports.edit = edit;
|
||||
exports.getFromID = getFromID;
|
|
@ -69,7 +69,6 @@ async function getFromID(id) {
|
|||
FROM scores.teams
|
||||
WHERE team_id = $1;`;
|
||||
const row = (await database.executeQuery(query, [id]))[0];
|
||||
console.log(row);
|
||||
return new Team(id, row[0], row[1]);
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -8,14 +8,18 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"async": "^3.2.2",
|
||||
"bcrypt": "^5.0.1",
|
||||
"connect-flash": "^0.1.1",
|
||||
"cookie-parser": "~1.4.3",
|
||||
"debug": "~2.6.9",
|
||||
"dotenv": "^10.0.0",
|
||||
"express": "~4.16.0",
|
||||
"express-session": "^1.17.2",
|
||||
"http-errors": "~1.6.2",
|
||||
"morgan": "~1.9.0",
|
||||
"nodemailer": "^6.6.5",
|
||||
"passport": "^0.5.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"pg": "^8.7.1",
|
||||
"pug": "2.0.0-beta11"
|
||||
},
|
||||
|
|
|
@ -65,8 +65,27 @@ export async function getGames(teamID = undefined, divisionID = undefined, seaso
|
|||
return gamesList;
|
||||
}
|
||||
|
||||
export async function getGamesByUser() {
|
||||
let URL = '/data/games?user=1';
|
||||
const response = await fetch(URL);
|
||||
const gamesList = await response.json();
|
||||
return gamesList;
|
||||
}
|
||||
|
||||
export async function getGame(gameID) {
|
||||
const response = await fetch(`/data/game?game=${gameID}`);
|
||||
const game = await response.json();
|
||||
return game;
|
||||
}
|
||||
|
||||
export async function getAccounts() {
|
||||
const response = await fetch(`/data/accounts`);
|
||||
const accounts = await response.json();
|
||||
return accounts;
|
||||
}
|
||||
|
||||
export async function getAccount(accountID) {
|
||||
const response = await fetch(`/data/account?account=${accountID}`);
|
||||
const account = await response.json();
|
||||
return account;
|
||||
}
|
|
@ -177,11 +177,14 @@ genderDropdown.onchange = listDivisions;
|
|||
teamDropdown.onchange = listGames;
|
||||
seasonDropdown.onchange = listGames;
|
||||
|
||||
if(addScoreButton) {
|
||||
addScoreButton.addEventListener('click', () => {
|
||||
window.location.href = '/manage/game';
|
||||
});
|
||||
}
|
||||
|
||||
addScoreButton.addEventListener('click', () => {
|
||||
window.location.href = '/manage/game';
|
||||
});
|
||||
|
||||
manageButton.addEventListener('click', () => {
|
||||
window.location.href = '/manage'
|
||||
});
|
||||
if(manageButton) {
|
||||
manageButton.addEventListener('click', () => {
|
||||
window.location.href = '/manage'
|
||||
});
|
||||
}
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
const logInButton = document.getElementById('login-button');
|
||||
const logOutButton = document.getElementById('logout-button');
|
||||
const homeButton = document.getElementById('home-button');
|
||||
|
||||
if(logInButton) {
|
||||
logInButton.addEventListener('click', () => {
|
||||
window.location.href = "/auth/login";
|
||||
});
|
||||
}
|
||||
|
||||
if(logOutButton) {
|
||||
logOutButton.addEventListener('click', () => {
|
||||
window.location.href = "/auth/logout";
|
||||
});
|
||||
}
|
||||
|
||||
if(homeButton) {
|
||||
homeButton.addEventListener('click', () => {
|
||||
window.location.href = '/';
|
||||
});
|
||||
}
|
|
@ -294,6 +294,49 @@ CATEGORIES.push(new Category(
|
|||
}
|
||||
));
|
||||
|
||||
CATEGORIES.push(new Category(
|
||||
"accounts",
|
||||
async function getAccounts() {
|
||||
return await Data.getAccounts();
|
||||
},
|
||||
async function listAccountHeaders() {
|
||||
const headerRow = document.createElement('tr');
|
||||
|
||||
const emailHeader = document.createElement('th');
|
||||
emailHeader.textContent = "Email";
|
||||
headerRow.appendChild(emailHeader);
|
||||
|
||||
const spacerHeader = document.createElement('th');
|
||||
spacerHeader.classList.add('spacer-column');
|
||||
headerRow.appendChild(spacerHeader);
|
||||
|
||||
const adminHeader = document.createElement('th');
|
||||
adminHeader.textContent = "Admin?";
|
||||
headerRow.appendChild(adminHeader);
|
||||
|
||||
itemsListTable.appendChild(headerRow);
|
||||
},
|
||||
function listAccount(account, row) {
|
||||
const emailCell = document.createElement('td');
|
||||
emailCell.textContent = account.email;
|
||||
row.appendChild(emailCell);
|
||||
|
||||
const spacerCell = document.createElement('td');
|
||||
row.appendChild(spacerCell);
|
||||
|
||||
const adminCell = document.createElement('td');
|
||||
adminCell.textContent = account.isAdmin;
|
||||
row.appendChild(adminCell);
|
||||
},
|
||||
async function addAccount() {
|
||||
window.location.href = "/manage/account";
|
||||
},
|
||||
async function editAccount(id) {
|
||||
window.location.href = `/manage/account?account=${id}`;
|
||||
}
|
||||
));
|
||||
|
||||
|
||||
|
||||
|
||||
async function listItems(category) {
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
import * as Data from "../data.js";
|
||||
import * as Form from "../form.js";
|
||||
|
||||
const submissionForm = document.getElementById('submission-form');
|
||||
const emailTextbox = document.getElementById('email-textbox');
|
||||
const passwordTextbox = document.getElementById('password-textbox');
|
||||
const adminCheckboxSection = document.getElementById('admin-checkbox-section');
|
||||
const adminCheckbox = document.getElementById('admin-checkbox');
|
||||
const submitButton = document.getElementById('submit-button');
|
||||
const deleteButton = document.getElementById('delete-button');
|
||||
|
||||
async function Initialize() {
|
||||
let params = new URLSearchParams(location.search);
|
||||
let accountID = params.get('account') || (document.getElementById('account-id') ? document.getElementById('account-id').value : null);
|
||||
if(accountID) {
|
||||
const account = await Data.getAccount(accountID);
|
||||
console.log(account);
|
||||
|
||||
emailTextbox.value = account.email;
|
||||
|
||||
passwordTextbox.placeholder = "leave unchanged";
|
||||
|
||||
adminCheckbox.checked = account.isAdmin;
|
||||
|
||||
if(!document.getElementById('account-id')) {
|
||||
adminCheckboxSection.style.visibility = "visible";
|
||||
adminCheckbox.disabled = false;
|
||||
|
||||
Form.addHiddenValue('account', accountID, submissionForm);
|
||||
}
|
||||
|
||||
deleteButton.style.visibility = "visible";
|
||||
deleteButton.disabled = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
adminCheckboxSection.style.visibility = "visible";
|
||||
adminCheckbox.disabled = false;
|
||||
}
|
||||
emailTextbox.disabled = false;
|
||||
emailTextbox.addEventListener('keyup', checkDataValidity);
|
||||
passwordTextbox.disabled = false;
|
||||
passwordTextbox.addEventListener('keyup', checkDataValidity);
|
||||
checkDataValidity();
|
||||
}
|
||||
Initialize();
|
||||
|
||||
async function checkDataValidity() {
|
||||
let dataIsValid = true;
|
||||
|
||||
if(!passwordTextbox.value && !passwordTextbox.placeholder) dataIsValid = false;
|
||||
if(!emailTextbox.value) dataIsValid = false;
|
||||
|
||||
if(dataIsValid) submitButton.disabled = false;
|
||||
else submitButton.disabled = true;
|
||||
}
|
||||
|
||||
Form.addRemoveFunction(deleteButton, submissionForm, "account");
|
|
@ -25,6 +25,8 @@ async function initializeForm() {
|
|||
|
||||
const game = await Data.getGame(gameID);
|
||||
|
||||
Form.addHiddenValue('game', gameID, submissionForm);
|
||||
|
||||
Form.populateSeasons(seasonDropdown, game.seasonID);
|
||||
Data.getDivision(game.divisionID)
|
||||
.then(data => {
|
||||
|
@ -65,6 +67,19 @@ async function initializeForm() {
|
|||
team1ScoreTextbox.disabled = false;
|
||||
team2ScoreTextbox.disabled = false;
|
||||
submitButton.disabled = false;
|
||||
|
||||
sportDropdown.onchange = () => {
|
||||
Form.populateGenders(genderDropdown, sportDropdown.value)
|
||||
.then(() => {
|
||||
Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value);
|
||||
});
|
||||
Form.populateTeams(team1Dropdown, sportDropdown.value);
|
||||
Form.populateTeams(team2Dropdown, sportDropdown.value);
|
||||
};
|
||||
|
||||
genderDropdown.onchange = () => {
|
||||
Form.populateDivisions(divisionDropdown, sportDropdown.value, genderDropdown.value);
|
||||
};
|
||||
}
|
||||
initializeForm();
|
||||
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
import * as Data from "./../data.js";
|
||||
|
||||
const gamesListTable = document.getElementById('games-list');
|
||||
const addNewButton = document.getElementById('add-new-button');
|
||||
const manageAccountButton = document.getElementById('manage-account-button');
|
||||
|
||||
|
||||
|
||||
function getGenderLetter(genderName) {
|
||||
return genderName == "female" ? "F" : "M";
|
||||
}
|
||||
|
||||
async function listGameHeaders() {
|
||||
const headerRow = document.createElement('tr');
|
||||
|
||||
const teamsHeader = document.createElement('th');
|
||||
teamsHeader.textContent = "Teams";
|
||||
headerRow.appendChild(teamsHeader);
|
||||
|
||||
const scoreHeader = document.createElement('th');
|
||||
headerRow.appendChild(scoreHeader);
|
||||
|
||||
const spacerHeader = document.createElement('th');
|
||||
spacerHeader.classList.add('spacer-column');
|
||||
headerRow.appendChild(spacerHeader);
|
||||
|
||||
const sportNameHeader = document.createElement('th');
|
||||
sportNameHeader.textContent = "Sport";
|
||||
headerRow.appendChild(sportNameHeader);
|
||||
|
||||
const dateHeader = document.createElement('th');
|
||||
dateHeader.textContent = "Date";
|
||||
headerRow.appendChild(dateHeader);
|
||||
|
||||
gamesListTable.appendChild(headerRow);
|
||||
}
|
||||
|
||||
|
||||
function listGame(game, row) {
|
||||
const teamsCell = document.createElement('td');
|
||||
const team1NameSpan = document.createElement('span');
|
||||
Data.getTeam(game.team1ID)
|
||||
.then(data => team1NameSpan.textContent = data.name);
|
||||
teamsCell.appendChild(team1NameSpan);
|
||||
const team2NameSpan = document.createElement('span');
|
||||
Data.getTeam(game.team2ID)
|
||||
.then(data => team2NameSpan.textContent = data.name);
|
||||
teamsCell.appendChild(team2NameSpan);
|
||||
row.appendChild(teamsCell);
|
||||
|
||||
const scoresCell = document.createElement('td');
|
||||
const team1ScoreSpan = document.createElement('span');
|
||||
team1ScoreSpan.textContent = game.team1Score;
|
||||
scoresCell.appendChild(team1ScoreSpan);
|
||||
const team2ScoreSpan = document.createElement('span');
|
||||
team2ScoreSpan.textContent = game.team2Score;
|
||||
scoresCell.appendChild(team2ScoreSpan);
|
||||
row.appendChild(scoresCell);
|
||||
|
||||
const spacerCell = document.createElement('td');
|
||||
row.appendChild(spacerCell);
|
||||
|
||||
const sportCell = document.createElement('td');
|
||||
const sportSpan = document.createElement('span');
|
||||
const divisionSpan = document.createElement('span');
|
||||
divisionSpan.classList.add('flat-content');
|
||||
const divisionNameSpan = document.createElement('span');
|
||||
const divisionGenderSpan = document.createElement('span');
|
||||
divisionSpan.appendChild(divisionNameSpan);
|
||||
divisionSpan.appendChild(divisionGenderSpan);
|
||||
Data.getDivision(game.divisionID)
|
||||
.then(data => {
|
||||
Data.getSportName(data.sportID)
|
||||
.then(data => sportSpan.textContent = data);
|
||||
divisionNameSpan.textContent = data.name;
|
||||
divisionGenderSpan.textContent = getGenderLetter(data.gender.name);
|
||||
});
|
||||
sportCell.appendChild(sportSpan);
|
||||
sportCell.appendChild(divisionSpan);
|
||||
row.appendChild(sportCell);
|
||||
|
||||
const dateCell = document.createElement('td');
|
||||
const yearSpan = document.createElement('span');
|
||||
yearSpan.textContent = game.date.slice(0,4);
|
||||
dateCell.appendChild(yearSpan);
|
||||
const dateSpan = document.createElement('span');
|
||||
dateSpan.textContent = game.date.slice(5);
|
||||
dateCell.appendChild(dateSpan);
|
||||
row.appendChild(dateCell);
|
||||
}
|
||||
|
||||
async function addGame() {
|
||||
window.location.href = "/manage/game";
|
||||
}
|
||||
|
||||
async function editGame(id) {
|
||||
window.location.href = `/manage/game?game=${id}`;
|
||||
}
|
||||
|
||||
|
||||
async function listItems() {
|
||||
const gamesList = await Data.getGamesByUser();
|
||||
|
||||
await listGameHeaders();
|
||||
|
||||
gamesList.forEach(game => {
|
||||
const row = document.createElement('tr');
|
||||
|
||||
listGame(game, row);
|
||||
|
||||
const manageCell = document.createElement('td');
|
||||
|
||||
const editSpan = document.createElement('span');
|
||||
const editButton = document.createElement('button');
|
||||
editButton.textContent = "E";
|
||||
editButton.addEventListener('click', () => {
|
||||
editGame(game.id);
|
||||
});
|
||||
editSpan.appendChild(editButton);
|
||||
manageCell.appendChild(editSpan);
|
||||
|
||||
row.appendChild(manageCell);
|
||||
|
||||
gamesListTable.appendChild(row);
|
||||
});
|
||||
}
|
||||
listItems();
|
||||
|
||||
addNewButton.addEventListener('click', () => addGame());
|
||||
manageAccountButton.addEventListener('click', () => {
|
||||
window.location.href = '/manage/account';
|
||||
});
|
|
@ -21,7 +21,6 @@ async function initializeForm() {
|
|||
|
||||
Form.populateSports(sportDropdown, team.sportID);
|
||||
|
||||
console.log(team.sportID);
|
||||
Form.addHiddenValue('team', teamID, submissionForm);
|
||||
}
|
||||
else {
|
||||
|
|
|
@ -36,3 +36,12 @@ form {
|
|||
#delete-button {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.form-section-checkbox {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#admin-checkbox {
|
||||
width: auto;
|
||||
}
|
|
@ -26,7 +26,5 @@ tr {
|
|||
}
|
||||
|
||||
#actions-div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
margin-left: auto;
|
||||
}
|
|
@ -17,4 +17,17 @@ a {
|
|||
|
||||
.flat-content {
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.send-to-right {
|
||||
margin-left: auto;
|
||||
}
|
||||
|
||||
#actions-div {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#home-button {
|
||||
margin-right: auto;
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
h1 {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
#admin-checkbox-section {
|
||||
visibility: hidden;
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
var express = require('express');
|
||||
var router = express.Router();
|
||||
const passport = require('passport');
|
||||
const app = require('../app');
|
||||
const accounts = require('./../database/accounts/accounts');
|
||||
|
||||
|
||||
function adminLoggedIn(req, res, next) {
|
||||
if (req.user && req.user[2]) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
req.flash('error', 'An admin account is required to access this page.');
|
||||
res.redirect('/auth/login');
|
||||
}
|
||||
}
|
||||
|
||||
router.get('/', adminLoggedIn, (req, res, next) => {
|
||||
res.render
|
||||
});
|
||||
|
||||
|
||||
|
||||
module.exports = router;
|
|
@ -0,0 +1,39 @@
|
|||
var express = require('express');
|
||||
var router = express.Router();
|
||||
const passport = require('passport');
|
||||
const accounts = require('./../database/accounts/accounts');
|
||||
const app = require('../app');
|
||||
|
||||
|
||||
function adminLoggedIn(req, res, next) {
|
||||
if (req.user && req.user[2]) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
req.flash('error', 'An admin account is required to access this page.');
|
||||
res.redirect('/auth/login');
|
||||
}
|
||||
}
|
||||
|
||||
router.get('/login', (req, res, next) => {
|
||||
res.render('accounts/login', { title : "Login", message: req.flash('error') });
|
||||
});
|
||||
|
||||
router.get('/logout', (req, res, next) => {
|
||||
req.logout();
|
||||
res.redirect("/");
|
||||
});
|
||||
|
||||
router.post('/login',
|
||||
passport.authenticate('local', {
|
||||
failureRedirect: '/auth/login',
|
||||
successRedirect: '/',
|
||||
failureFlash: "Invalid email or password.",
|
||||
}),
|
||||
(req, res, next) => {
|
||||
console.log(req.user);
|
||||
});
|
||||
|
||||
|
||||
|
||||
module.exports = router;
|
|
@ -6,7 +6,26 @@ 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');
|
||||
|
||||
function adminLoggedIn(req, res, next) {
|
||||
if (req.user && req.user[2]) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
req.flash('error', 'An admin account is required to access this page.');
|
||||
res.redirect('/auth/login');
|
||||
}
|
||||
}
|
||||
|
||||
function userLoggedIn(req, res, next) {
|
||||
if (req.user) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
res.redirect('/auth/login');
|
||||
}
|
||||
}
|
||||
|
||||
router.get('/sports', function(req, res, next) {
|
||||
sports.retrieveAll()
|
||||
|
@ -52,8 +71,9 @@ router.get('/team', function(req, res, next) {
|
|||
})
|
||||
|
||||
router.get('/games', function(req, res, next) {
|
||||
games.retrieve(req.query.team, req.query.division, req.query.season)
|
||||
.then(data => res.json(data));
|
||||
const userID = req.user ? req.user[0] : null;
|
||||
if(req.query.user) games.retrieveByUser(userID).then(data => res.json(data));
|
||||
else games.retrieve(req.query.team, req.query.division, req.query.season).then(data => res.json(data));
|
||||
})
|
||||
|
||||
router.get('/game', function(req, res, next) {
|
||||
|
@ -61,4 +81,22 @@ router.get('/game', function(req, res, next) {
|
|||
.then(data => res.json(data));
|
||||
})
|
||||
|
||||
router.get('/accounts', adminLoggedIn, function(req, res, next) {
|
||||
accounts.retrieveAll()
|
||||
.then(data => res.json(data));
|
||||
})
|
||||
|
||||
router.get('/account', userLoggedIn, function(req, res, next) {
|
||||
const userIsAdmin = req.user[2];
|
||||
const loggedInAccountID = req.user[0];
|
||||
const requestedAccountID = req.query.account;
|
||||
|
||||
if(!userIsAdmin && loggedInAccountID != requestedAccountID) {
|
||||
res.status(403).send("ACCESS DENIED");
|
||||
} else {
|
||||
accounts.getFromID(req.query.account)
|
||||
.then(data => res.json(data));
|
||||
}
|
||||
})
|
||||
|
||||
module.exports = router;
|
|
@ -3,7 +3,7 @@ var router = express.Router();
|
|||
|
||||
/* GET home page. */
|
||||
router.get('/', function(req, res, next) {
|
||||
res.render('index', { title: 'View Scores' });
|
||||
res.render('index', { title: 'View Scores', userLoggedIn: !!req.user, hideHomeButton: true });
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
|
121
routes/manage.js
121
routes/manage.js
|
@ -7,19 +7,40 @@ var sports = require('../database/scores/sports');
|
|||
var divisions = require('../database/scores/divisions');
|
||||
var genders = require('../database/scores/genders');
|
||||
var teams = require('../database/scores/teams');
|
||||
var accounts = require('../database/accounts/accounts');
|
||||
|
||||
function userLoggedIn(req, res, next) {
|
||||
if (req.user) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
res.redirect('/auth/login');
|
||||
}
|
||||
}
|
||||
|
||||
function adminLoggedIn(req, res, next) {
|
||||
if (req.user && req.user[2]) {
|
||||
next();
|
||||
}
|
||||
else {
|
||||
req.flash('error', 'An admin account is required to access this page.');
|
||||
res.redirect('/auth/login');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
router.get('/', function(req, res, next) {
|
||||
res.render('manage', { title: 'Score Management' });
|
||||
router.get('/' ,userLoggedIn, function(req, res, next) {
|
||||
if(req.user[2]) res.render('manage', { title: 'Score Management', userLoggedIn: !!req.user });
|
||||
else res.render('manage/manage-nonadmin', { title: "My Games", userLoggedIn: !!req.user });
|
||||
});
|
||||
|
||||
router.get('/game', function(req, res, next) {
|
||||
router.get('/game', userLoggedIn, function(req, res, next) {
|
||||
let title = req.query.game ? 'Edit Game' : 'Submit Score'
|
||||
|
||||
res.render('manage/addgame', { title });
|
||||
res.render('manage/addgame', { title, userLoggedIn: !!req.user });
|
||||
});
|
||||
|
||||
router.post('/game', function(req, res, next) {
|
||||
router.post('/game', userLoggedIn, function(req, res, next) {
|
||||
const seasonID = req.body['year'];
|
||||
const sportID = req.body['sport'];
|
||||
const gender = (req.body['gender'] == "female") ? genders.FEMALE : genders.MALE;
|
||||
|
@ -29,23 +50,33 @@ router.post('/game', function(req, res, next) {
|
|||
const team1Score = req.body['team1-score'];
|
||||
const team2ID = req.body['team2'];
|
||||
const team2Score = req.body['team2-score'];
|
||||
const userID = req.user[0];
|
||||
|
||||
const id = req.body['game'];
|
||||
const remove = req.body['delete'];
|
||||
const remove = req.body['remove'];
|
||||
|
||||
if(remove) games.remove(id)
|
||||
.then(res.redirect("/manage"));
|
||||
else if(id) games.edit(id, divisionId, seasonID, date, team1ID, team2ID, team1Score, team2Score)
|
||||
.then(res.redirect('/manage'));
|
||||
else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score)
|
||||
.then(res.redirect("/manage"));
|
||||
const loggedInUserID = req.user[0];
|
||||
const loggedInUserIsAdmin = req.user[2];
|
||||
|
||||
games.getFromID(id)
|
||||
.then(game => {
|
||||
if(!loggedInUserIsAdmin && loggedInUserID != game.submitterID) {
|
||||
res.status(403).send("ACCESS DENIED");
|
||||
}
|
||||
else if(remove) games.remove(id)
|
||||
.then(res.redirect("/manage"));
|
||||
else if(id) games.edit(id, divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score)
|
||||
.then(res.redirect('/manage'));
|
||||
else games.add(divisionID, seasonID, date, team1ID, team2ID, team1Score, team2Score, userID)
|
||||
.then(res.redirect("/manage"));
|
||||
});
|
||||
});
|
||||
|
||||
router.get('/season', function(req, res, next) {
|
||||
res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear() });
|
||||
router.get('/season', adminLoggedIn, function(req, res, next) {
|
||||
res.render('manage/addseason', { title: 'Add Season', currentYear : (new Date()).getFullYear(), userLoggedIn: !!req.user });
|
||||
});
|
||||
|
||||
router.post('/season', function(req, res, next) {
|
||||
router.post('/season', adminLoggedIn, function(req, res, next) {
|
||||
const year = req.body['year'];
|
||||
|
||||
const seasonID = req.body['season'];
|
||||
|
@ -55,11 +86,11 @@ router.post('/season', function(req, res, next) {
|
|||
else seasons.add(year).then(res.redirect("/manage"));
|
||||
});
|
||||
|
||||
router.get('/sport', function(req, res, next) {
|
||||
res.render('manage/addsport', { title: 'Add Sport' });
|
||||
router.get('/sport', adminLoggedIn, function(req, res, next) {
|
||||
res.render('manage/addsport', { title: 'Add Sport', userLoggedIn: !!req.user });
|
||||
});
|
||||
|
||||
router.post('/sport', function(req, res, next) {
|
||||
router.post('/sport', adminLoggedIn, function(req, res, next) {
|
||||
const name = req.body['name'];
|
||||
const id = req.body['sport'];
|
||||
const remove = req.body['remove'];
|
||||
|
@ -69,13 +100,13 @@ router.post('/sport', function(req, res, next) {
|
|||
else sports.add(name).then(res.redirect('/manage'));
|
||||
});
|
||||
|
||||
router.get('/division', function(req, res, next) {
|
||||
router.get('/division', adminLoggedIn, function(req, res, next) {
|
||||
let title = req.query.division ? 'Edit Division' : 'Add Division'
|
||||
|
||||
res.render('manage/adddivision', { title });
|
||||
res.render('manage/adddivision', { title, userLoggedIn: !!req.user });
|
||||
});
|
||||
|
||||
router.post('/division', function(req, res, next) {
|
||||
router.post('/division', adminLoggedIn, function(req, res, next) {
|
||||
const name = req.body['name'];
|
||||
const sport = req.body['sport'];
|
||||
const genderName = req.body['gender'];
|
||||
|
@ -100,13 +131,13 @@ router.post('/division', function(req, res, next) {
|
|||
}
|
||||
});
|
||||
|
||||
router.get('/team', function(req, res, next) {
|
||||
router.get('/team', adminLoggedIn, function(req, res, next) {
|
||||
let title = req.query.team ? 'Edit Team' : 'Add Team'
|
||||
|
||||
res.render('manage/addteam', { title });
|
||||
res.render('manage/addteam', { title, userLoggedIn: !!req.user });
|
||||
});
|
||||
|
||||
router.post('/team', function(req, res, next) {
|
||||
router.post('/team', adminLoggedIn, function(req, res, next) {
|
||||
const name = req.body['name'];
|
||||
const sport = req.body['sport'];
|
||||
|
||||
|
@ -118,4 +149,46 @@ router.post('/team', function(req, res, next) {
|
|||
else teams.add(name, sport).then(res.redirect("/manage"));
|
||||
});
|
||||
|
||||
router.get('/account', userLoggedIn, (req, res, next) => {
|
||||
const userIsAdmin = req.user[2];
|
||||
const accountID = req.user[0];
|
||||
|
||||
if(userIsAdmin) {
|
||||
let title = req.query.account ? 'Manage User' : 'Create User'
|
||||
|
||||
res.render('accounts/createuser', { title, userLoggedIn: !!req.user });
|
||||
}
|
||||
else {
|
||||
let title = 'Manage Account';
|
||||
|
||||
res.render('accounts/createuser', { title, accountID, userLoggedIn: !!req.user });
|
||||
}
|
||||
});
|
||||
|
||||
router.post('/account', userLoggedIn, (req, res, next) => {
|
||||
const email = req.body.email;
|
||||
const password = req.body.password;
|
||||
|
||||
const accountID = req.body.account;
|
||||
const remove = req.body.remove;
|
||||
|
||||
const loggedInAccountIsAdmin = req.user[2];
|
||||
const loggedInAccountID = req.user[0];
|
||||
|
||||
console.log(accountID);
|
||||
console.log(loggedInAccountID);
|
||||
|
||||
|
||||
if(!loggedInAccountIsAdmin && accountID != loggedInAccountID) {
|
||||
res.status(403).send("ACCESS DENIED");
|
||||
}
|
||||
else {
|
||||
const isAdmin = loggedInAccountIsAdmin ? !!req.body.admin : false;
|
||||
|
||||
if(remove) accounts.remove(accountID).then(res.redirect('/manage'));
|
||||
if(accountID) accounts.edit(accountID, email, password, isAdmin).then(res.redirect('/manage'));
|
||||
else accounts.create(req.body.email, req.body.password, !!req.body.admin).then(res.redirect('/manage'));
|
||||
}
|
||||
});
|
||||
|
||||
module.exports = router;
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
extends ../layout
|
||||
|
||||
block stylesheets
|
||||
link(rel='stylesheet', href='/stylesheets/submit.css')
|
||||
link(rel='stylesheet', href='/stylesheets/form.css')
|
||||
|
||||
block content
|
||||
form#submission-form(action='/manage/account', method='POST')
|
||||
if accountID
|
||||
input#account-id(type="hidden" name="account" value=accountID)
|
||||
span(class='form-section')
|
||||
label Email
|
||||
span(class='form-section-input')
|
||||
input#email-textbox(type="email", name="email" disabled)
|
||||
span(class='form-section')
|
||||
label Password
|
||||
span(class='form-section-input' )
|
||||
input#password-textbox(type="password" name="password" disabled)
|
||||
span#admin-checkbox-section(class='form-section')
|
||||
span(class='form-section-checkbox')
|
||||
input#admin-checkbox(type="checkbox" name="admin" disabled)
|
||||
label(for="admin-checkbox") Grant admin privileges
|
||||
span(class='form-section')
|
||||
button#submit-button(type="submit" disabled) Submit
|
||||
span(class='form-section')
|
||||
button#delete-button(type="delete" disabled) Delete
|
||||
|
||||
block scripts
|
||||
script(src='/scripts/manage/account.js' type="module")
|
|
@ -0,0 +1,19 @@
|
|||
extends ../layout
|
||||
|
||||
block stylesheets
|
||||
link(rel='stylesheet', href='/stylesheets/submit.css')
|
||||
link(rel='stylesheet', href='/stylesheets/form.css')
|
||||
|
||||
block content
|
||||
form(action='/auth/login', method='POST')
|
||||
span(class='form-section')
|
||||
label Email
|
||||
span(class='form-section-input')
|
||||
input(type="email", name="email")
|
||||
span(class='form-section')
|
||||
label Password
|
||||
span(class='form-section-input')
|
||||
input(type="password", name="password")
|
||||
.error #{message}
|
||||
span(class='form-section')
|
||||
button#submit-button(type="submit") Submit
|
|
@ -4,13 +4,13 @@ block stylesheets
|
|||
link(rel='stylesheet', href='/stylesheets/index.css')
|
||||
link(rel='stylesheet', href='/stylesheets/form.css')
|
||||
|
||||
block actions
|
||||
if userLoggedIn
|
||||
button#add-score-button Submit score
|
||||
button#manage-button Manage...
|
||||
|
||||
|
||||
block content
|
||||
div#mobile-view
|
||||
div#header-div
|
||||
h1 Score Tracker
|
||||
div#actions-div
|
||||
button#add-score-button +
|
||||
button#manage-button Manage
|
||||
div
|
||||
span(class='form-section')
|
||||
label Year
|
||||
|
|
|
@ -1,10 +1,21 @@
|
|||
doctype html
|
||||
html
|
||||
head
|
||||
title= title
|
||||
title= title + ' - Score Tracker'
|
||||
meta(name='viewport', content='width=device-width, initial-scale=1')
|
||||
link(rel='stylesheet', href='/stylesheets/style.css')
|
||||
block stylesheets
|
||||
body
|
||||
block content
|
||||
div#mobile-view
|
||||
div#actions-div
|
||||
if !hideHomeButton
|
||||
button#home-button Home
|
||||
block actions
|
||||
if userLoggedIn
|
||||
button#logout-button Log out
|
||||
else if userLoggedIn !== undefined
|
||||
button#login-button Log in
|
||||
h1 #{title}
|
||||
block content
|
||||
block scripts
|
||||
script(src='/scripts/main.js' type="module")
|
||||
|
|
|
@ -5,8 +5,6 @@ block stylesheets
|
|||
link(rel='stylesheet', href='/stylesheets/form.css')
|
||||
|
||||
block content
|
||||
div#mobile-view
|
||||
h1 Management Panel
|
||||
div
|
||||
span(class='form-section')
|
||||
label Category
|
||||
|
@ -17,6 +15,7 @@ block content
|
|||
option(value="divisions") Divisions
|
||||
option(value="teams") Teams
|
||||
option(value="games") Games
|
||||
option(value="accounts") Accounts
|
||||
div
|
||||
h2#table-header
|
||||
table#items-list
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
extends layout
|
||||
extends ../layout
|
||||
|
||||
block stylesheets
|
||||
link(rel='stylesheet', href='/stylesheets/submit.css')
|
||||
link(rel='stylesheet', href='/stylesheets/form.css')
|
||||
|
||||
block content
|
||||
div#mobile-view
|
||||
h1 #{title}
|
||||
form#submission-form(action='./division', method='POST')
|
||||
span(class='form-section')
|
||||
label Sport
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
extends layout
|
||||
extends ../layout
|
||||
|
||||
block stylesheets
|
||||
link(rel='stylesheet', href='/stylesheets/submit.css')
|
||||
link(rel='stylesheet', href='/stylesheets/form.css')
|
||||
|
||||
block content
|
||||
div#mobile-view
|
||||
h1 #{title}
|
||||
form#submission-form(action='./submitgame', method='POST')
|
||||
form#submission-form(action='./game', method='POST')
|
||||
span(class='form-section')
|
||||
label Year
|
||||
span(class='form-section-input')
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
extends layout
|
||||
extends ../layout
|
||||
|
||||
block stylesheets
|
||||
link(rel='stylesheet', href='/stylesheets/submit.css')
|
||||
link(rel='stylesheet', href='/stylesheets/form.css')
|
||||
|
||||
block content
|
||||
div#mobile-view
|
||||
h1 #{title}
|
||||
form(action='./season', method='POST')
|
||||
span(class='form-section')
|
||||
label Ending year
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
extends layout
|
||||
extends ../layout
|
||||
|
||||
block stylesheets
|
||||
link(rel='stylesheet', href='/stylesheets/submit.css')
|
||||
link(rel='stylesheet', href='/stylesheets/form.css')
|
||||
|
||||
block content
|
||||
div#mobile-view
|
||||
h1#main-header Add Sport
|
||||
form#submission-form(action='./sport', method='POST')
|
||||
span(class='form-section')
|
||||
label Sport name
|
||||
|
|
|
@ -1,12 +1,10 @@
|
|||
extends layout
|
||||
extends ../layout
|
||||
|
||||
block stylesheets
|
||||
link(rel='stylesheet', href='/stylesheets/submit.css')
|
||||
link(rel='stylesheet', href='/stylesheets/form.css')
|
||||
|
||||
block content
|
||||
div#mobile-view
|
||||
h1 #{title}
|
||||
form#submission-form(action='./team', method='POST')
|
||||
span(class='form-section')
|
||||
label Sport
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
extends ../layout
|
||||
|
||||
block stylesheets
|
||||
link(rel='stylesheet', href='/stylesheets/manage.css')
|
||||
link(rel='stylesheet', href='/stylesheets/form.css')
|
||||
|
||||
block actions
|
||||
button#manage-account-button(class="send-to-right") Manage account
|
||||
|
||||
block content
|
||||
div
|
||||
table#games-list
|
||||
div
|
||||
button#add-new-button Add new...
|
||||
|
||||
block scripts
|
||||
script(src='/scripts/manage/manage-nonadmin.js' type="module")
|
Reference in New Issue