diff --git a/client/src/assets/judges/tyler.png b/client/src/assets/judges/tyler.png new file mode 100644 index 00000000..943b61a9 Binary files /dev/null and b/client/src/assets/judges/tyler.png differ diff --git a/client/src/assets/judges/will.jpg b/client/src/assets/judges/will.jpg new file mode 100644 index 00000000..bed897fd Binary files /dev/null and b/client/src/assets/judges/will.jpg differ diff --git a/client/src/pages/FAQ/FAQ.jsx b/client/src/pages/FAQ/FAQ.jsx index 27d898ba..ccd86ae4 100644 --- a/client/src/pages/FAQ/FAQ.jsx +++ b/client/src/pages/FAQ/FAQ.jsx @@ -1,4 +1,4 @@ -import { React, useState, useEffect, useContext } from 'react'; +import React, { useState, useEffect, useContext } from 'react'; import PropTypes from 'prop-types'; import { getQuestions } from './functions'; import './FAQ.scss'; @@ -16,7 +16,7 @@ import LoadingAnimation from '../../components/misc/LoadingAnimation/LoadingAnim import Dragon from '../../assets/faq/dragon.svg'; const PageFAQ = () => { - const { darkMode, setDarkModeStatus } = useContext(DarkModeContext); + const { darkMode } = useContext(DarkModeContext); const [activeIndex, setActiveIndex] = useState(0); const [isSearch, setIsSearch] = useState(false); const [isMultiSearch, setIsMultiSearch] = useState(false); @@ -32,6 +32,10 @@ const PageFAQ = () => { const [errorLoading, setErrorLoading] = useState(false); const { setSnackbar } = useContext(SnackbarContext); + // Pagination state + const [currentPage, setCurrentPage] = useState(1); + const itemsPerPage = 5; + const loadQuestions = async () => { const data = await getQuestions(setSnackbar); if (!data) { @@ -79,6 +83,29 @@ const PageFAQ = () => { loadQuestions(); }, []); + useEffect(() => { + // Reset current page to 1 when category (activeIndex) changes + setCurrentPage(1); + }, [activeIndex]); + + // Calculate total pages + const totalPages = Math.ceil((allQuestions[activeIndex]?.length || 0) / itemsPerPage); + + // Handle page change + const handlePageChange = (page) => { + setCurrentPage(page); + }; + + // Get current page questions + const currentQuestions = allQuestions[activeIndex]?.slice( + (currentPage - 1) * itemsPerPage, + currentPage * itemsPerPage, + ); + + console.log('Current Page:', currentPage); + console.log('Total Pages:', totalPages); + console.log('Current Questions:', currentQuestions); + return (
@@ -129,10 +156,15 @@ const PageFAQ = () => { }`} > +
{ - const { darkMode, setDarkModeStatus } = useContext(DarkModeContext); + const { darkMode } = useContext(DarkModeContext); const filterQuestions = (questions, query) => { if (!query) { return questions; @@ -313,9 +345,9 @@ FAQButtons.propTypes = { questionCategories: PropTypes.array.isRequired, }; -const FAQCategoryAccordions = ({ allQuestions, activeIndex }) => { - if (allQuestions[activeIndex] === undefined) return <>; - const questionsAccordion = allQuestions[activeIndex].map((question, index) => ( +const FAQCategoryAccordions = ({ currentQuestions, activeIndex }) => { + if (!currentQuestions) return <>; + const questionsAccordion = currentQuestions.map((question, index) => (
@@ -324,7 +356,7 @@ const FAQCategoryAccordions = ({ allQuestions, activeIndex }) => { }; FAQCategoryAccordions.propTypes = { - allQuestions: PropTypes.array.isRequired, + currentQuestions: PropTypes.array.isRequired, activeIndex: PropTypes.number.isRequired, }; @@ -423,4 +455,24 @@ FAQDisplayAllSearchQuestion.propTypes = { questions: PropTypes.array.isRequired, }; +const PaginationControls = ({ currentPage, totalPages, handlePageChange }) => ( +
+ {Array.from({ length: totalPages }, (_, i) => ( + + ))} +
+); + +PaginationControls.propTypes = { + currentPage: PropTypes.number.isRequired, + totalPages: PropTypes.number.isRequired, + handlePageChange: PropTypes.func.isRequired, +}; + export { PageFAQ }; diff --git a/client/src/pages/FAQ/FAQ.scss b/client/src/pages/FAQ/FAQ.scss index 04d318b0..e13ba9aa 100644 --- a/client/src/pages/FAQ/FAQ.scss +++ b/client/src/pages/FAQ/FAQ.scss @@ -293,3 +293,30 @@ text-align: center; color: var(--text-dynamic); } + +.pagination-controls { + display: flex; + justify-content: center; + margin-top: 20px; + + button { + margin: 0 5px; + padding: 10px 15px; + border: none; + border-radius: 5px; + background-color: var(--purple); + color: white; + cursor: pointer; + transition: background-color 0.3s, color 0.3s; + + &:hover { + background-color: var(--light-purple); + color: var(--purple-shades-dark); + } + + &.active { + background-color: var(--purple-shades-dark); + color: white; + } + } +} diff --git a/client/src/pages/Initial/AshLanding/AshLanding.jsx b/client/src/pages/Initial/AshLanding/AshLanding.jsx index c560a044..2141a46a 100644 --- a/client/src/pages/Initial/AshLanding/AshLanding.jsx +++ b/client/src/pages/Initial/AshLanding/AshLanding.jsx @@ -44,7 +44,6 @@ const AshLanding = () => { />
-

Made with đź’ś by the F!rosh Week 2T4 Tech Team

diff --git a/client/src/pages/Initial/AshLanding/AshLanding.scss b/client/src/pages/Initial/AshLanding/AshLanding.scss index e4b6a7f0..d7fc86d6 100644 --- a/client/src/pages/Initial/AshLanding/AshLanding.scss +++ b/client/src/pages/Initial/AshLanding/AshLanding.scss @@ -28,6 +28,7 @@ text-align: center; padding-top: 20%; } + .ash-title-text { font-size: 64px; font-style: bold; @@ -35,6 +36,7 @@ margin-bottom: 24px; text-shadow: 0px 7px 10px rgba(0, 0, 0, 0.25); } + hr.ash-line { top: 10px; width: 851px; @@ -43,6 +45,7 @@ hr.ash-line { border-radius: 100px; margin-bottom: 24px; } + .ash-subtitle { font-size: 20px; letter-spacing: 0.05em; @@ -58,6 +61,7 @@ hr.ash-line { margin: 0 auto; padding-top: 70px; } + .ash-button-container { display: grid; grid-template-columns: 1fr; @@ -69,6 +73,7 @@ hr.ash-line { grid-template-columns: 1fr 1fr; } } + .ash-button { display: flex; align-items: center; @@ -101,6 +106,7 @@ hr.ash-line { color: var(--purple-shades-dark); transition: 0.3s; } + .ash-button-icon { width: 30px; height: 30px; @@ -121,6 +127,7 @@ hr.ash-line { align-items: center; color: var(--purple-shades-muted); } + .ash-footer-text { text-align: center; font-size: 20px; @@ -148,6 +155,7 @@ hr.ash-line { text-align: center; padding-top: 60%; } + .ash-title-text { font-style: bold; font-size: 20px; @@ -158,12 +166,14 @@ hr.ash-line { margin-bottom: 10px; } + hr.ash-line { width: 274px; border: 1px solid var(--yellow); border-radius: 100px; margin-bottom: 10px; } + .ash-subtitle { font-size: 12px; max-width: 274px; diff --git a/client/src/pages/Initial/LandingPage.jsx b/client/src/pages/Initial/LandingPage.jsx index 1c523107..2a5ab4e5 100644 --- a/client/src/pages/Initial/LandingPage.jsx +++ b/client/src/pages/Initial/LandingPage.jsx @@ -6,6 +6,8 @@ import { WilliamLanding } from './WilliamLanding/WilliamLanding'; import { AlissaLanding } from './AlissaLanding/AlissaLanding'; import { AsmitaLanding } from './AsmitaLanding/AsmitaLanding'; +const currentYear = '2T4'; + const landingPages = [ { key: 0, @@ -25,25 +27,28 @@ const landingPages = [ }, ]; +// Change this logic to determine which landing pages to show +const landingPagesFiltered = landingPages.filter((page) => page.year === currentYear); + function randomNumber(min, max) { return Math.floor(Math.random() * (max - min + 1) + min); } export const LandingPage = () => { const [pageIndex, setPageIndex] = useState(null); - useEffect(() => { - let randIdx = randomNumber(0, landingPages.length - 1); - const localIdx = window.localStorage.getItem('landing_page_idx'); + if (landingPagesFiltered.length !== 1) { + let randIdx = randomNumber(0, landingPagesFiltered.length - 1); + const localIdx = window.localStorage.getItem('landing_page_idx'); - if (localIdx !== null) { - while (randIdx === JSON.parse(localIdx)) { - randIdx = randomNumber(0, landingPages.length - 1); + if (localIdx !== null) { + while (randIdx === JSON.parse(localIdx)) { + randIdx = randomNumber(0, landingPagesFiltered.length - 1); + } } + window.localStorage.setItem('landing_page_idx', JSON.stringify(randIdx)); + setPageIndex(JSON.parse(randIdx)); } - window.localStorage.setItem('landing_page_idx', JSON.stringify(randIdx)); - - setPageIndex(JSON.parse(randIdx)); }, []); useEffect(() => { @@ -54,11 +59,14 @@ export const LandingPage = () => { return ( <> - {landingPages.map((item) => { - if (item.key == pageIndex) { - return
{item.component}
; - } - })} + {landingPagesFiltered.length === 1 + ? landingPagesFiltered[0].component + : landingPagesFiltered.map((item) => { + if (item.key === pageIndex) { + return
{item.component}
; + } + return null; + })} ); }; diff --git a/client/src/pages/ScuntLeaderboard/ScuntLeaderboard.jsx b/client/src/pages/ScuntLeaderboard/ScuntLeaderboard.jsx index 0d292d9b..6dafc07a 100644 --- a/client/src/pages/ScuntLeaderboard/ScuntLeaderboard.jsx +++ b/client/src/pages/ScuntLeaderboard/ScuntLeaderboard.jsx @@ -171,6 +171,8 @@ const ScuntLeaderboardFullScreen = ({ arr }) => { }; const ScuntLeaderboardDesktop = ({ arr }) => { + arr?.sort((a, b) => b.computedPoints - a.computedPoints); + return (
@@ -192,7 +194,7 @@ const ScuntLeaderboardDesktop = ({ arr }) => { }; const ScuntLeaderboardMobile = ({ arr }) => { - arr.sort((a, b) => b.computedPoints - a.computedPoints); + arr?.sort((a, b) => b.computedPoints - a.computedPoints); return (
diff --git a/client/src/util/scunt-judges.jsx b/client/src/util/scunt-judges.jsx index a36f0230..6e6a21c2 100644 --- a/client/src/util/scunt-judges.jsx +++ b/client/src/util/scunt-judges.jsx @@ -15,6 +15,8 @@ import katie from '../assets/judges/katie.jpg'; import emaan from '../assets/judges/emaan.jpg'; import amelie from '../assets/judges/amelie.jpg'; import tech from '../assets/judges/tech.png'; +import tyler from '../assets/judges/tyler.png'; +import will from '../assets/judges/will.jpg'; export const scuntJudges = [ { @@ -275,4 +277,31 @@ export const scuntJudges = [ ], img: amelie, }, + { + name: 'Tyler', + description: 'Hello fellow kids, I enjoy CFCs and tobogganing down hills on concrete.', + content: [ + 'An unusual but delicious food combination', + 'Convince the bnad to crash me and join them', + 'Artwork of a cool train', + 'Show me your fursona', + 'Bring me all of my roommates', + 'Find the city bylaw that allows me to sue my landlord for not having running water', + ], + img: tyler, + }, + { + name: 'Will', + description: + 'Hay there! My name is Will I’m a chem 2T5 I’m one of the scunt co-chairs and I L O V E love animal facts', + content: [ + 'Get sturdy for me', + 'Get something new banded from the SUDS, the pit or SkavEnger Hunt’ that wasn’t banned before', + 'Make fan art of me and Tyler', + 'Best performance of“Jamaica there trapped down in…”', + 'Bring me a grocery store cake, I’ll pay you back', + 'Decorate my hat', + ], + img: will, + }, ]; diff --git a/server/src/app.js b/server/src/app.js index 08013180..a4527e77 100644 --- a/server/src/app.js +++ b/server/src/app.js @@ -22,6 +22,6 @@ const corsOptions = { app.use(cors(corsOptions)); app.use('/payment/stripe-callback', bodyParser.raw({ type: '*/*' })); -app.use(bodyParser.json()); +app.use(bodyParser.json({ limit: '50mb' })); module.exports = app; diff --git a/server/src/services/ScuntTeamServices.js b/server/src/services/ScuntTeamServices.js index a2f28775..3beedf7a 100644 --- a/server/src/services/ScuntTeamServices.js +++ b/server/src/services/ScuntTeamServices.js @@ -69,9 +69,9 @@ const ScuntTeamServices = { ); // finds the rank of the team (i.e., index in teams array) - const teamPosition = teams?.findIndex((t) => teamNumber === t.number) + 1; + const teamPosition = teams?.findIndex((t) => teamNumber === t.number); - return (teamPosition / teams.length) * totalPoints; + return (teamPosition / teams.length + 1.0) * totalPoints; } catch (error) { throw new Error('UNABLE_TO_CALCULATE_POINTS', { cause: error }); } @@ -85,8 +85,7 @@ const ScuntTeamServices = { * @returns {ScuntTeam , Leadur} */ async bribeTransaction(teamNumber, points, user) { - const curvedPoints = await this.calculatePoints(teamNumber, points); - if (!user.scuntJudgeBribePoints || curvedPoints > user.scuntJudgeBribePoints) + if (!user.scuntJudgeBribePoints || points > user.scuntJudgeBribePoints) throw new Error('NOT_ENOUGH_BRIBE_POINTS'); await ScuntGameSettingsModel.findOne({}).then( @@ -116,12 +115,12 @@ const ScuntTeamServices = { return ScuntTeamModel.findOneAndUpdate( { number: teamNumber }, { - $inc: { points: curvedPoints }, + $inc: { points }, $push: { transactions: [ { name: `${points.toString()} points bribe from ${user.firstName} ${user.lastName}`, - points: curvedPoints, + points, }, ], }, diff --git a/server/src/services/emailAssets/registrationEmail.html b/server/src/services/emailAssets/registrationEmail.html index f273bbfa..6b38887a 100644 --- a/server/src/services/emailAssets/registrationEmail.html +++ b/server/src/services/emailAssets/registrationEmail.html @@ -70,7 +70,7 @@ frosh logo

Hi ${},

-

Thank you for registering for F!rosh Week 243!!!

+

Thank you for registering for F!rosh Week 2T4!!!

Please find attached your information PDF which includes a receipt of your data and your unique QR code that you will use for a variety of activities during F!rosh Week. diff --git a/server/test/services/ScuntTeamServices.test.js b/server/test/services/ScuntTeamServices.test.js index f3a06609..6f6d31fb 100644 --- a/server/test/services/ScuntTeamServices.test.js +++ b/server/test/services/ScuntTeamServices.test.js @@ -416,7 +416,7 @@ describe('ScuntTeamServices', () => { await ScuntTeamServices.addTransaction(4, 1, 20); await ScuntTeamServices.addTransaction(4, 1, 10); const points = await ScuntTeamServices.checkTransaction(4, 1); - assert.equal(points, 14); + assert.equal(points, 31); }); it('.checkTransaction()\t\t|\tCheck a transaction (INVALID TEAM NUMBER)', async () => {