diff --git a/src/assets/checkmark.svg b/src/assets/checkmark.svg new file mode 100644 index 00000000..85c424e3 --- /dev/null +++ b/src/assets/checkmark.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/hc2024qrcode.svg b/src/assets/hc2024qrcode.svg new file mode 100644 index 00000000..d3a1af3d --- /dev/null +++ b/src/assets/hc2024qrcode.svg @@ -0,0 +1,521 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/hc_background.svg b/src/assets/hc_background.svg new file mode 100644 index 00000000..342ceda7 --- /dev/null +++ b/src/assets/hc_background.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/components/ApplicationDashboard.jsx b/src/components/ApplicationDashboard.jsx index 564dec75..999dfc86 100644 --- a/src/components/ApplicationDashboard.jsx +++ b/src/components/ApplicationDashboard.jsx @@ -252,6 +252,22 @@ export const hackerStatuses = (relevantDates, hackerName = null, activeHackathon nwplus.io {' '} to learn about more events and other ways to engage with the technology community. +
+ While we are currently at full capacity, we'd love to still have you join our community! + Join us for Learn Day - a jam-packed day of workshops that's open to everyone, regardless of + your application status. Everyone is welcome to attend our{' '} + + pre-hackathon workshops + + , where you can expand your technical and career knowledge while connecting with other + students. Visit the workshop page to find detailed descriptions and pre-requisite + information for each session! ), }, @@ -373,12 +389,14 @@ const Dashboard = ({ setMediaConsentCheck, ageOfMajoritySelect, setAgeOfMajoritySelect, - willBeAttendingSelect, - setWillBeAttendingSelect, + willBeAttendingCheck, + setWillBeAttendingCheck, safewalkSelect, setSafewalkSelect, - nwMentorshipSelect, - setNwMentorshipSelect, + // nwMentorshipSelect, + // setNwMentorshipSelect, + hcFeatureSelect, + setHcFeatureSelect, username, editApplication, relevantDates, @@ -395,9 +413,10 @@ const Dashboard = ({ const [releaseLiability, setReleaseLiability] = useState(releaseLiabilityCheck || undefined) const [mediaConsent, setMediaConsent] = useState(mediaConsentCheck || undefined) // const [ageOfMajority, setAgeOfMajority] = useState(ageOfMajoritySelect || undefined) - const [willBeAttending, setWillBeAttending] = useState(willBeAttendingSelect || undefined) + const [willBeAttending, setWillBeAttending] = useState(willBeAttendingCheck || false) const [safewalk, setSafewalk] = useState(safewalkSelect || undefined) - const [nwMentorship, setNwMentorship] = useState(nwMentorshipSelect || undefined) + // const [nwMentorship, setNwMentorship] = useState(nwMentorshipSelect || undefined) + const [hcFeature, setHcFeature] = useState(hcFeatureSelect || false) const hackerRSVPStatus = hackerStatuses()[hackerStatus]?.sidebarText @@ -430,9 +449,9 @@ const Dashboard = ({ // setAgeOfMajoritySelect(e.target.value) // } - const handleWillBeAttendingSelectChange = e => { - setWillBeAttending(e.target.value) - setWillBeAttendingSelect(e.target.value) + const handleWillBeAttendingChange = () => { + setWillBeAttending(!willBeAttending) + setWillBeAttendingCheck(!willBeAttendingCheck) } const handleSafewalkSelectChange = e => { @@ -440,9 +459,14 @@ const Dashboard = ({ setSafewalkSelect(e.target.value) } - const handleNwMentorshipSelectChange = e => { - setNwMentorship(e.target.value) - setNwMentorshipSelect(e.target.value) + // const handleNwMentorshipSelectChange = e => { + // setNwMentorship(e.target.value) + // setNwMentorshipSelect(e.target.value) + // } + + const handleHcFeatureChange = value => { + setHcFeature(value) + setHcFeatureSelect(value) } const handleRSVPClick = () => { @@ -495,15 +519,11 @@ const Dashboard = ({ {relevantDates.hackathonWeekend}? - - @@ -594,7 +614,7 @@ const Dashboard = ({ width="130px" target="_blank" rel="noopener noreferrer" - href={waiversAndForms.COVID} + href={waiversAndForms.covid} > Read Full Waiver. {' '} @@ -632,6 +652,30 @@ const Dashboard = ({ + HackCamp 2024 Feature Preference +

+ We are looking for people to be featured in interview videos about their experience + at HackCamp. Filming will take ~10 mins and will take place during Build Day + (Sunday, November 10th). If chosen, our team will reach out with further + instructions. Are you interested in participating?{' '} +

+ + handleHcFeatureChange(true)} + label="Yes" + /> + + + handleHcFeatureChange(false)} + label="No" + /> + +
+ + {/* nwMentorship Program

I would like to participate in the nwMentorship program to connect with an industry @@ -669,7 +713,7 @@ const Dashboard = ({ /> - + */} )} diff --git a/src/components/MobileMenuBar.jsx b/src/components/MobileMenuBar.jsx index 2733184f..799a4c65 100644 --- a/src/components/MobileMenuBar.jsx +++ b/src/components/MobileMenuBar.jsx @@ -24,25 +24,22 @@ const MobileMenuBarContainer = styled.div` display: none; z-index: 100; ${p => p.theme.mediaQueries.mobile} { - padding: 15px; - display: inline-block; - text-align: center; + padding: 15px 0; width: 100%; - box-sizing: border-box; - // -webkit-box-shadow: 0 6px 8px -8px #000; - // -moz-box-shadow: 0 6px 8px -8px #000; - // box-shadow: 0 6px 8px -8px #000; + + display: flex; + justify-content: center; + align-items: center; } ` const Logo = styled.img` - display: inline-block; - margin-left: -50px; - width: 40px; + height: 60px; ` const Menu = styled.img` - float: left; + position: absolute; + left: 20px; width: 30px; cursor: pointer; ` diff --git a/src/components/QrCode.jsx b/src/components/QrCode.jsx index 1ec182d8..949cde5d 100644 --- a/src/components/QrCode.jsx +++ b/src/components/QrCode.jsx @@ -3,15 +3,18 @@ import styled from 'styled-components' import { useQRCode } from 'next-qrcode' // import JsPDF from 'jspdf' // import html2canvas from 'html2canvas' -import qrcodeBackground from '../assets/cmdf2024qrcode.svg' +import qrcodeBackground from '../assets/hc2024qrcode.svg' import AppleWalletButtonImage from '../assets/apple_wallet_button.svg' const QRContainer = styled.div` display: flex; z-index: 98; - ${p => p.theme.mediaQueries.mobile} { + justify-content: space-around; + + ${p => p.theme.mediaQueries.tabletLarge} { flex-direction: column; align-items: center; + justify-content: center; } ` @@ -20,21 +23,43 @@ const QRContainer = styled.div` // display: flex; // ` -const QRCodeDesignContainer = styled.div`` +const QRTicketContainer = styled.div` + position: relative; + width: 442.8px; + height: 583.2px; + margin-top: 30px; + + ${p => p.theme.mediaQueries.mobile} { + width: 354.24px; + height: 464.4px; + } +` + +const QRCodeBackground = styled.img` + position: absolute; + width: 442.8px; + height: 583.2px; + + ${p => p.theme.mediaQueries.mobile} { + width: 354.24px; + height: 464.4px; + } +` const QRCodeDesign = styled.div` + display: flex; + flex-direction: column; + align-items: flex-start; position: relative; - width: 300px; - height: 500px; - background-size: 100% auto; - background-repeat: no-repeat; - padding: 45px; + left: 50px; + top: 80px; border-radius: 20px; z-index: 10; - background-image: url(${qrcodeBackground}); - margin: 0 auto; - display: block; - margin-top: 30px; + + ${p => p.theme.mediaQueries.mobile} { + left: 30px; + top: 40px; + } ` const HackerName = styled.h1` @@ -43,12 +68,20 @@ const HackerName = styled.h1` margin-top: 60px; position: relative; color: ${p => p.theme.colors.cardText}; + + ${p => p.theme.mediaQueries.mobile} { + font-size: 1.5em; + } ` const HackerEmail = styled.p` color: #2e2e2e !important; font-size: 1.2em; margin-top: -10px; + + ${p => p.theme.mediaQueries.mobile} { + font-size: 1em; + } ` const QRTags = styled.div` @@ -78,14 +111,6 @@ const QRTags = styled.div` // display: block; // ` -const QRTicketContainer = styled.div` - float: left; - width: 50%; - ${p => p.theme.mediaQueries.mobile} { - float: none; - width: 100%; - } -` const QRInfo = styled.div` float: right; width: 45%; @@ -94,7 +119,7 @@ const QRInfo = styled.div` ${p => p.theme.mediaQueries.mobile} { float: none; - margin-top: 0; + margin-top: 20px; width: 100%; padding-left: 0px; } @@ -125,9 +150,6 @@ const QRInfoName = styled.h1` const QRInfoDes = styled.p`` const AppleWalletButton = styled.button` - position: absolute; - left: 40px; - bottom: 100px; width: 110px; height: 35px; padding: 10px; @@ -136,7 +158,9 @@ const AppleWalletButton = styled.button` background-size: auto auto; background-color: transparent; cursor: pointer; + margin-top: 30px; ` + // temporary comment out for lint fix // const generatePDF = () => { // // const report = new JsPDF('portrait', 'pt', [300, 500.01]) @@ -171,40 +195,33 @@ const QrCode = ({ userInfo, userId }) => { Welcome, {userInfo.displayName}! - - - {userInfo.displayName} - {userInfo.email} + + + {userInfo.displayName} + {userInfo.email} - - {/* {userInfo.safewalkNote ? SafeWalk: Yes : SafeWalk: No} + + {/* {userInfo.safewalkNote ? SafeWalk: Yes : SafeWalk: No} */} - - - - - {/* Please hold onto this QR Code for check-in, meals, etc */} - downloadAppleWalletPass()} /> - - - - {/* - {' '} - Save as PDF - */} + + + + + {/* downloadAppleWalletPass()} /> */} + @@ -212,7 +229,7 @@ const QrCode = ({ userInfo, userId }) => { {userInfo.displayName}! This ticket contains your personal QR code which will be scanned throughout the event. - Please add this ticket to your mobile wallet or take a screenshot. + Please take a screenshot or otherwise have your QR code ready to scan. diff --git a/src/components/Rewards/AttendedEvents.jsx b/src/components/Rewards/AttendedEvents.jsx new file mode 100644 index 00000000..017a9347 --- /dev/null +++ b/src/components/Rewards/AttendedEvents.jsx @@ -0,0 +1,83 @@ +import styled, { useTheme } from 'styled-components' +import { TagLegendContainer, TagLegends } from '../Schedule/Tag' +import { H1 } from '../Typography' +import AttendedEventsCard from './AttendedEventsCard' +import { EVENT_TYPES } from '../Schedule/Constants' +import { getEvents } from '../../utility/firebase' +import { useHackathon } from '../../utility/HackathonProvider' +import { useEffect } from 'react' +import { useState } from 'react' + +const AttendedEventsContainer = styled.div` + display: flex; + flex-direction: column; + gap: 0.75em; +` + +const AttendedEventsHeader = styled.div` + display: flex; + flex-direction: column; +` + +const TagLegendsContainer = styled(TagLegendContainer)` + justify-content: start; + padding-right: 0; +` + +const EventsList = styled.div` + display: flex; + flex-direction: column; + gap: 0.75em; + overflow-y: scroll; + width: 85%; + + ${p => p.theme.mediaQueries.mobile} { + width: 100%; + } +` + +const AttendedEvents = ({ userDetails }) => { + const { dbHackathonName } = useHackathon() + const theme = useTheme() + const event_type = EVENT_TYPES(theme) + const [events, setEvents] = useState([]) + + useEffect(() => { + // prettier insisted on the semicolon + ;(async () => { + if (userDetails && dbHackathonName) { + console.log(userDetails) + const eventIds = userDetails.dayOf.events.map(event => event.eventId) + const events = await getEvents(dbHackathonName) + const filteredEvents = events.filter(event => eventIds.includes(event.key)) + setEvents(filteredEvents) + } + })() + }, [userDetails, dbHackathonName]) + + return ( + + +

Attended events

+ + + + + {userDetails && ( + + {events.map((event, i) => ( + + ))} + + )} + + ) +} + +export default AttendedEvents diff --git a/src/components/Rewards/AttendedEventsCard.jsx b/src/components/Rewards/AttendedEventsCard.jsx new file mode 100644 index 00000000..087fbeca --- /dev/null +++ b/src/components/Rewards/AttendedEventsCard.jsx @@ -0,0 +1,86 @@ +import styled from 'styled-components' +import { StyledSVG } from '../Schedule/Tag' +import { P } from '../Typography' +import Icon from '../../assets/checkmark.svg?react' + +const AttendedEventsCardContainer = styled.div` + display: flex; + align-items: center; + gap: 1em; + + color: ${p => p.theme.colors.cardText}; + background: ${p => p.theme.colors.backgroundTertiary}; + border-radius: 5px; + padding: 0.875em 1em; +` + +const EventDetailsContainer = styled.div` + flex-grow: 1; + display: flex; + justify-content: space-between; + gap: 2em; + align-items: center; +` + +const TagPointsContainer = styled.div` + display: flex; + flex-direction: column; + align-items: end; +` + +const CheckmarkSVG = styled(Icon)` + fill: ${props => props.color}; + ${p => p.theme.mediaQueries.mobile} { + height: 0.75em; + width: 0.75em; + } +` + +const EventDetails = styled.div` + display: flex; + flex-direction: column; +` + +const EventName = styled(P)` + margin: 0; + font-weight: 700; +` + +const EventTime = styled(P)` + margin: 0; + font-size: 0.875em; +` + +const PointsText = styled(P)` + font-weight: 700; + color: ${props => props.color}; + font-size: 0.875em; + margin: 0; +` + +/** + * @typedef {{name: string, time: string, points: number, color: string}} AttendedEventsCardProps + */ + +/** + * @param {AttendedEventsCardProps} param0 + */ +const AttendedEventsCard = ({ name, time, points, color }) => { + return ( + + + + + {name} + {time} + + + + {points} pts earned + + + + ) +} + +export default AttendedEventsCard diff --git a/src/components/Rewards/TotalPoints.jsx b/src/components/Rewards/TotalPoints.jsx new file mode 100644 index 00000000..ff9c81d5 --- /dev/null +++ b/src/components/Rewards/TotalPoints.jsx @@ -0,0 +1,69 @@ +import styled from 'styled-components' +import { getEvents } from '../../utility/firebase' +import { useHackathon } from '../../utility/HackathonProvider' +import { useState, useEffect } from 'react' + +const TotalPointsName = styled.h2` + color: ${p => p.theme.colors.highlight}; + font-weight: 700; + font-size: 30px; +` + +const TotalPointsCard = styled.div` + background: ${p => p.theme.colors.backgroundTertiary}; + display: flex; + flex-direction: column; + align-items: left; + justify-content: center; + padding-left: 29px; + border-radius: 5px; + width: 302px; + height: 113px; +` + +const PointsTitle = styled.div` + color: ${p => p.theme.colors.textSecondary}; + font-size: 18px; + font-weight: 700; +` + +const PointsValue = styled.div` + color: ${p => p.theme.colors.highlight}; + font-size: 40px; + font-weight: 700; +` + +const TotalPoints = ({ userDetails }) => { + const { dbHackathonName } = useHackathon() + const [name, setName] = useState('') + const [totalPoints, setTotalPoints] = useState(0) + + useEffect(() => { + ;(async () => { + if (userDetails && dbHackathonName) { + const eventIds = userDetails.dayOf.events.map(event => event.eventId) + const events = await getEvents(dbHackathonName) + const filteredEvents = events.filter(event => eventIds.includes(event.key)) + + setName(`${userDetails.basicInfo.preferredName} ${userDetails.basicInfo.legalLastName}`) + setTotalPoints( + filteredEvents.reduce((accumulator, event) => { + return accumulator + parseInt(event.points) + }, 0) + ) + } + })() + }, [userDetails, dbHackathonName]) + + return ( +
+ {name} + + TOTAL POINTS + {totalPoints.toLocaleString()} pts + +
+ ) +} + +export default TotalPoints diff --git a/src/components/Schedule/Event.jsx b/src/components/Schedule/Event.jsx index 2b6db893..157d03b0 100644 --- a/src/components/Schedule/Event.jsx +++ b/src/components/Schedule/Event.jsx @@ -76,7 +76,7 @@ const EventCard = styled(Card)` margin-right: 0; margin-top: -0.5em; width: 100%; - height: ${MOBILE_HOUR_HEIGHT - EVENT_GAP} px; + height: ${MOBILE_HOUR_HEIGHT - EVENT_GAP}px; } } diff --git a/src/components/Schedule/Schedule.jsx b/src/components/Schedule/Schedule.jsx index 545c4b18..e485ccff 100644 --- a/src/components/Schedule/Schedule.jsx +++ b/src/components/Schedule/Schedule.jsx @@ -4,7 +4,7 @@ import { ScrollbarLike } from '../Common' import { H1 } from '../Typography' import { EVENT_GAP, MOBILE_HOUR_HEIGHT } from './Constants' import Event from './Event' -import { TagLegend } from './Tag' +import { TagLegends, TagLegendContainer } from './Tag' import { TimelineColumn } from './Timeline' import { useTheme } from 'styled-components' @@ -45,6 +45,7 @@ const MobileScrollableContainer = styled.div` margin-top: 1em; border-radius: 10px; background: #244556; + background: ${p => p.theme.colors.schedule.background}; &:after { content: ''; position: fixed; @@ -311,7 +312,9 @@ const Schedule = ({ events, hackathonStart, hackathonEnd }) => {
Day-Of-Events Schedule
- + + +
Saturday @@ -338,7 +341,9 @@ const Schedule = ({ events, hackathonStart, hackathonEnd }) => {
Day-Of-Events Schedule
- + + +
diff --git a/src/components/Schedule/Tag.jsx b/src/components/Schedule/Tag.jsx index 69922572..27a6131e 100644 --- a/src/components/Schedule/Tag.jsx +++ b/src/components/Schedule/Tag.jsx @@ -45,17 +45,13 @@ export const StyledSVG = styled(Icon)` } ` -export const TagLegend = ({ theme }) => { - return ( - - {Object.entries(EVENT_TYPES(theme)).map(([key, event_type], i) => { - return ( - - - {event_type.label} - - ) - })} - - ) +export const TagLegends = ({ theme }) => { + return Object.entries(EVENT_TYPES(theme)).map(([key, event_type], i) => { + return ( + + + {event_type.label} + + ) + }) } diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx index b519fcf1..be7c7af6 100644 --- a/src/components/Sidebar.jsx +++ b/src/components/Sidebar.jsx @@ -195,7 +195,7 @@ const Sidebar = ({ { location: '/', text: 'Home' }, { location: '/schedule', text: 'Schedule' }, { location: '/livestream', text: 'Livestream' }, - { location: '/rewards', text: 'Rewards' }, + // { location: '/rewards', text: 'Rewards' }, { location: '/sponsors', text: 'Sponsors' }, ], // Tools @@ -266,9 +266,9 @@ const Sidebar = ({ links.tools.push({ location: '/judging/admin', text: 'Judging Admin' }) } - if (import.meta.env.NODE_ENV !== 'production') { - links.information.push({ location: '/charcuterie', text: 'CHARCUTERIE' }) - } + // if (import.meta.env.NODE_ENV !== 'production') { + // links.information.push({ location: '/charcuterie', text: 'CHARCUTERIE' }) + // } if (isApplicationOpen) { // List the application as the last item on the menu diff --git a/src/containers/Application/Dashboard.jsx b/src/containers/Application/Dashboard.jsx index 562d0854..77f7fc4b 100644 --- a/src/containers/Application/Dashboard.jsx +++ b/src/containers/Application/Dashboard.jsx @@ -137,10 +137,10 @@ const ApplicationDashboardContainer = () => { forceSave() } - const setWillBeAttendingSelect = willBeAttendingSelect => { + const setWillBeAttendingCheck = willBeAttendingCheck => { updateApplication({ basicInfo: { - willBeAttendingSelect, + willBeAttendingCheck, }, }) forceSave() @@ -164,6 +164,15 @@ const ApplicationDashboardContainer = () => { forceSave() } + const setHcFeatureSelect = hcFeatureSelect => { + updateApplication({ + basicInfo: { + hcFeatureSelect, + }, + }) + forceSave() + } + const handleWaiver = async waiver => { // check to make sure its under 2mb const size = (waiver.size / 1024 / 1024).toFixed(2) @@ -206,14 +215,16 @@ const ApplicationDashboardContainer = () => { setMediaConsentCheck={mediaConsentCheck => setMediaConsentCheck(mediaConsentCheck)} ageOfMajoritySelect={application.basicInfo.ageOfMajoritySelect || undefined} setAgeOfMajoritySelect={ageOfMajoritySelect => setAgeOfMajoritySelect(ageOfMajoritySelect)} - willBeAttendingSelect={application.basicInfo.willBeAttendingSelect || undefined} - setWillBeAttendingSelect={willBeAttendingSelect => - setWillBeAttendingSelect(willBeAttendingSelect) + willBeAttendingCheck={application.basicInfo.willBeAttendingCheck || false} + setWillBeAttendingCheck={willBeAttendingCheck => + setWillBeAttendingCheck(willBeAttendingCheck) } safewalkSelect={application.basicInfo.safewalkSelect || undefined} setSafewalkSelect={safewalkSelect => setSafewalkSelect(safewalkSelect)} - nwMentorshipSelect={application.basicInfo.nwMentorshipSelect || undefined} - setNwMentorshipSelect={nwMentorshipSelect => setNwMentorshipSelect(nwMentorshipSelect)} + // nwMentorshipSelect={application.basicInfo.nwMentorshipSelect || undefined} + // setNwMentorshipSelect={nwMentorshipSelect => setNwMentorshipSelect(nwMentorshipSelect)} + hcFeatureSelect={application.basicInfo.hcFeatureSelect || undefined} + setHcFeatureSelect={hcFeatureSelect => setHcFeatureSelect(hcFeatureSelect)} relevantDates={relevantDates} waiversAndForms={waiversAndForms} notionLinks={notionLinks} diff --git a/src/containers/Rewards.jsx b/src/containers/Rewards.jsx index 49bec6be..103b322d 100644 --- a/src/containers/Rewards.jsx +++ b/src/containers/Rewards.jsx @@ -1,8 +1,12 @@ +import styled from 'styled-components' import React, { useEffect, useState } from 'react' import { useHackathon } from '../utility/HackathonProvider' import { rewardsRef } from '../utility/firebase' -import styled from 'styled-components' import RewardCard from '../components/RewardCard' +// import TotalPoints from '../components/Rewards/TotalPoints' +// import AttendedEvents from '../components/Rewards/AttendedEvents' +// import { useAuth } from '../utility/Auth' +// import { getUserApplication } from '../utility/firebase' const Container = styled.div` display: grid; @@ -30,7 +34,7 @@ const Subtitle = styled.h3` const Cards = styled.div` display: grid; grid-template-columns: 1fr 1fr; - column-gap: 0px; + column-gap: 32px; row-gap: 32px; @media (max-width: 768px) { @@ -62,10 +66,13 @@ const Rewards = () => { return ( -
+ {/*
*/}
Rewards
- Enter a raffle for prizes by collecting points or attending events! + + A list of hackathon and raffle prizes available! Enter a raffle for prizes by collecting + points or attending events! + {rewards.map((reward, index) => ( { ) } -export default Rewards \ No newline at end of file +export default Rewards diff --git a/src/pages/Home.jsx b/src/pages/Home.jsx index 9cec8c3e..0783d3e3 100644 --- a/src/pages/Home.jsx +++ b/src/pages/Home.jsx @@ -8,10 +8,11 @@ import QrCode from '../components/QrCode' import { useAuth } from '../utility/Auth' // import Hackcamp2023BG from '../components/BackgroundImage' import { APPLICATION_STATUS } from '../utility/Constants' -import backgroundImage from '../assets/cmdf_homebg.svg' +import backgroundImage from '../assets/hc_background.svg' import mobileBackgroundImage from '../assets/cmdf_mobilebg.svg' import { copyText } from '../utility/Constants' import { useHackathon } from '../utility/HackathonProvider' +import { css } from 'styled-components' //My Ticket const HomeContainer = styled.div` @@ -21,7 +22,6 @@ const HomeContainer = styled.div` flex-direction: column; gap: 1.5em; z-index: 3; - background-color: ${p => p.theme.colors.background}; ${p => p.theme.mediaQueries.mobile} { gap: 1em; } @@ -38,13 +38,11 @@ const HomeContainerBackground = styled.div` ${props => props.backgroundImage && css` - background-image: url(${backgroundImage}); + background-image: url(${props.backgroundImage}); background-size: cover; background-position: right bottom; ${p => p.theme.mediaQueries.mobile} { - background-image: url(${mobileBackgroundImage}); - background-size: cover; background-position: center; } `} @@ -68,7 +66,7 @@ export default withTheme(({ announcements, theme }) => { return ( <> - + {/* {activeHackathon === 'hackcamp' && } */} diff --git a/src/pages/Judging/index.jsx b/src/pages/Judging/index.jsx index 17ead80f..694c100e 100644 --- a/src/pages/Judging/index.jsx +++ b/src/pages/Judging/index.jsx @@ -15,50 +15,68 @@ const StyledJudgingCard = styled(JudgingCard)` const getProjects = async (userId, projectId, dbHackathonName) => { const getAndAssignProjects = async () => { - const projectDocs = await projectsRef(dbHackathonName) - .where('draftStatus', '==', 'public') - .orderBy('countAssigned') - .limit(PROJECTS_TO_JUDGE_COUNT + 1) // get an extra in case we got our own project - .get() - let projectIds = projectDocs.docs.map(project => project.id) - projectIds = projectIds.filter(id => id !== projectId) - if (projectIds.length > PROJECTS_TO_JUDGE_COUNT) { - projectIds.pop() - } - const batch = db.batch() + try { + return db.runTransaction(async transaction => { + const projectDocs = await transaction.get( + projectsRef(dbHackathonName) + .where('draftStatus', '==', 'public') + .orderBy('countAssigned') + .limit(PROJECTS_TO_JUDGE_COUNT + 1) // get an extra in case we got our own project + ) - // increment assigned counters - projectIds.forEach(projectId => { - batch.update(projectsRef(dbHackathonName).doc(projectId), { - countAssigned: firestore.FieldValue.increment(1), - }) - }) + let projectIds = projectDocs.docs.map(project => project.id) + projectIds = projectIds.filter(id => id !== projectId) + if (projectIds.length > PROJECTS_TO_JUDGE_COUNT) { + projectIds.pop() + } - // add projects to user - batch.update(applicantsRef(dbHackathonName).doc(userId), { projectsAssigned: projectIds }) - await batch.commit() - return projectIds + // increment assigned counters + projectIds.forEach(projectId => { + const projectRef = projectsRef(dbHackathonName).doc(projectId) + transaction.update(projectRef, { + countAssigned: firestore.FieldValue.increment(1), + }) + }) + + // add projects to user + const applicantRef = applicantsRef(dbHackathonName).doc(userId) + transaction.update(applicantRef, { + projectsAssigned: projectIds, + }) + return projectIds + }) + } catch (error) { + console.error('Error assigning projects:', error) + } } const queryProjects = async projectIds => { - const projectDocs = await projectsRef(dbHackathonName) - .where(firestore.FieldPath.documentId(), 'in', projectIds) - .get() - if (projectDocs.docs.length < 1) { - // projects are missing - const newProjectIds = await getAndAssignProjects() - return await queryProjects(newProjectIds) + try { + const projectDocs = await projectsRef(dbHackathonName) + .where(firestore.FieldPath.documentId(), 'in', projectIds) + .get() + if (projectDocs.docs.length < 1) { + // projects are missing + const newProjectIds = await getAndAssignProjects() + return await queryProjects(newProjectIds) + } + return projectDocs + } catch (error) { + console.error('Error querying projects:', error) } - return projectDocs } const getProjectsData = async projectIds => { - let projectDocs = await queryProjects(projectIds) - return projectDocs.docs.map(project => { - const formattedProject = formatProject({ ...project.data(), id: project.id }) - formattedProject.judged = formattedProject.grades && formattedProject.grades[userId] - return formattedProject - }) + try { + let projectDocs = await queryProjects(projectIds) + return projectDocs.docs.map(project => { + const formattedProject = formatProject({ ...project.data(), id: project.id }) + formattedProject.judged = formattedProject.grades && formattedProject.grades[userId] + return formattedProject + }) + } catch (error) { + console.error('Error getting project data:', error) + } } const applicantDoc = await applicantsRef(dbHackathonName).doc(userId).get() diff --git a/src/theme/ThemeProvider.jsx b/src/theme/ThemeProvider.jsx index 3d253981..c152f8df 100644 --- a/src/theme/ThemeProvider.jsx +++ b/src/theme/ThemeProvider.jsx @@ -45,8 +45,8 @@ const hackcampTheme = { ...base, name: 'hackcamp', colors: { - background: 'linear-gradient(0deg, #94D6EF, #94D6EF)', - backgroundSecondary: '#75AEE2', + background: '#C8E5F0', + backgroundSecondary: '#93BEE5', backgroundTertiary: 'linear-gradient(180deg, #F9C745 0%, #FF880F 100%)', text: '#45171A', textSecondary: '#01033D', @@ -106,8 +106,8 @@ const hackcampTheme = { schedule: { background: 'linear-gradient(180deg, rgba(0, 163, 224, 0.6) 0%, rgba(255, 248, 229, 0.6) 99.98%)', - mainEventTag: '#00A3E0', - workshopTag: '#F3F3F3', + mainEventTag: '#3268A5', + workshopTag: '#DE0148', activityTag: '#A9158D', lines: '#FFFFFF', }, diff --git a/src/utility/Auth.jsx b/src/utility/Auth.jsx index 397f2639..cf93917c 100644 --- a/src/utility/Auth.jsx +++ b/src/utility/Auth.jsx @@ -7,6 +7,7 @@ import Spinner from '../components/Loading' import { useLocation } from 'wouter' import { useHackathon } from './HackathonProvider' +/** @type {React.Context<{isAuthed: boolean, user: firebase.User | null, setUser: React.Dispatch>, logout: () => Promise}>} */ const AuthContext = createContext() export function useAuth() { @@ -81,7 +82,7 @@ export const getRedirectUrl = (redirect, activeHackathon) => { case REDIRECT_STATUS.ApplicationNotSubmitted: return `/app/${activeHackathon}/application/part-0` case REDIRECT_STATUS.ApplicationSubmitted: - return `/app/${activeHackathon}` + return `/app/${activeHackathon}/application` default: return `/app/${activeHackathon}/application` } diff --git a/src/utility/HackerApplicationContext.jsx b/src/utility/HackerApplicationContext.jsx index bdf058f9..ad9883ca 100644 --- a/src/utility/HackerApplicationContext.jsx +++ b/src/utility/HackerApplicationContext.jsx @@ -211,7 +211,7 @@ export function HackerApplicationProvider({ children }) { return null } - if (!applicationOpen && window.location.pathname !== '/application') { + if (!applicationOpen && !window.location.pathname.endsWith('/application')) { return } diff --git a/src/utility/Routes.jsx b/src/utility/Routes.jsx index 73bc1f2d..eba39f64 100644 --- a/src/utility/Routes.jsx +++ b/src/utility/Routes.jsx @@ -15,8 +15,9 @@ const NestedRoutes = props => { const hackathonFromURL = props.base.split('/')[2].toLowerCase() useEffect(() => { - if (VALID_HACKATHONS.includes(hackathonFromURL) && hackathonFromURL !== activeHackathon) { + if (VALID_HACKATHONS.includes(hackathonFromURL)) { setActiveHackathon(hackathonFromURL) + localStorage.setItem('activeHackathon', activeHackathon) } }, [props.base, activeHackathon, setActiveHackathon]) @@ -48,7 +49,8 @@ const PageRoute = ({ path, children }) => { return ( - {livesiteDoc?.applicationsOpen[activeHackathon] ? ( + {livesiteDoc?.applicationsOpen[activeHackathon] || + !livesiteDoc?.portalLive[activeHackathon] ? ( ) : ( {children} diff --git a/src/utility/firebase.js b/src/utility/firebase.js index bffc4194..a0f49184 100644 --- a/src/utility/firebase.js +++ b/src/utility/firebase.js @@ -41,6 +41,9 @@ export const applicantsRef = dbHackathonName => { export const projectsRef = dbHackathonName => { return db.collection(DB_COLLECTION).doc(dbHackathonName).collection('Projects') } +export const eventsRef = dbHackathonName => { + return db.collection(DB_COLLECTION).doc(dbHackathonName).collection('Events') +} export const announcementsRef = dbHackathonName => { return db.collection(DB_COLLECTION).doc(dbHackathonName).collection('Announcements') } @@ -54,6 +57,10 @@ export const getLivesiteDoc = callback => { }) } +export const getEvents = async dbHackathonName => { + return (await eventsRef(dbHackathonName).get()).docs.map(doc => doc.data()) +} + const createNewApplication = async (user, dbHackathonName) => { analytics.logEvent(ANALYTICS_EVENTS.signup, { userId: user.uid }) const userId = {