diff --git a/public/assets/css/style.css b/public/assets/css/style.css index 3a032e5..f81e833 100644 --- a/public/assets/css/style.css +++ b/public/assets/css/style.css @@ -89,6 +89,22 @@ body { margin-top: 30px; } +.info-container { + display: flex; + flex-direction: row; + gap: 15px +} + +.info { + display: flex; + flex-direction: row; + gap: 5px; +} + +.info p { + min-width: 0px; +} + form { display: flex; flex-direction: column; @@ -100,6 +116,43 @@ form { gap: 5px; } +.success { + display: none; + filter: hue-rotate(110deg); + flex-direction: column; + align-items: center; + border: 2px solid #a34b4b; + background-color: #3a1414; + border-radius: 10px; + padding: 5px; + padding-left: 10px; + padding-right: 10px; + margin-top: -10px; + gap: 5px; +} + +.success p { + color: #ff7777; +} + +.error { + display: none; + flex-direction: column; + align-items: center; + border: 2px solid #a34b4b; + background-color: #3a1414; + border-radius: 10px; + padding: 5px; + padding-left: 10px; + padding-right: 10px; + margin-top: -10px; + gap: 5px; +} + +.error p { + color: #ff7777; +} + .loginForm input { margin: 5px; } diff --git a/public/client.js b/public/client.js index 0b6605b..db37f59 100644 --- a/public/client.js +++ b/public/client.js @@ -16,7 +16,6 @@ export async function postData(url, data, token = null) { const resultObject = await res.json(); if (resultObject.logout === true) { - alert("Session expired; please log in again."); window.location.href = "/login"; return resultObject; } diff --git a/public/info.html b/public/info.html index e9bdf47..301958b 100644 --- a/public/info.html +++ b/public/info.html @@ -27,6 +27,13 @@ +
+

Error Message

+
+
+

Information updated successfully

+
+ Return Home diff --git a/public/info.js b/public/info.js index 82621a0..594c3fb 100644 --- a/public/info.js +++ b/public/info.js @@ -5,6 +5,11 @@ const token = window.localStorage.getItem("token"); const infoForm = document.getElementById("infoForm"); +const error = document.querySelector(".error"); +const errorMessage = document.querySelector(".error p"); +const success = document.querySelector(".success"); + + async function fetchPersonalInformation() { const userData = await postData("/getInfo", {}, token); @@ -30,7 +35,17 @@ async function updatePersonalInformation(e) { } console.log(userData.dob); const resultObject = await postData("/setInfo", userData, token); - alert(resultObject.message); + + + if (resultObject.message.includes("success")) { + error.style.display = "none"; + success.style.display = "flex"; + } else { + errorMessage.innerHTML = resultObject.message; + error.style.display = "flex"; + } + + window.localStorage.setItem("token", resultObject.token); } diff --git a/public/login.html b/public/login.html index df05ae9..6a837c4 100644 --- a/public/login.html +++ b/public/login.html @@ -12,6 +12,12 @@ +
+

Error Message

+
+
+

Login successful

+

Don't have an account?

Register diff --git a/public/login.js b/public/login.js index 3c2be49..7f36992 100644 --- a/public/login.js +++ b/public/login.js @@ -2,13 +2,23 @@ import { postData } from "./client.js"; const loginForm = document.getElementById("loginForm"); +const error = document.querySelector(".error"); +const errorMessage = document.querySelector(".error p"); +const success = document.querySelector(".success"); + if (loginForm) loginForm.onsubmit = async e => { e.preventDefault(); let resultObject = await postData("/login", { username: document.getElementById("logUser").value, password: document.getElementById("logPass").value }); - alert(resultObject.message); + if (resultObject.message.includes("success")) { + error.style.display = "none"; + success.style.display = "flex"; + } else { + errorMessage.innerHTML = resultObject.message; + error.style.display = "flex"; + } if (resultObject.success) { window.location.href = "/home" localStorage.setItem("token", resultObject.token); diff --git a/public/player.html b/public/player.html index a2d94f7..2c42a27 100644 --- a/public/player.html +++ b/public/player.html @@ -1,7 +1,7 @@ - [Player name] + [Player name] @@ -9,34 +9,45 @@

[Player Name]

- - - - - - - - - - - - - - - - - - - +
+
+

Position:

+

+
+
+

Team:

+

+
+
+

Height:

+

+
+
+

Weight:

+

+
+
+ +
NameIDContractTeamPosition
[Name][ID][Salary][Team][Position]
+ + +
+ + + + Back + + + + + -
- Back
diff --git a/public/player.js b/public/player.js index 3123b8d..2abf56f 100644 --- a/public/player.js +++ b/public/player.js @@ -10,7 +10,16 @@ const tableBody = document.getElementById("results"); const table = document.getElementById("table"); const nomatch = document.getElementById("nomatch"); + +const statsTable = document.querySelector("#stats"); +const statsHeader = document.querySelector("#stats thead"); +const statsBody = document.querySelector("#stats tbody"); + +const overviewHeader = document.querySelector("#overview thead"); +const overviewBody = document.querySelector("#overview tbody"); + const watchButton = document.querySelector("#watch"); +const showStatsButton = document.querySelector("#showStats"); //const watchlist = await postData("/getWatchlist", {}, token); @@ -26,6 +35,13 @@ watchButton.onclick = async e => { updateIsWatched(); } +showStatsButton.onclick = async e => { + e.preventDefault(); + + statsTable.style.display = statsTable.style.display == "" ? "none" : ""; + showStatsButton.innerHTML = statsTable.style.display == "" ? "Hide Full Stats" : "Show Full Stats"; +} + async function updateIsWatched() { const [, , id] = window.location.pathname.split("/"); const resultObject = await postData("/isWatched", {id: id}, token); @@ -35,15 +51,28 @@ async function updateIsWatched() { async function populatePlayerData() { const [, , id] = window.location.pathname.split("/"); - const result = await postData("/getPlayer", { id: id }, window.localStorage.getItem("token")); - if (result.success === false) { + let resultObject = await postData("/getPlayerStats", { + playerID: id + }, token); + if (resultObject.success === false) { console.log("Invalid ID"); - window.location.href = "/search"; return; } - const player = result.match; - for (const [attribute, value] of Object.entries(player)) { + const playerStatsObject = resultObject.matches; + + + let playerObjectResult = await postData("/getPlayer", { + id: id + }, token); + if (playerObjectResult.success === false) { + console.log("Invalid ID"); + return; + } + + const playerObject = playerObjectResult.match; + + for (const [attribute, value] of Object.entries(playerObject)) { console.log(`.attribute-${attribute}`); document.querySelectorAll(`.attribute-${attribute}`).forEach(element => { if (element.classList.contains("format-salary")) { @@ -54,5 +83,100 @@ async function populatePlayerData() { }) }; - console.log(player); + createStatsTable(playerStatsObject); + createOverviewTable(playerObject); } + +async function createOverviewTable(playerObject) { + const tableHeader = overviewHeader; + const tableBody = overviewBody; + + nomatch.style.display = "none" + tableHeader.innerHTML = ""; + const headerRow = document.createElement("tr") + //Object.keys(resultObject.matches[0]).forEach(attribute => { + // headerRow.innerHTML += `${attribute}`; + //});pass_attempts, complete_pass, total_yards, total_tds, interception, receptions, receiving_yards, receiving_touchdown, rush_attempts, rushing_yards, rush_touchdown, fumble + headerRow.innerHTML += ` + Contract + APY + Years + Duration + `; + tableHeader.appendChild(headerRow); + + tableBody.innerHTML = ""; + const row = playerObject; + const rowElement = document.createElement("tr"); + + //for (attribute in player) { + // row.innerHTML += `${player}l`; + //} + rowElement.innerHTML = ` + ${formatSalary(row.TrueAvgPerYear * row.Years)} + ${formatSalary(row.TrueAvgPerYear)} + ${row.Years} + ${row.StartYear}-${row.EndYear} + `; + tableBody.appendChild(rowElement); +} + +async function createStatsTable(playerObject) { + const tableHeader = statsHeader; + const tableBody = statsBody; + + nomatch.style.display = "none" + tableHeader.innerHTML = ""; + const headerRow = document.createElement("tr") + //Object.keys(resultObject.matches[0]).forEach(attribute => { + // headerRow.innerHTML += `${attribute}`; + //});pass_attempts, complete_pass, total_yards, total_tds, interception, receptions, receiving_yards, receiving_touchdown, rush_attempts, rushing_yards, rush_touchdown, fumble + headerRow.innerHTML += ` + Season + Season Type + Week + Pass Attempts + Complete Passes + Total Yards + Total Touchdowns + Interceptions + Receptions + Recieving Yards + Receiving Touchdowns + Rush Attempts + Rushing Yards + Rushing Touchdowns + Rushing Touchdowns + Fumbles + `; + tableHeader.appendChild(headerRow); + + tableBody.innerHTML = ""; + playerObject.forEach(row => { + const rowElement = document.createElement("tr"); + + //for (attribute in player) { + // row.innerHTML += `${player}l`; + //} + rowElement.innerHTML = ` + ${row.PlayerName} + ${row.Season} + ${row.SeasonType} + ${row.Week} + ${row.pass_attempts} + ${row.complete_pass} + ${row.total_yards} + ${row.total_tds} + ${row.interception} + ${row.receptions} + ${row.receiving_yards} + ${row.receiving_touchdown} + ${row.rush_attempts} + ${row.rushing_yards} + ${row.rush_touchdown} + ${row.fumble} + `; + tableBody.appendChild(rowElement); + }); +} + //});pass_attempts, complete_pass, total_yards, total_tds, interception, receptions, receiving_yards, receiving_touchdown, rush_attempts, rushing_yards, rush_touchdown, fumble \ No newline at end of file diff --git a/public/register.html b/public/register.html index 02ac277..f734b1d 100644 --- a/public/register.html +++ b/public/register.html @@ -17,6 +17,12 @@ +
+

Error Message

+
+
+

Login successful

+

Already have an account?

Login diff --git a/public/register.js b/public/register.js index 8893f59..27e4271 100644 --- a/public/register.js +++ b/public/register.js @@ -2,10 +2,15 @@ import { postData } from "./client.js"; const registerForm = document.getElementById("registerForm"); +const error = document.querySelector(".error"); +const errorMessage = document.querySelector(".error p"); +const success = document.querySelector(".success"); + if (registerForm) registerForm.onsubmit = async e => { e.preventDefault(); if (registerForm.regPass.value !== registerForm.regVerify.value) { - alert("Passwords must match"); + error.style.display = "flex"; + errorMessage.innerHTML = "Passwords must match"; return; } @@ -14,7 +19,13 @@ if (registerForm) registerForm.onsubmit = async e => { password: registerForm.regPass.value, role: registerForm.regRole.value }); - alert(resultObject.message); + if (resultObject.message.includes("success")) { + error.style.display = "none"; + success.style.display = "flex"; + } else { + errorMessage.innerHTML = resultObject.message; + error.style.display = "flex"; + } if (resultObject.success === true) { window.location.href = "/login"; } diff --git a/server.js b/server.js index 1179898..e25aeec 100644 --- a/server.js +++ b/server.js @@ -218,11 +218,7 @@ SUM(total_yards) + (SUM(offense_snaps) * 100.0 / SUM(team_offense_snaps)) - ((SUM(interception) + sum(fumble_lost)) * 75) - ( - CASE - WHEN Player.Position = 'QB' - THEN SUM(qb_dropback) - SUM(pass_attempts) - SUM(qb_scramble) - ELSE 0 - END) + SUM(tackled_for_loss)) - (SUM(safety) * 100.0) AS OffenseScore, @@ -233,11 +229,7 @@ AvgPerYear, + (SUM(offense_snaps) * 100.0 / SUM(team_offense_snaps)) - ((SUM(interception) + sum(fumble_lost)) * 75) - ( - CASE - WHEN Player.Position = 'QB' - THEN SUM(qb_dropback) - SUM(pass_attempts) - SUM(qb_scramble) - ELSE 0 - END +SUM(tackled_for_loss) ) - (SUM(safety) * 100.0)) / AvgPerYear AS PaydirtScore @@ -250,6 +242,81 @@ ORDER BY OffenseScore DESC; res.status(200).json({ matches: result.recordset }); }); + + +app.post("/getPlayerStats", authenticate, async (req, res) => { + const { playerID } = req.body; + + const result = await pool.request() + .input("playerID", sql.Int, playerID) + .query(` +SELECT Player.PlayerID, Player.PlayerName, Season, SeasonType, Week, pass_attempts, complete_pass, total_yards, total_tds, interception, receptions, receiving_yards, receiving_touchdown, rush_attempts, rushing_yards, rush_touchdown, fumble +FROM Player JOIN DatasetPlayerStats ON Player.PlayerID = DatasetPlayerStats.PlayerID +WHERE Player.PlayerID = @playerID +ORDER BY Season, week; + `); + + res.status(200).json({ matches: result.recordset }); +}); + +app.post("/getPlayerScores", authenticate, async (req, res) => { + const { playerID } = req.body; + + const result = await pool.request() + .input("playerID", sql.Int, playerID) + .query(` +SELECT Player.PlayerID, Player.[Position], Player.PlayerName, Player.Team, +SUM(total_yards) AS TotalYards, +SUM(passing_yards) AS PassingYards, +SUM(rushing_yards) AS RushingYards, +SUM(receiving_yards) AS RecievingYards, +CASE WHEN Player.Position = 'QB' THEN (SUM(pass_touchdown)) ELSE 0 END + SUM(receiving_touchdown) + SUM(rush_touchdown) AS AmendedTotalTDs, +CASE WHEN Player.Position = 'QB' THEN (SUM(pass_touchdown)) ELSE 0 END AS PassTDs, +SUM(receiving_touchdown) AS ReceivingTDs, +SUM(rush_touchdown) AS RushTDs, +SUM(offense_snaps) * 1.0 / SUM(team_offense_snaps) AS SnapPercentage, +SUM(interception) + sum(fumble_lost) AS Turnovers, +SUM(tackled_for_loss) AS TackledForLoss, + CASE + WHEN Player.Position = 'QB' + THEN SUM(qb_dropback) - SUM(pass_attempts) - SUM(qb_scramble) + ELSE 0 + END AS Sacks, +SUM(safety) AS Safties, + +SUM(total_yards) ++ ((CASE WHEN Player.Position = 'QB' THEN (SUM(pass_touchdown)) ELSE 0 END + SUM(receiving_touchdown) + SUM(rush_touchdown)) * 50) ++ (SUM(offense_snaps) * 100.0 / SUM(team_offense_snaps)) +- ((SUM(interception) + sum(fumble_lost)) * 75) +- ( + SUM(tackled_for_loss) + ) + +- (SUM(safety) * 100.0) +AS OffenseScore, + +AvgPerYear, +(SUM(total_yards) ++ ((CASE WHEN Player.Position = 'QB' THEN (SUM(pass_touchdown)) ELSE 0 END + SUM(receiving_touchdown) + SUM(rush_touchdown)) * 50) ++ (SUM(offense_snaps) * 100.0 / SUM(team_offense_snaps)) +- ((SUM(interception) + sum(fumble_lost)) * 75) +- ( + SUM(tackled_for_loss) +) +- (SUM(safety) * 100.0)) / AvgPerYear * 1000000 AS PaydirtScore + + +FROM Player JOIN DatasetPlayerStats ON Player.PlayerID = DatasetPlayerStats.PlayerID JOIN Contract ON Player.PlayerID = Contract.PlayerID +WHERE season = 2024 AND SeasonType = 'REG' AND Player.PlayerID = @playerID +GROUP BY Player.PlayerID, Player.PlayerName, Player.Team, Player.[Position], Contract.AvgPerYear +ORDER BY PaydirtScore DESC; + `); + + res.status(200).json({ matches: result.recordset }); +}); + + + app.post("/getHighestPaydirt", authenticate, async (req, res) => { const { amount } = req.body; @@ -280,11 +347,7 @@ SUM(total_yards) + (SUM(offense_snaps) * 100.0 / SUM(team_offense_snaps)) - ((SUM(interception) + sum(fumble_lost)) * 75) - ( - CASE - WHEN Player.Position = 'QB' - THEN SUM(qb_dropback) - SUM(pass_attempts) - SUM(qb_scramble) - ELSE 0 - END) + SUM(tackled_for_loss)) - (SUM(safety) * 100.0) AS OffenseScore, @@ -295,13 +358,10 @@ AvgPerYear, + (SUM(offense_snaps) * 100.0 / SUM(team_offense_snaps)) - ((SUM(interception) + sum(fumble_lost)) * 75) - ( - CASE - WHEN Player.Position = 'QB' - THEN SUM(qb_dropback) - SUM(pass_attempts) - SUM(qb_scramble) - ELSE 0 - END +SUM(tackled_for_loss) ) -- (SUM(safety) * 100.0)) / AvgPerYear AS PaydirtScore + +- (SUM(safety) * 100.0)) / AvgPerYear * 1000000 AS PaydirtScore FROM Player JOIN DatasetPlayerStats ON Player.PlayerID = DatasetPlayerStats.PlayerID JOIN Contract ON Player.PlayerID = Contract.PlayerID @@ -318,7 +378,7 @@ app.post("/getPlayer", authenticate, async (req, res) => { const result = await pool.request() .input("query", sql.VarChar, id) - .query(`SELECT p.PlayerName, p.PlayerID, c.TotalValue, p.Team, p.Position FROM Player AS p JOIN Contract AS c ON p.PlayerID = c.PlayerID WHERE p.PlayerID = @query`); + .query(`SELECT p.PlayerName, p.PlayerID, c.TotalValue, c.TrueAvgPerYear, c.Years, c.StartYear, c.EndYear, p.Team, p.Position, p.Height, p.Weight FROM Player AS p JOIN Contract AS c ON p.PlayerID = c.PlayerID WHERE p.PlayerID = @query`); if (result.recordset.length !== 1) { res.status(400).json({ success: false })