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 (
+
{
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"
+ >
+
+
+
+
+
+
+
+
+ {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
+
+
+
+
+
+ onExit(e)}
+ >
+ OK
+
+
+ >
+ );
+}
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}
+
+
+ onExit(e)}
+ >
+ OK
+
+
+ >
+ );
+}
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() {
-
+
Follow us on
{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() {
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() {
-
+
= 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);
}}
/>
+ setTermsAccepted(!termsAccepted)}
+ >
+ I have read and understood the
+
+ privacy policy
+ {" "}
+ and the
+
+ general regulation
+
+
{scanned && (
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(cv == null);
@@ -92,25 +93,10 @@ export default function UploadSection({ cv, onSubmit }) {
-
-
-
- {" "}
- ✓
-
-
-
+
By submitting your CV you are consenting to it being shared with
the companies present in the event.
-
+
-
-
-
-
-
-
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