Skip to content
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

UI part done for posting jobs #1106

Merged
merged 13 commits into from
Oct 16, 2024
Merged

UI part done for posting jobs #1106

merged 13 commits into from
Oct 16, 2024

Conversation

Nil2000
Copy link
Contributor

@Nil2000 Nil2000 commented Oct 11, 2024

✨ Codu Pull Request 💻

Fixes #1082

Pull Request details

  • Only UI part done for Job posting page

Any Breaking changes

  • None

Associated Screenshots

image

@Nil2000 Nil2000 requested a review from a team as a code owner October 11, 2024 08:47
Copy link

vercel bot commented Oct 11, 2024

@Nil2000 is attempting to deploy a commit to the Codú Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Oct 11, 2024

Walkthrough

The changes introduce a job posting form within a new React component called Content, located in app/(app)/jobs/create/_client.tsx. This form allows users to input various job-related details, including company information and job specifications. Additionally, a new page component is created to render the Content component. The configuration file next.config.js is updated to allow image loading from a specific Figma domain and to handle SVG files as React components.

Changes

File Path Change Summary
app/(app)/jobs/create/_client.tsx Introduced Content component for job posting form with various input fields and layout.
app/(app)/jobs/create/page.tsx Added page component that renders the Content component and is exported as default.
app/(app)/jobs/page.tsx Created a simple page component displaying "Jobs first page" and exported as default.
utils/flags.ts Added new feature flags JOB_POST_CREATE and JOBS to FEATURE_FLAGS.
next.config.js Updated to include a new remote pattern for image loading from Figma and added SVG handling.

Assessment against linked issues

Objective Addressed Explanation
Create a responsive form for job postings (#1082)
Use specified components and maintain design (#1082)
Feature flag implementation for the page (#1082) Feature flagging was not implemented.

🐰 In a world of jobs, I hop with glee,
A form for posting, as easy as can be!
With fields and buttons, all in a row,
Let’s share our dreams, let the good times flow!
From logos to titles, we’ll fill them with care,
A job posting journey, let’s all prepare! 🐇✨


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🧹 Outside diff range and nitpick comments (6)
app/(app)/jobs/create/page.tsx (2)

3-5: Consider renaming the component to follow React naming conventions.

While the component functions correctly, it's generally recommended to use PascalCase for React component names. Consider renaming page to Page for better adherence to React conventions.

-function page() {
+function Page() {
   return <Content />;
 }

This change would improve code readability and consistency with standard React practices.


1-7: Consider implementing the required feature flag.

The PR objectives mention that the page should be feature flagged with "course-video". Currently, there's no implementation of this feature flag in the component. Consider wrapping the Content component with a feature flag check.

Here's a suggestion on how you might implement this:

import { useFeatureFlag } from '@/hooks/useFeatureFlag'; // Assume this hook exists
import Content from "./_client";

function Page() {
  const isCourseVideoEnabled = useFeatureFlag('course-video');

  if (!isCourseVideoEnabled) {
    return null; // or some fallback UI
  }

  return <Content />;
}

export default Page;

Would you like assistance in implementing the feature flag logic or creating a custom hook for feature flags if one doesn't exist?

app/(app)/jobs/page.tsx (1)

3-5: Capitalize the component name to follow React conventions

React components should use PascalCase naming convention. Consider renaming page to Page.

Apply this change:

-function page() {
+function Page() {
   return <div>Jobs first page</div>;
 }

Don't forget to update the export statement accordingly:

-export default page;
+export default Page;
next.config.js (1)

17-17: LGTM! Consider implementing additional security measures.

The addition of the Figma domain to the REMOTE_PATTERNS array is correct and aligns with the PR objectives. This change will allow the application to load images from Figma, which is likely necessary for the job posting form UI.

While this change is necessary, it's important to be aware of potential security implications when allowing external image sources. Consider implementing additional security measures such as:

  1. Content Security Policy (CSP) headers to restrict image sources.
  2. Implementing image proxying to sanitize and validate images before serving them to users.
  3. Regularly auditing and updating the list of allowed domains to ensure it remains current and necessary.

These measures can help mitigate potential risks associated with loading external content.

app/(app)/jobs/create/_client.tsx (2)

1-22: Consider using a more descriptive component name.

The current component name Content is quite generic. Consider renaming it to something more specific like JobPostingForm or CreateJobListing to better reflect its purpose and improve code readability.


1-227: Overall assessment: Good structure, needs state management and form handling.

The Content component provides a comprehensive job posting form with all the necessary fields. However, there are several areas for improvement:

  1. Implement form state management (e.g., using react-hook-form or Formik).
  2. Add form validation for all input fields.
  3. Handle file upload for the company logo, including client-side validation.
  4. Implement state management for checkboxes and radio buttons.
  5. Add a checkbox for terms and conditions acceptance.
  6. Implement form submission logic and integrate with a payment gateway for checkout.
  7. Consider breaking down the component into smaller, reusable components for better maintainability.
  8. Add error handling and user feedback mechanisms throughout the form.

Addressing these points will significantly improve the functionality, user experience, and code quality of the job posting form.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 9f567e9 and fbb7bb7.

📒 Files selected for processing (4)
  • app/(app)/jobs/create/_client.tsx (1 hunks)
  • app/(app)/jobs/create/page.tsx (1 hunks)
  • app/(app)/jobs/page.tsx (1 hunks)
  • next.config.js (1 hunks)
🧰 Additional context used
🔇 Additional comments (2)
app/(app)/jobs/create/page.tsx (2)

1-1: LGTM: Import statement is correct and follows conventions.

The import statement correctly imports the Content component from the sibling file _client. This aligns with the PR objectives of creating a job posting form.


7-7: LGTM: Default export is correct.

The default export of the page component is appropriate for a Next.js page file. If you decide to rename the component to Page as suggested earlier, remember to update this line accordingly:

-export default page;
+export default Page;

This ensures consistency with the component name change.

import React from "react";

function page() {
return <div>Jobs first page</div>;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Component content doesn't match PR objectives

The current implementation seems to be a placeholder and doesn't align with the PR objectives of creating a job posting form. According to the PR description, this component should render a form for entering job postings.

Consider implementing the job posting form as described in the linked issue #1082. The form should include fields for company information and job specifications, and should be responsive. Utilize existing components from the codebase where possible.

Would you like assistance in scaffolding the basic structure for the job posting form?

Comment on lines 1 to 7
import React from "react";

function page() {
return <div>Jobs first page</div>;
}

export default page;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding TypeScript type annotations

To improve code maintainability and catch potential errors early, consider adding TypeScript type annotations to this component.

Here's an example of how you could add types:

import React from "react";

interface JobPostingFormProps {
  // Add any props here if needed
}

function Page({}: JobPostingFormProps): React.ReactElement {
  return <div>Jobs first page</div>;
}

export default Page;

This change introduces a JobPostingFormProps interface for any future props and explicitly types the return value of the component.

Comment on lines 25 to 62
<div className="flex justify-center">
<div className="flex flex-col gap-y-10 pt-6 md:w-[1000px]">
<Heading level={1}>Post a job</Heading>
<div className="flex flex-row justify-between">
<div className="flex flex-1 flex-col gap-1 text-sm">
<Text>
<Strong>Company Logo</Strong>
</Text>
<Text>Square format is best</Text>
</div>
<div className="flex-1">
<div className="flex flex-row gap-3">
<Image
src="https://s3-alpha-sig.figma.com/img/8a23/cb6a/8d462a922529d9ae7a44772fb9f64b61?Expires=1729468800&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=XnJwSdtoT2ZlVsJvhBPWF3AAxyOJ5qVcunVwoDqMLar78R6wuZUJvkxS3VqeOFued9~gWF8biRwIdWhSA4NHF9Fiw5P5S-KFzs68QXDGLYVL7AhoEA2u-GYaEYep52BRmsQWceF8Bd9IDPYceJkF7MyIQCIkJFkuZ6FvXcHa319yBMk0xS4qGux2sNiBqxXOjA9gcraKuBh~mj3bEQ9l4GrqzBeHRt1s6OaBqbIJUSic984Wfizmfyjcu7NDLxJaTVsYVhe8d5p2Sv8QVCrvc0UspSGLYpsmJjybLF41zoEEkIxSb~iX905tpAo2q757TKSTDOinzLdCcUwCJ-VZ8Q__"
width={80}
height={80}
alt="Company Logo"
className="rounded-[10px]"
/>
<div>
{/* Need to do the file selection logic using ref as well */}
<Button
color="dark/white"
className="mt-3 rounded-md"
onClick={() => {
fileInputRef.current?.click();
}}
>
<input type="file" hidden ref={fileInputRef} />
Change Logo
</Button>
<p className="mt-1 text-xs text-[#888888]">
JPG, GIF or PNG. 1MB max.
</p>
</div>
</div>
</div>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement file selection logic and add validation.

The logo upload functionality is incomplete. Consider the following improvements:

  1. Implement the file selection logic using the ref.
  2. Replace the hardcoded Figma image URL with a local placeholder or a more permanent solution.
  3. Add client-side validation for file type (JPG, GIF, PNG) and size (1MB max) as mentioned in the UI.

Here's a basic implementation to get you started:

const [logoUrl, setLogoUrl] = useState<string | null>(null);

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  const file = event.target.files?.[0];
  if (file) {
    if (file.size > 1024 * 1024) {
      alert('File size should not exceed 1MB');
      return;
    }
    if (!['image/jpeg', 'image/gif', 'image/png'].includes(file.type)) {
      alert('Only JPG, GIF, or PNG files are allowed');
      return;
    }
    const reader = new FileReader();
    reader.onload = (e) => setLogoUrl(e.target?.result as string);
    reader.readAsDataURL(file);
  }
};

// In the JSX:
<input type="file" hidden ref={fileInputRef} onChange={handleFileChange} accept="image/jpeg,image/gif,image/png" />
{logoUrl ? (
  <Image src={logoUrl} width={80} height={80} alt="Company Logo" className="rounded-[10px]" />
) : (
  <div className="w-20 h-20 bg-gray-200 rounded-[10px] flex items-center justify-center">
    <span className="text-gray-500">No logo</span>
  </div>
)}

Comment on lines 63 to 99
<div className="flex flex-row justify-between">
<div className="flex flex-1 flex-col gap-1 text-sm">
<Text>
<Strong>Company Name</Strong>
</Text>
<Text>This will be shown in the format you type it</Text>
</div>
<div className="flex-1">
<Input placeholder="Pixel Pulse Studios" />
</div>
</div>
<div className="flex flex-row justify-between">
<div className="flex flex-1 flex-col gap-1 text-sm">
<Text>
<Strong>Job Title</Strong>
</Text>
<Text>The job title for the position that you are opening</Text>
</div>
<div className="flex-1">
<Input placeholder="Reality Architect" />
</div>
</div>
<div className="flex flex-row justify-between">
<div className="flex flex-1 flex-col gap-1 text-sm">
<Text>
<Strong>Job Description</Strong>
</Text>
<Text>In markdown format</Text>
</div>
<div className="flex-1">
<Textarea
placeholder="As a Reality Architect, you'll be at the forefront of creating immersive mixed reality experiences that blur the line between the digital and physical..."
resizable={false}
rows={3}
/>
</div>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement form state management and validation.

The form fields for company name, job title, and job description are implemented correctly. However, there's no form state management or validation in place. Consider using a form library like Formik or react-hook-form for managing form state and validation.

Here's a basic example using react-hook-form:

import { useForm } from 'react-hook-form';

// Inside the component:
const { register, handleSubmit, formState: { errors } } = useForm();

const onSubmit = (data) => console.log(data);

// In the JSX:
<form onSubmit={handleSubmit(onSubmit)}>
  <Input 
    {...register("companyName", { required: "Company name is required" })} 
    placeholder="Pixel Pulse Studios" 
  />
  {errors.companyName && <span>{errors.companyName.message}</span>}

  <Input 
    {...register("jobTitle", { required: "Job title is required" })} 
    placeholder="Reality Architect" 
  />
  {errors.jobTitle && <span>{errors.jobTitle.message}</span>}

  <Textarea 
    {...register("jobDescription", { required: "Job description is required" })} 
    placeholder="As a Reality Architect, you'll be at the forefront of creating immersive mixed reality experiences that blur the line between the digital and physical..." 
    resizable={false} 
    rows={3} 
  />
  {errors.jobDescription && <span>{errors.jobDescription.message}</span>}
</form>

This will provide basic form state management and required field validation.

Comment on lines 100 to 128
<div className="flex flex-row justify-between">
<div className="flex flex-1 flex-col gap-1 text-sm">
<Text>
<Strong>Locations</Strong>
</Text>
<Text>
Where is the job location? (“Dublin”, “Remote USA”, “Anywhere”).
</Text>
</div>
<div className="flex-1">
<div className="flex flex-col gap-4">
<Input placeholder="Dublin (2 days in the office per week)" />
<CheckboxGroup>
<CheckboxField>
<Checkbox name="remote" value="is_remote" />
<Label>Work is remote</Label>
</CheckboxField>
<CheckboxField>
<Checkbox name="relocation" value="is_relocation_package" />
<Label>Relocation package given</Label>
</CheckboxField>
<CheckboxField>
<Checkbox name="visa" value="is_visa_sponsored" />
<Label>Visa sponsorship provided</Label>
</CheckboxField>
</CheckboxGroup>
</div>
</div>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance location handling and implement checkbox state management.

The locations section is implemented correctly, but it could benefit from some improvements:

  1. Implement state management for the checkboxes.
  2. Consider allowing multiple location inputs or using a more structured approach for location data.
  3. Add validation for the location input.

Here's an example of how you could improve this section:

import { useState } from 'react';

// Inside the component:
const [locations, setLocations] = useState(['']);
const [isRemote, setIsRemote] = useState(false);
const [hasRelocation, setHasRelocation] = useState(false);
const [hasVisa, setHasVisa] = useState(false);

const addLocation = () => setLocations([...locations, '']);
const updateLocation = (index, value) => {
  const newLocations = [...locations];
  newLocations[index] = value;
  setLocations(newLocations);
};

// In the JSX:
<div className="flex-1">
  <div className="flex flex-col gap-4">
    {locations.map((location, index) => (
      <Input 
        key={index}
        value={location}
        onChange={(e) => updateLocation(index, e.target.value)}
        placeholder="Dublin (2 days in the office per week)"
      />
    ))}
    <Button onClick={addLocation}>Add Another Location</Button>
    <CheckboxGroup>
      <CheckboxField>
        <Checkbox 
          name="remote" 
          value="is_remote" 
          checked={isRemote}
          onChange={(e) => setIsRemote(e.target.checked)}
        />
        <Label>Work is remote</Label>
      </CheckboxField>
      <CheckboxField>
        <Checkbox 
          name="relocation" 
          value="is_relocation_package"
          checked={hasRelocation}
          onChange={(e) => setHasRelocation(e.target.checked)}
        />
        <Label>Relocation package given</Label>
      </CheckboxField>
      <CheckboxField>
        <Checkbox 
          name="visa" 
          value="is_visa_sponsored"
          checked={hasVisa}
          onChange={(e) => setHasVisa(e.target.checked)}
        />
        <Label>Visa sponsorship provided</Label>
      </CheckboxField>
    </CheckboxGroup>
  </div>
</div>

This implementation allows for multiple locations and manages the state of the checkboxes.

Comment on lines 129 to 177
<div className="flex flex-row justify-between">
<div className="flex flex-1 flex-col gap-1 text-sm">
<Text>
<Strong>Application form URL</Strong>
</Text>
<Text>A link to your website (optional)</Text>
</div>
<div className="flex-1">
<Input />
</div>
</div>
<div className="flex flex-row justify-between">
<div className="flex flex-1 flex-col gap-1 text-sm">
<Text>
<Strong>Job Type</Strong>
</Text>
<Text>Full-time, part-time or freelancer</Text>
</div>
<div className="flex-1">
<RadioGroup defaultValue="full_time">
<RadioField>
<Radio value="full_time" />
<Label>Full-time (€150)</Label>
<Description>Salaried Position</Description>
</RadioField>
<RadioField>
<Radio value="part_time" />
<Label>Part-time (€100)</Label>
<Description>
Salaried position but less than 4 days per week
</Description>
</RadioField>
<RadioField>
<Radio value="freelancer" />
<Label>Freelancer (€100)</Label>
<Description>
Shorter-term usually or fixed term/job
</Description>
</RadioField>
<RadioField>
<Radio value="other_role_type" />
<Label>Other (€100)</Label>
<Description>
Looking for a co-founder or something else we haven’t thought
of
</Description>
</RadioField>
</RadioGroup>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement URL validation and radio button state management.

The application form URL and job type sections are implemented correctly, but could be improved:

  1. Add URL validation for the application form URL input.
  2. Implement state management for the job type radio buttons.

Here's an example of how you could improve these sections:

import { useState } from 'react';

// Inside the component:
const [applicationUrl, setApplicationUrl] = useState('');
const [jobType, setJobType] = useState('full_time');

const validateUrl = (url) => {
  const pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
    '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
    '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
    '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
    '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
    '(\\#[-a-z\\d_]*)?$','i'); // fragment locator
  return !!pattern.test(url);
};

// In the JSX:
<div className="flex-1">
  <Input 
    value={applicationUrl}
    onChange={(e) => setApplicationUrl(e.target.value)}
    onBlur={() => {
      if (applicationUrl && !validateUrl(applicationUrl)) {
        alert('Please enter a valid URL');
      }
    }}
  />
</div>

<div className="flex-1">
  <RadioGroup 
    value={jobType} 
    onChange={(value) => setJobType(value)}
  >
    <RadioField>
      <Radio value="full_time" />
      <Label>Full-time (€150)</Label>
      <Description>Salaried Position</Description>
    </RadioField>
    <RadioField>
      <Radio value="part_time" />
      <Label>Part-time (€100)</Label>
      <Description>Salaried position but less than 4 days per week</Description>
    </RadioField>
    <RadioField>
      <Radio value="freelancer" />
      <Label>Freelancer (€100)</Label>
      <Description>Shorter-term usually or fixed term/job</Description>
    </RadioField>
    <RadioField>
      <Radio value="other_role_type" />
      <Label>Other (€100)</Label>
      <Description>Looking for a co-founder or something else we haven't thought of</Description>
    </RadioField>
  </RadioGroup>
</div>

This implementation includes URL validation and manages the state of the job type selection.

Comment on lines 179 to 217
<div className="flex flex-row justify-between">
<div className="flex flex-1 flex-col gap-1 text-sm">
<Text>
<Strong>Terms & Conditions</Strong>
</Text>
<Text>Ah yes, the fine print.</Text>
</div>
<div className="flex flex-1 flex-col gap-1">
<Text>
By submitting this job listing, I acknowledge and agree to the
following terms:
</Text>
<Text>
<Strong>Content Restrictions:</Strong> My listing must not
contain: <br />- Adult or explicit content <br />- Fraudulent or
illegitimate work opportunities <br />- Inappropriate or offensive
language
</Text>
<Text>
<Strong>Accurate Classification: </Strong>I confirm that the job
type (e.g., full-time, part-time, freelance) is correctly
categorized.
</Text>
<Text>
<Strong>Removal Policy:</Strong> I understand that my listing may
be removed without notice if it violates any of the above
conditions.
</Text>
<Text>
<Strong>Refund Policy:</Strong> If my listing is removed due to a
violation within 7 days of posting, I may be eligible for a
refund, subject to review.
</Text>
<Text>
<Strong>Compliance:</Strong> I agree to comply with all applicable
laws and regulations regarding job postings and employment
practices.
</Text>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add a checkbox for terms and conditions acceptance.

The terms and conditions are clearly presented, but there's no mechanism for the user to explicitly accept them. Consider adding a checkbox that the user must check to indicate they've read and agreed to the terms.

Here's an example of how you could implement this:

import { useState } from 'react';

// Inside the component:
const [termsAccepted, setTermsAccepted] = useState(false);

// After the terms and conditions text, add:
<CheckboxField>
  <Checkbox 
    name="terms" 
    checked={termsAccepted}
    onChange={(e) => setTermsAccepted(e.target.checked)}
  />
  <Label>I have read and agree to the terms and conditions</Label>
</CheckboxField>

Then, in your form submission logic, you can check if termsAccepted is true before allowing the form to be submitted.

Comment on lines 219 to 223
<div className="flex justify-end">
<Button className="rounded-md" color="pink">
Submit and checkout
</Button>
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement form submission and checkout logic.

The submit button is correctly implemented, but there's no form submission or checkout logic. Consider the following improvements:

  1. Implement form validation before submission.
  2. Add error handling for the submission process.
  3. Integrate with a payment gateway for the checkout process.

Here's a basic example of how you could start implementing this:

import { useState } from 'react';

// Inside the component:
const [isSubmitting, setIsSubmitting] = useState(false);

const handleSubmit = async (event) => {
  event.preventDefault();
  setIsSubmitting(true);

  // Perform form validation here

  try {
    // Submit form data to your API
    const response = await fetch('/api/job-postings', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(formData),
    });

    if (!response.ok) throw new Error('Submission failed');

    // If submission is successful, proceed to checkout
    // Integrate with your payment gateway here
    // For example, redirect to a Stripe checkout page
    window.location.href = '/checkout';
  } catch (error) {
    console.error('Error:', error);
    alert('An error occurred. Please try again.');
  } finally {
    setIsSubmitting(false);
  }
};

// In the JSX:
<Button 
  className="rounded-md" 
  color="pink" 
  onClick={handleSubmit}
  disabled={isSubmitting}
>
  {isSubmitting ? 'Submitting...' : 'Submit and checkout'}
</Button>

This implementation includes basic form submission logic and error handling. You'll need to replace the placeholder API call and checkout process with your actual implementation.

@NiallJoeMaher
Copy link
Contributor

Just for uniformity and it being hard to explain, I added a PR with updates of the layout of how it all hangs together responsively (so hopefully the two screens look uniformed): #1109

Copy link
Contributor

@NiallJoeMaher NiallJoeMaher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would you feature flag this with a flag named jobs. Here is an example of how it works:
https://github.com/codu-code/codu/tree/develop/app/(app)/feature-flag-example

next.config.js Outdated
@@ -14,6 +14,7 @@ const REMOTE_PATTERNS = [
// Temporary wildcard
"*.s3.eu-west-1.amazonaws.com",
"s3.eu-west-1.amazonaws.com",
"s3-alpha-sig.figma.com", //Added for Figma
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No need for a placeholder image so we can remove this. A person should be able to see an empty placeholder and then a preview when they upload.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok will remove that as well

@Nil2000
Copy link
Contributor Author

Nil2000 commented Oct 12, 2024

Would you feature flag this with a flag named jobs. Here is an example of how it works: https://github.com/codu-code/codu/tree/develop/app/(app)/feature-flag-example

Yeah sure will do afterwards updating the UI according to settings page

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between fbb7bb7 and 5897e87.

📒 Files selected for processing (1)
  • app/(app)/jobs/create/_client.tsx (1 hunks)
🧰 Additional context used
🔇 Additional comments (8)
app/(app)/jobs/create/_client.tsx (8)

1-25: LGTM: Imports and component declaration look good.

The imports and component declaration are well-structured and appropriate for the functionality of the job posting form. The use of a custom UI library suggests consistency with the rest of the application.


69-122: LGTM: Company name, job title, and job description sections are well-structured.

The sections for company name, job title, and job description are implemented correctly using appropriate UI components. The placeholder text provides good guidance for users.


124-151: LGTM: Locations section is well-structured but could be enhanced.

The locations section is implemented correctly, providing options for specifying the job location and additional details like remote work, relocation package, and visa sponsorship.


153-169: LGTM: Application form URL section is well-structured.

The application form URL section is implemented correctly, providing an input for the optional website link.


171-207: LGTM: Job type section is well-structured and informative.

The job type section is implemented correctly using a radio group, providing clear options with descriptions and prices for each job type.


209-247: LGTM: Terms and conditions section is comprehensive.

The terms and conditions section is well-structured and provides comprehensive information about content restrictions, classification, removal policy, refund policy, and compliance requirements.


249-257: LGTM: Submit button is well-placed and styled.

The submit button is correctly placed at the end of the form and styled appropriately.


26-257: LGTM: Overall structure and layout are well-organized and responsive.

The form uses a consistent grid layout for each section, with dividers separating the sections. The responsive design (switching between one and two columns based on screen size) enhances usability across different devices.

Comment on lines 26 to 68
<form className="mx-auto max-w-4xl p-3 pt-8 sm:px-4">
<Heading level={1}>Post a job</Heading>
<Divider className="my-10 mt-6" />
<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Company Logo</Subheading>
<Text>Square format is best</Text>
</div>
<Field>
<div className="flex items-center space-x-4">
<Image
src="https://s3-alpha-sig.figma.com/img/8a23/cb6a/8d462a922529d9ae7a44772fb9f64b61?Expires=1729468800&Key-Pair-Id=APKAQ4GOSFWCVNEHN3O4&Signature=XnJwSdtoT2ZlVsJvhBPWF3AAxyOJ5qVcunVwoDqMLar78R6wuZUJvkxS3VqeOFued9~gWF8biRwIdWhSA4NHF9Fiw5P5S-KFzs68QXDGLYVL7AhoEA2u-GYaEYep52BRmsQWceF8Bd9IDPYceJkF7MyIQCIkJFkuZ6FvXcHa319yBMk0xS4qGux2sNiBqxXOjA9gcraKuBh~mj3bEQ9l4GrqzBeHRt1s6OaBqbIJUSic984Wfizmfyjcu7NDLxJaTVsYVhe8d5p2Sv8QVCrvc0UspSGLYpsmJjybLF41zoEEkIxSb~iX905tpAo2q757TKSTDOinzLdCcUwCJ-VZ8Q__"
width={80}
height={80}
alt="Company Logo"
className="rounded-[10px]"
/>
<div>
<Button
color="dark/white"
className="mt-3 rounded-md"
onClick={() => {
fileInputRef.current?.click();
}}
>
Change Logo
</Button>
<Input
type="file"
id="file-input"
name="company-logo"
accept="image/png, image/gif, image/jpeg"
onChange={() => {}}
className="hidden"
ref={fileInputRef}
/>
<Text className="mt-1 text-xs text-gray-500">
JPG, GIF or PNG. 1MB max.
</Text>
</div>
</div>
</Field>
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement file selection logic and replace hardcoded image URL.

The form structure looks good, but there are two issues to address:

  1. The logo upload functionality is incomplete. Implement the file selection logic using the fileInputRef.
  2. Replace the hardcoded Figma image URL with a local placeholder or a more permanent solution.

Here's a basic implementation to get you started:

const [logoUrl, setLogoUrl] = useState<string>("/path/to/placeholder-image.png");

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  const file = event.target.files?.[0];
  if (file) {
    if (file.size > 1024 * 1024) {
      alert('File size should not exceed 1MB');
      return;
    }
    if (!['image/jpeg', 'image/gif', 'image/png'].includes(file.type)) {
      alert('Only JPG, GIF, or PNG files are allowed');
      return;
    }
    const reader = new FileReader();
    reader.onload = (e) => setLogoUrl(e.target?.result as string);
    reader.readAsDataURL(file);
  }
};

// Update the Image component:
<Image
  src={logoUrl}
  width={80}
  height={80}
  alt="Company Logo"
  className="rounded-[10px]"
/>

// Update the Input component:
<Input
  type="file"
  id="file-input"
  name="company-logo"
  accept="image/png, image/gif, image/jpeg"
  onChange={handleFileChange}
  className="hidden"
  ref={fileInputRef}
/>

Comment on lines 26 to 257
<Text>A link to your website (optional)</Text>
</div>
<Field>
<Input
id="app-url"
type="text"
autoComplete="url"
placeholder="https://example.com"
/>
</Field>
{/* Add error part after validation here */}
</section>

<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Job Type</Subheading>
<Text>Full-time, part-time or freelancer</Text>
</div>
<Field>
<RadioGroup defaultValue="full_time">
<RadioField>
<Radio value="full_time" />
<Label>Full-time (€150)</Label>
<Description>Salaried Position</Description>
</RadioField>
<RadioField>
<Radio value="part_time" />
<Label>Part-time (€100)</Label>
<Description>
Salaried position but less than 4 days per week
</Description>
</RadioField>
<RadioField>
<Radio value="freelancer" />
<Label>Freelancer (€100)</Label>
<Description>Shorter-term usually or fixed term/job</Description>
</RadioField>
<RadioField>
<Radio value="other_role_type" />
<Label>Other (€100)</Label>
<Description>
Looking for a co-founder or something else we haven’t thought of
</Description>
</RadioField>
</RadioGroup>
</Field>
{/* Add error part after validation here */}
</section>

<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Terms & Conditions</Subheading>
<Text>Ah yes, the fine print.</Text>
</div>
<div className="space-y-2">
<Text>
By submitting this job listing, I acknowledge and agree to the
following terms:
</Text>
<Text>
<Strong>Content Restrictions:</Strong> My listing must not contain:{" "}
<br />- Adult or explicit content <br />- Fraudulent or illegitimate
work opportunities <br />- Inappropriate or offensive language
</Text>
<Text>
<Strong>Accurate Classification: </Strong>I confirm that the job
type (e.g., full-time, part-time, freelance) is correctly
categorized.
</Text>
<Text>
<Strong>Removal Policy:</Strong> I understand that my listing may be
removed without notice if it violates any of the above conditions.
</Text>
<Text>
<Strong>Refund Policy:</Strong> If my listing is removed due to a
violation within 7 days of posting, I may be eligible for a refund,
subject to review.
</Text>
<Text>
<Strong>Compliance:</Strong> I agree to comply with all applicable
laws and regulations regarding job postings and employment
practices.
</Text>
</div>
{/* Add error part after validation here */}
</section>

<Divider className="my-10" soft />

<div className="flex justify-end">
<Button className="rounded-md" color="pink">
Submit and checkout
</Button>
</div>
</form>
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement form state management and validation.

While the form structure is well-implemented, it currently lacks global state management and field validation. This could lead to submission of incomplete or invalid data. Consider using a form library like react-hook-form to manage form state and implement validation.

Here's a basic example of how you could implement this using react-hook-form:

import { useForm } from 'react-hook-form';

export default function Content() {
  const { register, handleSubmit, formState: { errors } } = useForm();

  const onSubmit = (data) => {
    console.log(data);
    // Implement your submission logic here
  };

  return (
    <form onSubmit={handleSubmit(onSubmit)} className="mx-auto max-w-4xl p-3 pt-8 sm:px-4">
      {/* ... other form elements ... */}
      
      <Input
        {...register("companyName", { required: "Company name is required" })}
        placeholder="Pixel Pulse Studios"
      />
      {errors.companyName && <span>{errors.companyName.message}</span>}

      <Input
        {...register("jobTitle", { required: "Job title is required" })}
        placeholder="Reality Architect"
      />
      {errors.jobTitle && <span>{errors.jobTitle.message}</span>}

      <Textarea
        {...register("jobDescription", { required: "Job description is required" })}
        placeholder="As a Reality Architect, you'll be at the forefront of creating immersive mixed reality experiences that blur the line between the digital and physical..."
        resizable={false}
        rows={3}
      />
      {errors.jobDescription && <span>{errors.jobDescription.message}</span>}

      {/* ... other form elements ... */}

      <Button type="submit" className="rounded-md" color="pink">
        Submit and checkout
      </Button>
    </form>
  );
}

This implementation provides basic form state management and required field validation. You can extend this to include more complex validation rules as needed.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🧹 Outside diff range and nitpick comments (2)
app/(app)/jobs/create/_client.tsx (2)

25-32: Good use of feature flag, but unused state detected.

The feature flag check is well-implemented for controlled rollout. However, the imgUrl state is declared but never used in the component. Consider removing it if it's not needed, or implement its usage if it's intended for future use.

- const [imgUrl, setImgUrl] = useState<string | null>(null);

1-269: Overall assessment: Well-structured form with areas for improvement

The job posting form is well-structured and covers all necessary fields. However, there are several areas that need attention:

  1. Implement proper logo upload functionality with validation.
  2. Add form state management and validation for all fields.
  3. Enhance the locations section to allow multiple entries.
  4. Implement URL validation for the application form URL.
  5. Add a checkbox for explicit acceptance of terms and conditions.
  6. Implement form submission and checkout logic.

Addressing these points will significantly improve the functionality and user experience of the form. Consider using a form library like react-hook-form for easier state management and validation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 5897e87 and 6465e95.

📒 Files selected for processing (2)
  • app/(app)/jobs/create/_client.tsx (1 hunks)
  • utils/flags.ts (1 hunks)
🧰 Additional context used
🔇 Additional comments (2)
utils/flags.ts (1)

5-5: New feature flag added correctly.

The new feature flag JOB_POST_CREATE has been added to the FEATURE_FLAGS object, which aligns with the PR objectives. This flag will allow for controlled rollout of the job posting feature.

A few points to consider:

  1. The flag name uses underscores, which is consistent with the existing FEATURE_FLAG_TEST. However, you might want to consider using kebab-case (e.g., job-post-create) for consistency with the feature-flag-test value.
  2. Ensure that this flag is properly implemented in the relevant components, particularly in the new job posting form.

Let's verify the usage of this new feature flag:

✅ Verification successful

Feature flag JOB_POST_CREATE correctly implemented and utilized.

The JOB_POST_CREATE flag has been properly added to the FEATURE_FLAGS object and is consistently used through this constant in the relevant components. No instances of direct string usage ('job-post-create') were found, ensuring maintainability and adherence to best practices.

  • Verified usage in:
    • utils/flags.ts
    • app/(app)/jobs/create/_client.tsx
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for the usage of the new JOB_POST_CREATE feature flag

# Test: Search for JOB_POST_CREATE usage
echo "Searching for JOB_POST_CREATE usage:"
rg --type typescript --type tsx "JOB_POST_CREATE"

# Test: Search for isFlagEnabled function calls
echo "Searching for isFlagEnabled function calls:"
rg --type typescript --type tsx "isFlagEnabled\\s*\\(\\s*['\"]job-post-create['\"]\\s*\\)"

Length of output: 391


Script:

#!/bin/bash
# Description: Check for the usage of the new JOB_POST_CREATE feature flag with corrected file types

# Test: Search for JOB_POST_CREATE usage
echo "Searching for JOB_POST_CREATE usage:"
rg --type ts --type tsx "JOB_POST_CREATE"

# Test: Search for isFlagEnabled function calls
echo "Searching for isFlagEnabled function calls:"
rg --type ts --type tsx "isFlagEnabled\s*\(\s*['\"]job-post-create['\"]\s*\)"

Length of output: 361


Script:

#!/bin/bash
# Description: Check for the usage of the new JOB_POST_CREATE feature flag with corrected file types

# Test: Search for JOB_POST_CREATE usage
echo "Searching for JOB_POST_CREATE usage:"
rg "JOB_POST_CREATE" -g "*.ts" -g "*.tsx"

# Test: Search for isFlagEnabled function calls
echo "Searching for isFlagEnabled function calls:"
rg "isFlagEnabled\s*\(\s*['\"]job-post-create['\"]\s*\)" -g "*.ts" -g "*.tsx"

Length of output: 460

app/(app)/jobs/create/_client.tsx (1)

1-23: Imports and initial setup look good.

The necessary UI components, utilities, and React hooks are properly imported. The organization is clean and follows best practices.

Comment on lines 34 to 79
return (
<form className="mx-auto max-w-4xl p-3 pt-8 sm:px-4">
<Heading level={1}>Post a job</Heading>
<Divider className="my-10 mt-6" />
<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Company Logo</Subheading>
<Text>Square format is best</Text>
</div>
<Field>
<div className="flex items-center space-x-4">
{imgUrl && (
<Image
src={imgUrl}
width={80}
height={80}
alt="Company Logo"
className="rounded-[10px]"
/>
)}
<div>
<Button
color="dark/white"
className="mt-3 rounded-md"
onClick={() => {
fileInputRef.current?.click();
}}
>
Change Logo
</Button>
<Input
type="file"
id="file-input"
name="company-logo"
accept="image/png, image/gif, image/jpeg"
onChange={() => {}}
className="hidden"
ref={fileInputRef}
/>
<Text className="mt-1 text-xs text-gray-500">
JPG, GIF or PNG. 1MB max.
</Text>
</div>
</div>
</Field>
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement proper logo upload functionality.

The form structure is well-organized, but the logo upload functionality needs improvement:

  1. The onChange handler for the file input is empty. Implement logic to handle file selection.
  2. Replace the hardcoded Figma URL with the uploaded image URL.
  3. Add validation for file type and size as mentioned in the UI text.

Here's a suggested implementation:

const [logoUrl, setLogoUrl] = useState<string | null>(null);

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  const file = event.target.files?.[0];
  if (file) {
    if (file.size > 1024 * 1024) {
      alert('File size should not exceed 1MB');
      return;
    }
    if (!['image/jpeg', 'image/gif', 'image/png'].includes(file.type)) {
      alert('Only JPG, GIF, or PNG files are allowed');
      return;
    }
    const reader = new FileReader();
    reader.onload = (e) => setLogoUrl(e.target?.result as string);
    reader.readAsDataURL(file);
  }
};

// Update the Image component:
{logoUrl && (
  <Image
    src={logoUrl}
    width={80}
    height={80}
    alt="Company Logo"
    className="rounded-[10px]"
  />
)}

// Update the Input component:
<Input
  type="file"
  id="file-input"
  name="company-logo"
  accept="image/png, image/gif, image/jpeg"
  onChange={handleFileChange}
  className="hidden"
  ref={fileInputRef}
/>

Comment on lines +81 to +115
<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Company Name</Subheading>
<Text>This will be shown in the format you type it</Text>
</div>
<Field>
<Input
id="company-name"
type="text"
placeholder="Pixel Pulse Studios"
autoComplete="given-company-name"
/>
</Field>
{/* Add error part after validation here */}
</section>

<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Job Title</Subheading>
<Text>The job title for the position that you are opening</Text>
</div>
<Field>
<Input
id="job-title"
type="text"
placeholder="Reality Architect"
autoComplete="given-job-title"
/>
</Field>
{/* Add error part after validation here */}
</section>
Copy link
Contributor

@coderabbitai coderabbitai bot Oct 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement form state management and validation.

The structure for company name and job title sections is consistent and well-organized. However, consider implementing form state management and validation for a more robust user experience. You could use a library like react-hook-form or implement a custom solution.

Here's a basic example using react-hook-form:

import { useForm } from 'react-hook-form';

// Inside the component:
const { register, handleSubmit, formState: { errors } } = useForm();

// In the JSX:
<Input
  {...register("companyName", { required: "Company name is required" })}
  placeholder="Pixel Pulse Studios"
/>
{errors.companyName && <span>{errors.companyName.message}</span>}

<Input
  {...register("jobTitle", { required: "Job title is required" })}
  placeholder="Reality Architect"
/>
{errors.jobTitle && <span>{errors.jobTitle.message}</span>}

This will provide basic form state management and required field validation. You can extend this to include more complex validation rules as needed.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Nil2000 I just noticed that we have no validation on the inputs. Would you add this to the list or would you rather if we merged this and you can do it as a next PR so it doesn't get stale.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@NiallJoeMaher Considering Hacktoberfest It would be good for me to have a next PR and you can directly assign that to me 😅

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would also love to progress with the other follow up issues mentioned in this issue

Comment on lines +117 to +162
<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Job Description</Subheading>
<Text>In markdown format</Text>
</div>
<Field>
<Textarea
id="job-description"
placeholder="As a Reality Architect, you'll be at the forefront of creating immersive mixed reality experiences that blur the line between the digital and physical..."
resizable={false}
rows={3}
/>
</Field>
{/* Add error part after validation here */}
</section>

<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Locations</Subheading>
<Text>
Where is the job location? (“Dublin”, “Remote USA”, “Anywhere”).
</Text>
</div>
<Field>
<Input placeholder="Dublin (2 days in the office per week)" />
<CheckboxGroup className="mt-3">
<CheckboxField>
<Checkbox name="remote" value="is_remote" />
<Label>Work is remote</Label>
</CheckboxField>
<CheckboxField>
<Checkbox name="relocation" value="is_relocation_package" />
<Label>Relocation package given</Label>
</CheckboxField>
<CheckboxField>
<Checkbox name="visa" value="is_visa_sponsored" />
<Label>Visa sponsorship provided</Label>
</CheckboxField>
</CheckboxGroup>
</Field>
{/* Add error part after validation here */}
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance location handling and implement checkbox state management.

The job description section looks good, but the locations section could be improved:

  1. Implement state management for the checkboxes.
  2. Consider allowing multiple location inputs or using a more structured approach for location data.
  3. Add validation for the location input.

Here's an example of how you could improve this section:

import { useState } from 'react';

// Inside the component:
const [locations, setLocations] = useState(['']);
const [isRemote, setIsRemote] = useState(false);
const [hasRelocation, setHasRelocation] = useState(false);
const [hasVisa, setHasVisa] = useState(false);

const addLocation = () => setLocations([...locations, '']);
const updateLocation = (index, value) => {
  const newLocations = [...locations];
  newLocations[index] = value;
  setLocations(newLocations);
};

// In the JSX:
<div className="flex-1">
  <div className="flex flex-col gap-4">
    {locations.map((location, index) => (
      <Input 
        key={index}
        value={location}
        onChange={(e) => updateLocation(index, e.target.value)}
        placeholder="Dublin (2 days in the office per week)"
      />
    ))}
    <Button onClick={addLocation}>Add Another Location</Button>
    <CheckboxGroup>
      <CheckboxField>
        <Checkbox 
          name="remote" 
          checked={isRemote}
          onChange={(e) => setIsRemote(e.target.checked)}
        />
        <Label>Work is remote</Label>
      </CheckboxField>
      {/* Similar changes for other checkboxes */}
    </CheckboxGroup>
  </div>
</div>

This implementation allows for multiple locations and manages the state of the checkboxes.

Comment on lines +164 to +218
<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Application form URL</Subheading>
<Text>A link to your website (optional)</Text>
</div>
<Field>
<Input
id="app-url"
type="text"
autoComplete="url"
placeholder="https://example.com"
/>
</Field>
{/* Add error part after validation here */}
</section>

<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Job Type</Subheading>
<Text>Full-time, part-time or freelancer</Text>
</div>
<Field>
<RadioGroup defaultValue="full_time">
<RadioField>
<Radio value="full_time" />
<Label>Full-time (€150)</Label>
<Description>Salaried Position</Description>
</RadioField>
<RadioField>
<Radio value="part_time" />
<Label>Part-time (€100)</Label>
<Description>
Salaried position but less than 4 days per week
</Description>
</RadioField>
<RadioField>
<Radio value="freelancer" />
<Label>Freelancer (€100)</Label>
<Description>Shorter-term usually or fixed term/job</Description>
</RadioField>
<RadioField>
<Radio value="other_role_type" />
<Label>Other (€100)</Label>
<Description>
Looking for a co-founder or something else we haven’t thought of
</Description>
</RadioField>
</RadioGroup>
</Field>
{/* Add error part after validation here */}
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement URL validation and radio button state management.

The structure of these sections is good, but consider the following improvements:

  1. Add URL validation for the application form URL input.
  2. Implement state management for the job type radio buttons.

Here's an example of how you could improve these sections:

import { useState } from 'react';

// Inside the component:
const [applicationUrl, setApplicationUrl] = useState('');
const [jobType, setJobType] = useState('full_time');

const validateUrl = (url) => {
  const pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
    '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
    '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
    '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
    '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
    '(\\#[-a-z\\d_]*)?$','i'); // fragment locator
  return !!pattern.test(url);
};

// In the JSX:
<Input 
  value={applicationUrl}
  onChange={(e) => setApplicationUrl(e.target.value)}
  onBlur={() => {
    if (applicationUrl && !validateUrl(applicationUrl)) {
      alert('Please enter a valid URL');
    }
  }}
/>

<RadioGroup 
  value={jobType} 
  onChange={(value) => setJobType(value)}
>
  {/* ... existing radio buttons ... */}
</RadioGroup>

This implementation includes URL validation and manages the state of the job type selection.

@Nil2000
Copy link
Contributor Author

Nil2000 commented Oct 12, 2024

@NiallJoeMaher I added the flag. Let me know if things are ok or anything to refactor.

/>
<Text className="mt-1 text-xs text-gray-500">
JPG, GIF or PNG. 1MB max.
</Text>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need a grey square as a placeholder for the image which will be uploaded.
Otherwise there will be layout shift when an image is added.

We need to be able to see the preview of the image to be uploaded. This preview only needs to be in state. Have a look at the settings page for an example of how this is handled.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@John-Paul-Larkin Sure will do that way

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We dont need this page yet, so could you remove it from this PR.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Got it

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this file being removed is one of the only things left

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🧹 Outside diff range and nitpick comments (1)
app/(app)/jobs/create/_client.tsx (1)

1-267: Overall assessment: Good structure, needs implementation of form logic.

The job posting form has a well-structured layout with all necessary sections. However, to make it fully functional and user-friendly, consider implementing the following:

  1. Form state management (e.g., using react-hook-form or a custom solution).
  2. Input validation for all fields, including file uploads, URLs, and required fields.
  3. Proper handling of the company logo upload and preview.
  4. Allow for multiple location inputs and improve checkbox state management.
  5. Add a checkbox for terms and conditions acceptance.
  6. Implement form submission logic and integrate with a payment gateway for the checkout process.

These improvements will enhance the user experience and ensure data integrity when submitting job postings.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 6465e95 and adc9d4f.

⛔ Files ignored due to path filters (1)
  • public/images/company_placeholder.png is excluded by !**/*.png, !**/*.png
📒 Files selected for processing (2)
  • app/(app)/jobs/create/_client.tsx (1 hunks)
  • utils/flags.ts (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • utils/flags.ts
🧰 Additional context used
🔇 Additional comments (1)
app/(app)/jobs/create/_client.tsx (1)

1-32: LGTM: Imports and initial setup are well-structured.

The import statements are organized, and the initial setup with feature flags and state management is appropriate for the job posting functionality.

Comment on lines +34 to +77
return (
<form className="mx-auto max-w-4xl p-3 pt-8 sm:px-4">
<Heading level={1}>Post a job</Heading>
<Divider className="my-10 mt-6" />
<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Company Logo</Subheading>
<Text>Square format is best</Text>
</div>
<Field>
<div className="flex items-center space-x-4">
<Image
src={imgUrl || "/images/company_placeholder.png"}
width={80}
height={80}
alt="Company Logo"
className="rounded-[10px]"
/>
<div>
<Button
color="dark/white"
className="mt-3 rounded-md"
onClick={() => {
fileInputRef.current?.click();
}}
>
Change Logo
</Button>
<Input
type="file"
id="file-input"
name="company-logo"
accept="image/png, image/gif, image/jpeg"
onChange={() => {}}
className="hidden"
ref={fileInputRef}
/>
<Text className="mt-1 text-xs text-gray-500">
JPG, GIF or PNG. 1MB max.
</Text>
</div>
</div>
</Field>
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement file upload functionality and validation.

The structure for the company logo upload is good, but there are a few improvements needed:

  1. The onChange handler for the file input is empty. Implement logic to handle file selection and update the image preview.
  2. Add validation for file type (JPG, GIF, PNG) and size (1MB max) as mentioned in the UI text.
  3. Update the image preview when a new file is selected.

Here's a suggested implementation:

const handleFileChange = (event: React.ChangeEvent<HTMLInputElement>) => {
  const file = event.target.files?.[0];
  if (file) {
    if (file.size > 1024 * 1024) {
      alert('File size should not exceed 1MB');
      return;
    }
    if (!['image/jpeg', 'image/gif', 'image/png'].includes(file.type)) {
      alert('Only JPG, GIF, or PNG files are allowed');
      return;
    }
    const reader = new FileReader();
    reader.onload = (e) => setImgUrl(e.target?.result as string);
    reader.readAsDataURL(file);
  }
};

// Update the Input component:
<Input
  type="file"
  id="file-input"
  name="company-logo"
  accept="image/png, image/gif, image/jpeg"
  onChange={handleFileChange}
  className="hidden"
  ref={fileInputRef}
/>

Comment on lines +79 to +95
<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Company Name</Subheading>
<Text>This will be shown in the format you type it</Text>
</div>
<Field>
<Input
id="company-name"
type="text"
placeholder="Pixel Pulse Studios"
autoComplete="given-company-name"
/>
</Field>
{/* Add error part after validation here */}
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement form state management and validation for company name.

The structure for the company name section is good, but consider implementing form state management and validation for a more robust user experience. You could use a library like react-hook-form or implement a custom solution.

Here's a basic example using react-hook-form:

import { useForm } from 'react-hook-form';

// Inside the component:
const { register, formState: { errors } } = useForm();

// Update the Input component:
<Input
  id="company-name"
  type="text"
  placeholder="Pixel Pulse Studios"
  autoComplete="given-company-name"
  {...register("companyName", { required: "Company name is required" })}
/>
{errors.companyName && <span>{errors.companyName.message}</span>}

This will provide basic form state management and required field validation. You can extend this to include more complex validation rules as needed.

Comment on lines +97 to +113
<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Job Title</Subheading>
<Text>The job title for the position that you are opening</Text>
</div>
<Field>
<Input
id="job-title"
type="text"
placeholder="Reality Architect"
autoComplete="given-job-title"
/>
</Field>
{/* Add error part after validation here */}
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement form state management and validation for job title.

Similar to the company name section, implement form state management and validation for the job title. Using the same approach with react-hook-form:

// Update the Input component:
<Input
  id="job-title"
  type="text"
  placeholder="Reality Architect"
  autoComplete="given-job-title"
  {...register("jobTitle", { required: "Job title is required" })}
/>
{errors.jobTitle && <span>{errors.jobTitle.message}</span>}

This will provide consistent form handling across the form fields.

Comment on lines +115 to +131
<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Job Description</Subheading>
<Text>In markdown format</Text>
</div>
<Field>
<Textarea
id="job-description"
placeholder="As a Reality Architect, you'll be at the forefront of creating immersive mixed reality experiences that blur the line between the digital and physical..."
resizable={false}
rows={3}
/>
</Field>
{/* Add error part after validation here */}
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance job description input and implement validation.

  1. Implement form state management and validation for the job description, similar to previous sections.
  2. Consider making the textarea resizable or adjustable to accommodate longer job descriptions.

Here's an example implementation:

<Textarea
  id="job-description"
  placeholder="As a Reality Architect, you'll be at the forefront of creating immersive mixed reality experiences that blur the line between the digital and physical..."
  resizable={true}
  rows={3}
  {...register("jobDescription", { 
    required: "Job description is required",
    minLength: { value: 50, message: "Job description should be at least 50 characters long" }
  })}
/>
{errors.jobDescription && <span>{errors.jobDescription.message}</span>}

This allows for resizable input and adds minimum length validation.

Comment on lines +133 to +160
<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Locations</Subheading>
<Text>
Where is the job location? (“Dublin”, “Remote USA”, “Anywhere”).
</Text>
</div>
<Field>
<Input placeholder="Dublin (2 days in the office per week)" />
<CheckboxGroup className="mt-3">
<CheckboxField>
<Checkbox name="remote" value="is_remote" />
<Label>Work is remote</Label>
</CheckboxField>
<CheckboxField>
<Checkbox name="relocation" value="is_relocation_package" />
<Label>Relocation package given</Label>
</CheckboxField>
<CheckboxField>
<Checkbox name="visa" value="is_visa_sponsored" />
<Label>Visa sponsorship provided</Label>
</CheckboxField>
</CheckboxGroup>
</Field>
{/* Add error part after validation here */}
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance location handling and implement checkbox state management.

Consider the following improvements:

  1. Implement state management for the location input and checkboxes.
  2. Allow for multiple location inputs.
  3. Add validation for the location input.

Here's an example implementation:

import { useState } from 'react';

// Inside the component:
const [locations, setLocations] = useState(['']);
const [isRemote, setIsRemote] = useState(false);
const [hasRelocation, setHasRelocation] = useState(false);
const [hasVisa, setHasVisa] = useState(false);

const addLocation = () => setLocations([...locations, '']);
const updateLocation = (index: number, value: string) => {
  const newLocations = [...locations];
  newLocations[index] = value;
  setLocations(newLocations);
};

// In the JSX:
<div className="flex flex-col gap-4">
  {locations.map((location, index) => (
    <Input 
      key={index}
      value={location}
      onChange={(e) => updateLocation(index, e.target.value)}
      placeholder="Dublin (2 days in the office per week)"
    />
  ))}
  <Button onClick={addLocation}>Add Another Location</Button>
  <CheckboxGroup>
    <CheckboxField>
      <Checkbox 
        name="remote" 
        checked={isRemote}
        onChange={(e) => setIsRemote(e.target.checked)}
      />
      <Label>Work is remote</Label>
    </CheckboxField>
    {/* Similar changes for other checkboxes */}
  </CheckboxGroup>
</div>

This implementation allows for multiple locations and manages the state of the checkboxes.

Comment on lines +162 to +178
<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Application form URL</Subheading>
<Text>A link to your website (optional)</Text>
</div>
<Field>
<Input
id="app-url"
type="text"
autoComplete="url"
placeholder="https://example.com"
/>
</Field>
{/* Add error part after validation here */}
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement URL validation and state management for application form URL.

Add URL validation and state management for the application form URL input. Here's a suggested implementation:

import { useState } from 'react';

// Inside the component:
const [applicationUrl, setApplicationUrl] = useState('');

const validateUrl = (url: string) => {
  const pattern = new RegExp('^(https?:\\/\\/)?'+ // protocol
    '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|'+ // domain name
    '((\\d{1,3}\\.){3}\\d{1,3}))'+ // OR ip (v4) address
    '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'+ // port and path
    '(\\?[;&a-z\\d%_.~+=-]*)?'+ // query string
    '(\\#[-a-z\\d_]*)?$','i'); // fragment locator
  return !!pattern.test(url);
};

// Update the Input component:
<Input 
  id="app-url"
  type="text"
  autoComplete="url"
  placeholder="https://example.com"
  value={applicationUrl}
  onChange={(e) => setApplicationUrl(e.target.value)}
  onBlur={() => {
    if (applicationUrl && !validateUrl(applicationUrl)) {
      // Handle invalid URL (e.g., show an error message)
    }
  }}
/>

This implementation includes URL validation and manages the state of the application URL input.

Comment on lines +180 to +216
<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Job Type</Subheading>
<Text>Full-time, part-time or freelancer</Text>
</div>
<Field>
<RadioGroup defaultValue="full_time">
<RadioField>
<Radio value="full_time" />
<Label>Full-time (€150)</Label>
<Description>Salaried Position</Description>
</RadioField>
<RadioField>
<Radio value="part_time" />
<Label>Part-time (€100)</Label>
<Description>
Salaried position but less than 4 days per week
</Description>
</RadioField>
<RadioField>
<Radio value="freelancer" />
<Label>Freelancer (€100)</Label>
<Description>Shorter-term usually or fixed term/job</Description>
</RadioField>
<RadioField>
<Radio value="other_role_type" />
<Label>Other (€100)</Label>
<Description>
Looking for a co-founder or something else we haven’t thought of
</Description>
</RadioField>
</RadioGroup>
</Field>
{/* Add error part after validation here */}
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Implement state management for job type selection.

Add state management for the job type radio buttons. Here's a suggested implementation:

import { useState } from 'react';

// Inside the component:
const [jobType, setJobType] = useState('full_time');

// Update the RadioGroup component:
<RadioGroup 
  value={jobType} 
  onChange={(value) => setJobType(value)}
>
  {/* ... existing radio buttons ... */}
</RadioGroup>

This implementation manages the state of the job type selection, allowing you to easily access the selected job type when submitting the form.

Comment on lines +218 to +256
<Divider className="my-10" soft />

<section className="grid gap-x-8 gap-y-6 sm:grid-cols-2">
<div className="space-y-1">
<Subheading level={2}>Terms & Conditions</Subheading>
<Text>Ah yes, the fine print.</Text>
</div>
<div className="space-y-2">
<Text>
By submitting this job listing, I acknowledge and agree to the
following terms:
</Text>
<Text>
<Strong>Content Restrictions:</Strong> My listing must not contain:{" "}
<br />- Adult or explicit content <br />- Fraudulent or illegitimate
work opportunities <br />- Inappropriate or offensive language
</Text>
<Text>
<Strong>Accurate Classification: </Strong>I confirm that the job
type (e.g., full-time, part-time, freelance) is correctly
categorized.
</Text>
<Text>
<Strong>Removal Policy:</Strong> I understand that my listing may be
removed without notice if it violates any of the above conditions.
</Text>
<Text>
<Strong>Refund Policy:</Strong> If my listing is removed due to a
violation within 7 days of posting, I may be eligible for a refund,
subject to review.
</Text>
<Text>
<Strong>Compliance:</Strong> I agree to comply with all applicable
laws and regulations regarding job postings and employment
practices.
</Text>
</div>
{/* Add error part after validation here */}
</section>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add a checkbox for terms and conditions acceptance.

To ensure users explicitly agree to the terms and conditions, add a checkbox at the end of this section. Here's an example implementation:

import { useState } from 'react';

// Inside the component:
const [termsAccepted, setTermsAccepted] = useState(false);

// After the terms and conditions text, add:
<CheckboxField>
  <Checkbox 
    name="terms" 
    checked={termsAccepted}
    onChange={(e) => setTermsAccepted(e.target.checked)}
  />
  <Label>I have read and agree to the terms and conditions</Label>
</CheckboxField>

Then, in your form submission logic, you can check if termsAccepted is true before allowing the form to be submitted.

Comment on lines +258 to +266
<Divider className="my-10" soft />

<div className="flex justify-end">
<Button className="rounded-md" color="pink">
Submit and checkout
</Button>
</div>
</form>
);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Implement form submission and checkout logic.

The submit button is correctly implemented, but there's no form submission or checkout logic. Consider the following improvements:

  1. Implement form validation before submission.
  2. Add error handling for the submission process.
  3. Integrate with a payment gateway for the checkout process.

Here's a basic example of how you could start implementing this:

import { useState } from 'react';

// Inside the component:
const [isSubmitting, setIsSubmitting] = useState(false);

const handleSubmit = async (event: React.FormEvent) => {
  event.preventDefault();
  setIsSubmitting(true);

  // Perform form validation here

  try {
    // Submit form data to your API
    const response = await fetch('/api/job-postings', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(formData),
    });

    if (!response.ok) throw new Error('Submission failed');

    // If submission is successful, proceed to checkout
    // Integrate with your payment gateway here
    // For example, redirect to a Stripe checkout page
    window.location.href = '/checkout';
  } catch (error) {
    console.error('Error:', error);
    alert('An error occurred. Please try again.');
  } finally {
    setIsSubmitting(false);
  }
};

// Update the Button component:
<Button 
  className="rounded-md" 
  color="pink" 
  onClick={handleSubmit}
  disabled={isSubmitting}
>
  {isSubmitting ? 'Submitting...' : 'Submit and checkout'}
</Button>

This implementation includes basic form submission logic and error handling. You'll need to replace the placeholder API call and checkout process with your actual implementation.

@Nil2000
Copy link
Contributor Author

Nil2000 commented Oct 14, 2024

@NiallJoeMaher @John-Paul-Larkin Let me know if things are fine now

</div>
<Field>
<div className="flex items-center space-x-4">
<Image
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@John-Paul-Larkin A placeholder will be shown if no url is present

Copy link
Contributor

@NiallJoeMaher NiallJoeMaher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🌮 You can start working on the validation and I'll tag it as Hacktoberfest.
Thanks for this.

Copy link

vercel bot commented Oct 16, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
codu ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 16, 2024 3:00pm

@Nil2000
Copy link
Contributor Author

Nil2000 commented Oct 16, 2024

🌮 You can start working on the validation and I'll tag it as Hacktoberfest. Thanks for this.

On it 💪

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add a jobs posting page form
3 participants