diff --git a/ProjectSourceCode/src/index.js b/ProjectSourceCode/src/index.js index c54a6c7..4bc8a8f 100644 --- a/ProjectSourceCode/src/index.js +++ b/ProjectSourceCode/src/index.js @@ -78,19 +78,51 @@ app.use( app.use(express.static(path.join(__dirname, 'resources'))); // ***************************************************** -// +// // ***************************************************** -/************************ - Header Scoreboard Routes -*************************/ - -const fetchMatchesData = require('./resources/routes/navigation-bar/current-match-information'); +// Middleware to automatically update live scoreboard +const fetchMatchesData = require('./resources/middleware/navigation-bar/current-match-information'); app.use(fetchMatchesData); -const convert_time = require('./resources/js/navigation-bar/scoreboard-header/convert-time'); +//Middleware to automatically update in-game time abbreviations + +const convert_time = require('./resources/middleware/navigation-bar/convert-time'); app.use(convert_time); + +// Leagues Page Middleware + +const fetchLeaguesData = require('./resources/middleware/leagues-page/get-current-league-information'); +const fetchLeagueScorerData = require('./resources/middleware/leagues-page/get-current-league-top-scorers'); + +app.get('/league/:leagueID', [fetchLeaguesData, fetchLeagueScorerData], (req, res) => { + // Render the Handlebars view with league data + res.render('pages/leagues-page', { + leagueID: req.params.leagueID, + leagues: res.locals.leagues, + scorers: res.locals.topScorers // Assuming fetchLeagueScorerData sets the data in res.locals.scorers + }); +}); + +// Clubs Page Middleware + +const fetchClubsData = require('./resources/middleware/clubs-page/get-current-club-information'); + +app.get('/club/:clubID', [fetchClubsData], (req, res) => { + // Render the Handlebars view with league data + res.render('pages/clubs-page', { + clubID: req.params.clubID, + clubs: res.locals.club + }); +}); + + + +// ***************************************************** +// +// ***************************************************** + /************************ Login Page Routes *************************/ @@ -98,91 +130,99 @@ app.use(convert_time); // Redirect to the /login endpoint app.get('/', (req, res) => { res.redirect('/home'); - }); +}); - // Render login page for /login route - app.get('/login', (req, res) => { +// Render login page for /login route +app.get('/login', (req, res) => { res.render('pages/home'); - }); +}); - // Trigger login form to check database for matching username and password - app.post('/login', async (req, res) => { +// Trigger login form to check database for matching username and password +app.post('/login', async (req, res) => { try { + // Check if username exists in DB + const user = await db.oneOrNone('SELECT * FROM users WHERE username = $1', req.body.username); - // Check if username exists in DB - const user = await db.oneOrNone('SELECT * FROM users WHERE username = $1', req.body.username); + if (!user) { + // Redirect user to login screen if no user is found with the provided username + return res.redirect('/register'); + } - if (!user) { - // Redirect user to login screen if no user is found with the provided username - return res.redirect('/register'); - } + // Check if password from request matches with password in DB + const match = await bcrypt.compare(req.body.password, user.password); - // Check if password from request matches with password in DB - const match = await bcrypt.compare(req.body.password, user.password); + // Check if match returns no data + if (!match) { + // Render the login page with the message parameter + return res.render('pages/home', { message: 'Password does not match' }); + } - // Check if mathc returns no data - if (!match) { - // Render the login page with the message parameter - return res.render('pages/home', { message: 'Password does not match' }); - } + // Save user information in the session variable + req.session.user = user; + req.session.save(); - // Save user information in the session variable - req.session.user = user; - req.session.save(); - - // Redirect user to the home page - res.redirect('/home'); - + // Redirect user to the home page + res.redirect('/home'); } catch (error) { - // Direct user to login screen if no user is found with matching password - res.redirect('/register'); + // Direct user to login screen if no user is found with matching password + res.redirect('/register'); } - }); +}); - /************************ - Registration Page Routes - *************************/ +/************************ + Registration Page Routes +*************************/ - // Render registration page for /register route - app.get('/register', (req, res) => { +// Render registration page for /register route +app.get('/register', (req, res) => { res.render('pages/register'); - }); - +}); // Trigger Registration Form to Post app.post('/register', async (req, res) => { - try { - if (!req.body.username || !req.body.password) { - // If username or password is missing, respond with status 400 and an error message - return res.status(400).json({ status: 'error', message: 'Invalid input' }); + try { + if (!req.body.username || !req.body.password) { + // If username or password is missing, respond with status 400 and an error message + return res.status(400).json({ status: 'error', message: 'Invalid input' }); + } + // Hash the password using bcrypt library + const hash = await bcrypt.hash(req.body.password, 10); + + // Insert username and hashed password into the 'users' table + await db.none('INSERT INTO users (username, password) VALUES ($1, $2)', [req.body.username, hash]); + + // Redirect user to the login page after successful registration + res.status(200).json({ status: 'success', message: 'Registration successful' }); + } catch (error) { + // If an error occurs during registration, respond with status 500 and an error message + res.status(500).json({ status: 'error', message: 'An error occurred during registration' }); } - // Hash the password using bcrypt library - const hash = await bcrypt.hash(req.body.password, 10); - - // Insert username and hashed password into the 'users' table - await db.none('INSERT INTO users (username, password) VALUES ($1, $2)', [req.body.username, hash]); - - // Redirect user to the login page after successful registration - res.status(200).json({ status: 'success', message: 'Registration successful' }); - } catch (error) { - // If an error occurs during registration, respond with status 500 and an error message - res.status(500).json({ status: 'error', message: 'An error occurred during registration' }); - } }); - /************************ Home Page Routes *************************/ app.get('/home', (req, res) => { - res.render('pages/home'); + res.render('pages/home'); }); +/************************ + League Page Routes +*************************/ + // Import and call generateLeagueRoutes function const generateLeagueRoutes = require('./resources/routes/league-pages/generate-league-routes'); generateLeagueRoutes(app); +/************************ + Club Page Routes +*************************/ + +// Import and call generateLeagueRoutes function +const generateClubRoutes = require('./resources/routes/club-pages/generate-club-routes'); +generateClubRoutes(app); + // ***************************************************** // // ***************************************************** diff --git a/ProjectSourceCode/src/resources/css/all-pages-style.css b/ProjectSourceCode/src/resources/css/all-pages-style.css new file mode 100644 index 0000000..ac4d128 --- /dev/null +++ b/ProjectSourceCode/src/resources/css/all-pages-style.css @@ -0,0 +1,5 @@ +body { + /* background-image: url('./../img/grass-background.png'); Specify the path to your background image */ + /* background-repeat: repeat; Set the background image to repeat both horizontally and vertically */ + /* background-size: 1000px; Set the size of the background image for each repeat */ +} \ No newline at end of file diff --git a/ProjectSourceCode/src/resources/css/club-pages/club-page.css b/ProjectSourceCode/src/resources/css/club-pages/club-page.css new file mode 100644 index 0000000..61433d8 --- /dev/null +++ b/ProjectSourceCode/src/resources/css/club-pages/club-page.css @@ -0,0 +1,66 @@ +#club-page-body { + display: flex; + flex-direction: column; + width: 100%; +} + +#club-information-container { + height: 100px; + width: fit-content; + margin: 20px; + background: linear-gradient(to right, white, rgb(245, 245, 245), rgb(227, 227, 227)); /* Gradient from white to gray */ + padding: 10px 20px; /* Adjust padding as needed */ + transform: skewX(-20deg); /* Skew the banner to create a triangular side */ + cursor: pointer; /* Change cursor to pointer on hover */ + box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.4); + + display: flex; /* Use flexbox for layout */ + align-items: center; /* Center content vertically */ + + /* Add any other styling you need for the club information container */ +} + +#club-information-container::after { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 8px; /* Width of the red strip */ + background-color: red; /* Red color */ + } + +#club-logo { + margin: 0px 30px; + transform: skewX(20deg); /* Skew the banner to create a triangular side */ +} + +#club-title { + transform: skewX(20deg); /* Skew the banner to create a triangular side */ + margin-right: 20px; +} + +#club-flag { + + transform: skewX(20deg); /* Skew the banner to create a triangular side */ + height: 20px; + margin-right: 200px; +} + +#club-stats-container { + width: 100%; + display: flex; + flex: 1; +} + +#club-stats-container .container { + margin: 0 10px; + + background-color: #eaeaea; /* Example background color */ + + border: 1px solid gray; + border-radius: 8px; + + box-shadow: 0px 3px 4px rgba(0, 0, 0, 0.4); + /* Add any other styling you need for the club table container */ +} diff --git a/ProjectSourceCode/src/resources/css/club-pages/club-top-scorers.css b/ProjectSourceCode/src/resources/css/club-pages/club-top-scorers.css new file mode 100644 index 0000000..2d62b21 --- /dev/null +++ b/ProjectSourceCode/src/resources/css/club-pages/club-top-scorers.css @@ -0,0 +1,6 @@ +#club-top-scorers-container { + flex: 1; + background-color: #dcdcdc; /* Example background color */ + width: 40%; + /* Add any other styling you need for the top scorers container */ +} diff --git a/ProjectSourceCode/src/resources/routes/league-pages/get-current-league-information.js b/ProjectSourceCode/src/resources/css/club-pages/players-table.css similarity index 100% rename from ProjectSourceCode/src/resources/routes/league-pages/get-current-league-information.js rename to ProjectSourceCode/src/resources/css/club-pages/players-table.css diff --git a/ProjectSourceCode/src/resources/css/league-page/league-page.css b/ProjectSourceCode/src/resources/css/league-page/league-page.css deleted file mode 100644 index 38a1182..0000000 --- a/ProjectSourceCode/src/resources/css/league-page/league-page.css +++ /dev/null @@ -1,28 +0,0 @@ -#league-page-body { - display: flex; - flex-direction: column; - height: 100vh; -} - -#league-information-container { - height: 100px; - background-color: #f0f0f0; /* Example background color */ - /* Add any other styling you need for the league information container */ -} - -#league-stats-container { - display: flex; - flex: 1; -} - -#league-table-container { - flex: 1; - background-color: #eaeaea; /* Example background color */ - /* Add any other styling you need for the league table container */ -} - -#top-scorers-container { - flex: 1; - background-color: #dcdcdc; /* Example background color */ - /* Add any other styling you need for the top scorers container */ -} diff --git a/ProjectSourceCode/src/resources/css/league-pages/league-page.css b/ProjectSourceCode/src/resources/css/league-pages/league-page.css new file mode 100644 index 0000000..37a9204 --- /dev/null +++ b/ProjectSourceCode/src/resources/css/league-pages/league-page.css @@ -0,0 +1,142 @@ +/* +============================================================== + OVERALL PAGE STYLES +============================================================== +*/ + +#league-page-body +{ + /* --- SIZE CONTAINER --- */ + width: 100%; /* Set the width to the full width of screen */ + padding: 10px 100px; /* Create some distance between page boundries and elements */ + + /* --- FORMAT CHILD ITEMS (Table and Leading Scorers Cards) --- */ + display: flex; /* Enable flexbox layout */ + flex-direction: column; /* Arrange child elements vertically */ +} + +/* +================================= + LEAGUE INFORMATION HEADER +================================= +*/ + +/* Stylization for League Information Header Card */ +#league-information-container +{ + /* --- POSITION CONTAINER --- */ + align-items: center; /* Center content vertically */ + margin: 20px; + + /* --- SIZE CONTAINER --- */ + height: 100px; + width: fit-content; + padding: 10px 20px; /* Adjust padding as needed */ + + /* --- STYLE CONTAINER --- */ + background: linear-gradient(to right, white, rgb(245, 245, 245), rgb(227, 227, 227)); /* Gradient from white to gray */ + transform: skewX(-20deg); /* Skew the banner to create a triangular side */ + box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.4); + + /* --- FORMAT CHILD ITEMS (logo, league name, flag) --- */ + display: flex; /* Enable flexbox layout */ + flex-direction: row; /* Arrange child elements horizontally */ +} + + /* + =========================== + LEAGUE INFORMATION CHILD ITEMS + =========================== + */ + + /* Adds Red Diagonal Strip at the end of the #league-information-container */ + #league-information-container::after + { + content: ''; + position: absolute; + top: 0; + right: 0; + bottom: 0; + width: 8px; /* Width of the red strip */ + background-color: red; /* Red color */ + } + + /* Styling for League Logo in League Information Header */ + #league-logo + { + margin: 0px 30px; + transform: skewX(20deg); /* Skew the banner to create a triangular side */ + height: 80%; + } + + /* Styling for League Title in League Information Header */ + #league-title + { + transform: skewX(20deg); /* Skew the banner to create a triangular side */ + margin-right: 20px; + } + + /* Styling for FLag in League Information Header */ + #league-flag + { + transform: skewX(20deg); /* Skew the banner to create a triangular side */ + height: 20px; + margin-right: 200px; + } + +/* +============================================= + CARDS IN PAGE BODY +============================================= +*/ + +#table-and-top-scorers-containers { + display: flex; + flex-direction: row; +} + +/* Styling for Standings and Top Scorers Cards */ +#table-stats-container, #top-scorers-stats-container +{ + /* --- POSITION CONTAINER --- */ + margin: 0 10px; + margin-bottom: 20px; + + /* --- SIZE CONTAINER --- */ + padding: 15px; + + /* --- STYLE CONTAINER --- */ + background: linear-gradient(to top, rgb(216, 216, 216), rgb(236, 236, 236), rgb(241, 240, 240)); + border: 1px solid gray; + box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.5); +} + +/* +============================================================== + HOVER STYLES +============================================================== +*/ + + /* Hover Styling for Standings and Top Scorers Cards */ + #table-stats-container:hover, #top-scorers-stats-container:hover + { + transform: scale(1.01); /* Scale the row by 1.1 on hover */ + box-shadow: 0px 3px 10px rgba(0, 0, 0, 0.3); + } + +/* +============================================================== + DYNAMIC PAGE WIDTH STYLES +============================================================== +*/ + +/* Width of Screen is Less Than 950px */ +@media (max-width: 1230px) +{ + #table-and-top-scorers-containers { + display: flex; + flex-direction: column; + } +} + + \ No newline at end of file diff --git a/ProjectSourceCode/src/resources/css/league-pages/league-table.css b/ProjectSourceCode/src/resources/css/league-pages/league-table.css new file mode 100644 index 0000000..60f6ea2 --- /dev/null +++ b/ProjectSourceCode/src/resources/css/league-pages/league-table.css @@ -0,0 +1,130 @@ +/* +================================= + TABLE ADN HEADER CONTAINER +================================= +*/ + +/* Container for table and header */ +#league-table-container +{ + width: 60%; + justify-content: center; + align-items: center; + + margin-right: 30px; + + transition: transform 0.3s ease; /* Add smooth transition effect */ +} + +/* +================================= + TABLE CONTAINER +================================= +*/ + +/* Container for Table */ +#table-stats-container +{ + width: 100%; +} + +/* Table that holds all the standing information */ +#standings-table +{ + width: 100%; + padding: 15px; + + /* Table Header Styling */ + th + { + border-bottom: 3px solid red; /* Add red bottom border */ + } + + /* Data in Row Style */ + td + { + padding: 5px; + } + + /* Every Odd Row in Table Style */ + tbody tr:nth-child(odd) + { + background-color: #d2d2d2; /* Light gray for odd rows */ + } +} + + /* + =========================== + TABLE COLUMNS STYLE + =========================== + */ + + /* Club Logo Column in Table Style */ + #club-logo-column { + width: 25px; + + /* Club Logo Style */ + #table-club-logo + { + width: 25px; + margin-right: 5px; + cursor: pointer; + } + + } + + /* Club Name Column in Table Style */ + #club-name-column + { + font-weight: 500; + cursor: pointer; + } + + /* Points Column in Table Style */ + #points-column + { + font-weight: bolder; + } + +/* +============================================================== + HOVER STYLES +============================================================== +*/ + +/* Add hover effect to table rows */ +#standings-table tbody tr:hover +{ + /* Create border around row on hover */ + border: 1px solid lightgray; + border-radius: 10px; + + /* Make club logo larger on hover */ + #table-club-logo + { + width: 32px; + } + + /* Undeline club name on hover */ + #club-name-column + { + text-decoration: underline; /* Add underline effect */ + } +} + +/* +============================================================== + DYNAMIC PAGE WIDTH STYLES +============================================================== +*/ + +/* Stlye for Screens Smaller than 1230px */ +@media (max-width: 1230px) +{ + #league-table-container + { + align-items: center; + width: 75%; + min-width: 580px; + } +} \ No newline at end of file diff --git a/ProjectSourceCode/src/resources/css/league-pages/top-scorers.css b/ProjectSourceCode/src/resources/css/league-pages/top-scorers.css new file mode 100644 index 0000000..03eacea --- /dev/null +++ b/ProjectSourceCode/src/resources/css/league-pages/top-scorers.css @@ -0,0 +1,96 @@ +/* +================================= + TABLE ADN HEADER CONTAINER +================================= +*/ + +#top-scorers-container +{ + width: 40%; +} + +#top-scorers-stats-container { + + /* Table Header Styling */ + th + { + border-bottom: 3px solid red; /* Add red bottom border */ + } + + tr + { + padding: 3px; + } + + /* Data in Row Style */ + td + { + padding: 5px; + } +} + + /* + =========================== + TABLE COLUMNS STYLE + =========================== + */ + + #goals-column + { + text-align: center; + font-size: 20px; + font-weight: bolder; + } + + #top-scorers-logo + { + width: 25px; + margin: 0 15px; + cursor: pointer; + } + + #player-name-column + { + + font-size: 14px; + font-weight: bold; + padding-right: 15px; + } + + #club-name-column + { + padding-right: 15px; + } + +/* +============================================================== + HOVER STYLES +============================================================== +*/ + +#top-scorers-stats-container tbody tr:hover +{ + + border: 1px solid lightgray; + + #top-scorers-logo { + width: 32px; + } + + #club-name-column { + text-decoration: underline; + } +} + +/* +============================================================== + DYNAMIC PAGE WIDTH STYLES +============================================================== +*/ + +@media (max-width: 1230px) { + #top-scorers-container { + align-items: center; + width: 75%; + } +} \ No newline at end of file diff --git a/ProjectSourceCode/src/resources/img/grass-background.jpeg b/ProjectSourceCode/src/resources/img/grass-background.jpeg new file mode 100644 index 0000000..620ec2c Binary files /dev/null and b/ProjectSourceCode/src/resources/img/grass-background.jpeg differ diff --git a/ProjectSourceCode/src/resources/img/grass-background.png b/ProjectSourceCode/src/resources/img/grass-background.png new file mode 100644 index 0000000..32ced85 Binary files /dev/null and b/ProjectSourceCode/src/resources/img/grass-background.png differ diff --git a/ProjectSourceCode/src/resources/js/homepage/redirect-to-league-url.js b/ProjectSourceCode/src/resources/js/homepage/redirect-to-league-url.js deleted file mode 100644 index a9445ef..0000000 --- a/ProjectSourceCode/src/resources/js/homepage/redirect-to-league-url.js +++ /dev/null @@ -1,7 +0,0 @@ -function redirectToLeaguePage(leagueName) { - // Append the league name to the URL - var url = "/league/" + leagueName.toLowerCase().replace(/\s+/g, '-'); - - // Redirect to the league page - window.location.href = url; -} \ No newline at end of file diff --git a/ProjectSourceCode/src/resources/js/league-page/change-goal-difference-color.js b/ProjectSourceCode/src/resources/js/league-page/change-goal-difference-color.js new file mode 100644 index 0000000..a4e764a --- /dev/null +++ b/ProjectSourceCode/src/resources/js/league-page/change-goal-difference-color.js @@ -0,0 +1,29 @@ +document.addEventListener("DOMContentLoaded", function() { + var goalDifferenceCells = document.querySelectorAll("#goal-difference-column"); // Selecting the cells in the goal_difference column + + goalDifferenceCells.forEach(function(cell) { + var goalDifference = parseInt(cell.textContent); + var color; + + if (goalDifference < 0) + { + // Gradually darken the text color for negative goal differences + var darkenFactor = Math.ceil(goalDifference / -10); // Calculate the darken factor + var shade = Math.max(0, 255 - darkenFactor * 25); // Calculate the shade of red + color = "rgb(" + shade + ", 0, 0)"; // Create the color value + } + else if (goalDifference > 0) + { + // Gradually darken the text color for positive goal differences + var darkenFactor = Math.floor(goalDifference / 10); // Calculate the darken factor + var shade = Math.max(0, 155 - darkenFactor * 15); // Adjusted the starting point to make greens darker + color = "rgb(0, " + shade + ", 0)"; // Create the color value + } + else + { + color = "inherit"; // If goal difference is 0, leave text color unchanged + } + + cell.style.color = color; // Apply the calculated color + }); +}); diff --git a/ProjectSourceCode/src/resources/middleware/clubs-page/get-current-club-information.js b/ProjectSourceCode/src/resources/middleware/clubs-page/get-current-club-information.js new file mode 100644 index 0000000..c28ec43 --- /dev/null +++ b/ProjectSourceCode/src/resources/middleware/clubs-page/get-current-club-information.js @@ -0,0 +1,75 @@ +const axios = require('axios'); + +// Middleware function to fetch clubs data +const fetchClubsData = async (req, res, next) => { + try { + // Extract club ID from the URL + const clubID = req.params.clubID; + + // Make GET request to the API endpoint using the club ID + const response = await axios.get(`http://api.football-data.org/v4/teams/${clubID}/?offset=&limit=`, { + headers: { + 'X-Auth-Token': '0aa1ed31245d4a36b1ef5a79150324b3', // Add your API key here + }, + }); + + // Extract relevant data from the API response + const clubData = response.data; + + // Attach the data to res.locals + res.locals.club = { + area: { + id: clubData.area.id, + name: clubData.area.name, + code: clubData.area.code, + club_flag: clubData.area.flag, + }, + club_id: clubData.id, + name: clubData.name, + shortName: clubData.shortName, + tla: clubData.tla, + crest: clubData.crest, + address: clubData.address, + website: clubData.website, + founded: clubData.founded, + clubColors: clubData.clubColors, + venue: clubData.venue, + runningCompetitions: clubData.runningCompetitions.map(competition => ({ + id: competition.id, + name: competition.name, + code: competition.code, + type: competition.type, + emblem: competition.emblem + })), + coach: { + id: clubData.coach.id, + firstName: clubData.coach.firstName, + lastName: clubData.coach.lastName, + name: clubData.coach.name, + dateOfBirth: clubData.coach.dateOfBirth, + nationality: clubData.coach.nationality, + contract: { + start: clubData.coach.contract.start, + until: clubData.coach.contract.until + } + }, + squad: clubData.squad.map(player => ({ + id: player.id, + name: player.name, + position: player.position, + dateOfBirth: player.dateOfBirth, + nationality: player.nationality + })), + staff: clubData.staff, + lastUpdated: clubData.lastUpdated + }; + + next(); + } catch (error) { + console.error('Error fetching clubs data:', error); + res.locals.club = null; // Set to null if there's an error + next(); // Call next middleware or route handler + } +}; + +module.exports = fetchClubsData; diff --git a/ProjectSourceCode/src/resources/middleware/clubs-page/get-current-club-top-scorers.js b/ProjectSourceCode/src/resources/middleware/clubs-page/get-current-club-top-scorers.js new file mode 100644 index 0000000..91caaad --- /dev/null +++ b/ProjectSourceCode/src/resources/middleware/clubs-page/get-current-club-top-scorers.js @@ -0,0 +1,44 @@ +const axios = require('axios'); + +// Middleware function to fetch leagues data +const fetchLeagueScorerData = async (req, res, next) => { + try { + // Extract league ID from the URL + const leagueID = req.params.leagueID; + + // Make GET request to the API endpoint using the league ID + const response = await axios.get(`http://api.football-data.org/v4/competitions/${leagueID}/scorers?season&limit=20`, { + headers: { + 'X-Auth-Token': '0aa1ed31245d4a36b1ef5a79150324b3', // Add your API key here + }, + }); + + // Extract relevant data from the API response + const scorerData = response.data; + + // Attach the data to res.locals + res.locals.topScorers = { + scorers: scorerData.scorers.map(player => ({ + player: { + player_id: player.player.id, + player_name: player.player.name, + }, + team: { + team_id: player.player.id, + team_name: player.team.name, + team_crest: player.team.crest, + }, + games_played: player.playedMatches, + goals: player.goals, + })) + }; + + next(); + } catch (error) { + console.error('Error fetching leagues data:', error); + res.locals.topScorers = null; // Set to null if there's an error + next(); // Call next middleware or route handler + } +}; + +module.exports = fetchLeagueScorerData; diff --git a/ProjectSourceCode/src/resources/middleware/leagues-page/get-current-league-information.js b/ProjectSourceCode/src/resources/middleware/leagues-page/get-current-league-information.js new file mode 100644 index 0000000..09eaf0f --- /dev/null +++ b/ProjectSourceCode/src/resources/middleware/leagues-page/get-current-league-information.js @@ -0,0 +1,52 @@ +const axios = require('axios'); + +// Middleware function to fetch leagues data +const fetchLeaguesData = async (req, res, next) => { + try { + // Extract league ID from the URL + const leagueID = req.params.leagueID; + + // Make GET request to the API endpoint using the league ID + const response = await axios.get(`http://api.football-data.org/v4/competitions/${leagueID}/standings?season`, { + headers: { + 'X-Auth-Token': '0aa1ed31245d4a36b1ef5a79150324b3', // Add your API key here + }, + }); + + // Extract relevant data from the API response + const leagueData = response.data; + + // Attach the data to res.locals + res.locals.league = { + area: { + league_flag: leagueData.area.flag, + }, + competition: { + league_id: leagueData.competition.id, + league_name: leagueData.competition.name, + league_emblem: leagueData.competition.emblem + }, + standings: leagueData.standings[0].table.map(team => ({ + table: { + league_position: team.position, + team_id: team.team.id, + team_name: team.team.name, + team_crest: team.team.crest + }, + games_played: team.playedGames, + wins: team.won, + losses: team.lost, + draws: team.draw, + goal_difference: team.goalDifference, + points: team.points + })) + }; + next(); + } catch (error) { + console.error('Error fetching leagues data:', error); + res.locals.league = null; // Set to null if there's an error + next(); // Call next middleware or route handler + } +}; + +module.exports = fetchLeaguesData; diff --git a/ProjectSourceCode/src/resources/middleware/leagues-page/get-current-league-top-scorers.js b/ProjectSourceCode/src/resources/middleware/leagues-page/get-current-league-top-scorers.js new file mode 100644 index 0000000..8d8fef5 --- /dev/null +++ b/ProjectSourceCode/src/resources/middleware/leagues-page/get-current-league-top-scorers.js @@ -0,0 +1,44 @@ +const axios = require('axios'); + +// Middleware function to fetch leagues data +const fetchLeagueScorerData = async (req, res, next) => { + try { + // Extract league ID from the URL + const leagueID = req.params.leagueID; + + // Make GET request to the API endpoint using the league ID + const response = await axios.get(`http://api.football-data.org/v4/competitions/${leagueID}/scorers?season&limit=20`, { + headers: { + 'X-Auth-Token': '0aa1ed31245d4a36b1ef5a79150324b3', // Add your API key here + }, + }); + + // Extract relevant data from the API response + const scorerData = response.data; + + // Attach the data to res.locals + res.locals.topScorers = { + scorers: scorerData.scorers.map(player => ({ + player: { + player_id: player.player.id, + player_name: player.player.name, + }, + team: { + team_id: player.team.id, + team_name: player.team.name, + team_crest: player.team.crest, + }, + games_played: player.playedMatches, + goals: player.goals, + })) + }; + + next(); + } catch (error) { + console.error('Error fetching leagues data:', error); + res.locals.topScorers = null; // Set to null if there's an error + next(); // Call next middleware or route handler + } +}; + +module.exports = fetchLeagueScorerData; diff --git a/ProjectSourceCode/src/resources/js/navigation-bar/scoreboard-header/convert-time.js b/ProjectSourceCode/src/resources/middleware/navigation-bar/convert-time.js similarity index 90% rename from ProjectSourceCode/src/resources/js/navigation-bar/scoreboard-header/convert-time.js rename to ProjectSourceCode/src/resources/middleware/navigation-bar/convert-time.js index 7b826df..68ba8ff 100644 --- a/ProjectSourceCode/src/resources/js/navigation-bar/scoreboard-header/convert-time.js +++ b/ProjectSourceCode/src/resources/middleware/navigation-bar/convert-time.js @@ -9,6 +9,9 @@ const convert_time = (req, res, next) => { if (match.minute === "FINISHED") { match.minute = "FT"; } + else if (match.minute === "IN_PLAY") { + match.minute = "IP" + } else if (match.minute === "TIMED") { match.minute = "TM"; } diff --git a/ProjectSourceCode/src/resources/routes/navigation-bar/current-match-information.js b/ProjectSourceCode/src/resources/middleware/navigation-bar/current-match-information.js similarity index 100% rename from ProjectSourceCode/src/resources/routes/navigation-bar/current-match-information.js rename to ProjectSourceCode/src/resources/middleware/navigation-bar/current-match-information.js diff --git a/ProjectSourceCode/src/resources/routes/club-pages/generate-club-routes.js b/ProjectSourceCode/src/resources/routes/club-pages/generate-club-routes.js new file mode 100644 index 0000000..c3ea81c --- /dev/null +++ b/ProjectSourceCode/src/resources/routes/club-pages/generate-club-routes.js @@ -0,0 +1,18 @@ +const express = require('express'); +const app = express(); + +// generate-league-routes.js + +// Define a function to generate league routes +module.exports = function generateClubRoutes(app) { + // Define a route to handle requests to "/league/:leagueName" + app.get('/club/:clubID', (req, res) => { + // Extract the league name from the URL parameters + const clubID = req.params.clubID; + + // Render the league page template using Handlebars + res.render('pages/club-page', { clubID: clubID }); + }); +}; + + diff --git a/ProjectSourceCode/src/resources/routes/club-pages/redirect-to-club-url.js b/ProjectSourceCode/src/resources/routes/club-pages/redirect-to-club-url.js new file mode 100644 index 0000000..408d964 --- /dev/null +++ b/ProjectSourceCode/src/resources/routes/club-pages/redirect-to-club-url.js @@ -0,0 +1,17 @@ +// Add click event listener to club logos +document.querySelectorAll('#table-club-logo, #club-name, #top-scorers-logo, #club-name-column').forEach(element => { + element.addEventListener('click', (event) => { + // Get the club ID from the clicked club logo's clubID attribute + const clubId = element.getAttribute('clubID'); + redirectToClubPage(clubId); + }); +}); + +// Function to redirect to the club page +function redirectToClubPage(clubID) { + // Append the club ID to the URL + var url = "/club/" + clubID; + + // Redirect to the club page + window.location.href = url; +} diff --git a/ProjectSourceCode/src/resources/routes/league-pages/generate-league-routes.js b/ProjectSourceCode/src/resources/routes/league-pages/generate-league-routes.js index 3e91ef7..8dca5e8 100644 --- a/ProjectSourceCode/src/resources/routes/league-pages/generate-league-routes.js +++ b/ProjectSourceCode/src/resources/routes/league-pages/generate-league-routes.js @@ -6,12 +6,12 @@ const app = express(); // Define a function to generate league routes module.exports = function generateLeagueRoutes(app) { // Define a route to handle requests to "/league/:leagueName" - app.get('/league/:leagueName', (req, res) => { + app.get('/league/:leagueID', (req, res) => { // Extract the league name from the URL parameters - const leagueName = req.params.leagueName; + const leagueID = req.params.leagueID; // Render the league page template using Handlebars - res.render('pages/league-page', { leagueName: leagueName }); + res.render('pages/leagues-page', { leagueID: leagueID }); }); }; diff --git a/ProjectSourceCode/src/resources/routes/league-pages/get-team-names-for-registry.js b/ProjectSourceCode/src/resources/routes/league-pages/get-team-names-for-registry.js new file mode 100644 index 0000000..9f61dac --- /dev/null +++ b/ProjectSourceCode/src/resources/routes/league-pages/get-team-names-for-registry.js @@ -0,0 +1,21 @@ +const axios = require('axios'); + +const fetchTeamNames = async (selectedLeague) => { + try { + const response = await axios({ + url: `http://api.football-data.org/v4/competitions/${selectedLeague}/teams`, + method: 'GET', + headers: { + 'X-Auth-Token': '0aa1ed31245d4a36b1ef5a79150324b3', + }, + }); + + const teams = response.data.teams.map(team => team.name); + return teams; + } catch (error) { + console.error('Error fetching teams data:', error); + return []; + } +}; + +module.exports = fetchTeamNames; diff --git a/ProjectSourceCode/src/resources/routes/league-pages/redirect-to-league-url.js b/ProjectSourceCode/src/resources/routes/league-pages/redirect-to-league-url.js new file mode 100644 index 0000000..7200b4b --- /dev/null +++ b/ProjectSourceCode/src/resources/routes/league-pages/redirect-to-league-url.js @@ -0,0 +1,7 @@ +function redirectToLeaguePage(leagueID) { + // Append the league name to the URL + var url = "/league/" + leagueID; + + // Redirect to the league page + window.location.href = url; +} \ No newline at end of file diff --git a/ProjectSourceCode/src/views/pages/clubs-page.hbs b/ProjectSourceCode/src/views/pages/clubs-page.hbs new file mode 100644 index 0000000..8ac0445 --- /dev/null +++ b/ProjectSourceCode/src/views/pages/clubs-page.hbs @@ -0,0 +1,24 @@ +
+ + +
+ +

{{club.name}}

+ {{club.clubData.name}} Flag +
+ + +
+ + +
+ +
+ + +
+ +
+ +
+
diff --git a/ProjectSourceCode/src/views/pages/home.hbs b/ProjectSourceCode/src/views/pages/home.hbs index 3659c87..3d718bf 100644 --- a/ProjectSourceCode/src/views/pages/home.hbs +++ b/ProjectSourceCode/src/views/pages/home.hbs @@ -3,32 +3,32 @@
- + {{> homepage/league-card leagueName="Premier League" logo="./img/homepage/premier-league/icon.png" title="./img/homepage/premier-league/title.png"}} - + {{> homepage/league-card leagueName="La Liga" logo="./img/homepage/la-liga/icon.png" title="./img/homepage/la-liga/title.png"}} - + {{> homepage/league-card leagueName="Bundesliga" logo="./img/homepage/bundesliga/icon.png" title="./img/homepage/bundesliga/title.png"}} - + {{> homepage/league-card leagueName="Serie A" logo="./img/homepage/serie-a/icon.png" title="./img/homepage/serie-a/title.png"}} - + {{> homepage/league-card leagueName="Ligue 1" logo="./img/homepage/ligue-1/icon.png" title="./img/homepage/ligue-1/title.png"}} - + {{> homepage/league-card leagueName="Brasileirao" logo="./img/homepage/brasileirao/icon.png" title="./img/homepage/brasileirao/title.png"}} diff --git a/ProjectSourceCode/src/views/pages/league-page.hbs b/ProjectSourceCode/src/views/pages/league-page.hbs deleted file mode 100644 index 4a2eaf0..0000000 --- a/ProjectSourceCode/src/views/pages/league-page.hbs +++ /dev/null @@ -1,22 +0,0 @@ -
- - -
- -
- - -
- - -
- -
- - -
- -
- -
-
\ No newline at end of file diff --git a/ProjectSourceCode/src/views/pages/leagues-page.hbs b/ProjectSourceCode/src/views/pages/leagues-page.hbs new file mode 100644 index 0000000..1b2665d --- /dev/null +++ b/ProjectSourceCode/src/views/pages/leagues-page.hbs @@ -0,0 +1,89 @@ +
+ + +
+ +

{{league.competition.league_name}}

+ {{league.competition.league_name}} Flag +
+ + +
+ + +
+ + +
+

Table

+
+ + +
+ + + + + + + + + + + + + + + + {{#each league.standings}} + + + + + + + + + + + + {{/each}} + +
#ClubGPWLDGDPts
{{table.league_position}}{{table.team_name}}{{games_played}}{{wins}}{{losses}}{{draws}}{{goal_difference}}{{points}}
+
+
+ + +
+
+

Top Scorers

+
+ +
+ + + + + + + + + + + + {{#each topScorers.scorers}} + + + + + + + + {{/each}} + +
GoalsNameClubGP
{{goals}}{{player.player_name}}{{team.team_name}}{{games_played}}
+
+
+ +
+
diff --git a/ProjectSourceCode/src/views/partials/footer.hbs b/ProjectSourceCode/src/views/partials/footer.hbs index 3f78c75..a9a1901 100644 --- a/ProjectSourceCode/src/views/partials/footer.hbs +++ b/ProjectSourceCode/src/views/partials/footer.hbs @@ -11,8 +11,11 @@ - - + + + + + \ No newline at end of file diff --git a/ProjectSourceCode/src/views/partials/head.hbs b/ProjectSourceCode/src/views/partials/head.hbs index 3c432ff..92d27bd 100644 --- a/ProjectSourceCode/src/views/partials/head.hbs +++ b/ProjectSourceCode/src/views/partials/head.hbs @@ -5,7 +5,8 @@ - + + @@ -22,7 +23,14 @@ - + + + + + + + + Group 6 Final Project diff --git a/ProjectSourceCode/src/views/partials/navigation-bar/nav.hbs b/ProjectSourceCode/src/views/partials/navigation-bar/nav.hbs index 152d0ba..38357c1 100644 --- a/ProjectSourceCode/src/views/partials/navigation-bar/nav.hbs +++ b/ProjectSourceCode/src/views/partials/navigation-bar/nav.hbs @@ -15,27 +15,27 @@ diff --git a/ProjectSourceCode/src/views/partials/player-card/player-card.hbs b/ProjectSourceCode/src/views/partials/player-card/player-card.hbs new file mode 100644 index 0000000..dc5c093 --- /dev/null +++ b/ProjectSourceCode/src/views/partials/player-card/player-card.hbs @@ -0,0 +1,20 @@ +
+
+ + + + + + + + + + + + + + + +
Player NameNationalityPosition
persons.namepersons.nationalitypersons.position
+
+
\ No newline at end of file diff --git a/TeamMeetingLogs/minutes.txt b/TeamMeetingLogs/minutes.txt index 51f8665..8ac0625 100644 --- a/TeamMeetingLogs/minutes.txt +++ b/TeamMeetingLogs/minutes.txt @@ -11,3 +11,9 @@ 15 : 49 : overview of features and display of initial website 15 : 52 : discussion on database 15 : 53 : end of meeting + +==== Meeting 3 04/11/2024 ==== +16 : 00 : meeting start +16 : 01 : progress check and showcase +16 : 08 : feedback +16 : 10 : end of meeting