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} + + + + + + + + + {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: