diff --git a/components/Checkbox/index.tsx b/components/Checkbox/index.tsx new file mode 100644 index 00000000..252dacae --- /dev/null +++ b/components/Checkbox/index.tsx @@ -0,0 +1,18 @@ +export default function Checkbox({ onChange, selected, children }) { + return ( +
+ + +
+ ); +} diff --git a/components/Footer/index.tsx b/components/Footer/index.tsx index 1207c72f..20d68553 100644 --- a/components/Footer/index.tsx +++ b/components/Footer/index.tsx @@ -129,11 +129,9 @@ export default function Footer(props: IFooterProps) { > Report a Problem - {/* Survival Guide - */} General Regulation diff --git a/components/Social/index.jsx b/components/Social/index.jsx index 792f0a47..7fcfb8db 100644 --- a/components/Social/index.jsx +++ b/components/Social/index.jsx @@ -4,6 +4,7 @@ import { faInstagram, faGithub, faLinkedinIn, + faDiscord, } from "@fortawesome/free-brands-svg-icons"; import { faXTwitter } from "@fortawesome/free-brands-svg-icons"; import { faEnvelope } from "@fortawesome/free-solid-svg-icons"; @@ -51,6 +52,14 @@ export default function Social() { > + + + { e.preventDefault(); @@ -236,11 +239,15 @@ function Profile() { {levelEntries[3]} entries

- Level 5 24 companies → + + Level 5 25 companies → + {levelEntries[4]} entries

+

+ Level 6 29 companies → + + {levelEntries[5]} entries +

- + {level != 5 && (

diff --git a/layout/Attendee/Slots/Slots.tsx b/layout/Attendee/Slots/Slots.tsx new file mode 100644 index 00000000..eb2adb45 --- /dev/null +++ b/layout/Attendee/Slots/Slots.tsx @@ -0,0 +1,123 @@ +import { useRef, useState } from "react"; +import { withAuth, useAuth, IAttendee } from "@context/Auth"; +import Layout from "@components/Layout"; +import Button from "@components/Button"; +import { faArrowUpRightFromSquare } from "@fortawesome/free-solid-svg-icons"; +import { spinSlots } from "@lib/api"; +import Balance from "@components/Balance"; +import { Machine, SlotsMessage } from "./components"; +import Paytable from "./components/Paytable"; +import { FontAwesomeIcon } from "@fortawesome/react-fontawesome"; + +function Slots() { + const [isSpinning, setIsSpinning] = useState(false); + const [bet, setBet] = useState(null); + const machineRef = useRef(null); + const [slotsMessage, updateSlotsMessage] = useState(<>); + + const { user } = useAuth() as { + user: IAttendee; + refetchUser: () => void; + }; + + const spinReels = async () => { + if (bet != null && bet > 0 && bet <= user.token_balance) { + user.token_balance -= bet; + setIsSpinning(true); + const response = await spinSlots(bet); + await machineRef.current.rollAll(response.multiplier); + setIsSpinning(false); + user.token_balance += response.tokens; + switch (response.multiplier) { + case 0: + break; + case 1: + updateSlotsMessage( + updateSlotsMessage(null)} + /> + ); + break; + default: + updateSlotsMessage( + updateSlotsMessage(null)} + /> + ); + break; + } + } + }; + + const showPaytable = () => { + updateSlotsMessage( updateSlotsMessage(null)} />); + }; + + const setMaxBet = () => { + setBet(user.token_balance.toString()); + }; + + const canSpin = () => { + return !isSpinning && bet != null && bet > 0 && bet <= user.token_balance; + }; + + return ( + +

+ +
+
+
+ +
+
+
+ setBet(e.target.value.replace(/[^0-9]/g, ""))} + className="ml-8 h-16 w-28 bg-transparent uppercase outline-none placeholder:text-white/30" + placeholder="Tokens" + > +
+
+
+
+
+ + + See Paytable + +
+
+ {slotsMessage} + + ); +} + +export default withAuth(Slots); diff --git a/layout/Attendee/Slots/components/Machine/index.tsx b/layout/Attendee/Slots/components/Machine/index.tsx new file mode 100644 index 00000000..d2ea3dee --- /dev/null +++ b/layout/Attendee/Slots/components/Machine/index.tsx @@ -0,0 +1,160 @@ +import { forwardRef, useRef, useImperativeHandle, useState } from "react"; +import slots from "@data/slots.json"; + +// Max-speed in ms for animating one icon down +const rotationSpeed = 100; + +// Time to wait before sending the response after the reels stop +const extraTime = 1000; + +// Number of icons in each reel +const numIcons = slots.symbols; + +// Size of the icons +var iconSize = 79; + +interface MachineRef { + rollAll: (multiplier: any) => Promise; +} + +// Check if an array contains an element +const contains = (arr, elem) => { + return arr.some((e) => JSON.stringify(e) === JSON.stringify(elem)); +}; + +const isTargetInMultipliers = (target) => { + for (const multiplier in slots.multipliers) { + if (contains(slots.multipliers[multiplier], target)) { + return true; + } + } + return false; +}; + +// Get a random target that corresponds to the multiplier +const getReelsTarget = (multiplier) => { + if (multiplier != 0) { + const combinations = slots.multipliers[multiplier]; + return combinations[Math.floor(Math.random() * combinations.length)]; + } + + // Finds a random target that is not in the multipliers + let target; + do { + target = Array.from({ length: 3 }, () => + Math.floor(Math.random() * numIcons) + ); + } while (isTargetInMultipliers(target)); + return target; +}; + +const Machine = forwardRef((_, ref) => { + const machineRef = useRef(null); + const [indexes, setIndexes] = useState([1, 1, 1]); + + const rollAll = (multiplier) => { + return new Promise((resolve) => { + const reelsList = document.querySelectorAll(".slots > .reel"); + const reelsTarget = getReelsTarget(multiplier); + const promises = []; + + for (let i = 0; i < reelsList.length; i++) { + promises.push(roll(reelsList[i], i, reelsTarget[i])); + } + + Promise.all(promises).then(() => { + resolve(); + }); + }); + }; + + const roll = (reel, reelIndex, target) => { + // Number of reel rotations + const rotations = reelIndex + 2; + // Calculate the delta for the reels rotation + const rotationsDelta = rotations * numIcons; + // Delta for the target + const delta = + rotationsDelta + + (rotationsDelta + (indexes[reelIndex] % numIcons)) - + target; + + // Sets the new index + setIndexes((prev) => { + const newIndexes = [...prev]; + newIndexes[reelIndex] = target; + return newIndexes; + }); + + // Return promise so we can wait for all reels to finish + return new Promise((resolve, reject) => { + const style = getComputedStyle(reel), + // Current background position + backgroundPositionY = parseFloat(style["background-position-y"]), + // Target background position + targetBackgroundPositionY = backgroundPositionY + delta * iconSize, + // Normalized background position, for reset + normTargetBackgroundPositionY = + targetBackgroundPositionY % (numIcons * iconSize); + + // Delay animation with timeout + setTimeout(() => { + reel.style.transition = `background-position-y ${ + (8 + 1 * delta) * rotationSpeed + }ms cubic-bezier(.41,-0.01,.63,1.09)`; + // Set background position + reel.style.backgroundPositionY = `${ + backgroundPositionY + delta * iconSize + }px`; + }, reelIndex * 150); + + // After animation + setTimeout(() => { + // Reset position, so that it doesn't get higher without limit + reel.style.transition = `none`; + reel.style.backgroundPositionY = `${normTargetBackgroundPositionY}px`; + // Resolve this promise + resolve(delta % numIcons); + }, (8 + 1 * delta) * rotationSpeed + reelIndex * 150 + extraTime); + }); + }; + + useImperativeHandle(ref, () => ({ + rollAll: (multiplier) => rollAll(multiplier), + })); + + return ( +
+
+
+
+
+
+
+
+
+ ); +}); + +export default Machine; diff --git a/layout/Attendee/Slots/components/Paytable/index.tsx b/layout/Attendee/Slots/components/Paytable/index.tsx new file mode 100644 index 00000000..e5fbee22 --- /dev/null +++ b/layout/Attendee/Slots/components/Paytable/index.tsx @@ -0,0 +1,30 @@ +import { Dialog } from "@headlessui/react"; + +export default function Paytable({ onExit }) { + return ( + <> +
+ {}} + > + + + + Paytable + + + + + + + + + ); +} diff --git a/layout/Attendee/Slots/components/SlotsMessage/index.tsx b/layout/Attendee/Slots/components/SlotsMessage/index.tsx new file mode 100644 index 00000000..7046a632 --- /dev/null +++ b/layout/Attendee/Slots/components/SlotsMessage/index.tsx @@ -0,0 +1,30 @@ +import { Dialog } from "@headlessui/react"; + +export default function SlotsMessage({ title, description, onExit }) { + return ( + <> +
+ {}} + > + + + + {title} + + + {description} + + + + + + ); +} diff --git a/layout/Attendee/Slots/components/index.ts b/layout/Attendee/Slots/components/index.ts new file mode 100644 index 00000000..ef3915eb --- /dev/null +++ b/layout/Attendee/Slots/components/index.ts @@ -0,0 +1,4 @@ +import Machine from "./Machine"; +import SlotsMessage from "./SlotsMessage"; + +export { Machine, SlotsMessage }; diff --git a/layout/Attendee/Slots/index.ts b/layout/Attendee/Slots/index.ts new file mode 100644 index 00000000..a8843c74 --- /dev/null +++ b/layout/Attendee/Slots/index.ts @@ -0,0 +1 @@ +export { default } from "./Slots"; diff --git a/layout/Attendee/Store/Store.tsx b/layout/Attendee/Store/Store.tsx index bb757b2f..ab0e1acb 100644 --- a/layout/Attendee/Store/Store.tsx +++ b/layout/Attendee/Store/Store.tsx @@ -22,18 +22,16 @@ function Store() { }, []); return ( - + +
+

*subject to existing stock

+
-
{products && products diff --git a/layout/Home/components/Hero/index.tsx b/layout/Home/components/Hero/index.tsx index ed7f2f19..f446afef 100644 --- a/layout/Home/components/Hero/index.tsx +++ b/layout/Home/components/Hero/index.tsx @@ -17,7 +17,7 @@ export default function Hero() {
- <div className="relative mt-20 text-white"> + <div className="relative mt-24 text-white"> <div className="flex items-center justify-between"> <h5 className="font-imedium">Follow us on</h5> {isAuthenticated || ( diff --git a/layout/ResetPassword/components/ResetPasswordForm/index.tsx b/layout/ResetPassword/components/ResetPasswordForm/index.tsx index 7e64bb43..f45f586f 100644 --- a/layout/ResetPassword/components/ResetPasswordForm/index.tsx +++ b/layout/ResetPassword/components/ResetPasswordForm/index.tsx @@ -97,7 +97,7 @@ export default function ResetPasswordForm() { <Button type="button" title="BACK TO LOGIN" - className="border-quinary bg-quinary text-secondary" + className="border-quinary bg-quinary p-4 text-secondary" /> </Link> </div> diff --git a/layout/SignUp/SignUp.tsx b/layout/SignUp/SignUp.tsx index 22a0faea..b206286d 100644 --- a/layout/SignUp/SignUp.tsx +++ b/layout/SignUp/SignUp.tsx @@ -31,7 +31,7 @@ function Signup() { <Title text="Sign up" /> <SignUpForm courses={courses} /> <Text text="Already have an account?" link="Login here" href="/login" /> - <div className="absolute bottom-0 right-60 hidden lg:block"> + <div className="fixed bottom-0 right-60 hidden lg:block"> <Motion.div initial={{ y: 30, opacity: 0 }} animate={{ y: 0, opacity: 1 }} diff --git a/layout/SignUp/components/SignUpForm/index.tsx b/layout/SignUp/components/SignUpForm/index.tsx index c2a70b95..270d42e7 100644 --- a/layout/SignUp/components/SignUpForm/index.tsx +++ b/layout/SignUp/components/SignUpForm/index.tsx @@ -9,6 +9,8 @@ import Select from "@components/Select"; import PasswordInput from "@components/PasswordInput"; import BarebonesQRScanner from "@components/QRScanner/BarebonesQRScanner"; +import Checkbox from "@components/Checkbox"; +import Link from "next/link"; interface Course { id: any; @@ -29,6 +31,8 @@ export default function SignUpForm({ courses }) { const [local_error, updateError] = useState(""); const [scanned, updateScanned] = useState(false); const [scanning, updateScanning] = useState(false); + const [termsAccepted, setTermsAccepted] = useState(false); + const pauseRef = useRef(false); pauseRef.current = false; @@ -36,7 +40,7 @@ export default function SignUpForm({ courses }) { return ( nickname.length >= 2 && nickname.length <= 15 && - nickname.match(/^[a-zA-Z0-9]+([a-zA-Z0-9](_|-)[a-zA-Z0-9])*[a-zA-Z0-9]+$/) + nickname.match(/^[\w\d-_]{3,15}$/) ); }; @@ -57,6 +61,8 @@ export default function SignUpForm({ courses }) { ); } else if (!validatePassword(password)) { updateError("Your password must be at least 8 characters long"); + } else if (!termsAccepted) { + updateError("You must accept the privacy policy and general regulation"); } else { updateError(""); @@ -137,6 +143,27 @@ export default function SignUpForm({ courses }) { updateScanning(!scanning); }} /> + <Checkbox + selected={termsAccepted} + onChange={(e) => setTermsAccepted(!termsAccepted)} + > + I have read and understood the   + <Link + href="/docs/privacy_policy.pdf" + target="_blank" + className="text-quinary" + > + privacy policy + </Link>{" "} + and the   + <Link + href="/docs/regulation.pdf" + target="_blank" + className="text-quinary" + > + general regulation + </Link> + </Checkbox> {scanned && ( <p className="mt-3 font-iregular text-lg text-quinary"> QR Code scanned successfully: {uuid} diff --git a/layout/Staff/UploadCV/components/UploadSection.tsx b/layout/Staff/UploadCV/components/UploadSection.tsx index b43e3974..b6e97e37 100644 --- a/layout/Staff/UploadCV/components/UploadSection.tsx +++ b/layout/Staff/UploadCV/components/UploadSection.tsx @@ -1,5 +1,6 @@ import { useState } from "react"; import { UploadIcon, CheckCircleIcon } from "@heroicons/react/solid"; +import Checkbox from "@components/Checkbox"; export default function UploadSection({ cv, onSubmit }) { const [uploading, setUploading] = useState<boolean>(cv == null); @@ -92,25 +93,10 @@ export default function UploadSection({ cv, onSubmit }) { </div> </label> <div className="mt-4 block"> - <label className="rounded-sm bg-quinary"> - <input - className="hidden" - type="checkbox" - onChange={handleConsentChange} - ></input> - <span - className={`text-sm text-white ${ - consent ? "bg-quinary" : "bg-white" - } select-none border-2 border-quinary px-1 font-ibold`} - > - {" "} - ✓ - </span> - </label> - <label className="ml-2 select-none text-sm"> + <Checkbox selected={consent} onChange={handleConsentChange}> By submitting your CV you are consenting to it being shared with the companies present in the event. - </label> + </Checkbox> </div> <input diff --git a/lib/api.js b/lib/api.js index 8990bcce..d45ad0ab 100644 --- a/lib/api.js +++ b/lib/api.js @@ -312,4 +312,12 @@ export async function createSpotlight(company_id) { return response.data; } +export async function spinSlots(bet) { + const response = await API.post("/api/slots", { + bet: bet, + }); + + return response.data; +} + export default API; diff --git a/pages/_app.tsx b/pages/_app.tsx index b49194cd..23612835 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -1,5 +1,8 @@ import { AuthProvider } from "@context/Auth"; import Header from "@components/Header"; +import { config } from "@fortawesome/fontawesome-svg-core"; +import "@fortawesome/fontawesome-svg-core/styles.css"; +config.autoAddCss = false; import "../styles/globals.css"; import { NotifyProvider } from "@context/Notification"; diff --git a/pages/attendee/slots.tsx b/pages/attendee/slots.tsx new file mode 100644 index 00000000..c18ea7c3 --- /dev/null +++ b/pages/attendee/slots.tsx @@ -0,0 +1 @@ +export { default } from "@layout/Attendee/Slots"; diff --git a/pages/safira.tsx b/pages/safira.tsx new file mode 100644 index 00000000..3f838804 --- /dev/null +++ b/pages/safira.tsx @@ -0,0 +1,14 @@ +function Safira() { + return null; +} + +export async function getServerSideProps() { + return { + redirect: { + destination: "https://www.youtube.com/watch?v=YvrWzbp7B4g", + permanent: false, + }, + }; +} + +export default Safira; diff --git a/public/algoritmos.html b/public/algoritmos.html deleted file mode 100644 index 40aa15b9..00000000 --- a/public/algoritmos.html +++ /dev/null @@ -1,13 +0,0 @@ -<!DOCTYPE html> -<html lang="en"> - <head> - <meta charset="UTF-8" /> - <meta name="viewport" content="width=device-width, initial-scale=1.0" /> - <meta http-equiv="X-UA-Compatible" content="ie=edge" /> - <title>Um meme da SEI - - - - Não passaste a algoritmos - - diff --git a/public/docs/privacy_policy.pdf b/public/docs/privacy_policy.pdf new file mode 100644 index 00000000..1e0a67e3 Binary files /dev/null and b/public/docs/privacy_policy.pdf differ diff --git a/public/docs/survival.pdf b/public/docs/survival.pdf new file mode 100644 index 00000000..be254b96 Binary files /dev/null and b/public/docs/survival.pdf differ diff --git a/public/images/slots/paytable.svg b/public/images/slots/paytable.svg new file mode 100644 index 00000000..efc72d07 --- /dev/null +++ b/public/images/slots/paytable.svg @@ -0,0 +1,639 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/slots/reel_0.svg b/public/images/slots/reel_0.svg new file mode 100644 index 00000000..307f53f4 --- /dev/null +++ b/public/images/slots/reel_0.svg @@ -0,0 +1,210 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/slots/reel_1.svg b/public/images/slots/reel_1.svg new file mode 100644 index 00000000..b24a306c --- /dev/null +++ b/public/images/slots/reel_1.svg @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/slots/reel_2.svg b/public/images/slots/reel_2.svg new file mode 100644 index 00000000..b68ffd84 --- /dev/null +++ b/public/images/slots/reel_2.svg @@ -0,0 +1,213 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/public/images/speakers/brunocarvalho.png b/public/images/speakers/brunocarvalho.png new file mode 100644 index 00000000..bf64dfeb Binary files /dev/null and b/public/images/speakers/brunocarvalho.png differ