diff --git a/constants.js b/constants.js
index 6a1cfeaf..1042959f 100644
--- a/constants.js
+++ b/constants.js
@@ -76,6 +76,7 @@ export const HACKATHON_NAVBAR = {
intro: 'Intro',
events: 'Edit Events',
spocos: 'Edit Sponsors',
+ rewards: 'Edit Rewards',
FeatureFlags: 'Feature Flags',
BuildConfig: 'Build Config',
HackerInfo: 'Firebase Queries',
diff --git a/pages/[id]/rewards.js b/pages/[id]/rewards.js
new file mode 100644
index 00000000..f854a902
--- /dev/null
+++ b/pages/[id]/rewards.js
@@ -0,0 +1,397 @@
+import setHours from 'date-fns/setHours'
+import setMinutes from 'date-fns/setMinutes'
+import React, { useEffect, useState } from 'react'
+import Button from '../../components/button'
+import Card, { CardButtonContainer, CardContent, CardHeader, CardTitle } from '../../components/card'
+import Modal, { ModalContent, ModalField } from '../../components/modal'
+import Page from '../../components/page'
+import {
+ ActionsButtonContainer,
+ TableContent,
+ TableData,
+ TableEmptyText,
+ TableHeader,
+ TableRow,
+ TableWrapper,
+} from '../../components/table'
+import { COLOR, DELETE, EDIT, HACKATHON_NAVBAR, NEW, VIEW } from '../../constants'
+import { useAuth } from '../../utility/auth'
+import {
+ addReward,
+ deleteReward,
+ formatDate,
+ getRewards,
+ getHackathonPaths,
+ getHackathons,
+ getTimestamp,
+ updateReward,
+} from '../../utility/firebase'
+
+export default function Rewards({ id, hackathons }) {
+ const [rewards, setRewards] = useState([])
+ const [newReward, setNewReward] = useState({})
+ const [rewardViewing, setRewardViewing] = useState({})
+ const [rewardEditing, setRewardEditing] = useState({})
+ const [rewardConfirm, setRewardConfirm] = useState({})
+ const [isLoading, setIsLoading] = useState(true)
+ const [alertMsg, setAlertMsg] = useState('')
+ const { email: user } = useAuth().user
+
+ const fetchRewards = async () => {
+ const rewardsFetched = await getRewards(id)
+ if (Object.keys(rewardsFetched).length > 0) {
+ setRewards(rewardsFetched)
+ }
+ setIsLoading(false)
+ }
+
+ useEffect(() => {
+ if (alertMsg.length > 0) {
+ alert(alertMsg)
+ }
+ }, [alertMsg])
+
+ useEffect(() => {
+ fetchRewards()
+ }, [window.location.pathname])
+
+ const handleNew = async () => {
+ newReward.lastmodBy = user
+ newReward.date = new Date(newReward.date.toUTCString())
+ const rewardID = await addReward(id, newReward)
+ newReward.lastmod = formatDate(getTimestamp().seconds)
+ setRewards({
+ ...rewards,
+ [rewardID]: {
+ ...newReward,
+ date: formatDate(newReward.date, true),
+ rewardID,
+ },
+ })
+ setNewReward({})
+ setAlertMsg(`Successfully added the following reward: \n${newReward.reward}`)
+ }
+
+ const handleUpdate = async () => {
+ rewardEditing.lastmodBy = user
+ await updateReward(id, rewardEditing)
+ rewardEditing.lastmod = formatDate(getTimestamp().seconds)
+ setRewards({
+ ...rewards,
+ [rewardEditing.rewardID]: {
+ ...rewardEditing,
+ },
+ })
+ setRewardEditing({})
+ setAlertMsg(`Successfully updated the following reward: \n${rewardEditing.reward}`)
+ }
+
+ const handleDelete = (rewardID, confirmed = false) => {
+ if (!confirmed) {
+ setRewardConfirm({ ...rewards[rewardID] })
+ return
+ }
+ deleteReward(id, rewardID)
+ setRewards(
+ Object.keys(rewards)
+ .filter(curr => {
+ return curr !== rewardID
+ })
+ .reduce((obj, curr) => {
+ obj[curr] = rewards[curr]
+ return obj
+ }, {})
+ )
+ setRewardConfirm({}, setAlertMsg(`Successfully deleted the following reward: \n${rewards[rewardID].reward}`))
+ }
+
+ const handleInput = (property, value, reward, setState) => {
+ setState({
+ ...reward,
+ [property]: value,
+ })
+ }
+
+ const RewardRow = props => {
+ return (
+
+ {props.reward}
+ {props.blurb}
+ {props.prizesAvailable}
+ {props.requiredPoints}
+ {props.lastmod}
+ {props.lastmodBy}
+
+
+
+
+
+
+
+
+
+ )
+ }
+ return (
+
+
+
+ Rewards
+
+
+
+
+
+
+
+ {isLoading && (
+
+
+ Loading Rewards...
+
+
+ )}
+ {Object.keys(rewards).length === 0 && !isLoading && (
+
+
+ No Rewards found.
+
+
+ )}
+ {Object.keys(rewards).length > 0 && !isLoading && (
+ <>
+
+
+ Reward
+ Blurb
+ Number of Prizes Available
+ Required Points
+ Last Modified
+ Last Modified By
+ Actions
+
+
+
+ {Object.keys(rewards).map(curr => (
+
+ ))}
+
+ >
+ )}
+
+
+
+ {/* Modal for adding new reward */}
+ handleNew()}
+ handleClose={() => setNewReward({})}
+ modalAction={NEW}
+ >
+
+ handleInput('reward', reward.target.value, newReward, setNewReward)}
+ />
+
+
+ {
+ handleInput('blurb', reward.target.value, newReward, setNewReward)
+ }}
+ />
+
+
+
+ {
+ handleInput('imgName', reward.target.value, newReward, setNewReward)
+ }}
+ />
+
+
+ {
+ handleInput('imgURL', reward.target.value, newReward, setNewReward)
+ }}
+ />
+
+
+
+ {
+ handleInput('prizesAvailable', reward.target.value, newReward, setNewReward)
+ }}
+ />
+ handleInput('requiredPoints', reward.target.value, newReward, setNewReward)}
+ />
+
+
+
+ {/* Modal for viewing reward */}
+ 0}
+ handleClose={() => setRewardViewing({})}
+ modalAction={VIEW}
+ lastmod={`${rewardViewing.lastmod} by ${rewardViewing.lastmodBy}`}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* Modal for editing reward */}
+ 0}
+ handleClose={() => setRewardEditing({})}
+ handleSave={() => handleUpdate()}
+ modalAction={EDIT}
+ lastmod={`${rewardEditing.lastmod} by ${rewardEditing.lastmodBy}`}
+ >
+
+ {
+ handleInput('reward', reward.target.value, rewardEditing, setRewardEditing)
+ }}
+ />
+
+
+ {
+ handleInput('blurb', reward.target.value, rewardEditing, setRewardEditing)
+ }}
+ />
+
+
+ {
+ handleInput('imgName', reward.target.value, rewardEditing, setRewardEditing)
+ }}
+ />
+
+
+ {
+ handleInput('imgURL', reward.target.value, rewardEditing, setRewardEditing)
+ }}
+ />
+
+
+ {
+ handleInput('prizesAvailable', reward.target.value, rewardEditing, setRewardEditing)
+ }}
+ />
+ {
+ handleInput('requiredPoints', reward.target.value, rewardEditing, setRewardEditing)
+ }}
+ />
+
+
+ {/* Confirmation modal before deleting reward */}
+ 0}
+ handleClose={() => setRewardConfirm({})}
+ handleSave={() => handleDelete(rewardConfirm.rewardID, true)}
+ modalTitle={`Are you sure you want to delete this reward in ${id}?`}
+ modalAction={DELETE}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ )
+}
+export const getStaticPaths = async () => {
+ return getHackathonPaths()
+}
+
+export const getStaticProps = async ({ params }) => {
+ const hackathons = await getHackathons()
+ return {
+ props: {
+ hackathons,
+ id: params.id,
+ },
+ }
+}
diff --git a/utility/firebase.js b/utility/firebase.js
index e64d27d4..f7c59980 100644
--- a/utility/firebase.js
+++ b/utility/firebase.js
@@ -171,6 +171,73 @@ export const deleteEvent = async (hackathon, eventID) => {
await db.collection('Hackathons').doc(hackathon).collection('Events').doc(eventID).delete()
}
+// Rewards
+export const getReward = (rewardID, data) => {
+ return data
+ ? {
+ rewardID,
+ reward: data.reward || 'Empty reward field', // Title of the reward
+ key: data.key || rewardID, // Key of the reward (defaults to rewardID)
+ blurb: data.blurb || 'Empty blurb description for reward', // Short description of the reward
+ from: data.from || 'None', // Source or sponsor of the reward
+ imgName: data.imgName || 'None', // Image name (if applicable)
+ imgURL: data.imgURL || '', // URL to the reward image
+ prizesAvailable: data.prizesAvailable || '0', // Number of winners for the reward
+ requiredPoints: data.requiredPoints || '0', // Points required to win the reward
+ lastmod: data.lastmod ? formatDate(data.lastmod.seconds) : formatDate(getTimestamp().seconds), // Last modified date
+ lastmodBy: data.lastmodBy || 'Unknown user', // Last person who modified the reward
+ }
+ : null
+}
+
+export const getRewards = async hackathon => {
+ const rewardIDs = await db.collection('Hackathons').doc(hackathon).collection('Rewards').get()
+ const rewards = {}
+ rewardIDs.docs.forEach(doc => {
+ const currReward = getReward(doc.id, doc.data())
+ if (currReward) rewards[doc.id] = currReward
+ })
+ return rewards
+}
+
+export const addReward = async (hackathon, reward) => {
+ const ref = db.collection('Hackathons').doc(hackathon).collection('Rewards').doc()
+ await ref.set({
+ reward: reward.reward, // Title of the reward
+ key: ref.id, // Key generated for the reward
+ blurb: reward.blurb, // Short description of the reward
+ imgName: reward.imgName, // Image name (if applicable)
+ imgURL: reward.imgURL, // URL to the reward image
+ prizesAvailable: reward.prizesAvailable, // Number of prizes we have
+ requiredPoints: reward.requiredPoints, // Points required to win the reward
+ lastmod: getTimestamp(), // Timestamp of when the reward was last modified
+ lastmodBy: reward.lastmodBy, // User who last modified the reward
+ })
+ return ref.id
+}
+
+export const updateReward = async (hackathon, reward) => {
+ const ref = db.collection('Hackathons').doc(hackathon).collection('Rewards').doc(reward.rewardID)
+ const currDate = getTimestamp()
+ await ref.update({
+ reward: reward.reward || 'Empty reward field',
+ key: reward.key || reward.rewardID,
+ blurb: reward.blurb || 'Empty blurb description for reward',
+ from: reward.from || 'None',
+ imgName: reward.imgName || 'None',
+ imgURL: reward.imgURL || '',
+ prizesAvailable: reward.prizesAvailable || '0',
+ requiredPoints: reward.requiredPoints || '0',
+ lastmod: currDate,
+ lastmodBy: reward.lastmodBy,
+ })
+}
+
+export const deleteReward = async (hackathon, rewardID) => {
+ await db.collection('Hackathons').doc(hackathon).collection('Rewards').doc(rewardID).delete()
+}
+// Rewards ^^
+
const getFaqCategory = faqCategory => {
switch (faqCategory) {
case FAQCategory.LOGS: