diff --git a/prisma/migrations/20241023172043_fix/migration.sql b/prisma/migrations/20241023172043_fix/migration.sql new file mode 100644 index 00000000..9e5d3691 --- /dev/null +++ b/prisma/migrations/20241023172043_fix/migration.sql @@ -0,0 +1,35 @@ +/* + Warnings: + + - A unique constraint covering the columns `[companyId]` on the table `User` will be added. If there are existing duplicate values, this will fail. + - Added the required column `companyId` to the `Job` table without a default value. This is not possible if the table is not empty. + +*/ +-- AlterTable +ALTER TABLE "Job" ADD COLUMN "companyId" TEXT NOT NULL; + +-- AlterTable +ALTER TABLE "User" ADD COLUMN "companyId" TEXT; + +-- CreateTable +CREATE TABLE "Company" ( + "id" TEXT NOT NULL, + "name" TEXT NOT NULL, + "website" TEXT, + "description" TEXT, + "userId" TEXT, + + CONSTRAINT "Company_pkey" PRIMARY KEY ("id") +); + +-- CreateIndex +CREATE UNIQUE INDEX "Company_userId_key" ON "Company"("userId"); + +-- CreateIndex +CREATE UNIQUE INDEX "User_companyId_key" ON "User"("companyId"); + +-- AddForeignKey +ALTER TABLE "Company" ADD CONSTRAINT "Company_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE; + +-- AddForeignKey +ALTER TABLE "Job" ADD CONSTRAINT "Job_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241023180340_fix_project_mode/migration.sql b/prisma/migrations/20241023180340_fix_project_mode/migration.sql new file mode 100644 index 00000000..871668ca --- /dev/null +++ b/prisma/migrations/20241023180340_fix_project_mode/migration.sql @@ -0,0 +1,12 @@ +-- CreateEnum +CREATE TYPE "ProjectStack" AS ENUM ('GO', 'PYTHON', 'MERN', 'NEXTJS', 'AI_GPT_APIS', 'SPRINGBOOT', 'OTHERS'); + +-- DropForeignKey +ALTER TABLE "Project" DROP CONSTRAINT "Project_userId_fkey"; + +-- AlterTable +ALTER TABLE "Project" ADD COLUMN "projectThumbnail" TEXT, +ADD COLUMN "stack" "ProjectStack" NOT NULL DEFAULT 'OTHERS'; + +-- AddForeignKey +ALTER TABLE "Project" ADD CONSTRAINT "Project_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241023193014_added_logo_field/migration.sql b/prisma/migrations/20241023193014_added_logo_field/migration.sql new file mode 100644 index 00000000..258c08ef --- /dev/null +++ b/prisma/migrations/20241023193014_added_logo_field/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Company" ADD COLUMN "logo" TEXT; diff --git a/prisma/migrations/20241024174828_profileupdate/migration.sql b/prisma/migrations/20241024174828_profileupdate/migration.sql deleted file mode 100644 index 1c77885b..00000000 --- a/prisma/migrations/20241024174828_profileupdate/migration.sql +++ /dev/null @@ -1,66 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[username]` on the table `User` will be added. If there are existing duplicate values, this will fail. - - Added the required column `aboutMe` to the `User` table without a default value. This is not possible if the table is not empty. - - Added the required column `contactEmail` to the `User` table without a default value. This is not possible if the table is not empty. - - Added the required column `username` to the `User` table without a default value. This is not possible if the table is not empty. - -*/ --- CreateEnum -CREATE TYPE "ProjectStack" AS ENUM ('GO', 'PYTHON', 'MERN', 'NEXTJS', 'AI_GPT_APIS', 'SPRINGBOOT', 'OTHERS'); - --- CreateEnum -CREATE TYPE "DegreeType" AS ENUM ('BTech', 'MTech', 'BCA', 'MCA'); - --- CreateEnum -CREATE TYPE "FieldOfStudyType" AS ENUM ('AI', 'Machine_Learning', 'CS', 'Mechanical'); - --- DropForeignKey -ALTER TABLE "Experience" DROP CONSTRAINT "Experience_userId_fkey"; - --- DropForeignKey -ALTER TABLE "Project" DROP CONSTRAINT "Project_userId_fkey"; - --- AlterTable -ALTER TABLE "Job" ADD COLUMN "deletedAt" TIMESTAMP(3); - --- AlterTable -ALTER TABLE "Project" ADD COLUMN "isFeature" BOOLEAN NOT NULL DEFAULT false, -ADD COLUMN "projectThumbnail" TEXT, -ADD COLUMN "stack" "ProjectStack" NOT NULL DEFAULT 'OTHERS'; - --- AlterTable -ALTER TABLE "User" ADD COLUMN "aboutMe" TEXT NOT NULL, -ADD COLUMN "contactEmail" TEXT NOT NULL, -ADD COLUMN "discordLink" TEXT, -ADD COLUMN "githubLink" TEXT, -ADD COLUMN "linkedinLink" TEXT, -ADD COLUMN "portfolioLink" TEXT, -ADD COLUMN "twitterLink" TEXT, -ADD COLUMN "username" TEXT NOT NULL; - --- CreateTable -CREATE TABLE "Education" ( - "id" SERIAL NOT NULL, - "instituteName" TEXT NOT NULL, - "degree" "DegreeType" NOT NULL, - "fieldOfStudy" "FieldOfStudyType" NOT NULL, - "startDate" TIMESTAMP(3) NOT NULL, - "endDate" TIMESTAMP(3), - "userId" TEXT NOT NULL, - - CONSTRAINT "Education_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "User_username_key" ON "User"("username"); - --- AddForeignKey -ALTER TABLE "Experience" ADD CONSTRAINT "Experience_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Education" ADD CONSTRAINT "Education_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; - --- AddForeignKey -ALTER TABLE "Project" ADD CONSTRAINT "Project_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/migrations/20241025095014_user_updated/migration.sql b/prisma/migrations/20241025095014_user_updated/migration.sql deleted file mode 100644 index 1ab2f3cc..00000000 --- a/prisma/migrations/20241025095014_user_updated/migration.sql +++ /dev/null @@ -1,3 +0,0 @@ --- AlterTable -ALTER TABLE "User" ALTER COLUMN "aboutMe" DROP NOT NULL, -ALTER COLUMN "contactEmail" DROP NOT NULL; diff --git a/prisma/migrations/20241027195735_add_deleted_at_field/migration.sql b/prisma/migrations/20241027195735_add_deleted_at_field/migration.sql new file mode 100644 index 00000000..091492ed --- /dev/null +++ b/prisma/migrations/20241027195735_add_deleted_at_field/migration.sql @@ -0,0 +1,2 @@ +-- AlterTable +ALTER TABLE "Job" ADD COLUMN "deletedAt" TIMESTAMP(3); diff --git a/prisma/migrations/20241031043344_username_remove/migration.sql b/prisma/migrations/20241031043344_username_remove/migration.sql deleted file mode 100644 index ee88353a..00000000 --- a/prisma/migrations/20241031043344_username_remove/migration.sql +++ /dev/null @@ -1,11 +0,0 @@ -/* - Warnings: - - - You are about to drop the column `username` on the `User` table. All the data in the column will be lost. - -*/ --- DropIndex -DROP INDEX "User_username_key"; - --- AlterTable -ALTER TABLE "User" DROP COLUMN "username"; diff --git a/prisma/migrations/20241031064849_company/migration.sql b/prisma/migrations/20241031064849_company/migration.sql deleted file mode 100644 index 13bf066d..00000000 --- a/prisma/migrations/20241031064849_company/migration.sql +++ /dev/null @@ -1,26 +0,0 @@ -/* - Warnings: - - - A unique constraint covering the columns `[companyId]` on the table `User` will be added. If there are existing duplicate values, this will fail. - -*/ --- AlterTable -ALTER TABLE "User" ADD COLUMN "companyId" TEXT, -ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP; - --- CreateTable -CREATE TABLE "Company" ( - "id" TEXT NOT NULL, - "companyName" TEXT NOT NULL, - "companyLogo" TEXT, - "companyEmail" TEXT NOT NULL, - "companyBio" TEXT NOT NULL, - - CONSTRAINT "Company_pkey" PRIMARY KEY ("id") -); - --- CreateIndex -CREATE UNIQUE INDEX "User_companyId_key" ON "User"("companyId"); - --- AddForeignKey -ALTER TABLE "User" ADD CONSTRAINT "User_companyId_fkey" FOREIGN KEY ("companyId") REFERENCES "Company"("id") ON DELETE SET NULL ON UPDATE CASCADE; diff --git a/prisma/migrations/20241101174959_added_some_user_feilds/migration.sql b/prisma/migrations/20241101174959_added_some_user_feilds/migration.sql new file mode 100644 index 00000000..f6bac3e5 --- /dev/null +++ b/prisma/migrations/20241101174959_added_some_user_feilds/migration.sql @@ -0,0 +1,34 @@ +-- CreateEnum +CREATE TYPE "DegreeType" AS ENUM ('BTech', 'MTech', 'BCA', 'MCA'); + +-- CreateEnum +CREATE TYPE "FieldOfStudyType" AS ENUM ('AI', 'Machine_Learning', 'CS', 'Mechanical'); + +-- AlterTable +ALTER TABLE "Project" ADD COLUMN "isFeature" BOOLEAN NOT NULL DEFAULT false; + +-- AlterTable +ALTER TABLE "User" ADD COLUMN "about" TEXT, +ADD COLUMN "contactEmail" TEXT, +ADD COLUMN "createdAt" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP, +ADD COLUMN "discordLink" TEXT, +ADD COLUMN "githubLink" TEXT, +ADD COLUMN "linkedinLink" TEXT, +ADD COLUMN "portfolioLink" TEXT, +ADD COLUMN "twitterLink" TEXT; + +-- CreateTable +CREATE TABLE "Education" ( + "id" SERIAL NOT NULL, + "instituteName" TEXT NOT NULL, + "degree" "DegreeType" NOT NULL, + "fieldOfStudy" "FieldOfStudyType" NOT NULL, + "startDate" TIMESTAMP(3) NOT NULL, + "endDate" TIMESTAMP(3), + "userId" TEXT NOT NULL, + + CONSTRAINT "Education_pkey" PRIMARY KEY ("id") +); + +-- AddForeignKey +ALTER TABLE "Education" ADD CONSTRAINT "Education_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE CASCADE ON UPDATE CASCADE; diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 1cc0363b..859197b0 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -8,18 +8,16 @@ datasource db { } model User { - id String @id @default(cuid()) - name String - - password String? - avatar String? - isVerified Boolean @default(false) - role Role @default(USER) - jobs Job[] - + id String @id @default(cuid()) + name String email String @unique + password String? + avatar String? + isVerified Boolean @default(false) + role Role @default(USER) emailVerified DateTime? + jobs Job[] skills String[] experience Experience[] project Project[] @@ -31,31 +29,29 @@ model User { blockedByAdmin DateTime? onBoard Boolean @default(false) bookmark Bookmark[] - - githubLink String? - portfolioLink String? - linkedinLink String? - twitterLink String? - discordLink String? - - contactEmail String? - - aboutMe String? - + company Company? @relation("UserCompany") + companyId String? @unique education Education[] + resumeUpdateDate DateTime? + contactEmail String? + about String? + discordLink String? + linkedinLink String? + twitterLink String? + githubLink String? + portfolioLink String? - resumeUpdateDate DateTime? - companyId String? @unique - company Company? @relation(fields: [companyId], references: [id]) } model Company { - id String @id @default(cuid()) - companyName String - companyLogo String? - companyEmail String - companyBio String - user User? + id String @id @default(cuid()) + name String + logo String? + website String? + description String? + jobs Job[] + user User? @relation("UserCompany", fields: [userId], references: [id]) + userId String? @unique } enum OauthProvider { @@ -80,6 +76,7 @@ enum TokenType { model Job { id String @id @default(cuid()) userId String + companyId String title String description String? companyName String @map("company_name") @@ -108,8 +105,10 @@ model Job { deletedAt DateTime? postedAt DateTime @default(now()) updatedAt DateTime @updatedAt - user User @relation(fields: [userId], references: [id], onDelete: Cascade) - bookmark Bookmark[] + + user User @relation(fields: [userId], references: [id], onDelete: Cascade) + company Company @relation(fields: [companyId], references: [id], onDelete: Cascade) + bookmark Bookmark[] } model Bookmark { @@ -132,18 +131,18 @@ model Experience { endDate DateTime? description String userId String - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + user User @relation(fields: [userId], references: [id]) } model Education { - id Int @id @default(autoincrement()) - instituteName String - degree DegreeType - fieldOfStudy FieldOfStudyType - startDate DateTime - endDate DateTime? - userId String - user User @relation(fields: [userId], references: [id], onDelete: Cascade) + id Int @id @default(autoincrement()) + instituteName String + degree DegreeType + fieldOfStudy FieldOfStudyType + startDate DateTime + endDate DateTime? + userId String + user User @relation(fields: [userId], references: [id], onDelete: Cascade) } model Project { @@ -156,7 +155,7 @@ model Project { stack ProjectStack @default(OTHERS) userId String user User @relation(fields: [userId], references: [id], onDelete: Cascade) - isFeature Boolean @default(false) + isFeature Boolean @default(false) } enum ProjectStack { diff --git a/prisma/seed.ts b/prisma/seed.ts index 4d08dfc2..2897404e 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -1,4 +1,3 @@ -/* eslint-disable no-console */ import { PrismaClient, Currency, @@ -14,20 +13,10 @@ const prisma = new PrismaClient(); const users = [ { id: '1', name: 'Jack', email: 'user@gmail.com' }, { id: '2', name: 'Admin', email: 'admin@gmail.com', role: Role.ADMIN, onBoard: true }, - { id: '3', companyId: '1', name: 'Hr', email: 'hr@gmail.com', role: Role.HR, onBoard: true }, - { id: '4', companyId: '2', name: 'John', email: 'john@gmail.com', role: Role.HR, onBoard: true }, - { id: '5', companyId: '3', name: 'Jane', email: 'jane@gmail.com', role: Role.HR, onBoard: true }, + { id: '3', name: 'Hr', email: 'hr@gmail.com', role: Role.HR }, ]; - -const companies = [ - { id: '1', compnayEmail: "careers@techcorps.com", companyName: 'Tech Corp', companyBio: 'Leading tech solutions provider specializing in innovative web development.', companyLogo: '/main.svg' }, - { id: '2', companyEmail: "careers@globalsolutions.com", companyName: 'Global Solutions', companyBio: 'Global Solutions offers comprehensive IT services for businesses worldwide.', companyLogo: '/main.svg' }, - { id: '3', companyEmail: 'careers@innovatech.com', companyName: 'Innovatech', companyBio: 'Innovatech specializes in backend systems and cloud-based solutions.', companyLogo: '/main.svg' }, -] - - -let jobs = [ +const jobs = [ { id: '1', userId: '1', @@ -41,7 +30,7 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.remote, currency: Currency.USD, - hasExperiencerange: true, + hasExperienceRange: true, minExperience: 1, maxExperience: 2, companyLogo: '', @@ -67,43 +56,19 @@ let jobs = [ currency: Currency.USD, hasExpiryDate: true, expiryDate: new Date(new Date().setDate(new Date().getDate() + 49)), - hasExperiencerange: false, + hasExperienceRange: false, companyLogo: '', hasSalaryRange: false, minSalary: null, maxSalary: null, isVerifiedJob: false, }, - { - id: '3', - userId: '1', - title: 'Full Stack Developer', - description: 'Develop both client-side and server-side software.', - companyName: 'Global Solutions', - companyBio: - 'Global Solutions offers comprehensive IT services for businesses worldwide.', - companyEmail: 'recruitment@globalsolutions.com', - category: 'development', - type: EmployementType.Full_time, - workMode: WorkMode.hybrid, - currency: Currency.USD, - hasExpiryDate: false, - hasExperiencerange: true, - minExperience: 3, - maxExperience: 4, - companyLogo: '', - hasSalaryRange: true, - minSalary: 90000, - maxSalary: 120000, - isVerifiedJob: true, - deleted: true, - }, { id: '4', userId: '2', title: 'DevOps Engineer', description: - 'Automate and streamline the company’s operations and processes.', + 'Automate and streamline the company operations and processes.', companyName: 'DevOps Ltd.', companyBio: 'DevOps Ltd. specializes in automation and cloud infrastructure management.', @@ -112,7 +77,7 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.remote, currency: Currency.USD, - hasExperiencerange: true, + hasExperienceRange: true, minExperience: 1, maxExperience: 2, hasExpiryDate: true, @@ -123,30 +88,6 @@ let jobs = [ maxSalary: 70000, isVerifiedJob: true, }, - { - id: '5', - userId: '1', - title: 'Product Manager', - description: - 'Oversee product development and ensure the success of the product.', - companyName: 'Productive Minds', - companyBio: - 'Productive Minds helps businesses achieve their goals through strategic product management.', - companyEmail: 'hr@productiveminds.com', - category: 'management', - type: EmployementType.Full_time, - workMode: WorkMode.hybrid, - currency: Currency.USD, - hasExpiryDate: true, - expiryDate: new Date(new Date().setDate(new Date().getDate() + 49)), - hasExperiencerange: false, - companyLogo: '', - hasSalaryRange: true, - minSalary: 110000, - maxSalary: 150000, - isVerifiedJob: true, - deleted: true, - }, { id: '6', userId: '2', @@ -161,7 +102,7 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.office, currency: Currency.USD, - hasExperiencerange: true, + hasExperienceRange: true, minExperience: 1, maxExperience: 2, companyLogo: '', @@ -172,56 +113,6 @@ let jobs = [ maxSalary: 100000, isVerifiedJob: false, }, - { - id: '7', - userId: '1', - title: 'UX/UI Designer', - description: - 'Design user-friendly interfaces for web and mobile applications.', - companyName: 'Creative Designs', - companyBio: - 'Creative Designs excels in crafting intuitive and visually appealing user interfaces.', - companyEmail: 'careers@creativedesigns.com', - category: 'design', - type: EmployementType.Full_time, - workMode: WorkMode.remote, - currency: Currency.USD, - hasExperiencerange: true, - hasExpiryDate: false, - minExperience: 1, - maxExperience: 2, - companyLogo: '', - hasSalaryRange: true, - minSalary: 70000, - maxSalary: 90000, - isVerifiedJob: false, - delted: true, - }, - { - id: '8', - userId: '2', - title: 'Mobile App Developer', - description: 'Develop and maintain mobile applications.', - companyName: 'App Innovators', - companyBio: - 'App Innovators is a leader in mobile application development and innovation.', - companyEmail: 'careers@appinnovators.com', - category: 'development', - type: EmployementType.Full_time, - workMode: WorkMode.hybrid, - currency: Currency.USD, - hasExperiencerange: true, - hasExpiryDate: true, - expiryDate: new Date(new Date().setDate(new Date().getDate() + 49)), - minExperience: 1, - maxExperience: 2, - companyLogo: '', - hasSalaryRange: false, - minSalary: null, - maxSalary: null, - isVerifiedJob: true, - deleted: true, - }, { id: '9', userId: '1', @@ -234,7 +125,7 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.office, currency: Currency.USD, - hasExperiencerange: true, + hasExperienceRange: true, hasExpiryDate: false, minExperience: 1, maxExperience: 2, @@ -257,7 +148,7 @@ let jobs = [ type: EmployementType.Full_time, workMode: WorkMode.remote, currency: Currency.USD, - hasExperiencerange: true, + hasExperienceRange: true, minExperience: 1, maxExperience: 2, companyLogo: '', @@ -284,7 +175,7 @@ let jobs = [ companyLogo: '', hasSalaryRange: true, hasExpiryDate: false, - hasExperiencerange: false, + hasExperienceRange: false, minSalary: 25000, maxSalary: 50000, isVerifiedJob: true, @@ -304,7 +195,7 @@ let jobs = [ currency: Currency.USD, hasExpiryDate: true, expiryDate: new Date(new Date().setDate(new Date().getDate() + 49)), - hasExperiencerange: true, + hasExperienceRange: true, minExperience: 1, maxExperience: 2, companyLogo: '', @@ -312,7 +203,6 @@ let jobs = [ minSalary: null, maxSalary: null, isVerifiedJob: true, - delted: false, }, ]; @@ -331,7 +221,7 @@ async function seedUsers() { password: hashedPassword, role: u.role || Role.USER, emailVerified: new Date(), - companyId: u.companyId + onBoard: u.onBoard || false, }, }); console.log(`User created or updated: ${u.email}`); @@ -344,28 +234,6 @@ async function seedUsers() { console.error('Error seeding users:', error); } } -async function seedCompanies() { - try { - await Promise.all( - companies.map(async (c) => - prisma.company.upsert({ - where: { id: c.id }, - create: { - id: c.id, - companyName: c.companyName, - companyEmail: c.companyEmail ?? "default@example.com", - companyBio: c.companyBio, - companyLogo: c.companyLogo, - }, - update: {}, - }) - ) - ); - console.log('✅ Company seed completed successfully'); - } catch (error) { - console.error('Error seeding companies:', error); - } -} async function seedJobs() { try { @@ -376,13 +244,35 @@ async function seedJobs() { const validJobs = jobs.filter((job) => existingUserIds.has(job.userId)); + // Create companies first, one per user + const userCompanies = new Map(); + for (const job of validJobs) { + if (!userCompanies.has(job.userId)) { + const company = await prisma.company.upsert({ + where: { userId: job.userId }, + update: {}, + create: { + name: job.companyName, + description: job.companyBio, + website: faker.internet.url(), + userId: job.userId, + }, + }); + userCompanies.set(job.userId, company); + } + } + + // Then create jobs using the existing companies await Promise.all( - validJobs.map(async (j) => - prisma.job.upsert({ + validJobs.map(async (j) => { + const company = userCompanies.get(j.userId); + + return prisma.job.upsert({ where: { id: j.id }, create: { id: j.id, userId: j.userId, + companyId: company.id, title: j.title, description: j.description, companyName: j.companyName, @@ -395,7 +285,7 @@ async function seedJobs() { application: 'https://x.com/100xDevs', city: faker.location.city(), address: faker.location.city(), - hasExperiencerange: j.hasExperiencerange, + hasExperiencerange: j.hasExperienceRange, hasExpiryDate: j.hasExpiryDate, expiryDate: j.expiryDate, minExperience: j.minExperience, @@ -417,8 +307,8 @@ async function seedJobs() { isVerifiedJob: j.isVerifiedJob, }, update: {}, - }) - ) + }); + }) ); console.log('✅ Job seed completed successfully'); } catch (error) { @@ -427,9 +317,8 @@ async function seedJobs() { } async function main() { - await seedCompanies(); await seedUsers(); await seedJobs(); } -main(); +main() diff --git a/src/actions/auth.actions.ts b/src/actions/auth.actions.ts index 9db6d30b..04aad73d 100644 --- a/src/actions/auth.actions.ts +++ b/src/actions/auth.actions.ts @@ -20,31 +20,65 @@ import { cookies } from 'next/headers'; import { SuccessResponse } from '@/lib/success'; import { isTokenExpiredUtil } from '@/lib/utils'; import { TokenType } from '@prisma/client'; +type ExtendedSignupType = SignupSchemaType & { + companyInfo?: { + name: string; + website?: string; + description?: string; + }; +}; + export const signUp = withServerActionAsyncCatcher< - SignupSchemaType, + ExtendedSignupType, ServerActionReturnType >(async (_data) => { - const data = SignupSchema.parse(_data); + const baseData = SignupSchema.parse(_data); const userExist = await prisma.user.findFirst({ - where: { email: data.email }, + where: { email: baseData.email }, }); if (userExist) - throw new ErrorHandler('User with this email already exist', 'BAD_REQUEST'); + throw new ErrorHandler( + 'User with this email already exists', + 'BAD_REQUEST' + ); const hashedPassword = await bcryptjs.hash( - data.password, + baseData.password, PASSWORD_HASH_SALT_ROUNDS ); try { - await prisma.$transaction( + const result = await prisma.$transaction( async (txn) => { const user = await txn.user.create({ - data: { ...data, password: hashedPassword }, + data: { + name: baseData.name, + email: baseData.email, + password: hashedPassword, + role: baseData.role, + isVerified: false, + skills: [], + onBoard: false, + }, }); + let company = null; + // If HR role and company info provided, create company-related data + if (baseData.role === 'HR' && _data.companyInfo) { + // Create the company entry and store the result + company = await txn.company.create({ + data: { + name: _data.companyInfo.name, + website: _data.companyInfo.website || '', + description: _data.companyInfo.description || '', + logo: _data.companyInfo.logo || '', + userId: user.id, + }, + }); + } + const verificationToken = await txn.verificationToken.create({ data: { identifier: user.id, @@ -55,18 +89,19 @@ export const signUp = withServerActionAsyncCatcher< const confirmationLink = `${process.env.NEXTAUTH_URL}${APP_PATHS.VERIFY_EMAIL}/${verificationToken.token}`; await sendConfirmationEmail( - data.email, + baseData.email, confirmationLink, 'EMAIL_VERIFICATION' ); cookies().set(PENDING_EMAIL_VERIFICATION_USER_ID, user.id, { - maxAge: 5 * 60, // 5 minutes + maxAge: 5 * 60, httpOnly: true, secure: process.env.NODE_ENV === 'production', }); - return user; + // Return both user and company information + return { user, company }; }, { maxWait: 5000, @@ -76,9 +111,11 @@ export const signUp = withServerActionAsyncCatcher< return new SuccessResponse( 'User registered successfully. A verification link has been sent to your email.', - 201 + 201, + { userId: result.user.id, companyId: result.company?.id } ).serialize(); - } catch (_err) { + } catch (err) { + console.error('Signup error:', err); throw new ErrorHandler( 'Registration Failed, please try again!', 'INTERNAL_SERVER_ERROR' diff --git a/src/actions/job.action.ts b/src/actions/job.action.ts index b3b15ade..e5d1b9a2 100644 --- a/src/actions/job.action.ts +++ b/src/actions/job.action.ts @@ -69,6 +69,7 @@ export const createJob = withSession< description, hasSalaryRange, hasExperiencerange, + companyId, hasExpiryDate, expiryDate, maxSalary, @@ -82,6 +83,7 @@ export const createJob = withSession< title, description, hasExperiencerange, + companyId, minExperience, expiryDate, hasExpiryDate, diff --git a/src/actions/user.profile.actions.ts b/src/actions/user.profile.actions.ts index e4e1219b..ad8732a9 100644 --- a/src/actions/user.profile.actions.ts +++ b/src/actions/user.profile.actions.ts @@ -378,7 +378,7 @@ export const getUserDetailsWithId = async (id: string) => { contactEmail: true, resume: true, avatar: true, - aboutMe: true, + about: true, project: true, resumeUpdateDate: true, discordLink: true, @@ -420,7 +420,7 @@ export const updateUserDetails = withSession< name: data.name, email: data.email, contactEmail: data.contactEmail, - aboutMe: data.aboutMe, + about: data.aboutMe, avatar: data.avatar, discordLink: data.discordLink, linkedinLink: data.linkedinLink, @@ -456,7 +456,7 @@ export const updateAboutMe = withSession< id: session.user.id, }, data: { - aboutMe: data.aboutMe, + about: data.aboutMe, }, }); } @@ -647,8 +647,8 @@ export const getUserRecruiters = async () => { }, company: { select: { - companyName: true, - companyEmail: true, + name: true, + website: true, }, }, }, diff --git a/src/app/profile/[userId]/page.tsx b/src/app/profile/[userId]/page.tsx index 6384616f..014d6162 100644 --- a/src/app/profile/[userId]/page.tsx +++ b/src/app/profile/[userId]/page.tsx @@ -30,10 +30,7 @@ const Page = async ({ params: { userId } }: { params: { userId: string } }) => { {userDetails && ( <> - + { const recruiterList = recruiters.additional?.recruiters ?? []; - const filteredRecruiters = recruiterList.filter( - (recruiter) => - recruiter.company?.companyName - .toLowerCase() - .includes(searchTerm.toLowerCase()) || - recruiter.company?.companyEmail - .toLowerCase() - .includes(searchTerm.toLowerCase()) - ); + const filteredRecruiters = recruiterList.filter((recruiter) => { + const companyName = recruiter.company?.name?.toLowerCase() ?? ''; + const companyWebsite = recruiter.company?.website?.toLowerCase() ?? ''; + const searchTermLower = searchTerm.toLowerCase(); + + return ( + companyName.includes(searchTermLower) || + companyWebsite.includes(searchTermLower) + ); + }); const totalPages = Math.ceil(filteredRecruiters.length / itemsPerPage); @@ -112,10 +113,10 @@ const ManageRecruiters = ({ recruiters }: props) => { currentRecruiters.map((recruiter) => ( - {recruiter.company?.companyName} + {recruiter.company?.name} - {recruiter.company?.companyEmail} + {recruiter.company?.website} {recruiter._count.jobs} diff --git a/src/components/auth/signin.tsx b/src/components/auth/signin.tsx index a5bc47e0..8afdd818 100644 --- a/src/components/auth/signin.tsx +++ b/src/components/auth/signin.tsx @@ -40,10 +40,11 @@ export const Signin = () => { const response = await signIn('signin', { ...data, redirect: false }); if (!response?.ok) { const errorMessage = - response?.error?.includes('User') && response?.error?.includes('does not exist') + response?.error?.includes('User') && + response?.error?.includes('does not exist') ? 'User does not exist' : response?.error || 'Internal server error'; - + return toast({ title: errorMessage, variant: 'destructive', @@ -53,7 +54,7 @@ export const Signin = () => { title: 'Login successful! Welcome back!', variant: 'success', }); - + const searchParams = new URLSearchParams(window.location.search); const redirect = searchParams.get('next') || APP_PATHS.HOME; router.push(redirect); @@ -65,7 +66,7 @@ export const Signin = () => { }); } } - + return (
diff --git a/src/components/auth/signup.tsx b/src/components/auth/signup.tsx index 5319d393..72e8b2cc 100644 --- a/src/components/auth/signup.tsx +++ b/src/components/auth/signup.tsx @@ -1,45 +1,76 @@ 'use client'; +import { useState } from 'react'; +import { useRouter } from 'next/navigation'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; -import APP_PATHS from '@/config/path.config'; -import { - SignupSchema, - SignupSchemaType, -} from '@/lib/validators/auth.validator'; +import { Textarea } from '@/components/ui/textarea'; import { zodResolver } from '@hookform/resolvers/zod'; - -import Link from 'next/link'; -import { useRouter } from 'next/navigation'; import { useForm } from 'react-hook-form'; +import { useToast } from '../ui/use-toast'; +import { signUp } from '@/actions/auth.actions'; +import APP_PATHS from '@/config/path.config'; import { Form, - FormControl, FormField, FormItem, + FormControl, FormLabel, FormMessage, -} from '../ui/form'; -import { useToast } from '../ui/use-toast'; -import { signUp } from '@/actions/auth.actions'; -import { DemarcationLine, GoogleOauthButton } from './social-auth'; -import { PasswordInput } from '../password-input'; +} from '@/components/ui/form'; +import { Label } from '@/components/ui/label'; +import { Switch } from '@/components/ui/switch'; +import { + companyInfoSchema, + SignupSchema, + SignupData, + CompanyInfo, +} from '@/lib/validators/auth.validator'; +import ImageUpload from '../image-upload'; export const Signup = () => { const { toast } = useToast(); const router = useRouter(); + const [isHr, setIsHr] = useState(false); - const form = useForm({ + const form = useForm({ resolver: zodResolver(SignupSchema), defaultValues: { name: '', email: '', password: '', + role: 'USER', + }, + }); + + const companyForm = useForm({ + resolver: zodResolver(companyInfoSchema), + defaultValues: { + name: '', + website: '', + description: '', }, }); - async function signupHandler(data: SignupSchemaType) { + async function signupHandler(data: Omit) { try { - const response = await signUp(data); + const signupData: SignupData = { + ...data, + role: isHr ? 'HR' : 'USER', + }; + + if (isHr) { + const companyData = companyForm.getValues(); + if (companyData.name) { + signupData.companyInfo = { + name: companyData.name, + website: companyData.website, + description: companyData.description, + }; + } + } + + const response = await signUp(signupData); + if (!response.status) { toast({ title: response.message || 'Something went wrong', @@ -50,12 +81,11 @@ export const Signup = () => { title: response.message || 'Signup successful! Welcome to 100xJobs!', variant: 'success', }); - - router.push(APP_PATHS.WELCOME); + router.push(APP_PATHS.HOME); } - } catch { + } catch (error) { toast({ - title: 'something went wrong', + title: `something went wrong ${error}`, variant: 'destructive', }); } @@ -101,20 +131,69 @@ export const Signup = () => { Password - + )} /> -
- - Forgot your password? - +
+ setIsHr(checked)} + /> +
+ + {isHr && ( +
+ ( + + Company Name + + + + + + )} + /> + ( + + Company Website + + + + + + )} + /> + ( + + Company Description + +