diff --git a/app/(app)/jobs/create/_client.tsx b/app/(app)/jobs/create/_client.tsx index f2a7bf63..dcbb7acf 100644 --- a/app/(app)/jobs/create/_client.tsx +++ b/app/(app)/jobs/create/_client.tsx @@ -24,11 +24,15 @@ import { Strong, Text } from "@/components/ui-components/text"; import { Textarea } from "@/components/ui-components/textarea"; import { saveJobsInput, saveJobsSchema } from "@/schema/job"; import { FEATURE_FLAGS, isFlagEnabled } from "@/utils/flags"; +import { uploadFile } from "@/utils/s3helpers"; +import { getUploadUrl } from "@/app/actions/getUploadUrl"; import { zodResolver } from "@hookform/resolvers/zod"; import Image from "next/image"; import { notFound } from "next/navigation"; import React, { useRef, useState } from "react"; import { Controller, SubmitHandler, useForm } from "react-hook-form"; +import * as Sentry from "@sentry/nextjs"; +import { toast } from "sonner"; export default function Content() { const { @@ -54,8 +58,70 @@ export default function Content() { const flagEnabled = isFlagEnabled(FEATURE_FLAGS.JOBS); const fileInputRef = useRef(null); const [imgUrl, setImgUrl] = useState(null); + const [uploadStatus, setUploadStatus] = useState< + "idle" | "loading" | "success" | "error" + >("idle"); const onSubmit: SubmitHandler = (values) => { - console.log(values); + const formData = { + ...values, + companyLogo: imgUrl || undefined, + }; + console.log(formData); + }; + + const handleLogoUpload = async (e: React.ChangeEvent) => { + if (uploadStatus === "loading") { + return toast.info("Upload in progress, please wait..."); + } + + if (e.target.files && e.target.files.length > 0) { + setUploadStatus("loading"); + + const file = e.target.files[0]; + const { size, type } = file; + + if (size > 1048576) { + setUploadStatus("error"); + return toast.error("File size too big (max 1MB)."); + } + + try { + const res = await getUploadUrl({ + size, + type, + uploadType: "uploads", + }); + + const signedUrl = res?.data; + + if (!signedUrl) { + setUploadStatus("error"); + return toast.error( + "Something went wrong uploading the logo, please retry.", + ); + } + + const { fileLocation } = await uploadFile(signedUrl, file); + if (!fileLocation) { + setUploadStatus("error"); + return toast.error( + "Something went wrong uploading the logo, please retry.", + ); + } + + setUploadStatus("success"); + setImgUrl(fileLocation); + toast.success("Company logo uploaded successfully!"); + } catch (error) { + setUploadStatus("error"); + toast.error( + error instanceof Error + ? error.message + : "An error occurred while uploading the logo.", + ); + Sentry.captureException(error); + } + } }; if (!flagEnabled) { notFound(); @@ -89,15 +155,16 @@ export default function Content() { onClick={() => { fileInputRef.current?.click(); }} + disabled={uploadStatus === "loading"} > - Change Logo + {uploadStatus === "loading" ? "Uploading..." : "Change Logo"} {}} + onChange={handleLogoUpload} className="hidden" ref={fileInputRef} /> diff --git a/schema/job.ts b/schema/job.ts index 4167f7ba..90b5fa43 100644 --- a/schema/job.ts +++ b/schema/job.ts @@ -23,6 +23,7 @@ export const saveJobsSchema = z.object({ .url("Provide a valid url") .optional() .or(z.literal("")), + companyLogo: z.string().optional(), remote: z.boolean().optional().default(false), relocation: z.boolean().optional().default(false), visa_sponsorship: z.boolean().optional().default(false),