diff --git a/package.json b/package.json index a0e11748..ce284728 100644 --- a/package.json +++ b/package.json @@ -27,7 +27,7 @@ ] }, "prisma": { - "seed": "ts-node --compiler-options {\"module\":\"CommonJS\"} prisma/seed.ts" + "seed": "node --import 'data:text/javascript,import { register } from \"node:module\"; import { pathToFileURL } from \"node:url\"; register(\"ts-node/esm\", pathToFileURL(\"./\"));' prisma/seed.ts" }, "dependencies": { "@aws-sdk/client-s3": "^3.645.0", @@ -67,7 +67,7 @@ "next": "^14.2.12", "next-auth": "^4.24.7", "next-themes": "^0.3.0", - "nextjs-toploader": "^1.6.12", + "nextjs-toploader": "^3.7.15", "node-cron": "^3.0.3", "nodemailer": "^6.9.15", "react": "^18", diff --git a/prisma/seed.ts b/prisma/seed.ts index 436034ed..5a47f17f 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,5 +1,11 @@ /* eslint-disable no-console */ -import { PrismaClient, Currency, EmployementType, Role, WorkMode } from '@prisma/client'; +import { + PrismaClient, + Currency, + EmployementType, + Role, + WorkMode, +} from '@prisma/client'; import { faker } from '@faker-js/faker'; import bcrypt from 'bcryptjs'; @@ -24,7 +30,6 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.remote, currency: Currency.USD, - application: 'apply@techcorp.com', hasExperiencerange: true, minExperience: 1, maxExperience: 2, @@ -47,7 +52,7 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.office, currency: Currency.USD, - application: 'jobs@innovatech.com', + hasExperiencerange: false, companyLogo: '', hasSalaryRange: false, @@ -68,7 +73,6 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.hybrid, currency: Currency.USD, - application: 'careers@globalsolutions.com', hasExperiencerange: true, minExperience: 3, maxExperience: 4, @@ -92,7 +96,6 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.remote, currency: Currency.USD, - application: 'apply@devopsltd.com', hasExperiencerange: true, minExperience: 1, maxExperience: 2, @@ -116,7 +119,6 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.hybrid, currency: Currency.USD, - application: 'careers@productiveminds.com', hasExperiencerange: false, companyLogo: '', hasSalaryRange: true, @@ -138,7 +140,6 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.office, currency: Currency.USD, - application: 'apply@datainsights.com', hasExperiencerange: true, minExperience: 1, maxExperience: 2, @@ -162,7 +163,6 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.remote, currency: Currency.USD, - application: 'jobs@creativedesigns.com', hasExperiencerange: true, minExperience: 1, maxExperience: 2, @@ -185,7 +185,6 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.hybrid, currency: Currency.USD, - application: 'apply@appinnovators.com', hasExperiencerange: true, minExperience: 1, maxExperience: 2, @@ -207,7 +206,6 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.office, currency: Currency.USD, - application: 'careers@cloudworks.com', hasExperiencerange: true, minExperience: 1, maxExperience: 2, @@ -230,14 +228,13 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.remote, currency: Currency.USD, - application: 'jobs@securetech.com', hasExperiencerange: true, minExperience: 1, maxExperience: 2, companyLogo: '', hasSalaryRange: true, - minSalary: 75, - maxSalary: 95, + minSalary: 75000, + maxSalary: 95000, isVerifiedJob: false, }, { @@ -253,12 +250,11 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.remote, currency: Currency.USD, - application: 'apply@qasolutions.com', companyLogo: '', hasSalaryRange: true, hasExperiencerange: false, - minSalary: 25, - maxSalary: 50, + minSalary: 25000, + maxSalary: 50000, isVerifiedJob: true, }, { @@ -274,7 +270,6 @@ let jobs = [ type: EmployementType.Contract, workMode: WorkMode.remote, currency: Currency.USD, - application: 'careers@writetech.com', hasExperiencerange: true, minExperience: 1, maxExperience: 2, @@ -316,14 +311,12 @@ async function seedUsers() { async function seedJobs() { try { - const existingUsers = await prisma.user.findMany({ select: { id: true }, }); - const existingUserIds = new Set(existingUsers.map(user => user.id)); + const existingUserIds = new Set(existingUsers.map((user) => user.id)); - - const validJobs = jobs.filter(job => existingUserIds.has(job.userId)); + const validJobs = jobs.filter((job) => existingUserIds.has(job.userId)); await Promise.all( validJobs.map(async (j) => @@ -341,7 +334,7 @@ async function seedJobs() { type: j.type, workMode: j.workMode, currency: j.currency, - application: j.application, + application: 'https://x.com/100xDevs', city: faker.location.city(), address: faker.location.city(), hasExperiencerange: j.hasExperiencerange, @@ -378,4 +371,4 @@ async function main() { await seedJobs(); } -main(); \ No newline at end of file +main(); diff --git a/src/actions/job.action.ts b/src/actions/job.action.ts index 93152e5a..95e505a2 100644 --- a/src/actions/job.action.ts +++ b/src/actions/job.action.ts @@ -260,6 +260,7 @@ export const getJobById = withServerActionAsyncCatcher< minSalary: true, maxSalary: true, postedAt: true, + application: true, }, }); return new SuccessResponse(`${id} Job fetched successfully`, 200, { @@ -303,6 +304,7 @@ export const getRecentJobs = async () => { postedAt: true, companyLogo: true, type: true, + application: true, }, take: 6, }); diff --git a/src/app/jobs/[id]/page.tsx b/src/app/jobs/[id]/page.tsx index 2c8c12d0..e34810b3 100644 --- a/src/app/jobs/[id]/page.tsx +++ b/src/app/jobs/[id]/page.tsx @@ -1,6 +1,6 @@ import { getJobById, getRecommendedJobs } from '@/actions/job.action'; import { Job } from '@/components/job'; -import JobCard from '@/components/job-card'; +import JobCard from '@/components/job-card-rec'; import { JobByIdSchemaType } from '@/lib/validators/jobs.validator'; import { ArrowLeft } from 'lucide-react'; import Link from 'next/link'; @@ -8,7 +8,6 @@ import { redirect } from 'next/navigation'; const page = async ({ params }: { params: JobByIdSchemaType }) => { const job = await getJobById(params); - if (!job.status) { return; } diff --git a/src/app/layout.tsx b/src/app/layout.tsx index 9fc68b7f..352bb044 100644 --- a/src/app/layout.tsx +++ b/src/app/layout.tsx @@ -3,6 +3,7 @@ import Header from '@/layouts/header'; import { cn } from '@/lib/utils'; import Providers from '@/providers/providers'; import type { Metadata } from 'next'; +import NextTopLoader from 'nextjs-toploader'; import './globals.css'; import localFont from 'next/font/local'; @@ -33,6 +34,8 @@ export default async function RootLayout({ satoshi.variable )} > + {' '} +
{children}
diff --git a/src/components/Jobcard.tsx b/src/components/Jobcard.tsx index c6002133..f98921de 100644 --- a/src/components/Jobcard.tsx +++ b/src/components/Jobcard.tsx @@ -1,4 +1,4 @@ -import { AlertCircle, Briefcase } from 'lucide-react'; +import { Briefcase } from 'lucide-react'; import Image from 'next/image'; import Link from 'next/link'; import Icon from './ui/icon'; @@ -6,6 +6,7 @@ import { formatSalary } from '@/lib/utils'; import { JobType } from '@/types/jobs.types'; import _ from 'lodash'; import { cn } from '@/lib/utils'; +import { JobSkills } from './job-skills'; export default function JobCard({ job, className, @@ -24,26 +25,15 @@ export default function JobCard({ >
- {job.companyLogo ? ( - job.companyLogo === 'https://www.example.com' ? ( -
- company-logo -
- ) : ( - company-logo - ) - ) : null} + {job.companyLogo && ( + company-logo + )}

@@ -60,51 +50,37 @@ export default function JobCard({ {_.startCase(job.type)}

- - {job.minExperience && job.maxExperience - ? `${job.minExperience}-${job.maxExperience} Yrs` - : 'Not disclosed'} + {job.minSalary && job.maxSalary ? ( + + + {`${formatSalary(job.minSalary)}-${formatSalary(job.maxSalary)}`} + + ) : ( + 'Not disclosed' + )} - - {job.minSalary && job.maxSalary - ? `${formatSalary(job.minSalary)}-${formatSalary(job.maxSalary)}` - : 'Not disclosed'} + {job.minExperience && job.maxExperience ? ( + + + + {`${job.minExperience}-${job.maxExperience} Yrs`} + + ) : ( + 'Ex: Not disclosed' + )} + - {job.address} - ({job.workMode}) + {job.city} - + + {job.workMode} +
-
- {job.skills && job.skills.length !== 0 ? ( - !(job.skills.length > 4) ? ( - // If there are more than 3 skills, show them all - job.skills.map((item, index) => ( -
- {item} -
- )) - ) : ( - job.skills.slice(0, 7).map((item, index) => ( -
- {item} -
- )) - ) - ) : ( - // If there are no skills, show the "No skills provided" message -
- No skills provided -
- )} +
+
); diff --git a/src/components/RecentJobs.tsx b/src/components/RecentJobs.tsx index c2655b77..bb16731f 100644 --- a/src/components/RecentJobs.tsx +++ b/src/components/RecentJobs.tsx @@ -10,7 +10,7 @@ export default async function RecentJobs() { return (
{recentJobs.additional.recentJobs.map((job, index) => ( - + ))}
); diff --git a/src/components/job-card-loader.tsx b/src/components/job-card-loader.tsx deleted file mode 100644 index 2ae82b45..00000000 --- a/src/components/job-card-loader.tsx +++ /dev/null @@ -1,37 +0,0 @@ -import { Skeleton } from '@/components/ui/skeleton'; -import React from 'react'; - -const JobCardLoader = () => { - return ( -
-
-
- -
-
-

- -

-

- -

-
-
-
-
- -
-
-

- -

-

- -

-
-
-
- ); -}; - -export default JobCardLoader; diff --git a/src/components/job-card.tsx b/src/components/job-card-rec.tsx similarity index 95% rename from src/components/job-card.tsx rename to src/components/job-card-rec.tsx index cd217597..e7f88798 100644 --- a/src/components/job-card.tsx +++ b/src/components/job-card-rec.tsx @@ -8,7 +8,6 @@ import _ from 'lodash'; export default function JobCard({ job }: { job: JobType }) { return ( - {/* job card */}
@@ -37,7 +36,7 @@ export default function JobCard({ job }: { job: JobType }) {

- {job.address} ({_.startCase(job.workMode)}) + {job.address} - {_.startCase(job.workMode)}

diff --git a/src/components/job-skills.tsx b/src/components/job-skills.tsx new file mode 100644 index 00000000..c3fbb187 --- /dev/null +++ b/src/components/job-skills.tsx @@ -0,0 +1,34 @@ +import { JobType } from '@/types/jobs.types'; +import icons from '@/lib/icons'; +type JobSkillsProps = Pick; +export const JobSkills = ({ skills }: JobSkillsProps) => { + const maxSkills = 6; + + if (skills.length === 0) { + return ; + } + const displayedSkills = skills.slice(0, maxSkills); + const remainingSkillsCount = skills.length - maxSkills; + return ( +
+ {displayedSkills.map((skill, index) => ( + + ))} + {remainingSkillsCount > 0 && ( +
+ +{remainingSkillsCount} more +
+ )} +
+ ); +}; +const NoSkills = () => ( +
+ No skills provided +
+); +const SkillTag = ({ skill }: { skill: string }) => ( +
+ {skill} +
+); diff --git a/src/components/job.tsx b/src/components/job.tsx index 2b942023..38b6202f 100644 --- a/src/components/job.tsx +++ b/src/components/job.tsx @@ -4,7 +4,8 @@ import Icon from './ui/icon'; import { formatSalary } from '@/lib/utils'; import { Button } from './ui/button'; import Image from 'next/image'; -import { MapPin } from 'lucide-react'; +import { Briefcase, MapPin } from 'lucide-react'; +import Link from 'next/link'; import { Twitter } from 'lucide-react'; import Linkify from 'linkify-react'; const options = { @@ -26,21 +27,15 @@ export const Job = ({ job }: { job: JobType }) => {
- {job.companyLogo ? ( - job.companyLogo === 'https://www.example.com' ? ( -
- hi -
- ) : ( - company-logo - ) - ) : null} + {job.companyLogo && ( + company-logo + )}

{job.title}

@@ -57,11 +52,23 @@ export const Job = ({ job }: { job: JobType }) => {
- {!!job.minSalary && } + {job.minSalary && job.maxSalary ? `${formatSalary(job.minSalary)}-${formatSalary(job.maxSalary)}` : 'Not disclosed'} + + {job.minExperience && job.maxExperience ? ( + + + + {`${job.minExperience}-${job.maxExperience} Yrs`} + + ) : ( + 'Ex: Not disclosed' + )} + +

{job.address}

@@ -85,14 +92,15 @@ export const Job = ({ job }: { job: JobType }) => {
- - + + +