site/pages/arcade/showcase/vote.js
2026-01-27 11:41:17 -05:00

1414 lines
43 KiB
JavaScript

import { useEffect, useState, useRef, useCallback } from 'react'
import { Button, Text, Box, Close, Flex } from 'theme-ui'
import ProjectView from '../../../components/arcade/showcase/project-view'
import SmallView from '../../../components/arcade/showcase/small-view-card'
import { DragDropContext, Droppable, Draggable } from 'react-beautiful-dnd'
/** @jsxImportSource theme-ui */
// https://github.com/atlassian/react-beautiful-dnd/blob/master/docs/guides/drop-animation.md#skipping-the-drop-animation
function getStyle(style, snapshot) {
if (!snapshot.isDropAnimating) {
return style
}
return {
...style,
// cannot be 0, but make it super tiny
transitionDuration: `0.001s`
}
}
const styled = `
@import url(https://fonts.googleapis.com/css2?family=Slackey&family=Emblema+One&family=Gaegu&display=swap);
body, html {
overflow-x: hidden;
color: #35290F;
}
.slackey {
font-family: "Slackey", sans-serif;
}
.emblema {
font-family: "Emblema One", system-ui;
}
.gaegu {
font-family: "Gaegu", sans-serif;
}
body {
background-color: #FAEFD6;
}
@keyframes float {
from,
to {
transform: translate(0%, -37%) rotate(-2deg);
}
25% {
transform: translate(-2%, -40%) rotate(2deg);
}
50% {
transform: translate(0%, -43%) rotate(-1deg);
}
75% {
transform: translate(-1%, -40%) rotate(-1deg);
}
}
a {
color: inherit;
}
`
const Loading = () => (
<div
sx={{
width: '100%',
maxWidth: '1200px',
margin: 'auto',
textAlign: 'center'
}}
>
Loading...
</div>
)
const ErrorMessage = () => (
<div
sx={{
width: '100%',
maxWidth: '1200px',
margin: 'auto',
textAlign: 'center'
}}
>
There was an error loading your project.
</div>
)
//CREDIT: https://www.joshwcomeau.com/snippets/react-hooks/use-mouse-position/
const useMousePosition = () => {
const [mousePosition, setMousePosition] = useState({ x: null, y: null })
useEffect(() => {
const updateMousePosition = ev => {
setMousePosition({ x: ev.clientX, y: ev.clientY })
}
window.addEventListener('mousemove', updateMousePosition)
return () => {
window.removeEventListener('mousemove', updateMousePosition)
}
}, [])
return mousePosition
}
const Vote = () => {
/* test data */
// let originalProjects = {
// voted: true,
// cohort: {
// id: 'rectAjJ2Lv4dDhUGR'
// },
// showcases: [
// {
// id: 'rec4cl4TSvfwwnU6H',
// createdTime: '2024-08-14T13:47:49.000Z',
// fields: {
// Name: 'Blast-off',
// 'Code Link': 'https://github.com/Ranger-NF/BlastOff',
// 'Play Link': 'https://ranger-nf.itch.io/blastoff',
// Description: 'This is a description',
// color: '#14f0cb',
// textColor: '#ffffff',
// ScreenshotLink:
// 'https://cloud-c6ul4axwx-hack-club-bot.vercel.app/0instagram_profile_downloader.jpg',
// ReadMeLink:
// 'https://raw.githubusercontent.com/remarkjs/react-markdown/main/readme.md',
// 'View link':
// 'https://hackclub.com/arcade/showcase/project/rec4cl4TSvfwwnU6H'
// }
// },
// {
// id: 'recLyu8vD4mmUqYrA',
// createdTime: '2024-08-19T17:12:16.000Z',
// fields: {
// Name: 'Turtle T1 (A rabbit R1 spinoff)',
// 'Code Link': 'https://github.com/briyandyju09/Turtle-T1',
// Description: 'A web based spin off to the not so liked rabbit R1',
// color: '#FAEFD6',
// ScreenshotLink:
// 'https://cloud-k7p388c60-hack-club-bot.vercel.app/0image-19.png',
// ReadMeLink:
// 'https://raw.githubusercontent.com/briyandyju09/Turtle-T1/main/README.md',
// 'View link':
// 'https://hackclub.com/arcade/showcase/project/recLyu8vD4mmUqYrA'
// }
// },
// {
// id: 'recYZd9J9MS4fDuJC',
// createdTime: '2024-08-16T22:28:10.000Z',
// fields: {
// Name: 'peace-and-tranquility',
// 'Code Link': 'https://github.com/maxwofford/peace-and-tranquility',
// 'Play Link': 'https://dinosaurbbq.org/',
// Description: 'No one is around to help',
// color: '#293438',
// textColor: '#ffffff',
// ScreenshotLink:
// 'https://cloud-c6ul4axwx-hack-club-bot.vercel.app/0instagram_profile_downloader.jpg',
// ReadMeLink:
// 'https://raw.githubusercontent.com/remarkjs/react-markdown/main/readme.md',
// 'View link':
// 'https://hackclub.com/arcade/showcase/project/recYZd9J9MS4fDuJC'
// }
// },
// {
// id: 'reclIN8evh60EH90v',
// createdTime: '2024-08-17T05:57:05.000Z',
// fields: {
// Name: 'site2eeeEEE534',
// 'Code Link': 'https://github.com/hackclub/site',
// 'Play Link': 'https://hackclub.com',
// Description:
// '🌈 The new, new Hack Club website (uses Next.js & Theme UI).',
// color: '#68d0f8',
// textColor: '#fafafa',
// ScreenshotLink:
// 'https://cloud-c6ul4axwx-hack-club-bot.vercel.app/0instagram_profile_downloader.jpg',
// ReadMeLink:
// 'https://raw.githubusercontent.com/remarkjs/react-markdown/main/readme.md',
// 'View link':
// 'https://hackclub.com/arcade/showcase/project/reclIN8evh60EH90v'
// }
// },
// {
// id: 'reclIN8evh60EH2220v',
// createdTime: '2024-08-17T05:57:05.000Z',
// fields: {
// Name: 'site2eeeEEE53344',
// 'Code Link': 'https://github.com/hackclub/site',
// 'Play Link': 'https://hackclub.com',
// Description:
// '🌈 The new, new Hack Club website (uses Next.js & Theme UI).',
// color: '#68d0f8',
// textColor: '#fafafa',
// ScreenshotLink:
// 'https://cloud-c6ul4axwx-hack-club-bot.vercel.app/0instagram_profile_downloader.jpg',
// ReadMeLink:
// 'https://raw.githubusercontent.com/remarkjs/react-markdown/main/readme.md',
// 'View link':
// 'https://hackclub.com/arcade/showcase/project/reclIN8evh60EH90v'
// }
// }
// ]
// }
// originalProjects = originalProjects.showcases
/* necessary to make drag and drop work */
const [showUIElements, setShowUIElements] = useState(false)
/* get projects */
const [originalProjects, setOriginalProjects] = useState([])
const [projects, setProjects] = useState(originalProjects)
const projectCount = originalProjects.length
/* for showing individual projects */
const [openProjectId, setOpenProjectId] = useState('')
const [openProject, setOpenProject] = useState([])
/* status variables */
const [loadStatus, setLoadStatus] = useState('loading')
const [status, setStatus] = useState('loading')
const [submitStatus, setSubmitStatus] = useState('loading')
/* collect votes */
const [creative, setCreative] = useState([])
const [technical, setTechnical] = useState([])
const [overall, setOverall] = useState([])
const [voted, setVoted] = useState(false)
// const [userId, setUserId] = useState('')
/* change what is shown on page */
const [showCreative, setShowCreative] = useState(true)
const [showTechnical, setShowTechnical] = useState(false)
const [showOverall, setShowOverall] = useState(false)
const [endPage, setEndPage] = useState(false)
const [isButtonActive, setIsButtonActive] = useState(false)
const [startVote, setStartVote] = useState(false)
const [startViewProject, setStartViewProject] = useState(false)
const [currentView, setCurrentView] = useState(0)
const [showForm, setShowForm] = useState(false)
/* drag and drop logic */
const [activeDroppableId, setActiveDroppableId] = useState(null)
const [activeDroppable, setActiveDroppable] = useState(true)
const dragItemRef = useRef(null)
const [votes, setVotes] = useState({})
/* check if it's loaded */
useEffect(() => {
setShowUIElements(true)
}, [])
/* load projects */
const loadProjects = async () => {
const token = window.localStorage.getItem('arcade.authToken')
const response = await fetch('/api/arcade/showcase/cohort/active', {
method: 'GET',
headers: {
Authorization: `Bearer ${token}`
}
}).catch(e => {
console.error(e)
setLoadStatus('error')
})
setLoadStatus('loading')
const data = await response.json()
if (data.error) {
setLoadStatus('error')
return
} else {
setProjects(data.showcases)
setOriginalProjects(data.showcases)
setVoted(data.voted)
// setUserId(data.userId)
setLoadStatus('success')
}
}
useEffect(() => {
loadProjects()
}, [])
/* get individual project details */
const getProjectDetails = useCallback(async () => {
const token = window.localStorage.getItem('arcade.authToken')
setStatus('loading')
try {
if (openProjectId) {
const response = await fetch(
`/api/arcade/showcase/projects/${openProjectId}/show`,
{
method: 'GET',
headers: {
Authorization: `Bearer ${token}`
}
}
)
const data = await response.json()
console.log(data)
if (data.error) {
console.log(data.error)
setStatus('error')
} else {
setOpenProject(data.project)
setStatus('success')
}
}
} catch (e) {
console.error(e)
setStatus('error')
}
}, [openProjectId])
useEffect(() => {
getProjectDetails()
}, [getProjectDetails])
const dialogRef = useRef(null)
// useEffect(() => {
// const handleClickOutside = event => {
// if (dialogRef.current) {
// const rect = dialogRef.current.getBoundingClientRect()
// if (
// event.clientX < rect.left ||
// event.clientX > rect.right ||
// event.clientY < rect.top ||
// event.clientY > rect.bottom
// ) {
// dialogRef.current.close()
// }
// }
// }
// document.addEventListener('mousedown', handleClickOutside)
// return () => {
// document.removeEventListener('mousedown', handleClickOutside)
// }
// }, [])
/* HANDLE DRAG AND DROP LOGIC BELOW*/
const [mousePosition, setMousePosition] = useState({ x: null, y: null })
useEffect(() => {
const updateMousePosition = ev => {
setMousePosition({ x: ev.clientX, y: ev.clientY })
}
window.addEventListener('mousemove', updateMousePosition)
return () => {
window.removeEventListener('mousemove', updateMousePosition)
}
}, [])
const isCursorInsideBoundingBox = (cursorPosition, boundingBox) => {
const { x, y, width, height } = boundingBox
return (
cursorPosition.x >= x &&
cursorPosition.x <= x + width &&
cursorPosition.y >= y &&
cursorPosition.y <= y + height
)
}
const getBoundingBoxes = () => {
if (typeof document === 'undefined') {
return []
}
const droppableIds = ['votes-1', 'votes-2', 'votes-3', 'votes-4', 'votes-5']
const boundingBoxes = droppableIds
.map(id => {
const element = document.getElementById(id)
if (element) {
const rect = element.getBoundingClientRect()
return {
id,
x: rect.left,
y: rect.top,
width: rect.width,
height: rect.height
}
}
return null
})
.filter(box => box !== null)
return boundingBoxes
}
const boundingBoxes = getBoundingBoxes()
// useEffect(() => {
// if (dragging) {
// console.log("DRAGGING")
// let insideVotingBox = false
// for (const box of boundingBoxes) {
// if (
// box.id.startsWith('votes') &&
// isCursorInsideBoundingBox(mousePosition, box)
// ) {
// insideVotingBox = true
// if (activeDroppableId != box.id) {
// setActiveDroppable(true)
// setActiveDroppableId(box.id)
// }
// break
// }
// }
// // if (!insideVotingBox && activeDroppable) {
// // setActiveDroppable(false)
// // setActiveDroppableId(null)
// // }
// }
// }, [mousePosition])
const onDragUpdate = update => {
let insideVotingBox = false
for (const box of boundingBoxes) {
console.log(mousePosition)
if (
box.id.startsWith('votes') &&
isCursorInsideBoundingBox(mousePosition, box)
) {
insideVotingBox = true
if (activeDroppableId !== box.id) {
setActiveDroppable(true)
setActiveDroppableId(box.id)
}
break
} else {
insideVotingBox = false
}
}
if (!insideVotingBox && activeDroppable) {
setActiveDroppable(false)
setActiveDroppableId(null)
}
}
const onDragEnd = result => {
const { source, destination } = result
if (!destination) return
console.log(destination)
if (destination.droppableId === 'projects') {
console.log('projects')
return
}
if (activeDroppableId && activeDroppableId.startsWith('votes')) {
const linkId = result.draggableId
const voteId = activeDroppableId
setVotes(prevVotes => {
const updatedVotes = {
...prevVotes,
[voteId]: linkId
}
console.log(updatedVotes)
const votedProjectIds = Object.values(updatedVotes)
const updatedProjects = originalProjects.filter(
project => !votedProjectIds.includes(project.id)
)
setProjects(updatedProjects)
return updatedVotes
})
setActiveDroppableId(null)
setActiveDroppable(false)
}
}
const deleteVote = voteId => {
setVotes(prevVotes => {
const updatedVotes = { ...prevVotes }
delete updatedVotes[voteId]
const votedProjectIds = Object.values(updatedVotes)
const updatedProjects = originalProjects.filter(
project => !votedProjectIds.includes(project.id)
)
setProjects(updatedProjects)
return updatedVotes
})
}
const renderVoteBox = voteId => {
const linkId = votes[voteId] //checks if it was voted
const project = originalProjects.find(link => link.id === linkId)
if (project) {
return (
<div
sx={{
backgroundColor: '#F6E7C5',
borderRadius: '10px',
position: 'relative',
height: '100%',
display: 'flex',
alignItems: 'center'
}}
>
<Close
sx={{
'&:hover': { cursor: 'pointer' },
position: 'absolute',
top: '0px',
right: '0px',
zIndex: 2,
color: '#09AFB4'
}}
onClick={e => {
deleteVote(voteId)
}}
/>
<Flex
sx={{
background: '#09AFB4',
px: 2,
py: 1,
color: '#FAEFD6',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
width: '50px',
flex: '0 0 50px'
}}
>
<Text variant="subtitle" sx={{ mt: 0 }}>
{voteId.replace('votes-', '')}
</Text>
</Flex>
<Box sx={{ pl: 3 }}>
<Text variant="subtitle" as="h3" sx={{ mt: 0, fontWeight: 'bold' }}>
{project.fields.Name}
</Text>
<Text variant="caption" as="p" sx={{ color: '#35290F' }}>
{project.fields.Description}
</Text>
</Box>
</div>
)
}
return (
<div
sx={{
display: 'flex',
alignItems: 'center',
// justifyContent: 'center',
height: '100%',
position: 'relative',
backgroundColor: '#FAEFD6',
borderRadius: '10px'
}}
>
<Flex
sx={{
background: '#09AFB4',
px: 2,
py: 1,
color: '#FAEFD6',
height: '100%',
alignItems: 'center',
justifyContent: 'center',
width: '50px',
flex: '0 0 50px'
}}
>
<Text variant="subtitle" sx={{ mt: 0 }}>
{voteId.replace('votes-', '')}
</Text>
</Flex>
<Text sx={{ display: 'block', textAlign: 'center', width: '100%' }}>
Drop here
</Text>
</div>
)
}
/* VOTE SUBMISSION BELOW */
useEffect(() => {
if (Object.keys(votes).length === 5) {
setIsButtonActive(true)
} else {
setIsButtonActive(false)
}
}, [votes])
const extractVotes = votes => {
const sortedKeys = Object.keys(votes).sort((a, b) => {
const numA = parseInt(a.split('-')[1], 10)
const numB = parseInt(b.split('-')[1], 10)
return numA - numB
})
return sortedKeys.map(key => votes[key])
}
const voteCreative = () => {
let ids = extractVotes(votes)
setCreative(ids)
setShowCreative(false)
setVotes({})
setProjects(originalProjects)
setShowTechnical(true)
}
const voteTechnical = () => {
let ids = extractVotes(votes)
setTechnical(ids)
setShowTechnical(false)
setVotes({})
setProjects(originalProjects)
setShowOverall(true)
}
const voteOverall = () => {
let ids = extractVotes(votes)
setOverall(ids)
setShowTechnical(false)
setVotes({})
setProjects(originalProjects)
setEndPage(true)
}
const voteCreativeMobile = ids => {
setCreative(ids)
setShowCreative(false)
setVotes({})
setProjects(originalProjects)
setShowTechnical(true)
}
const voteTechnicalMobile = ids => {
setTechnical(ids)
setShowTechnical(false)
setVotes({})
setProjects(originalProjects)
setShowOverall(true)
}
const voteOverallMobile = ids => {
setOverall(ids)
setShowTechnical(false)
setVotes({})
setProjects(originalProjects)
setEndPage(true)
}
const submitVote = async (overall, technical, creative) => {
const authToken = window.localStorage.getItem('arcade.authToken')
try {
const response = await fetch('/api/arcade/showcase/vote', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Authorization: 'Bearer ' + authToken
},
body: JSON.stringify({
overall,
technical,
creative
})
})
const data = await response.json()
if (data.error) {
console.error('Error submitting vote:', data.error)
setSubmitStatus('error')
} else {
console.log(data)
setSubmitStatus('success')
jsConfetti.current.addConfetti({
confettiColors: ['#09AFB4', '#FF5C00']
})
}
} catch (error) {
console.error('Error submitting vote:', error)
setSubmitStatus('error')
throw error
}
}
let jsConfetti = useRef()
useEffect(() => {
import('js-confetti').then(({ default: JSConfetti }) => {
jsConfetti.current = new JSConfetti()
})
}, [])
useEffect(() => {
if (overall.length > 0 && technical.length > 0 && creative.length > 0) {
//skips first render
submitVote(overall, technical, creative)
}
}, [endPage, creative, overall, technical])
//MOBILE w/ ChatGPT help
const [selectedProjects, setSelectedProjects] = useState(Array(5).fill(''))
const handleChange = (index, event) => {
const newSelection = [...selectedProjects]
newSelection[index] = event.target.value
setSelectedProjects(newSelection)
}
const getFilteredOptions = currentIndex => {
return projects.filter(
project =>
!selectedProjects.includes(project.fields.Name) ||
selectedProjects[currentIndex] === project.fields.Name
)
}
const handleSubmit = event => {
event.preventDefault()
const projectIds = selectedProjects.map(
name => projects.find(project => project.fields.Name === name)?.id
)
console.log('Selected Project IDs:', projectIds)
showCreative
? voteCreativeMobile(projectIds)
: showTechnical
? voteTechnicalMobile(projectIds)
: showOverall
? voteOverallMobile(projectIds)
: null
console.log(creative)
setSelectedProjects(Array(5).fill(''))
}
return startVote === true ? (
endPage === true ? (
<div
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
width: '100vw',
backgroundColor: '#FAEFD6',
color: '#35290F',
flexDirection: 'column',
gap: '20px',
position: 'relative'
}}
>
<img
src="https://cloud-677i45opw-hack-club-bot.vercel.app/0arcade_1.png"
alt="Arcade logo"
sx={{
width: '30%',
maxWidth: '200px',
position: 'absolute',
top: '20px',
right: '20px'
}}
/>
<Text
variant="title"
className="gaegu"
sx={{ textAlign: 'center', maxWidth: '800px' }}
>
{submitStatus === 'loading'
? 'Loading...'
: submitStatus === 'success'
? 'Thanks for voting!'
: 'Ran into an error sending your votes'}
</Text>
<Text
variant="subtitle"
className="gaegu"
sx={{ textAlign: 'center', maxWidth: '800px', mt: 0 }}
>
{submitStatus === 'loading' ? 'It takes a while' : ''}
</Text>
<Button
as="a"
href="/arcade/showcase/my"
sx={{
backgroundColor:
currentView < projectCount - 1 ? '#09AFB4' : '#FF5C00',
color: '#FAEFD6',
borderRadius: '5px',
border: 'none',
px: '20px',
transitionDuration: '0.3s',
'&:hover': {
transform: 'scale(1.05)'
},
width: 'fit-content'
}}
>
See my projects
</Button>
<style>{styled}</style>
</div>
) : (
showUIElements && (
<div>
<div
sx={{
zIndex: 5,
position: 'relative',
color: '#35290F',
height: ['100%', '100%', '100vh']
}}
className="gaegu"
>
<DragDropContext onDragEnd={onDragEnd} onDragUpdate={onDragUpdate}>
<div
sx={{
display: 'grid',
gridTemplateColumns: ['1fr', '1fr', '1fr 1fr'],
height: '100%',
minHeight: '100vh'
}}
>
<div
sx={{
backgroundColor: '#F6E7C5',
pr: 3,
py: 4,
pl: 4,
overflowY: 'scroll',
height: '100%'
}}
>
<Text variant="title" className="slackey" as="h1">
Ships
</Text>
<Text variant="subtitle" as="h4" sx={{ mb: 3 }}>
Drag and drop the projects to vote for them. Click on the
projects to see the details.
</Text>
<Droppable droppableId="projects">
{provided => (
<div
{...provided.droppableProps}
ref={provided.innerRef}
sx={{
width: '100%',
display: 'grid',
gridTemplateColumns: ['1fr', '1fr', '1fr 1fr'],
gap: '10px'
}}
>
{projects.map((project, index) => (
<Draggable
key={project.id}
draggableId={project.id}
index={index}
>
{(provided, snapshot) => (
<div
ref={el => {
provided.innerRef(el)
if (index === 0) dragItemRef.current = el // Capture ref for the first item as an example
}}
{...provided.draggableProps}
{...provided.dragHandleProps}
onClick={e => {
document
.getElementById('show-project')
.showModal()
setOpenProjectId(project.id)
}}
style={getStyle(
provided.draggableProps.style,
snapshot
)}
>
<SmallView
id={project.id}
title={project.fields.Name}
desc={project.fields.Description}
/>
</div>
)}
</Draggable>
))}
{provided.placeholder}
</div>
)}
</Droppable>
</div>
<div
sx={{
pl: 4,
py: 3,
pr: 4,
display: 'flex',
justifyContent: 'center',
alignItems: 'center'
}}
>
<div>
<Text variant="subtitle">Choose top 5 for </Text>
<Text className="slackey" variant="subtitle" sx={{ pb: 3 }}>
{showCreative
? 'Most creative ships'
: showTechnical
? 'Most technical ships'
: 'Best overall ships'}
</Text>{' '}
<Text
variant="subtitle"
sx={{ pb: 3, fontStyle: 'italic' }}
>
Order matters!
</Text>
{showForm ? (
<Button
onClick={() => {
setShowForm(false)
}}
sx={{
border: '2px dashed #09AFB4',
background: 'transparent',
display: 'block',
mt: 3,
color: '#09AFB4',
borderRadius: '5px',
px: '20px',
transitionDuration: '0.3s',
'&:hover': {
transform: 'scale(1.05)'
},
width: 'fit-content'
}}
>
Use drag and Drop View
</Button>
) : (
<Button
onClick={() => {
setShowForm(true)
}}
sx={{
backgroundColor: '#09AFB4',
display: 'block',
color: '#FAEFD6',
mt: 3,
borderRadius: '5px',
border: 'none',
px: '20px',
transitionDuration: '0.3s',
'&:hover': {
transform: 'scale(1.05)'
},
width: 'fit-content'
}}
>
Use form View
</Button>
)}
{!showForm ? (
<div sx={{ mt: 2 }}>
{[
'votes-1',
'votes-2',
'votes-3',
'votes-4',
'votes-5'
].map(voteId => (
<Droppable key={voteId} droppableId={voteId}>
{provided => (
<div
ref={provided.innerRef}
{...provided.droppableProps}
id={voteId}
sx={{
width: '100%',
height: ['14vh', '14vh', '13.5vh'],
minHeight: '80px',
border: '2px dashed #09AFB4',
borderRadius: '10px',
mb: 3,
backgroundColor:
activeDroppableId === voteId
? '#F6E7C5'
: '#FAEFD6',
transition: 'transform 0.2s ease',
transform:
activeDroppableId === voteId
? activeDroppable === true
? 'scale(1.05)'
: 'scale(1)'
: 'scale(1)'
}}
>
{renderVoteBox(voteId)}
{provided.placeholder}
</div>
)}
</Droppable>
))}
{isButtonActive ? (
<Button
onClick={e => {
showCreative
? voteCreative()
: showTechnical
? voteTechnical()
: showOverall
? voteOverall()
: null
}}
sx={{
backgroundColor: '#09AFB4',
color: '#FAEFD6',
borderRadius: '5px',
border: 'none',
px: '20px',
transitionDuration: '0.3s',
'&:hover': {
transform: 'scale(1.05)'
},
width: 'fit-content'
}}
>
Place votes
</Button>
) : (
<Button
sx={{
backgroundColor: '#09AFB4',
color: '#FAEFD6',
borderRadius: '5px',
border: 'none',
px: '20px',
width: 'fit-content',
opacity: 0.4,
transition: 'none',
'&:hover': {
transform: 'none'
},
cursor: 'not-allowed'
}}
>
Place votes
</Button>
)}
</div>
) : (
<div sx={{ pb: 5, pt: 2 }}>
<form onSubmit={handleSubmit} id="s-form">
{[0, 1, 2, 3, 4].map(index => (
<div key={index} sx={{ display: 'flex' }}>
<label
htmlFor={`project-${index}`}
sx={{ mr: 2 }}
>
{index + 1}
</label>
<select
id={`project-${index}`}
value={selectedProjects[index]}
onChange={event => handleChange(index, event)}
required
sx={{
border: '2px dashed #09AFB4',
background: 'transparent',
px: 2,
py: 1,
borderRadius: '2px',
width: '100%',
fontSize: [2, 3],
mb: 3
}}
className="gaegu"
>
<option value="">Select a project</option>
{getFilteredOptions(index).map(project => (
<option
key={project.id}
value={project.fields.Name}
>
{project.fields.Name}
</option>
))}
</select>
</div>
))}
<Button
type="submit"
sx={{
backgroundColor: '#09AFB4',
color: '#FAEFD6',
borderRadius: '5px',
border: 'none',
px: '20px',
transitionDuration: '0.3s',
'&:hover': {
transform: 'scale(1.05)'
},
width: 'fit-content'
}}
>
Vote now
</Button>
</form>
</div>
)}
</div>
</div>
</div>
</DragDropContext>
<dialog
ref={dialogRef}
id="show-project"
sx={{
borderRadius: '10px',
border: '3px dashed #09AFB4',
width: ['90vw', '90vw', '70vw'],
position: 'relative'
}}
className="gaegu"
>
{status === 'loading' && <Loading />}
{status === 'error' && <ErrorMessage />}
{status === 'success' && (
<ProjectView
preview="preview"
key={openProject.id}
id={openProject.id}
title={openProject.title}
desc={openProject.desc}
slack={openProject.slackLink}
codeLink={openProject.codeLink}
playLink={openProject.playLink}
images={openProject.images}
githubProf={openProject.githubProf}
user={openProject.user}
color={openProject.color}
textColor={openProject.textColor}
screenshot={openProject.screenshot}
video={openProject.video}
readMeLink={openProject.readMeLink}
/>
)}
<Close
sx={{
'&:hover': { cursor: 'pointer' },
position: 'absolute',
top: '10px',
right: '10px',
zIndex: 2,
color: '#FAEFD6',
background: '#09AFB4'
}}
onClick={e => {
document.getElementById('show-project').close()
}}
/>
</dialog>
</div>
<style>{styled}</style>
</div>
)
)
) : startViewProject === true ? (
<div>
<div
className="gaegu"
sx={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'center',
position: 'fixed',
zIndex: 5,
px: 3,
backgroundColor: '#FAEFD6',
width: '100vw',
py: 2
}}
>
<Text
variant="subtitle"
sx={{ m: 0, color: '#35290F', fontSize: [1, 2] }}
>
{' '}
Ship {currentView + 1}/{projectCount}
</Text>
<div>
<Button
onClick={e => {
currentView > 0 ? setCurrentView(currentView - 1) : null
}}
sx={{
backgroundColor: '#09AFB4',
color: '#FAEFD6',
borderRadius: '5px',
border: 'none',
px: '20px',
transitionDuration: '0.3s',
'&:hover': {
transform: currentView > 0 ? 'scale(1.05)' : 'scale(1)'
},
width: 'fit-content',
cursor: currentView > 0 ? 'pointer' : 'auto',
opacity: currentView > 0 ? 1 : 0.3,
mr: 3
}}
>
Prev project
</Button>
<Button
onClick={e => {
currentView < projectCount - 1
? setCurrentView(currentView + 1)
: setStartVote(true)
}}
sx={{
backgroundColor:
currentView < projectCount - 1 ? '#09AFB4' : '#FF5C00',
color: '#FAEFD6',
borderRadius: '5px',
border: 'none',
px: '20px',
transitionDuration: '0.3s',
'&:hover': {
transform: 'scale(1.05)'
},
width: 'fit-content'
}}
>
{currentView < projectCount - 1 ? 'Next project' : 'Start voting'}
</Button>
</div>
</div>
<div sx={{ height: '40px' }}></div>
<ProjectView
preview="preview"
key={originalProjects[currentView].id}
id={originalProjects[currentView].id}
title={originalProjects[currentView].fields.Name}
desc={originalProjects[currentView].fields.Description}
codeLink={originalProjects[currentView].fields['Code Link']}
playLink={originalProjects[currentView].fields['Play Link']}
images={originalProjects[currentView].fields.ScreenshotLink}
color={originalProjects[currentView].fields.color}
textColor={originalProjects[currentView].fields.textColor}
screenshot={originalProjects[currentView].fields.ScreenshotLink}
readMeLink={originalProjects[currentView].fields.ReadMeLink}
/>
<style>{styled}</style>
</div>
) : (
<div
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
height: '100vh',
width: '100vw',
backgroundColor: '#FAEFD6',
position: 'relative'
}}
>
<img
src="https://cloud-677i45opw-hack-club-bot.vercel.app/0arcade_1.png"
alt="Arcade logo"
sx={{
width: '30%',
maxWidth: '200px',
position: 'absolute',
top: '20px',
right: '20px'
}}
/>
<div
sx={{ width: '90vw', margin: 'auto', maxWidth: '800px' }}
className="gaegu"
>
{loadStatus === 'success' ? (
voted === true ? (
<div
sx={{
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#FAEFD6',
color: '#35290F',
flexDirection: 'column',
gap: '20px',
position: 'relative',
textAlign: 'center'
}}
>
<Text variant="title" className="gaegu">
You've already voted.
</Text>
<Text variant="subtitle" sx={{ mt: 0 }}>
If this is a mistake, please send a message to #arcade-help
</Text>
<Button
as="a"
href="/arcade/showcase/my"
sx={{
backgroundColor:
currentView < projectCount - 1 ? '#09AFB4' : '#FF5C00',
color: '#FAEFD6',
borderRadius: '5px',
border: 'none',
px: '20px',
transitionDuration: '0.3s',
'&:hover': {
transform: 'scale(1.05)'
},
width: 'fit-content'
}}
>
See my projects
</Button>
<style>{styled}</style>
</div>
) : (
<>
<Text
variant="title"
className="slackey"
as="h1"
sx={{ color: '#FF5C00' }}
>
Voting for your favourite ships
</Text>
<Text variant="subtitle" as="h4" sx={{ mb: 3 }}>
Please vote on a device with a mouse if possible. When you click
start voting, you will first be shown {projectCount} projects in
your cohort. After viewing these projects, you will get the
chance to pick your top 5 projects for each category.
</Text>
<Button
onClick={e => {
setStartViewProject(true)
}}
sx={{
backgroundColor: '#09AFB4',
color: '#FAEFD6',
borderRadius: '5px',
border: 'none',
px: '20px',
transitionDuration: '0.3s',
'&:hover': {
transform: 'scale(1.05)'
},
width: 'fit-content'
}}
>
Vote now
</Button>{' '}
</>
)
) : (
<>
<Text variant="subtitle" as="h4" sx={{ mb: 3 }}>
{loadStatus}
</Text>
<Text variant="caption" as="h4">
{loadStatus === 'loading'
? "Please give it some time. If it's taken too long, ask for help in #arcade-help"
: 'Try logging in again with /showcase in Slack. If it persists, please ask for help in #arcade-help.'}
</Text>
</>
)}
</div>
<style>{styled}</style>
</div>
)
}
export default Vote