diff --git a/components/Layout/Layout.tsx b/components/Layout/Layout.tsx index 6619ac00..79c46656 100644 --- a/components/Layout/Layout.tsx +++ b/components/Layout/Layout.tsx @@ -20,7 +20,7 @@ const roleNavigations = { "identifier", ], admin: ["scanner", "visitors", "badges", "leaderboard", "users", "events"], - staff: ["badges", "leaderboard", "prizes", "identifier"], + staff: ["badges", "leaderboard", "prizes", "identifier", "cv"], }; type LayoutProps = { diff --git a/components/Navbar/index.jsx b/components/Navbar/index.jsx index 5ce1a27e..67468dd4 100644 --- a/components/Navbar/index.jsx +++ b/components/Navbar/index.jsx @@ -29,6 +29,7 @@ const userNavigation = (type) => { { name: "Leaderboard", slug: "/staff/leaderboard" }, { name: "Give Badges", slug: "/staff/badges" }, { name: "Give Prizes", slug: "/staff/prizes" }, + { name: "Upload CV", slug: "/staff/cv" }, ]; case USER.ROLES.SPONSOR: return [ diff --git a/context/Auth/withAuth.js b/context/Auth/withAuth.js index bec7d993..114ddac4 100644 --- a/context/Auth/withAuth.js +++ b/context/Auth/withAuth.js @@ -41,6 +41,7 @@ export function withAuth(WrappedComponent) { "/staff/prizes/[uuid]", "/staff/identifier", "/staff/leaderboard", + "/staff/cv", "/attendees/[uuid]", ].includes(router.pathname) ) { diff --git a/layout/Attendee/Profile/Profile.tsx b/layout/Attendee/Profile/Profile.tsx index b4b54fdd..59caab25 100644 --- a/layout/Attendee/Profile/Profile.tsx +++ b/layout/Attendee/Profile/Profile.tsx @@ -31,9 +31,7 @@ function Profile() { const [username, setUsername] = useState(user.nickname || ""); const [course, setCourse] = useState(user.course.toString() || ""); - const [courses, setCourses] = useState([ - { id: "", name: "None" }, - ]); + const [courses, setCourses] = useState([{ id: "", name: "None" }]); useEffect(() => { getCourses().then((response) => { diff --git a/layout/SignUp/SignUp.tsx b/layout/SignUp/SignUp.tsx index 6c1a6c46..22a0faea 100644 --- a/layout/SignUp/SignUp.tsx +++ b/layout/SignUp/SignUp.tsx @@ -16,9 +16,7 @@ interface Course { } function Signup() { - const [courses, setCourses] = useState([ - { id: "", name: "None" }, - ]); + const [courses, setCourses] = useState([{ id: "", name: "None" }]); useEffect(() => { getCourses().then((response) => { diff --git a/layout/Staff/UploadCV/UploadCV.tsx b/layout/Staff/UploadCV/UploadCV.tsx new file mode 100644 index 00000000..796b8502 --- /dev/null +++ b/layout/Staff/UploadCV/UploadCV.tsx @@ -0,0 +1,31 @@ +import { useState, useEffect } from "react"; +import { withAuth, useAuth, IStaff } from "@context/Auth"; +import { getStaffCV, uploadStaffCV } from "@lib/api"; +import Layout from "@components/Layout"; +import UploadSection from "./components/UploadSection"; + +function UploadCV() { + const { user } = useAuth() as { user: IStaff }; + const [staff, setStaff] = useState(null); + + useEffect(() => { + getStaffCV(user.id).then((response) => setStaff(response)); + }, []); + + const submitCV = (f: File) => { + const formData = new FormData(); + formData.append("staff[cv]", f); + setStaff(null); + uploadStaffCV(user.id, formData).then((response) => { + setStaff(response); + }); + }; + + return ( + +
{staff && }
+
+ ); +} + +export default withAuth(UploadCV); diff --git a/layout/Staff/UploadCV/components/UploadSection.tsx b/layout/Staff/UploadCV/components/UploadSection.tsx new file mode 100644 index 00000000..b43e3974 --- /dev/null +++ b/layout/Staff/UploadCV/components/UploadSection.tsx @@ -0,0 +1,156 @@ +import { useState } from "react"; +import { UploadIcon, CheckCircleIcon } from "@heroicons/react/solid"; + +export default function UploadSection({ cv, onSubmit }) { + const [uploading, setUploading] = useState(cv == null); + const [file, setFile] = useState(null); + const [consent, setConsent] = useState(false); + + const handleConsentChange = () => { + setConsent(!consent); + }; + + const handleSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + + if (!consent || !file) { + return; + } + + try { + await onSubmit(file); + alert("CV sent successfully"); + } catch (error) { + alert("Error sending CV, please try again later"); + } + }; + + const handleOnFileChange = (e: React.ChangeEvent) => { + const file = e.target.files[0]; + if (!file) return; + + if (file.type !== "application/pdf") return; + + setFile(file); + }; + + const onDragOver = (e) => { + let event = e as Event; + event.stopPropagation(); + event.preventDefault(); + }; + + const onFileDrop = (e) => { + e.stopPropagation(); + e.preventDefault(); + + const file = e.dataTransfer.files[0]; + if (!file) return; + + if (file.type !== "application/pdf") return; + + setFile(file); + }; + + return ( + <> + {cv == null || uploading ? ( +
+ +
+ + +
+ + +
+ ) : ( +
+
+ +
+

+ You have already submitted your curriculum! +

+

+ Feel free to download it{" "} + + here + + . +

+

+ Event sponsors are able to see it. +

+ +
+ )} + + ); +} diff --git a/layout/Staff/UploadCV/index.ts b/layout/Staff/UploadCV/index.ts new file mode 100644 index 00000000..cd7afbd7 --- /dev/null +++ b/layout/Staff/UploadCV/index.ts @@ -0,0 +1 @@ +export { default } from "./UploadCV"; diff --git a/lib/api.js b/lib/api.js index fcd28e79..41043e88 100644 --- a/lib/api.js +++ b/lib/api.js @@ -273,6 +273,22 @@ export async function getCompanyVisitors(id) { return response.data; } +export async function uploadStaffCV(id, formData) { + const response = await API.patch(`/api/staff/cv/${id}`, formData, { + headers: { + "Content-Type": "multipart/form-data", + }, + }); + + return response.data; +} + +export async function getStaffCV(id) { + const response = await API.get(`/api/staff/cv/${id}`); + + return response.data; +} + export async function downloadCVInBulk(id) { const response = await API.get(`/api/company/attendees/cvs/${id}`, { responseType: "blob", diff --git a/pages/register/[uuid].js b/pages/register/[uuid].js index d6dd32a2..1b5e5fc8 100644 --- a/pages/register/[uuid].js +++ b/pages/register/[uuid].js @@ -29,10 +29,8 @@ function Register() { const [password, updatePassword] = useState(""); const [password_confirmation, updatePasswordConfirmation] = useState(""); - const [courses, setCourses] = useState([ - { id: "", name: "None" } - ]); - + const [courses, setCourses] = useState([{ id: "", name: "None" }]); + useEffect(() => { getCourses().then((response) => { setCourses(response.data.concat(courses)); diff --git a/pages/staff/cv.tsx b/pages/staff/cv.tsx new file mode 100644 index 00000000..8ad1260f --- /dev/null +++ b/pages/staff/cv.tsx @@ -0,0 +1 @@ +export { default } from "@layout/Staff/UploadCV";