Skip to content

Commit

Permalink
feat: staff curriculum upload (#613)
Browse files Browse the repository at this point in the history
  • Loading branch information
joaodiaslobo authored Jan 30, 2024
1 parent 6a3ddaf commit 7731b8f
Show file tree
Hide file tree
Showing 11 changed files with 212 additions and 11 deletions.
2 changes: 1 addition & 1 deletion components/Layout/Layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down
1 change: 1 addition & 0 deletions components/Navbar/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 [
Expand Down
1 change: 1 addition & 0 deletions context/Auth/withAuth.js
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ export function withAuth(WrappedComponent) {
"/staff/prizes/[uuid]",
"/staff/identifier",
"/staff/leaderboard",
"/staff/cv",
"/attendees/[uuid]",
].includes(router.pathname)
) {
Expand Down
4 changes: 1 addition & 3 deletions layout/Attendee/Profile/Profile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,7 @@ function Profile() {
const [username, setUsername] = useState(user.nickname || "");
const [course, setCourse] = useState(user.course.toString() || "");

const [courses, setCourses] = useState<Course[]>([
{ id: "", name: "None" },
]);
const [courses, setCourses] = useState<Course[]>([{ id: "", name: "None" }]);

useEffect(() => {
getCourses().then((response) => {
Expand Down
4 changes: 1 addition & 3 deletions layout/SignUp/SignUp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,7 @@ interface Course {
}

function Signup() {
const [courses, setCourses] = useState<Course[]>([
{ id: "", name: "None" },
]);
const [courses, setCourses] = useState<Course[]>([{ id: "", name: "None" }]);

useEffect(() => {
getCourses().then((response) => {
Expand Down
31 changes: 31 additions & 0 deletions layout/Staff/UploadCV/UploadCV.tsx
Original file line number Diff line number Diff line change
@@ -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 (
<Layout title="Upload CV" description="Upload your curriculum">
<div>{staff && <UploadSection cv={staff.cv} onSubmit={submitCV} />}</div>
</Layout>
);
}

export default withAuth(UploadCV);
156 changes: 156 additions & 0 deletions layout/Staff/UploadCV/components/UploadSection.tsx
Original file line number Diff line number Diff line change
@@ -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<boolean>(cv == null);
const [file, setFile] = useState<null | File>(null);
const [consent, setConsent] = useState<boolean>(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<HTMLInputElement>) => {
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 ? (
<form onSubmit={handleSubmit} id="cv-form" className="mt-2">
<label className="m-auto inline-block h-[50vh] w-full cursor-pointer select-none rounded-lg border-[3px] border-dashed border-quinary bg-quinary/5 text-center duration-500 hover:bg-quinary/10">
<div
className="flex h-full flex-col items-center justify-center"
onDragOver={onDragOver}
onDrop={onFileDrop}
>
{file ? (
<CheckCircleIcon className="h-12 w-12 flex-shrink-0" />
) : (
<UploadIcon className="h-12 w-12 flex-shrink-0" />
)}
<input
type="file"
accept=".pdf"
className="hidden"
onChange={handleOnFileChange}
/>
{file ? (
<>
<span className="text-lg font-medium">Ready to upload</span>
<p className="text-quinary">{file.name}</p>
</>
) : (
<>
<span className="text-lg font-medium">
Drop your PDF here
</span>
<p>
<span className="text-quinary">Browse file</span> from your
device
</p>
</>
)}
</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`}
>
{" "}
&#10003;
</span>
</label>
<label className="ml-2 select-none text-sm">
By submitting your CV you are consenting to it being shared with
the companies present in the event.
</label>
</div>

<input
className="m-auto my-4 block h-10 w-28 rounded-full bg-quinary font-iregular hover:cursor-pointer"
type="submit"
value="Submit"
disabled={!consent}
></input>
</form>
) : (
<div className="m-auto flex w-full flex-col flex-wrap content-center justify-center pt-10">
<div className="flex justify-center">
<CheckCircleIcon className="h-12 w-12 flex-shrink-0" />
</div>
<p className="mt-2 text-center font-iregular text-lg">
You have already submitted your curriculum!
</p>
<p className="mt-2 text-center font-iregular text-lg">
Feel free to download it{" "}
<a
className="text-quinary underline"
target="_blank"
rel="noreferrer"
href={cv}
>
here
</a>
.
</p>
<p className="mt-2 text-center font-iregular text-lg">
Event sponsors are able to see it.
</p>
<button
className="m-auto mt-4 block h-10 w-28 select-none rounded-full bg-quinary font-iregular"
onClick={(_) => setUploading(true)}
>
Resubmit
</button>
</div>
)}
</>
);
}
1 change: 1 addition & 0 deletions layout/Staff/UploadCV/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "./UploadCV";
16 changes: 16 additions & 0 deletions lib/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
6 changes: 2 additions & 4 deletions pages/register/[uuid].js
Original file line number Diff line number Diff line change
Expand Up @@ -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));
Expand Down
1 change: 1 addition & 0 deletions pages/staff/cv.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default } from "@layout/Staff/UploadCV";

0 comments on commit 7731b8f

Please sign in to comment.