Fix Replit form

This commit is contained in:
Malted 2024-08-29 19:22:21 -04:00
parent 3c0a47c9b9
commit cc7fd17beb
No known key found for this signature in database
5 changed files with 130 additions and 112 deletions

View file

@ -1,19 +1,98 @@
import { useState } from 'react'
import { useState, useRef, useEffect } from 'react'
import { Box, Button, Card, Link, Input, Text, Flex, Image } from 'theme-ui'
import Icon from '@hackclub/icons'
import JSConfetti from 'js-confetti'
import { FilloutStandardEmbed } from '@fillout/react'
import '@fillout/react/style.css'
import theme from '../../lib/theme'
const ReplitForm = ({ cssDark }) => {
const [currentStep, setCurrentStep] = useState(1)
const [isSubmitted, setIsSubmitted] = useState(false)
const [buttonText, setButtonText] = useState('Submit')
const [formData, setFormData] = useState({})
let jsConfetti = useRef()
let draggedSticker = useRef()
useEffect(() => {
jsConfetti.current = new JSConfetti()
document.onmousedown = e => {
if (e.target.classList.contains('sticker')) {
const rect = e.target.getBoundingClientRect()
const stickerCentreX = rect.left + rect.width / 2
const stickerCentreY = rect.top + rect.height / 2
e.target.dataset.offsetX = e.clientX - stickerCentreX
e.target.dataset.offsetY = e.clientY - stickerCentreY
document.body.appendChild(e.target)
draggedSticker.current = e.target
draggedSticker.current.style.left = `${e.pageX - draggedSticker.current.dataset.offsetX}px`
draggedSticker.current.style.top = `${e.pageY - draggedSticker.current.dataset.offsetY}px`
setTimeout(() => draggedSticker.current.classList.add('dragged'), 0)
}
}
document.onmousemove = e => {
if (draggedSticker.current) {
draggedSticker.current.style.left = `${e.pageX - draggedSticker.current.dataset.offsetX}px`
draggedSticker.current.style.top = `${e.pageY - draggedSticker.current.dataset.offsetY}px`
}
}
document.onmouseup = () => {
if (draggedSticker.current) {
draggedSticker.current.classList.remove('dragged')
draggedSticker.current = null
}
}
}, [])
const handleInputChange = e => {
const { name, value } = e.target
setFormData(prev => ({ ...prev, [name]: value }))
}
const submitForm = () => {
console.log('submitting')
const submitForm = async () => {
setIsSubmitted(true)
try {
const res = await fetch('/api/replit/signup', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(formData)
})
const data = await res.json()
if (res.ok) {
setButtonText(data.message)
if (data.success) {
jsConfetti.current.addConfetti({
confettiColors: [
theme.colors.red,
theme.colors.orange,
theme.colors.yellow,
theme.colors.green,
theme.colors.cyan,
theme.colors.blue,
theme.colors.purple
]
})
setCurrentStep(3)
}
} else {
setButtonText('Error')
console.error('Error', res, data)
}
} catch (error) {
console.error('Error:', error)
} finally {
setIsSubmitted(false)
}
}
const stickers = [
@ -36,7 +115,7 @@ const ReplitForm = ({ cssDark }) => {
const buttonStyle = ({ disabled }) => ({
backgroundColor: cssDark,
cursor: disabled ? 'not-allowed' : 'auto',
cursor: disabled ? 'not-allowed' : 'pointer',
opacity: disabled ? 0.5 : 1
})
@ -151,7 +230,7 @@ const ReplitForm = ({ cssDark }) => {
...buttonStyle({ disabled: submitButtonDisabled })
}}
>
Submit
{buttonText}
</Button>
</Flex>
<StepIndicator step={2} />
@ -174,27 +253,13 @@ const ReplitForm = ({ cssDark }) => {
Get free stickers Get free stickers{' '}
</Text>
<style>
{`
.sticker {
scale: 1;
filter: drop-shadow(0 0 0.2rem #0008);
transition: scale 0.1s, filter 0.1s;
}
.sticker:hover {
scale: 1.2;
filter: drop-shadow(0 0 0.6rem #0004);
}
`}
</style>
{stickers.map((sticker, idx) => {
const pos = getRandomPointOnUnitSquare()
return (
<Image
src={sticker}
width="64"
height="64"
width="72"
height="72"
alt="orpheus dinosaur labelled 'hackers assemble'"
className="sticker"
sx={{
@ -209,6 +274,9 @@ const ReplitForm = ({ cssDark }) => {
/>
)
})}
<Box sx={{ width: 'calc(100% + calc(1.82rem * 2))', marginX: '-1.82em' }}>
<FilloutStandardEmbed filloutId="ji6Jw9xpBPus" />
</Box>
<StepIndicator step={3} />
</Box>
)
@ -226,7 +294,7 @@ const ReplitForm = ({ cssDark }) => {
case 2:
return [1 + margin, 1 - position]
case 3:
return [1 - position, -margin]
return [1 - position, 0]
}
}
@ -241,7 +309,20 @@ const ReplitForm = ({ cssDark }) => {
overflow: 'initial'
}}
>
<style>{`.step { transition: opacity 0.1s; }`}</style>
<style>{`
.step { transition: opacity 0.1s; }
.sticker {
scale: 1;
filter: drop-shadow(0 0 0.2rem #0008);
transition: scale 0.3s ease, filter 0.3s ease;
}
.sticker.dragged {
scale: 1.2;
filter: drop-shadow(0 0 0.6rem #0004);
}
`}</style>
{step1()}
{step2()}
{step3()}

View file

@ -15,6 +15,7 @@
"@apollo/client": "^3.9.11",
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.5",
"@fillout/react": "^1.1.2",
"@github/time-elements": "^4.0.0",
"@hackclub/icons": "^0.0.14",
"@hackclub/markdown": "^0.0.43",

View file

@ -13,7 +13,7 @@ export default async function handler(req, res) {
throw new Error(`HTTP error! status: ${response.status}`)
}
const data = await response.text()
const data = await response.json()
// Send the response from the replit-takeout service back to the client
res.status(200).json(data)

View file

@ -1,97 +1,12 @@
import {
Box,
Link,
Grid,
Image,
Container,
Button,
Heading,
Text,
Label,
Input,
Card,
Progress
} from 'theme-ui'
import { Box, Link, Image, Button, Heading, Text, Card } from 'theme-ui'
import Head from 'next/head'
import Meta from '@hackclub/meta'
import Footer from '../components/footer'
import Nav from '../components/nav'
import { useState, useEffect, useRef } from 'react'
import Submit from '../components/submit'
import ForceTheme from '../components/force-theme'
import ReplitForm from '../components/replit/form'
const ReplitPage = () => {
const [token, setToken] = useState('')
const [email, setEmail] = useState('')
const [submitStatus, setSubmitStatus] = useState('default')
const [responseText, setResponseText] = useState('')
const [progressText, setProgressText] = useState(0)
const intervalRef = useRef(null)
useEffect(() => {
const token = localStorage.getItem('token')
const email = localStorage.getItem('email')
if (token) setToken(token)
if (email) setEmail(email)
}, [])
useEffect(() => {
if (token) {
intervalRef.current = setInterval(() => {
try {
fetch(`/api/replit/progress?token=${token}`)
.then(res => res.text())
.then(data => {
const split = data.split('/')
setProgressText(split[0] / split[1])
})
} catch (e) {
console.warn(e)
}
}, 5000)
}
return () => {
clearInterval(intervalRef.current)
}
}, [token])
const handleSubmit = async event => {
setSubmitStatus('submitting')
event.preventDefault()
const formData = new FormData(event.target)
const data = {
email: formData.get('email'),
token: formData.get('token')
}
try {
const response = await fetch('/api/replit/signup', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
},
body: new URLSearchParams(data).toString()
})
const result = await response.text()
setResponseText(result)
// Store the email and token in localStorage
localStorage.setItem('token', data.token)
localStorage.setItem('email', data.email)
setSubmitStatus('success')
} catch (error) {
setSubmitStatus('error')
setResponseText('Error submitting form')
console.error('Error:', error)
}
}
const steps = [
'Enter your email',
'Enter your replit token',

View file

@ -1212,6 +1212,11 @@
resolved "https://registry.yarnpkg.com/@eslint/js/-/js-8.57.0.tgz#a5417ae8427873f1dd08b70b3574b453e67b5f7f"
integrity sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==
"@fillout/react@^1.1.2":
version "1.1.2"
resolved "https://registry.yarnpkg.com/@fillout/react/-/react-1.1.2.tgz#833c28aa53aa79a712b61e915e3fc710312f02f9"
integrity sha512-XyzLY74Zhxxwym3A9770Tb3NINwaaWyWwvaw1lMJ5sA/P6hgsdzvefUOqohzR3+KVyspvBOR4BoR0nBMPFd/Vw==
"@github/time-elements@^4.0.0":
version "4.0.0"
resolved "https://registry.yarnpkg.com/@github/time-elements/-/time-elements-4.0.0.tgz#ca6aec5e9fbc18348d3fc980610b79c244b1b81f"
@ -8536,7 +8541,16 @@ string-hash@1.1.3:
resolved "https://registry.yarnpkg.com/string-hash/-/string-hash-1.1.3.tgz#e8aafc0ac1855b4666929ed7dd1275df5d6c811b"
integrity sha512-kJUvRUFK49aub+a7T1nNE66EJbZBMnBgoC1UbCZ5n6bsZKBRga4KgBRTMn/pFkeCZSYtNeSyMxPDM0AXWELk2A==
"string-width-cjs@npm:string-width@^4.2.0", string-width@^4.1.0:
"string-width-cjs@npm:string-width@^4.2.0":
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
dependencies:
emoji-regex "^8.0.0"
is-fullwidth-code-point "^3.0.0"
strip-ansi "^6.0.1"
string-width@^4.1.0:
version "4.2.3"
resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010"
integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==
@ -8647,7 +8661,7 @@ stringify-entities@^4.0.0:
character-entities-html4 "^2.0.0"
character-entities-legacy "^3.0.0"
"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1:
"strip-ansi-cjs@npm:strip-ansi@^6.0.1":
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
@ -8661,6 +8675,13 @@ strip-ansi@6.0.0:
dependencies:
ansi-regex "^5.0.0"
strip-ansi@^6.0.0, strip-ansi@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9"
integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==
dependencies:
ansi-regex "^5.0.1"
strip-ansi@^7.0.1:
version "7.1.0"
resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-7.1.0.tgz#d5b6568ca689d8561370b0707685d22434faff45"