From 139175f9c78c4b953a47c09ccf2f8b565cc94ab0 Mon Sep 17 00:00:00 2001 From: Paribesh Nepal <100255987+Paribesh01@users.noreply.github.com> Date: Thu, 31 Oct 2024 12:08:44 +0530 Subject: [PATCH] Feat/admin UI (#553) * fix: deleted jobs were showing * feat: jobs management page done * feat: added new ui for the payment and recruiters * fix: minor changes * fix:fixed responsiveness * fix:minor fix * feat:removed payment * fix:removed payments * feat:added filters * feat:added recruiters * fix:minor change * fix:minor changes * fix:filter fixed * fix: fixed responsiveness * fix:minor change --- prisma/schema.prisma | 19 +- prisma/seed.ts | 38 ++- src/actions/job.action.ts | 6 + src/actions/user.profile.actions.ts | 36 +++ src/app/manage/{ => jobs}/page.tsx | 6 +- src/app/manage/recruiters/page.tsx | 22 ++ src/components/DeleteDialog.tsx | 10 +- src/components/JobManagement.tsx | 7 +- src/components/JobManagementTable.tsx | 282 +++++++++++++++------- src/components/ManageRecruiters.tsx | 181 ++++++++++++++ src/components/ToggleApproveJobButton.tsx | 10 +- src/components/pagination-client.tsx | 4 +- src/components/ui/pagination.tsx | 2 - src/components/ui/paginator.tsx | 32 ++- src/config/path.config.ts | 3 +- src/lib/constant/app.constant.ts | 11 +- src/lib/icons.ts | 4 + src/types/jobs.types.ts | 1 + src/types/recruiters.types.ts | 17 ++ 19 files changed, 561 insertions(+), 130 deletions(-) rename src/app/manage/{ => jobs}/page.tsx (87%) create mode 100644 src/app/manage/recruiters/page.tsx create mode 100644 src/components/ManageRecruiters.tsx create mode 100644 src/types/recruiters.types.ts diff --git a/prisma/schema.prisma b/prisma/schema.prisma index d7350ec2..9a420bdb 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -25,12 +25,23 @@ model User { project Project[] resume String? - oauthProvider OauthProvider? // Tracks OAuth provider (e.g., 'google') - oauthId String? - + oauthProvider OauthProvider? // Tracks OAuth provider (e.g., 'google') + oauthId String? + createdAt DateTime @default(now()) blockedByAdmin DateTime? - onBoard Boolean @default(false) + onBoard Boolean @default(false) bookmark Bookmark[] + 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? } enum OauthProvider { diff --git a/prisma/seed.ts b/prisma/seed.ts index 5aa0b4a8..e2ba7cdb 100644 --- a/prisma/seed.ts +++ b/prisma/seed.ts @@ -14,9 +14,21 @@ 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', name: 'Hr', email: 'hr@gmail.com', role: Role.HR }, + { 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 }, + + ]; + +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 = [ { id: '1', @@ -328,6 +340,7 @@ async function seedUsers() { password: hashedPassword, role: u.role || Role.USER, emailVerified: new Date(), + companyId: u.companyId }, }); console.log(`User created or updated: ${u.email}`); @@ -340,6 +353,28 @@ 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 { @@ -401,6 +436,7 @@ async function seedJobs() { } async function main() { + await seedCompanies(); await seedUsers(); await seedJobs(); } diff --git a/src/actions/job.action.ts b/src/actions/job.action.ts index 659edb3e..b3b15ade 100644 --- a/src/actions/job.action.ts +++ b/src/actions/job.action.ts @@ -156,6 +156,7 @@ export const getAllJobs = withSession< skills: true, address: true, workMode: true, + expired: true, category: true, minSalary: true, maxSalary: true, @@ -219,6 +220,7 @@ export const getRecommendedJobs = withServerActionAsyncCatcher< maxSalary: true, postedAt: true, skills: true, + expired: true, isVerifiedJob: true, companyLogo: true, }, @@ -252,6 +254,7 @@ export const getRecommendedJobs = withServerActionAsyncCatcher< companyLogo: true, minExperience: true, maxExperience: true, + expired: true, isVerifiedJob: true, category: true, }, @@ -294,6 +297,7 @@ export const getJobById = withServerActionAsyncCatcher< minExperience: true, maxExperience: true, skills: true, + expired: true, address: true, workMode: true, hasSalaryRange: true, @@ -352,6 +356,7 @@ export const getRecentJobs = async () => { minExperience: true, maxExperience: true, skills: true, + expired: true, postedAt: true, companyLogo: true, type: true, @@ -601,6 +606,7 @@ export async function GetBookmarkByUserId() { minSalary: true, maxSalary: true, postedAt: true, + expired: true, companyLogo: true, }, }, diff --git a/src/actions/user.profile.actions.ts b/src/actions/user.profile.actions.ts index 51128cda..d68e0857 100644 --- a/src/actions/user.profile.actions.ts +++ b/src/actions/user.profile.actions.ts @@ -317,3 +317,39 @@ export const getUserDetails = async () => { return new ErrorHandler('Internal server error', 'DATABASE_ERROR'); } }; + +export const getUserRecruiters = async () => { + const auth = await getServerSession(authOptions); + + if (!auth || !auth?.user?.id || auth?.user?.role !== 'ADMIN') + throw new ErrorHandler('Not Authorized', 'UNAUTHORIZED'); + try { + const res = await prisma.user.findMany({ + where: { + role: 'HR', + }, + select: { + id: true, + email: true, + name: true, + createdAt: true, + _count: { + select: { + jobs: true, + }, + }, + company: { + select: { + companyName: true, + companyEmail: true, + }, + }, + }, + }); + return new SuccessResponse('Recruiter SuccessFully Fetched', 200, { + recruiters: res, + }).serialize(); + } catch (_error) { + return new ErrorHandler('Internal server error', 'DATABASE_ERROR'); + } +}; diff --git a/src/app/manage/page.tsx b/src/app/manage/jobs/page.tsx similarity index 87% rename from src/app/manage/page.tsx rename to src/app/manage/jobs/page.tsx index afcddb1d..65d3819f 100644 --- a/src/app/manage/page.tsx +++ b/src/app/manage/jobs/page.tsx @@ -25,11 +25,7 @@ const ManageJob = async ({ redirect('/jobs'); } const searchParamss = parsedData.data; - return ( -
- -
- ); + return ; }; export default ManageJob; diff --git a/src/app/manage/recruiters/page.tsx b/src/app/manage/recruiters/page.tsx new file mode 100644 index 00000000..160cefbf --- /dev/null +++ b/src/app/manage/recruiters/page.tsx @@ -0,0 +1,22 @@ +import { getUserRecruiters } from '@/actions/user.profile.actions'; +import ManageRecruiters from '@/components/ManageRecruiters'; + +import { options } from '@/lib/auth'; +import { getServerSession } from 'next-auth'; +import { redirect } from 'next/navigation'; +import React from 'react'; + +const RecruitersPage = async () => { + const server = await getServerSession(options); + if (!server?.user) { + redirect('/api/auth/signin'); + } else if (server.user.role !== 'ADMIN') { + redirect('/jobs'); + } + + const Recruiters = await getUserRecruiters(); + + return ; +}; + +export default RecruitersPage; diff --git a/src/components/DeleteDialog.tsx b/src/components/DeleteDialog.tsx index 873eb442..ff5713b6 100644 --- a/src/components/DeleteDialog.tsx +++ b/src/components/DeleteDialog.tsx @@ -4,6 +4,7 @@ import { Button } from './ui/button'; import { useToast } from './ui/use-toast'; import { toggleDeleteJobById } from '@/actions/job.action'; import { JobType } from '@/types/jobs.types'; +import icons from '@/lib/icons'; import { Dialog, DialogTrigger, @@ -13,7 +14,6 @@ import { DialogDescription, DialogFooter, } from './ui/dialog'; -import { ArchiveRestore, Trash } from 'lucide-react'; const JobDialog = ({ job }: { job: JobType }) => { const [dialogOpen, setDialogOpen] = useState(false); // State to manage dialog visibility @@ -42,7 +42,9 @@ const JobDialog = ({ job }: { job: JobType }) => { role="button" onClick={() => setDialogOpen(true)} > - {/* Icon for restoring the job */} + ) : ( { role="button" onClick={() => setDialogOpen(true)} > - {/* Icon for deleting the job */} + )} diff --git a/src/components/JobManagement.tsx b/src/components/JobManagement.tsx index a0e6918d..53aaa29d 100644 --- a/src/components/JobManagement.tsx +++ b/src/components/JobManagement.tsx @@ -1,6 +1,5 @@ import React from 'react'; import { getAllJobs } from '@/actions/job.action'; -import JobManagementHeader from './JobManagementHeader'; import JobManagementTable from './JobManagementTable'; import { JobQuerySchemaType } from '@/lib/validators/jobs.validator'; @@ -13,10 +12,10 @@ const JobManagement = async ({ if (!jobs.status) { return
Error {jobs.message}
; } + return ( -
- - +
+
); }; diff --git a/src/components/JobManagementTable.tsx b/src/components/JobManagementTable.tsx index 564a4035..e92a69a4 100644 --- a/src/components/JobManagementTable.tsx +++ b/src/components/JobManagementTable.tsx @@ -1,3 +1,4 @@ +'use client'; import { Table, TableBody, @@ -6,107 +7,226 @@ import { TableHeader, TableRow, } from './ui/table'; -import { Edit } from 'lucide-react'; -import { Badge } from '@/components/ui/badge'; +import { Plus, Search } from 'lucide-react'; -import { getAllJobsAdditonalType } from '@/types/jobs.types'; -import { ServerActionReturnType } from '@/types/api.types'; +import { JobType, getAllJobsAdditonalType } from '@/types/jobs.types'; import { JobQuerySchemaType } from '@/lib/validators/jobs.validator'; import { DEFAULT_PAGE, JOBS_PER_PAGE } from '@/config/app.config'; -import { Pagination, PaginationContent, PaginationItem } from './ui/pagination'; -import { - PaginationNextButton, - PaginationPreviousButton, -} from './pagination-client'; -import APP_PATHS from '@/config/path.config'; -import { PaginationPages } from './ui/paginator'; + import DeleteDialog from './DeleteDialog'; import ToggleApproveJobButton from './ToggleApproveJobButton'; +import { Input } from './ui/input'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from './ui/select'; +import { Button } from './ui/button'; +import Link from 'next/link'; +import { useState, useEffect } from 'react'; +import { + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from './ui/pagination'; type props = { searchParams: JobQuerySchemaType; - jobs: ServerActionReturnType; + jobs: getAllJobsAdditonalType | undefined; }; const JobManagementTable = ({ jobs, searchParams }: props) => { - if (!jobs.status) { - return
Error {jobs.message}
; - } + const [filteredJobs, setFilteredJobs] = useState([]); + const [statusFilter, setStatusFilter] = useState('All'); + const [orderFilter, setOrderFilter] = useState('latest'); + const [currentPage, setCurrentPage] = useState( + searchParams.page || DEFAULT_PAGE + ); + const [searchTerm, setSearchTerm] = useState(''); + + useEffect(() => { + if (jobs?.jobs) { + const filtered = jobs?.jobs.filter((job) => { + if (statusFilter === 'All') return true; + if (statusFilter === 'active') + return !job.deleted && !job.expired && job.isVerifiedJob; + if (statusFilter === 'deleted') return job.deleted; + if (statusFilter === 'closed') + return (job.expired || !job.isVerifiedJob) && !job.deleted; + return true; + }); + + const searched = filtered.filter((job) => { + const lowerCaseTitle = job.title.toLowerCase(); + const lowerCaseCompanyName = job.companyName.toLowerCase(); + return ( + lowerCaseTitle.includes(searchTerm.toLowerCase()) || + lowerCaseCompanyName.includes(searchTerm.toLowerCase()) + ); + }); + + const sorted = searched.sort((a, b) => a.title.localeCompare(b.title)); + + if (orderFilter === 'latest') { + sorted.sort( + (a, b) => + new Date(b.postedAt).getTime() - new Date(a.postedAt).getTime() + ); + } else { + sorted.sort( + (a, b) => + new Date(a.postedAt).getTime() - new Date(b.postedAt).getTime() + ); + } + + setFilteredJobs(sorted); + } + }, [jobs?.jobs, statusFilter, orderFilter, searchTerm]); const totalPages = - Math.ceil((jobs.additional?.totalJobs || 0) / JOBS_PER_PAGE) || - DEFAULT_PAGE; - const currentPage = searchParams.page || DEFAULT_PAGE; + Math.ceil(filteredJobs.length / JOBS_PER_PAGE) || DEFAULT_PAGE; + const startIndex = (currentPage - 1) * JOBS_PER_PAGE; + const currentJobs = filteredJobs.slice( + startIndex, + startIndex + JOBS_PER_PAGE + ); + + const handlePageChange = (pageNumber: number) => { + setCurrentPage(Math.min(Math.max(pageNumber, 1), totalPages)); + }; + return ( - <> -
- - - - Job Title - JobType - Location - isVerified - Actions - - - - {jobs.additional?.jobs?.map((job) => ( - - {job?.title} - {job?.workMode} - {job?.city} - - {job.deleted ? ( - Deleted - ) : ( +
+
+

Manage Jobs

+ + + +
+ +
+
+ + setSearchTerm(e.target.value)} + /> +
+ +
+ + + +
+
+ +
+
+
+ + + Job Title + Company Name + Job Category + Job Type + Posted Date + Status + Verified + Action + + + + {currentJobs.map((job) => ( + + {job.title} + {job.companyName} + {job.category} + {job.type} + + {new Date(job.postedAt).toLocaleDateString()} + + + + {job.deleted + ? 'Deleted' + : job.expired || !job.isVerifiedJob + ? 'Closed' + : 'Active'} + + + - )} - - - - - - + + - - - - ))} - -
- - - {totalPages ? ( - - + + + ))} + + +
+
+ +
+
+ + + handlePageChange(currentPage - 1)}> + - ) : null} - - {totalPages ? ( - - + {[...Array(totalPages)].map((_, index) => ( + handlePageChange(index + 1)} + > + {index + 1} + + ))} + handlePageChange(currentPage + 1)}> + - ) : null} - - + + +
- + ); }; diff --git a/src/components/ManageRecruiters.tsx b/src/components/ManageRecruiters.tsx new file mode 100644 index 00000000..7bf6251b --- /dev/null +++ b/src/components/ManageRecruiters.tsx @@ -0,0 +1,181 @@ +'use client'; +import React, { useState } from 'react'; +import { + Table, + TableBody, + TableCell, + TableHead, + TableHeader, + TableRow, +} from './ui/table'; +import { Search, Trash2 } from 'lucide-react'; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from './ui/select'; +import { Input } from './ui/input'; +import { getAllRecruiters } from '@/types/recruiters.types'; +import { Button } from './ui/button'; +import { + Pagination, + PaginationContent, + PaginationItem, + PaginationLink, + PaginationNext, + PaginationPrevious, +} from './ui/pagination'; +import { ServerActionReturnType } from '@/types/api.types'; + +type props = { + recruiters: ServerActionReturnType; +}; + +const ManageRecruiters = ({ recruiters }: props) => { + const [currentPage, setCurrentPage] = useState(1); + const [searchTerm, setSearchTerm] = useState(''); + const itemsPerPage = 10; + + if (!recruiters.status) { + return
Error {recruiters.message}
; + } + + 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 totalPages = Math.ceil(filteredRecruiters.length / itemsPerPage); + + const currentRecruiters = filteredRecruiters.slice( + (currentPage - 1) * itemsPerPage, + currentPage * itemsPerPage + ); + + const handlePageChange = (page: number) => { + if (page > 0 && page <= totalPages) { + setCurrentPage(page); + } + }; + return ( +
+
+

Manage Recruiters

+
+ +
+
+ + setSearchTerm(e.target.value)} + /> +
+
+ +
+
+ +
+
+ + + + Company Name + Company Email + Jobs Posted + Created At + Action + + + + {currentRecruiters.length ? ( + currentRecruiters.map((recruiter) => ( + + + {recruiter.company?.companyName} + + + {recruiter.company?.companyEmail} + + + {recruiter._count.jobs} + + + {new Date(recruiter.createdAt).toLocaleDateString()} + + + + + + )) + ) : ( + + + No Recruiters + + + )} + +
+
+
+ +
+ + + + handlePageChange(currentPage - 1)} + className="border hover:border-blue-600 dark:bg-slate-400 dark:bg-opacity-5 dark:text-white text-black bg-slate-600 bg-opacity-15" + /> + + {[...Array(totalPages)].map((_, index) => ( + + handlePageChange(index + 1)} + className={`border hover:border-blue-600 dark:bg-slate-400 dark:bg-opacity-5 ${ + currentPage === index + 1 + ? 'bg-blue-600 text-white' + : 'text-black dark:text-white' + }`} + > + {index + 1} + + + ))} + + handlePageChange(currentPage + 1)} + className="border hover:border-blue-600 dark:bg-slate-400 dark:bg-opacity-5 dark:text-white text-black bg-slate-600 bg-opacity-15" + /> + + + +
+
+ ); +}; + +export default ManageRecruiters; diff --git a/src/components/ToggleApproveJobButton.tsx b/src/components/ToggleApproveJobButton.tsx index 5f964b78..d12ef494 100644 --- a/src/components/ToggleApproveJobButton.tsx +++ b/src/components/ToggleApproveJobButton.tsx @@ -14,7 +14,7 @@ import { Button } from './ui/button'; import { useToast } from './ui/use-toast'; import { toggleApproveJob } from '@/actions/job.action'; import { JobType } from '@/types/jobs.types'; -import { Badge } from './ui/badge'; +import { Switch } from './ui/switch'; const ToggleApproveJobButton = ({ job }: { job: JobType }) => { const { toast } = useToast(); @@ -41,13 +41,7 @@ const ToggleApproveJobButton = ({ job }: { job: JobType }) => { return ( - - {isApproved ? 'Approved' : 'Unapproved'} - + diff --git a/src/components/pagination-client.tsx b/src/components/pagination-client.tsx index 69d0e0d1..26881fa9 100644 --- a/src/components/pagination-client.tsx +++ b/src/components/pagination-client.tsx @@ -19,9 +19,9 @@ const PaginationPreviousButton = ({ page: (currentPage - PAGE_INCREMENT).toString(), }) } + className=" border dark:bg-slate-400 dark:bg-opacity-5 dark:text-white text-black bg-slate-600 bg-opacity-15 " aria-disabled={currentPage - PAGE_INCREMENT < PAGE_INCREMENT} role="button" - className="aria-disabled:pointer-events-none aria-disabled:text-gray-400 dark:bg-neutral-900 rounded-full bg-neutral-100" /> ); }; @@ -43,8 +43,8 @@ const PaginationNextButton = ({ page: (currentPage + PAGE_INCREMENT).toString(), }) } + className=" border dark:bg-slate-400 dark:bg-opacity-5 dark:text-white text-black bg-slate-600 bg-opacity-15" aria-disabled={currentPage > totalPages - PAGE_INCREMENT} - className="aria-disabled:pointer-events-none aria-disabled:text-gray-400 dark:bg-neutral-900 rounded-full bg-neutral-100" /> ); }; diff --git a/src/components/ui/pagination.tsx b/src/components/ui/pagination.tsx index b39745d0..2031e564 100644 --- a/src/components/ui/pagination.tsx +++ b/src/components/ui/pagination.tsx @@ -71,7 +71,6 @@ const PaginationPrevious = ({ {...props} > - Previous ); PaginationPrevious.displayName = 'PaginationPrevious'; @@ -86,7 +85,6 @@ const PaginationNext = ({ className={cn('gap-1 pr-2.5', className)} {...props} > - Next ); diff --git a/src/components/ui/paginator.tsx b/src/components/ui/paginator.tsx index f780318e..ffac24a7 100644 --- a/src/components/ui/paginator.tsx +++ b/src/components/ui/paginator.tsx @@ -34,10 +34,12 @@ export const PaginationPages = ({ paginationHandler(i)} role="button" - className={cn('rounded-full dark:bg-neutral-900 bg-neutral-100', { - 'bg-neutral-900 text-white dark:bg-neutral-100 dark:text-black ': - i === currentPage, - })} + className={cn( + ' border dark:bg-slate-400 dark:bg-opacity-5 dark:text-white', + { + ' text-black bg-slate-600 bg-opacity-15 ': i === currentPage, + } + )} > {i} @@ -52,10 +54,12 @@ export const PaginationPages = ({ onClick={() => paginationHandler(i)} // isActive={i === currentPage} role="button" - className={cn('rounded-full dark:bg-neutral-900 bg-neutral-100', { - 'bg-neutral-900 text-white dark:bg-neutral-100 dark:text-black ': - i === currentPage, - })} + className={cn( + ' border dark:bg-slate-400 dark:bg-opacity-5 dark:text-white', + { + ' text-black bg-slate-600 bg-opacity-15 ': i === currentPage, + } + )} > {i} @@ -77,11 +81,13 @@ export const PaginationPages = ({ paginationHandler(i)} - // isActive={i === currentPage} - className={cn('rounded-full dark:bg-neutral-900 bg-neutral-100', { - 'bg-neutral-900 text-white dark:bg-neutral-100 dark:text-black ': - i === currentPage, - })} + isActive={i === currentPage} + className={cn( + ' border dark:bg-slate-400 dark:bg-opacity-5 dark:text-white', + { + ' text-black bg-slate-600 bg-opacity-15 ': i === currentPage, + } + )} > {i} diff --git a/src/config/path.config.ts b/src/config/path.config.ts index c8efde2a..31ebd971 100644 --- a/src/config/path.config.ts +++ b/src/config/path.config.ts @@ -5,7 +5,8 @@ const APP_PATHS = { SIGNUP: '/signup', RESET_PASSWORD: '/reset-password', JOBS: '/jobs', - MANAGE_JOBS: '/manage', + MANAGE_RECRUITERS: '/manage/recruiters', + MANAGE_JOBS: '/manage/jobs', CONTACT_US: 'mailto:vineetagarwal.now@gmail.com', TESTIMONIALS: '#testimonials', FAQS: '#faq', diff --git a/src/lib/constant/app.constant.ts b/src/lib/constant/app.constant.ts index f854f9a5..8c500b92 100644 --- a/src/lib/constant/app.constant.ts +++ b/src/lib/constant/app.constant.ts @@ -20,19 +20,18 @@ export const userNavbar = [ { id: 2, label: 'Contact us', path: APP_PATHS.CONTACT_US }, ]; export const adminNavbar = [ - { id: 1, label: 'Explore jobs', path: APP_PATHS.JOBS }, { - id: 2, + id: 1, label: 'Manage Jobs', path: APP_PATHS.MANAGE_JOBS, roleRequired: ['ADMIN', 'HR'], icon: PackageSearch, }, { - id: 3, - label: 'Post a job', - path: APP_PATHS.POST_JOB, - roleRequired: ['ADMIN', 'HR'], + id: 2, + label: 'Manage Recruiters', + path: APP_PATHS.MANAGE_RECRUITERS, + roleRequired: ['ADMIN'], icon: PackageSearch, }, ]; diff --git a/src/lib/icons.ts b/src/lib/icons.ts index 8cb54c36..d14aebf5 100644 --- a/src/lib/icons.ts +++ b/src/lib/icons.ts @@ -13,6 +13,7 @@ import { Menu, Sparkles, Copyright, + ArchiveRestore, Sun, Moon, type Icon as LucideIconType, @@ -23,6 +24,7 @@ import { DollarSign, BookText, User, + Trash2, LogOut, SlidersHorizontal, AlertCircle, @@ -41,6 +43,8 @@ const icons = { loading: FaSpinner, copyright: Copyright, sun: Sun, + ArchiveRestore: ArchiveRestore, + trash: Trash2, moon: Moon, check: Check, 'chevron-right': ChevronRight, diff --git a/src/types/jobs.types.ts b/src/types/jobs.types.ts index 265f2ae8..ae34c5e0 100644 --- a/src/types/jobs.types.ts +++ b/src/types/jobs.types.ts @@ -13,6 +13,7 @@ export type JobType = { skills: string[]; id: string; title: string; + expired: Boolean; description: string | null; companyName: string; postedAt: Date; diff --git a/src/types/recruiters.types.ts b/src/types/recruiters.types.ts new file mode 100644 index 00000000..a4152150 --- /dev/null +++ b/src/types/recruiters.types.ts @@ -0,0 +1,17 @@ +export type RecruitersTypes = { + id: string; + email: string; + name: string; + createdAt: Date; + _count: { + jobs: number; + }; + company: { + companyName: string; + companyEmail: string; + } | null; +}; + +export type getAllRecruiters = { + recruiters: RecruitersTypes[]; +};