diff --git a/prisma/migrations/20240901095201_/migration.sql b/prisma/migrations/20240901095201_/migration.sql new file mode 100644 index 00000000..3ac83c34 --- /dev/null +++ b/prisma/migrations/20240901095201_/migration.sql @@ -0,0 +1,50 @@ +-- CreateEnum +CREATE TYPE "Currency" AS ENUM ('INR', 'USD'); + +-- CreateEnum +CREATE TYPE "WorkMode" AS ENUM ('remote', 'hybrid', 'office'); + +-- CreateEnum +CREATE TYPE "Role" AS ENUM ('USER', 'ADMIN'); + +-- CreateEnum +CREATE TYPE "Location" AS ENUM ('BANGLORE', 'DELHI', 'MUMBAI', 'PUNE', 'CHENNAI', 'HYDERABAD', 'KOLKATA', 'AHMEDABAD', 'JAIPUR', 'SURAT'); + +-- CreateTable +CREATE TABLE "User" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "email" TEXT NOT NULL, + "password" TEXT NOT NULL, + "avatar" TEXT, + "isVerified" BOOLEAN NOT NULL DEFAULT false, + "role" "Role" NOT NULL DEFAULT 'USER', + + CONSTRAINT "User_pkey" PRIMARY KEY ("id") +); + +-- CreateTable +CREATE TABLE "Job" ( + "id" TEXT NOT NULL, + "userId" TEXT NOT NULL, + "title" TEXT NOT NULL, + "description" TEXT, + "company_name" TEXT NOT NULL, + "work_mode" "WorkMode" NOT NULL, + "currency" "Currency" NOT NULL DEFAULT 'INR', + "location" "Location" NOT NULL, + "has_salary_range" BOOLEAN NOT NULL DEFAULT false, + "minSalary" INTEGER, + "maxSalary" INTEGER, + "is_verified_job" BOOLEAN NOT NULL DEFAULT false, + "postedAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, + "updatedAt" TIMESTAMP(3) NOT NULL, + + CONSTRAINT "Job_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "User_email_key" ON "User"("email"); + +-- AddForeignKey +ALTER TABLE "Job" ADD CONSTRAINT "Job_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE RESTRICT ON UPDATE CASCADE; diff --git a/prisma/migrations/20240902211016_/migration.sql b/prisma/migrations/20240902211016_/migration.sql new file mode 100644 index 00000000..3029dfde --- /dev/null +++ b/prisma/migrations/20240902211016_/migration.sql @@ -0,0 +1,15 @@ +/* + Warnings: + + - Changed the type of `location` on the `Job` table. No cast exists, the column would be dropped and recreated, which cannot be done if there is data, since the column is required. + +*/ +-- CreateEnum +CREATE TYPE "JobLocations" AS ENUM ('BANGLORE', 'DELHI', 'MUMBAI', 'PUNE', 'CHENNAI', 'HYDERABAD', 'KOLKATA', 'AHMEDABAD', 'JAIPUR', 'SURAT'); + +-- AlterTable +ALTER TABLE "Job" DROP COLUMN "location", +ADD COLUMN "location" "JobLocations" NOT NULL; + +-- DropEnum +DROP TYPE "Location"; diff --git a/prisma/migrations/migration_lock.toml b/prisma/migrations/migration_lock.toml new file mode 100644 index 00000000..fbffa92c --- /dev/null +++ b/prisma/migrations/migration_lock.toml @@ -0,0 +1,3 @@ +# Please do not edit this file manually +# It should be added in your version-control system (i.e. Git) +provider = "postgresql" \ No newline at end of file diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 5e496f59..29409066 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -7,34 +7,35 @@ datasource db { url = env("DATABASE_URL") } -model User{ +model User { id String @id @default(cuid()) name String email String @unique - password String + password String avatar String? isVerified Boolean @default(false) role Role @default(USER) jobs Job[] } -model Job { - id String @id @default(cuid()) +model Job { + id String @id @default(cuid()) userId String title String description String? - companyName String @map("company_name") - workMode WorkMode @map("work_mode") - currency Currency @default(INR) - location String - hasSalaryRange Boolean @default(false) @map("has_salary_range") + companyName String @map("company_name") + workMode WorkMode @map("work_mode") + currency Currency @default(INR) + location JobLocations + hasSalaryRange Boolean @default(false) @map("has_salary_range") minSalary Int? maxSalary Int? - isVerifiedJob Boolean @default(false) @map("is_verified_job") - postedAt DateTime @default(now()) - updatedAt DateTime @updatedAt - user User @relation(fields: [userId], references: [id]) + isVerifiedJob Boolean @default(false) @map("is_verified_job") + postedAt DateTime @default(now()) + updatedAt DateTime @updatedAt + user User @relation(fields: [userId], references: [id]) } + enum Currency { INR USD @@ -50,3 +51,16 @@ enum Role { USER ADMIN } + +enum JobLocations { + BANGLORE + DELHI + MUMBAI + PUNE + CHENNAI + HYDERABAD + KOLKATA + AHMEDABAD + JAIPUR + SURAT +} diff --git a/prisma/seed.ts b/prisma/seed.ts index dc68687d..81001261 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -2,13 +2,16 @@ import { Currency, Role, WorkMode } from '@prisma/client'; import bcrypt from 'bcryptjs'; import prisma from '../src/config/prisma.config'; +import { JobLocations } from '@prisma/client'; const users = [ { id: '1', name: 'Jack', email: 'user@gmail.com' }, { id: '2', name: 'Admin', email: 'admin@gmail.com', role: Role.ADMIN }, ]; -const jobs = [ +const locationArr = Object.keys(JobLocations); + +let jobs = [ { id: '1', userId: '1', @@ -17,7 +20,6 @@ const jobs = [ companyName: 'Tech Corp', workMode: WorkMode.remote, currency: Currency.USD, - location: 'New York', hasSalaryRange: true, minSalary: 60000, maxSalary: 80000, @@ -31,7 +33,6 @@ const jobs = [ companyName: 'Innovatech', workMode: WorkMode.office, currency: Currency.INR, - location: 'Bangalore', hasSalaryRange: false, minSalary: null, maxSalary: null, @@ -45,7 +46,6 @@ const jobs = [ companyName: 'Global Solutions', workMode: WorkMode.hybrid, currency: Currency.USD, - location: 'San Francisco', hasSalaryRange: true, minSalary: 90000, maxSalary: 120000, @@ -60,7 +60,6 @@ const jobs = [ companyName: 'DevOps Ltd.', workMode: WorkMode.remote, currency: Currency.INR, - location: 'Mumbai', hasSalaryRange: true, minSalary: 50000, maxSalary: 70000, @@ -75,7 +74,6 @@ const jobs = [ companyName: 'Productive Minds', workMode: WorkMode.hybrid, currency: Currency.USD, - location: 'Chicago', hasSalaryRange: true, minSalary: 110000, maxSalary: 150000, @@ -90,7 +88,6 @@ const jobs = [ companyName: 'Data Insights', workMode: WorkMode.office, currency: Currency.INR, - location: 'Hyderabad', hasSalaryRange: true, minSalary: 80000, maxSalary: 100000, @@ -105,7 +102,6 @@ const jobs = [ companyName: 'Creative Designs', workMode: WorkMode.remote, currency: Currency.USD, - location: 'Seattle', hasSalaryRange: true, minSalary: 70000, maxSalary: 90000, @@ -119,7 +115,6 @@ const jobs = [ companyName: 'App Innovators', workMode: WorkMode.hybrid, currency: Currency.INR, - location: 'Delhi', hasSalaryRange: false, minSalary: null, maxSalary: null, @@ -133,7 +128,6 @@ const jobs = [ companyName: 'Cloud Works', workMode: WorkMode.office, currency: Currency.USD, - location: 'Austin', hasSalaryRange: true, minSalary: 100000, maxSalary: 130000, @@ -147,7 +141,6 @@ const jobs = [ companyName: 'SecureTech', workMode: WorkMode.remote, currency: Currency.INR, - location: 'Pune', hasSalaryRange: true, minSalary: 75000, maxSalary: 95000, @@ -161,7 +154,6 @@ const jobs = [ companyName: 'QA Solutions', workMode: WorkMode.remote, currency: Currency.USD, - location: 'Boston', hasSalaryRange: true, minSalary: 45000, maxSalary: 50000, @@ -175,7 +167,6 @@ const jobs = [ companyName: 'Tech Docs', workMode: WorkMode.hybrid, currency: Currency.USD, - location: 'San Diego', hasSalaryRange: true, minSalary: 30000, maxSalary: 35000, @@ -189,7 +180,6 @@ const jobs = [ companyName: 'Support Corp', workMode: WorkMode.office, currency: Currency.USD, - location: 'Dallas', hasSalaryRange: true, minSalary: 20000, maxSalary: 25000, @@ -203,7 +193,6 @@ const jobs = [ companyName: 'Net Admins', workMode: WorkMode.remote, currency: Currency.USD, - location: 'Houston', hasSalaryRange: true, minSalary: 35000, maxSalary: 40000, @@ -217,7 +206,6 @@ const jobs = [ companyName: 'Sys Solutions', workMode: WorkMode.hybrid, currency: Currency.USD, - location: 'Miami', hasSalaryRange: true, minSalary: 27000, maxSalary: 32000, @@ -231,7 +219,6 @@ const jobs = [ companyName: 'Sales Tech', workMode: WorkMode.office, currency: Currency.USD, - location: 'Chicago', hasSalaryRange: true, minSalary: 30000, maxSalary: 35000, @@ -245,7 +232,6 @@ const jobs = [ companyName: 'Market Pro', workMode: WorkMode.remote, currency: Currency.USD, - location: 'Los Angeles', hasSalaryRange: true, minSalary: 20000, maxSalary: 25000, @@ -259,7 +245,6 @@ const jobs = [ companyName: 'Content Creators', workMode: WorkMode.hybrid, currency: Currency.USD, - location: 'New York', hasSalaryRange: true, minSalary: 25000, maxSalary: 30000, @@ -273,7 +258,6 @@ const jobs = [ companyName: 'Design Pros', workMode: WorkMode.office, currency: Currency.USD, - location: 'San Francisco', hasSalaryRange: true, minSalary: 22000, maxSalary: 27000, @@ -287,7 +271,6 @@ const jobs = [ companyName: 'Business Solutions', workMode: WorkMode.remote, currency: Currency.USD, - location: 'Seattle', hasSalaryRange: true, minSalary: 38000, maxSalary: 43000, @@ -301,7 +284,6 @@ const jobs = [ companyName: 'SEO Experts', workMode: WorkMode.hybrid, currency: Currency.USD, - location: 'Denver', hasSalaryRange: true, minSalary: 15000, maxSalary: 20000, @@ -315,7 +297,6 @@ const jobs = [ companyName: 'DataPro', workMode: WorkMode.office, currency: Currency.USD, - location: 'Atlanta', hasSalaryRange: true, minSalary: 23000, maxSalary: 28000, @@ -329,7 +310,6 @@ const jobs = [ companyName: 'OpsCorp', workMode: WorkMode.remote, currency: Currency.USD, - location: 'Phoenix', hasSalaryRange: true, minSalary: 29000, maxSalary: 34000, @@ -343,7 +323,6 @@ const jobs = [ companyName: 'Customer Care Inc.', workMode: WorkMode.hybrid, currency: Currency.USD, - location: 'Orlando', hasSalaryRange: true, minSalary: 26000, maxSalary: 31000, @@ -357,7 +336,6 @@ const jobs = [ companyName: 'Product Innovators', workMode: WorkMode.office, currency: Currency.USD, - location: 'Portland', hasSalaryRange: true, minSalary: 32000, maxSalary: 37000, @@ -371,7 +349,6 @@ const jobs = [ companyName: 'Social Media Pros', workMode: WorkMode.remote, currency: Currency.USD, - location: 'Las Vegas', hasSalaryRange: true, minSalary: 18000, maxSalary: 23000, @@ -385,7 +362,6 @@ const jobs = [ companyName: 'HR Hub', workMode: WorkMode.hybrid, currency: Currency.USD, - location: 'Charlotte', hasSalaryRange: true, minSalary: 24000, maxSalary: 29000, @@ -399,7 +375,6 @@ const jobs = [ companyName: 'Supply Chain Solutions', workMode: WorkMode.office, currency: Currency.USD, - location: 'Detroit', hasSalaryRange: true, minSalary: 30000, maxSalary: 35000, @@ -413,7 +388,6 @@ const jobs = [ companyName: 'E-commerce Pros', workMode: WorkMode.remote, currency: Currency.USD, - location: 'Philadelphia', hasSalaryRange: true, minSalary: 27000, maxSalary: 32000, @@ -427,7 +401,6 @@ const jobs = [ companyName: 'Project Managers Inc.', workMode: WorkMode.hybrid, currency: Currency.USD, - location: 'Nashville', hasSalaryRange: true, minSalary: 12000, maxSalary: 17000, @@ -464,6 +437,13 @@ async function seedUsers() { } async function seedJobs() { + jobs = jobs.map((j, index) => { + return { + ...j, + location: + locationArr[index] !== undefined ? locationArr[index] : locationArr[3], + }; + }); try { await Promise.all( jobs.map(async (j) => @@ -477,6 +457,7 @@ async function seedJobs() { companyName: j.companyName, workMode: j.workMode, currency: j.currency, + //@ts-ignore location: j.location, hasSalaryRange: j.hasSalaryRange, minSalary: j.minSalary, diff --git a/src/components/job-form.tsx b/src/components/job-form.tsx index f5e6621d..6219e3e5 100644 --- a/src/components/job-form.tsx +++ b/src/components/job-form.tsx @@ -28,7 +28,8 @@ import { Textarea } from './ui/textarea'; import { Label } from './ui/label'; import { Switch } from './ui/switch'; import { useToast } from './ui/use-toast'; -import { filters } from '@/lib/constant/jobs.constant'; +import { WorkMode } from '@prisma/client'; + const PostJobForm = () => { const { toast } = useToast(); const form = useForm({ @@ -37,7 +38,7 @@ const PostJobForm = () => { title: '', description: '', companyName: '', - location: '', + location: undefined, hasSalaryRange: false, minSalary: 0, maxSalary: 0, @@ -163,9 +164,9 @@ const PostJobForm = () => { - {filters.workMode.map((item) => ( - - {item.label} + {Object.keys(WorkMode).map((item, key) => ( + + {item} ))} diff --git a/src/layouts/job-filters.tsx b/src/layouts/job-filters.tsx index f1b1772c..bce63fa1 100644 --- a/src/layouts/job-filters.tsx +++ b/src/layouts/job-filters.tsx @@ -1,11 +1,12 @@ 'use client'; -import { filters, WorkModeEnums } from '@/lib/constant/jobs.constant'; +import { filters } from '@/lib/constant/jobs.constant'; import { JobQuerySchema, JobQuerySchemaType, } from '@/lib/validators/jobs.validator'; import { zodResolver } from '@hookform/resolvers/zod'; import { useForm } from 'react-hook-form'; +import { JobLocations } from '@prisma/client'; import { Accordion, AccordionContent, @@ -26,6 +27,8 @@ import { ScrollArea } from '@/components/ui/scroll-area'; import { cn } from '@/lib/utils'; import useSetQueryParams from '@/hooks/useSetQueryParams'; import { useEffect } from 'react'; +import { WorkMode } from '@prisma/client'; +import _ from 'lodash'; const JobFilters = ({ searchParams }: { searchParams: JobQuerySchemaType }) => { const setQueryParams = useSetQueryParams(); @@ -79,38 +82,36 @@ const JobFilters = ({ searchParams }: { searchParams: JobQuerySchemaType }) => { name="workmode" render={() => ( - {filters.workMode.map((item) => ( + {Object.keys(WorkMode).map((item, index) => ( { return ( { checked ? field.onChange([ ...(field.value || []), - item.value, + item, ]) : field.onChange( field.value?.filter( - (value) => value !== item.value + (value) => value !== item ) ); }} /> - {item.label} + {_.startCase(item)} ); @@ -187,34 +188,30 @@ const JobFilters = ({ searchParams }: { searchParams: JobQuerySchemaType }) => { name="location" render={() => ( - {filters.location.map((item) => ( + {Object.keys(JobLocations).map((item, index) => ( { return ( { checked ? field.onChange([ ...(field.value || []), - item.value, + item, ]) : field.onChange( field.value?.filter( - (value) => value !== item.value + (value) => value !== item ) ); }} @@ -222,7 +219,7 @@ const JobFilters = ({ searchParams }: { searchParams: JobQuerySchemaType }) => { /> - {item.label} + {_.startCase(item.toLowerCase())} ); diff --git a/src/lib/constant/jobs.constant.ts b/src/lib/constant/jobs.constant.ts index 46c3ee29..484ffdf3 100644 --- a/src/lib/constant/jobs.constant.ts +++ b/src/lib/constant/jobs.constant.ts @@ -1,47 +1,8 @@ -export const workMode = { - remote: 'remote', - office: 'office', - hybrid: 'hybrid', -}; -export enum WorkModeEnums { - REMOTE = 'remote', - OFFICE = 'office', - HYBRID = 'hybrid', -} export enum SortByEnums { POSTEDAT_ASC = 'postedat_asc', POSTEDAT_DESC = 'postedat_desc', } export const filters = { - workMode: [ - { - id: 1, - label: 'Remote', - value: workMode.remote, - }, - { - id: 2, - label: 'Office', - value: workMode.office, - }, - { - id: 3, - label: 'Hybrid', - value: workMode.hybrid, - }, - ], - chooseCurrency: [ - { - id: 1, - label: 'INR', - value: 'INR', - }, - { - id: 2, - label: 'USD', - value: 'USD', - }, - ], salaryRange: [ { id: 1, @@ -69,18 +30,6 @@ export const filters = { value: '50000-above', }, ], - location: [ - { id: 1, label: 'Bangalore', value: 'bangalore' }, - { id: 2, label: 'New Delhi', value: 'new delhi' }, - { id: 3, label: 'Mumbai', value: 'mumbai' }, - { id: 4, label: 'Pune', value: 'pune' }, - { id: 5, label: 'Hyderabad', value: 'hyderabad' }, - { id: 6, label: 'Chennai', value: 'chennai' }, - { id: 7, label: 'Kolkata', value: 'kolkata' }, - { id: 8, label: 'Ahmedabad', value: 'ahmedabad' }, - { id: 9, label: 'Jaipur', value: 'jaipur' }, - { id: 10, label: 'Surat', value: 'surat' }, - ], }; export const jobSorting = [ diff --git a/src/lib/validators/jobs.validator.ts b/src/lib/validators/jobs.validator.ts index 26cfb5b4..85fe0c73 100644 --- a/src/lib/validators/jobs.validator.ts +++ b/src/lib/validators/jobs.validator.ts @@ -1,12 +1,14 @@ import { z } from 'zod'; -import { WorkModeEnums } from '../constant/jobs.constant'; +import { JobLocations, WorkMode } from '@prisma/client'; export const JobPostSchema = z .object({ title: z.string().min(1, 'Title is required'), description: z.string().min(1, 'Description is required'), companyName: z.string().min(1, 'Company Name is required'), - location: z.string().min(1, 'Location is required'), + location: z.nativeEnum(JobLocations, { + message: 'Location is Required', + }), hasSalaryRange: z.boolean(), minSalary: z.coerce .number({ message: 'Min salary must be a number' }) @@ -16,7 +18,7 @@ export const JobPostSchema = z .number({ message: 'Max salary must be a number' }) .nonnegative() .optional(), - workMode: z.enum(['remote', 'office', 'hybrid'], { + workMode: z.nativeEnum(WorkMode, { message: 'Work mode is required', }), }) @@ -49,16 +51,7 @@ export const JobPostSchema = z export const JobQuerySchema = z.object({ workmode: z - .union([ - z.string(), - z.array( - z.enum([ - WorkModeEnums.REMOTE, - WorkModeEnums.HYBRID, - WorkModeEnums.OFFICE, - ]) - ), - ]) + .union([z.string(), z.array(z.nativeEnum(WorkMode))]) .optional() .transform((val) => { if (typeof val === 'string') {