-
Notifications
You must be signed in to change notification settings - Fork 27
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
job recommendation graph #172
Changes from 42 commits
6fd17a1
2a9fb19
a8a7253
2908c8e
c057830
5d67749
72f1852
ef897a7
4235f02
2581f63
de2b4d6
c1bfe3c
9f8e4fd
1301458
440e0bb
5714af5
234a3ed
4342629
d5e486b
91d368d
32c3144
78a2766
12946f2
3403a61
7fb85bf
2c14531
f4c65fb
03fd05d
e0500fb
b77b7e9
f2710ab
03d28e7
755bfca
4909768
19222f6
be2d7b5
8964c85
a1741ef
c003633
c1ab4a6
f9d97ef
b34a807
b3b79a2
fc4a25f
1f296cf
58336ac
805798f
d6cdbe3
19f51c8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
@@ -0,0 +1,198 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||
import React, { useState } from 'react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
import { | ||||||||||||||||||||||||||||||||||||||||||||||||||
MapPin, | ||||||||||||||||||||||||||||||||||||||||||||||||||
Building, | ||||||||||||||||||||||||||||||||||||||||||||||||||
Calendar, | ||||||||||||||||||||||||||||||||||||||||||||||||||
DollarSign, | ||||||||||||||||||||||||||||||||||||||||||||||||||
BriefcaseIcon, | ||||||||||||||||||||||||||||||||||||||||||||||||||
Globe, | ||||||||||||||||||||||||||||||||||||||||||||||||||
CheckCircle, | ||||||||||||||||||||||||||||||||||||||||||||||||||
Star, | ||||||||||||||||||||||||||||||||||||||||||||||||||
} from 'lucide-react'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
import Link from 'next/link'; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
const JobDescription = ({ job, makeCoverletter }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
const [expanded, setExpanded] = useState(false); | ||||||||||||||||||||||||||||||||||||||||||||||||||
console.log({ job }); | ||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div | ||||||||||||||||||||||||||||||||||||||||||||||||||
className="p-6 bg-white shadow-lg rounded-lg transition-transform transform hover:scale-105 cursor-pointer" | ||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={() => setExpanded(!expanded)} | ||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<header className="mb-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<h1 className="text-2xl font-bold text-gray-800"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{job.title || 'Not available'} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</h1> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="grid grid-cols-1 md:grid-cols-2 gap-4 text-gray-600 mt-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex items-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<Building className="w-5 h-5 mr-2" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<span>{job.company || 'Not available'}</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex items-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<DollarSign className="w-5 h-5 mr-2 text-warning-500" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{job.salary | ||||||||||||||||||||||||||||||||||||||||||||||||||
? `$${Number(job.salary).toLocaleString()}/year` | ||||||||||||||||||||||||||||||||||||||||||||||||||
: 'Not available'} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex items-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<MapPin className="w-5 h-5 mr-2 text-secondary-500" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{job.location | ||||||||||||||||||||||||||||||||||||||||||||||||||
? `${job.location.city}, ${job.location.countryCode}` | ||||||||||||||||||||||||||||||||||||||||||||||||||
: 'Not available'} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex items-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<BriefcaseIcon className="w-5 h-5 mr-2 text-accent-500" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<span>{job.experience || 'Not available'}</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex items-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<Globe className="w-5 h-5 mr-2 text-accent-500" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<span>Remote - {job.remote || 'Not available'}</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex items-center"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<Calendar className="w-5 h-5 mr-2 text-success-500" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<span>{job.date || 'Not available'}</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="text-gray-600 col-span-full"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{expanded ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<p>{job.description || 'Not available'}</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{job.description | ||||||||||||||||||||||||||||||||||||||||||||||||||
? job.description.slice(0, 100) + '...' | ||||||||||||||||||||||||||||||||||||||||||||||||||
: 'Not available'} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</header> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{expanded && ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="mt-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="mb-6"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<h2 className="text-2xl font-semibold mb-3 text-gray-800"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
Responsibilities | ||||||||||||||||||||||||||||||||||||||||||||||||||
</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<ul className="list-none"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{job.responsibilities?.length ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
job.responsibilities.map((resp, index) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<li key={index} className="flex items-start mb-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<CheckCircle className="w-5 h-5 mr-2 text-success-500 flex-shrink-0 mt-1" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<span>{resp}</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</li> | ||||||||||||||||||||||||||||||||||||||||||||||||||
)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<p>Not available</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</ul> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="mb-6"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<h2 className="text-2xl font-semibold mb-3 text-gray-800"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
Qualifications | ||||||||||||||||||||||||||||||||||||||||||||||||||
</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<ul className="list-none"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{job.qualifications?.length ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
job.qualifications.map((qual, index) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<li key={index} className="flex items-start mb-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<CheckCircle className="w-5 h-5 mr-2 text-secondary-500 flex-shrink-0 mt-1" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<span>{qual}</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</li> | ||||||||||||||||||||||||||||||||||||||||||||||||||
)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<p>Not available</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</ul> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="mb-6"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<h2 className="text-2xl font-semibold mb-3 text-gray-800"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
Skills | ||||||||||||||||||||||||||||||||||||||||||||||||||
</h2> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{job.skills?.length ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
job.skills.map((skill, index) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div key={index} className="mb-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<h3 className="text-xl font-semibold text-gray-700"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{skill.name} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</h3> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex items-center mb-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<Star className="w-5 h-5 mr-2 text-warning-500" /> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<span>{skill.level}</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex flex-wrap gap-2"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{skill.keywords?.length ? ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
skill.keywords.map((keyword, kidx) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<span | ||||||||||||||||||||||||||||||||||||||||||||||||||
key={kidx} | ||||||||||||||||||||||||||||||||||||||||||||||||||
className="px-3 py-1 bg-gray-200 text-gray-700 rounded-full text-sm" | ||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{keyword} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</span> | ||||||||||||||||||||||||||||||||||||||||||||||||||
)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<p>Not available</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
)) | ||||||||||||||||||||||||||||||||||||||||||||||||||
) : ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<p>Not available</p> | ||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="mt-4 flex gap-4"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<button | ||||||||||||||||||||||||||||||||||||||||||||||||||
className="bg-secondary-500 text-white py-2 px-4 rounded hover:bg-secondary-700 transition-colors duration-200" | ||||||||||||||||||||||||||||||||||||||||||||||||||
onClick={() => makeCoverletter(job.raw)} | ||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||
Make Cover Letter | ||||||||||||||||||||||||||||||||||||||||||||||||||
</button> | ||||||||||||||||||||||||||||||||||||||||||||||||||
<a | ||||||||||||||||||||||||||||||||||||||||||||||||||
href={job.raw?.url || '#'} | ||||||||||||||||||||||||||||||||||||||||||||||||||
target="_blank" | ||||||||||||||||||||||||||||||||||||||||||||||||||
rel="noopener noreferrer" | ||||||||||||||||||||||||||||||||||||||||||||||||||
className="bg-gray-200 text-gray-700 py-2 px-4 rounded hover:bg-gray-300 transition-colors duration-200" | ||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||
View Original Job | ||||||||||||||||||||||||||||||||||||||||||||||||||
</a> | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
<Link | ||||||||||||||||||||||||||||||||||||||||||||||||||
href={`/jobs/${job.raw.uuid}`} | ||||||||||||||||||||||||||||||||||||||||||||||||||
target="_blank" | ||||||||||||||||||||||||||||||||||||||||||||||||||
rel="noopener noreferrer" | ||||||||||||||||||||||||||||||||||||||||||||||||||
className="bg-gray-200 text-gray-700 py-2 px-4 rounded hover:bg-gray-300 transition-colors duration-200" | ||||||||||||||||||||||||||||||||||||||||||||||||||
> | ||||||||||||||||||||||||||||||||||||||||||||||||||
View Job Candiates | ||||||||||||||||||||||||||||||||||||||||||||||||||
</Link> | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+160
to
+168
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add null checks and improve accessibility
<Link
- href={`/jobs/${job.raw.uuid}`}
+ href={job.raw?.uuid ? `/jobs/${job.raw.uuid}` : '#'}
+ aria-label="View job candidates"
target="_blank"
rel="noopener noreferrer"
className="bg-gray-200 text-gray-700 py-2 px-4 rounded hover:bg-gray-300 transition-colors duration-200"
>
View Job Candiates
</Link> Also, fix the typo in "Candiates" → "Candidates" 📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Actions: CI[error] Lint command failed with exit code 1 |
||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
)} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
const JobList = ({ jobs, makeCoverletter }) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
const validJobs = jobs?.filter( | ||||||||||||||||||||||||||||||||||||||||||||||||||
(job) => job.gpt_content && job.gpt_content !== 'FAILED' | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
const fullJobs = validJobs?.map((job) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||
const fullJob = JSON.parse(job.gpt_content); | ||||||||||||||||||||||||||||||||||||||||||||||||||
fullJob.raw = job; | ||||||||||||||||||||||||||||||||||||||||||||||||||
return fullJob; | ||||||||||||||||||||||||||||||||||||||||||||||||||
}); | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
Comment on lines
+175
to
+184
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add error handling for JSON parsing The current implementation could throw runtime errors if job.gpt_content contains invalid JSON. const fullJobs = validJobs?.map((job) => {
- const fullJob = JSON.parse(job.gpt_content);
- fullJob.raw = job;
- return fullJob;
+ try {
+ const fullJob = JSON.parse(job.gpt_content);
+ fullJob.raw = job;
+ return fullJob;
+ } catch (error) {
+ console.error(`Failed to parse job content: ${error}`);
+ return null;
+ }
- });
+ }).filter(Boolean); 📝 Committable suggestion
Suggested change
🧰 Tools🪛 GitHub Actions: CI[error] Lint command failed with exit code 1 |
||||||||||||||||||||||||||||||||||||||||||||||||||
return ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<div className="flex flex-col gap-5"> | ||||||||||||||||||||||||||||||||||||||||||||||||||
{fullJobs?.map((job, index) => ( | ||||||||||||||||||||||||||||||||||||||||||||||||||
<JobDescription | ||||||||||||||||||||||||||||||||||||||||||||||||||
key={index} | ||||||||||||||||||||||||||||||||||||||||||||||||||
job={job} | ||||||||||||||||||||||||||||||||||||||||||||||||||
makeCoverletter={makeCoverletter} | ||||||||||||||||||||||||||||||||||||||||||||||||||
/> | ||||||||||||||||||||||||||||||||||||||||||||||||||
))} | ||||||||||||||||||||||||||||||||||||||||||||||||||
</div> | ||||||||||||||||||||||||||||||||||||||||||||||||||
); | ||||||||||||||||||||||||||||||||||||||||||||||||||
}; | ||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||
export default JobList; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
'use client'; | ||
|
||
export default function Home({ children }) { | ||
return <>{children}</>; | ||
} |
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
Remove debug console.log and add prop types validation
- console.log({ job });
Also, consider stopping event propagation for button clicks to prevent unintended expansions.
📝 Committable suggestion
🧰 Tools
🪛 GitHub Actions: CI
[error] Lint command failed with exit code 1