-
-
Notifications
You must be signed in to change notification settings - Fork 173
feat: add company logo upload functionality to job creation form #1283
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add company logo upload functionality to job creation form #1283
Conversation
- Implemented file upload for company logo with size validation (max 1MB). - Integrated S3 upload functionality and error handling with Sentry for logging. - Updated job schema to include optional companyLogo field. - Enhanced user feedback with toast notifications during upload process.
|
@Sahejmaharjan1 is attempting to deploy a commit to the Codú Team on Vercel. A member of the Team first needs to authorize it. |
WalkthroughIntroduces a client-side logo upload feature for job creation. Users can upload a company logo with size validation, obtain a signed upload URL, and submit the file. A new optional schema field accommodates the logo data in job submissions. Changes
Sequence DiagramsequenceDiagram
participant User
participant Client as Client Component
participant API as Backend API
participant Storage as File Storage
User->>Client: Select logo file
Client->>Client: Validate file size
alt File validation fails
Client->>User: Show error toast
else Validation succeeds
Client->>Client: Set uploadStatus to loading
Client->>API: Request signed upload URL
API-->>Client: Return signed URL
Client->>Storage: Upload file to signed URL
Storage-->>Client: Upload complete
Client->>Client: Store logo location<br/>Set uploadStatus to success
Client->>User: Show success toast
User->>Client: Submit job form
Client->>API: Submit with companyLogo URL
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes The changes involve new upload handling logic with validation and error states across multiple concerns (UI state management, API interaction, form integration), plus a straightforward schema extension. Moderate complexity due to multiple interacting pieces despite affecting only two files. Possibly related PRs
Suggested labels
Suggested reviewers
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
Hello @Sahejmaharjan1, thanks for opening your first Pull Request. The maintainers will review this Pull Request and provide feedback as soon as possible. Keep up the great work! |
NiallJoeMaher
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🌮
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 2
🧹 Nitpick comments (1)
app/(app)/jobs/create/_client.tsx (1)
77-86: Add client-side file type validation before upload.While the file input accepts only specific types (line 166), there's no validation in the handler before calling
getUploadUrl. The server will reject invalid types, but this wastes bandwidth and time. Consider validating the file type against the accepted formats before initiating the upload.Add type validation after line 81:
const file = e.target.files[0]; const { size, type } = file; + + const acceptedTypes = ["image/png", "image/gif", "image/jpeg", "image/jpg"]; + if (!acceptedTypes.includes(type)) { + setUploadStatus("error"); + return toast.error("Invalid file type. Please upload PNG, GIF, or JPEG."); + } if (size > 1048576) {
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
app/(app)/jobs/create/_client.tsx(3 hunks)schema/job.ts(1 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
app/(app)/jobs/create/_client.tsx (4)
schema/job.ts (1)
saveJobsInput(33-33)app/actions/getUploadUrl.ts (1)
getUploadUrl(15-44)utils/s3helpers.ts (1)
uploadFile(17-28)app/(app)/settings/_client.tsx (3)
onSuccess(139-150)signedUrl(107-123)onError(133-138)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: e2e
🔇 Additional comments (3)
app/(app)/jobs/create/_client.tsx (3)
64-70: Good integration of companyLogo into form submission.The form data correctly includes the uploaded logo URL using the imgUrl state, with proper handling of null values.
88-110: Solid upload flow with proper error handling.The upload logic correctly obtains a signed URL, uploads the file, and validates the response at each step with appropriate error messages.
158-161: Excellent UX improvements with upload state feedback.The button correctly disables during upload and shows dynamic text ("Uploading..." vs "Change Logo"), providing clear feedback to users.
| const handleLogoUpload = async (e: React.ChangeEvent<HTMLInputElement>) => { | ||
| 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); | ||
| } | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
File input should be reset after upload to allow re-uploading the same file.
After a successful (or failed) upload, the file input retains its value. If a user attempts to upload the same file again, the onChange event won't fire because the input value hasn't changed.
Add input reset after setting upload status:
try {
const res = await getUploadUrl({
size,
type,
uploadType: "uploads",
});
const signedUrl = res?.data;
if (!signedUrl) {
setUploadStatus("error");
+ if (fileInputRef.current) fileInputRef.current.value = "";
return toast.error(
"Something went wrong uploading the logo, please retry.",
);
}
const { fileLocation } = await uploadFile(signedUrl, file);
if (!fileLocation) {
setUploadStatus("error");
+ if (fileInputRef.current) fileInputRef.current.value = "";
return toast.error(
"Something went wrong uploading the logo, please retry.",
);
}
setUploadStatus("success");
setImgUrl(fileLocation);
+ if (fileInputRef.current) fileInputRef.current.value = "";
toast.success("Company logo uploaded successfully!");
} catch (error) {
setUploadStatus("error");
+ if (fileInputRef.current) fileInputRef.current.value = "";
toast.error(Committable suggestion skipped: line range outside the PR's diff.
| .url("Provide a valid url") | ||
| .optional() | ||
| .or(z.literal("")), | ||
| companyLogo: z.string().optional(), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion | 🟠 Major
Add URL validation to the companyLogo field.
The companyLogo field should validate URL format for consistency with applicationUrl (lines 21-25) and to ensure only valid URLs are stored.
Apply this diff:
- companyLogo: z.string().optional(),
+ companyLogo: z.string().url("Provide a valid logo URL").optional().or(z.literal("")),Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In schema/job.ts around line 26, the companyLogo field currently allows any
string; update its schema to validate URLs like applicationUrl does. Replace the
current definition with a string URL validator and keep it optional (e.g., use
the same z.string().url().optional() pattern as applicationUrl) so only valid
URL values are accepted and existing optional semantics are preserved.
✨ Codu Pull Request 💻
Fixes #1148
Pull Request details
Any Breaking changes
Associated Screenshots
[Optional] What gif best describes this PR or how it makes you feel