diff --git a/src/actions/user.profile.actions.ts b/src/actions/user.profile.actions.ts index e8921939..66d9e4d2 100644 --- a/src/actions/user.profile.actions.ts +++ b/src/actions/user.profile.actions.ts @@ -251,3 +251,69 @@ export const addUserResume = async (resume: string) => { return new ErrorHandler('Internal server error', 'DATABASE_ERROR'); } }; + +export const getUserExperience = async () => { + const auth = await getServerSession(authOptions); + + if (!auth || !auth?.user?.id) + throw new ErrorHandler('Not Authorized', 'UNAUTHORIZED'); + try { + const res = await prisma.experience.findMany({ + where: { + userId: auth.user.id, + }, + }); + return new SuccessResponse( + 'Experience SuccessFully Fetched', + 200, + res + ).serialize(); + } catch (_error) { + return new ErrorHandler('Internal server error', 'DATABASE_ERROR'); + } +}; +export const getUserProjects = async () => { + const auth = await getServerSession(authOptions); + + if (!auth || !auth?.user?.id) + throw new ErrorHandler('Not Authorized', 'UNAUTHORIZED'); + try { + const res = await prisma.project.findMany({ + where: { + userId: auth.user.id, + }, + }); + return new SuccessResponse( + 'Project SuccessFully Fetched', + 200, + res + ).serialize(); + } catch (_error) { + return new ErrorHandler('Internal server error', 'DATABASE_ERROR'); + } +}; + +export const getUserDetails = async () => { + const auth = await getServerSession(authOptions); + + if (!auth || !auth?.user?.id) + throw new ErrorHandler('Not Authorized', 'UNAUTHORIZED'); + try { + const res = await prisma.user.findFirst({ + where: { + id: auth.user.id, + }, + select: { + skills: true, + resume: true, + }, + }); + return new SuccessResponse( + 'Project SuccessFully Fetched', + 200, + res + ).serialize(); + } catch (_error) { + return new ErrorHandler('Internal server error', 'DATABASE_ERROR'); + } +}; diff --git a/src/app/create-profile/page.tsx b/src/app/create-profile/page.tsx index 0975521e..9dbc412a 100644 --- a/src/app/create-profile/page.tsx +++ b/src/app/create-profile/page.tsx @@ -6,7 +6,7 @@ import { redirect } from 'next/navigation'; export default async function Home() { const session = await getServerSession(authOptions); if (!session || session.user.role !== 'USER') redirect('/'); - if (session.user.onBoard === true) redirect('/profile'); + if (session.user.onBoard === true) redirect('/jobs'); return (

diff --git a/src/app/page.tsx b/src/app/page.tsx index a0669a33..bd78e16e 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -2,18 +2,8 @@ import Faqs from '@/components/Faqs'; import HeroSection from '@/components/hero-section'; import { JobLanding } from '@/components/job-landing'; import Testimonials from '@/components/Testimonials'; -import { authOptions } from '@/lib/authOptions'; -import { getServerSession } from 'next-auth'; -import { redirect } from 'next/navigation'; const HomePage = async () => { - const session = await getServerSession(authOptions); - { - session && - session.user.role === 'USER' && - !session.user.onBoard && - redirect('/create-profile'); - } return (
diff --git a/src/app/profile/experience/page.tsx b/src/app/profile/experience/page.tsx new file mode 100644 index 00000000..adf6b8b0 --- /dev/null +++ b/src/app/profile/experience/page.tsx @@ -0,0 +1,20 @@ +'use client'; +import { UserExperience } from '@/components/profile/UserExperience'; +import APP_PATHS from '@/config/path.config'; +import { useSession } from 'next-auth/react'; +import { useRouter } from 'next/navigation'; +import React, { useEffect } from 'react'; + +export default function AccountExperiencePage() { + const router = useRouter(); + const session = useSession(); + useEffect(() => { + if (session.status !== 'loading' && session.status === 'unauthenticated') + router.push(`${APP_PATHS.SIGNIN}?redirectTo=/profile`); + }, [session.status, router]); + return ( +
+ +
+ ); +} diff --git a/src/app/profile/layout.tsx b/src/app/profile/layout.tsx index f69e44ca..a7cba91d 100644 --- a/src/app/profile/layout.tsx +++ b/src/app/profile/layout.tsx @@ -1,4 +1,5 @@ 'use client'; +import { getUserDetails } from '@/actions/user.profile.actions'; import Sidebar from '@/components/profile/sidebar'; import APP_PATHS from '@/config/path.config'; import { useSession } from 'next-auth/react'; @@ -11,7 +12,25 @@ const ProfileLayout = ({ children }: { children: React.ReactNode }) => { useEffect(() => { if (session.status !== 'loading' && session.status === 'unauthenticated') router.push(`${APP_PATHS.SIGNIN}?redirectTo=/profile`); - }, [session.status]); + }, [session.status, router]); + useEffect(() => { + async function fetchUserDetails() { + try { + const res = await getUserDetails(); + if (res.status) { + localStorage.setItem( + 'skills', + JSON.stringify(res.additional?.skills) + ); + localStorage.setItem( + 'resume', + JSON.stringify(res.additional?.resume) + ); + } + } catch (_error) {} + } + fetchUserDetails(); + }, []); return (
diff --git a/src/app/profile/page.tsx b/src/app/profile/page.tsx index d6213f14..30509244 100644 --- a/src/app/profile/page.tsx +++ b/src/app/profile/page.tsx @@ -14,7 +14,8 @@ const ProfilePage = () => { useEffect(() => { if (session.status !== 'loading' && session.status === 'unauthenticated') router.push(`${APP_PATHS.SIGNIN}?redirectTo=/create`); - }, [session.status]); + }, [session.status, router]); + return (
diff --git a/src/app/profile/projects/page.tsx b/src/app/profile/projects/page.tsx new file mode 100644 index 00000000..e92c1775 --- /dev/null +++ b/src/app/profile/projects/page.tsx @@ -0,0 +1,20 @@ +'use client'; +import { UserProjects } from '@/components/profile/UserProject'; +import APP_PATHS from '@/config/path.config'; +import { useSession } from 'next-auth/react'; +import { useRouter } from 'next/navigation'; +import React, { useEffect } from 'react'; + +export default function AccountProjectPage() { + const router = useRouter(); + const session = useSession(); + useEffect(() => { + if (session.status !== 'loading' && session.status === 'unauthenticated') + router.push(`${APP_PATHS.SIGNIN}?redirectTo=/profile`); + }, [session.status, router]); + return ( +
+ +
+ ); +} diff --git a/src/app/profile/resume/page.tsx b/src/app/profile/resume/page.tsx new file mode 100644 index 00000000..b0639fa9 --- /dev/null +++ b/src/app/profile/resume/page.tsx @@ -0,0 +1,20 @@ +'use client'; +import { UserResume } from '@/components/profile/UserResume'; +import APP_PATHS from '@/config/path.config'; +import { useSession } from 'next-auth/react'; +import { useRouter } from 'next/navigation'; +import React, { useEffect } from 'react'; + +export default function AccountResumePage() { + const router = useRouter(); + const session = useSession(); + useEffect(() => { + if (session.status !== 'loading' && session.status === 'unauthenticated') + router.push(`${APP_PATHS.SIGNIN}?redirectTo=/profile`); + }, [session.status, router]); + return ( +
+ +
+ ); +} diff --git a/src/app/profile/settings/page.tsx b/src/app/profile/settings/page.tsx index 8ad13135..9cd66ff1 100644 --- a/src/app/profile/settings/page.tsx +++ b/src/app/profile/settings/page.tsx @@ -11,7 +11,7 @@ const AccountSettingsPage = () => { useEffect(() => { if (session.status !== 'loading' && session.status === 'unauthenticated') router.push(`${APP_PATHS.SIGNIN}?redirectTo=/profile`); - }, [session.status]); + }, [session.status, router]); return (
diff --git a/src/app/profile/skills/page.tsx b/src/app/profile/skills/page.tsx new file mode 100644 index 00000000..8a731a40 --- /dev/null +++ b/src/app/profile/skills/page.tsx @@ -0,0 +1,20 @@ +'use client'; +import { UserSkills } from '@/components/profile/UserSkills'; +import APP_PATHS from '@/config/path.config'; +import { useSession } from 'next-auth/react'; +import { useRouter } from 'next/navigation'; +import React, { useEffect } from 'react'; + +export default function AccountResumePage() { + const router = useRouter(); + const session = useSession(); + useEffect(() => { + if (session.status !== 'loading' && session.status === 'unauthenticated') + router.push(`${APP_PATHS.SIGNIN}?redirectTo=/profile`); + }, [session.status, router]); + return ( +
+ +
+ ); +} diff --git a/src/components/profile/UserExperience.tsx b/src/components/profile/UserExperience.tsx new file mode 100644 index 00000000..366ef085 --- /dev/null +++ b/src/components/profile/UserExperience.tsx @@ -0,0 +1,86 @@ +import { getUserExperience } from '@/actions/user.profile.actions'; +import { useEffect, useState } from 'react'; +import { useToast } from '../ui/use-toast'; +import { Experience } from '@prisma/client'; +import { Card, CardContent, CardHeader, CardTitle } from '../ui/card'; +import _ from 'lodash'; + +export function UserExperience() { + const { toast } = useToast(); + const [experiences, setExperiences] = useState(); + + useEffect(() => { + async function fetchExperience() { + try { + const res = await getUserExperience(); + if (!res.status) { + toast({ + title: 'Cannot Fetch Experiences! Please Try Again Later', + variant: 'destructive', + }); + } + if (res.status) { + setExperiences(res.additional); + } + } catch (_error) { + toast({ + title: 'Cannot Fetch Experiences! Please Try Again Later', + variant: 'destructive', + }); + } + } + + fetchExperience(); + }, []); + + if (!experiences) { + return null; + } + + return ( +
+ {experiences.map((item: Experience) => ( + + + + Company Name: + {item.companyName} + + + +

+ Designation: {item.designation} +

+

+ Employment Type:{' '} + {_.startCase(item.EmploymentType)} +

+

+ Work Mode: {item.workMode} +

+

+ Current Status:{' '} + {item.currentWorkStatus + ? 'Currently Employed here' + : 'Not Currently Employed here'} +

+

+ Duration:{' '} + {new Date(item.startDate).toLocaleDateString()}{' '} + {item.endDate + ? ` - ${new Date(item.endDate).toLocaleDateString()}` + : ' - Present'} +

+

+ Description: + {item.description} +

+
+
+ ))} +
+ ); +} diff --git a/src/components/profile/UserProject.tsx b/src/components/profile/UserProject.tsx new file mode 100644 index 00000000..5da4af54 --- /dev/null +++ b/src/components/profile/UserProject.tsx @@ -0,0 +1,80 @@ +import { getUserProjects } from '@/actions/user.profile.actions'; +import { useEffect, useState } from 'react'; +import { useToast } from '../ui/use-toast'; +import { Project } from '@prisma/client'; +import Link from 'next/link'; +import { + Card, + CardContent, + CardFooter, + CardHeader, + CardTitle, +} from '../ui/card'; + +export function UserProjects() { + const { toast } = useToast(); + const [projects, setProjects] = useState(); + useEffect(() => { + async function fetchProjects() { + try { + const res = await getUserProjects(); + if (!res.status) { + toast({ + title: 'Can not Fetch Projects! Please Try agian Later', + variant: 'destructive', + }); + } + if (res.status) { + setProjects(res.additional); + } + } catch (_error) { + toast({ + title: 'Can not Fetch Projects! Please Try agian Later', + variant: 'destructive', + }); + } + } + + fetchProjects(); + }, [toast]); + + if (!projects) { + return null; + } + + return ( +
+ {projects.map((item: Project) => ( + + + + {item.projectName} + + + +

{item.projectSummary}

+
+ + {item.projectLiveLink && ( + + Live Project + + )} + + GitHub Repository + + +
+ ))} +
+ ); +} diff --git a/src/components/profile/UserResume.tsx b/src/components/profile/UserResume.tsx new file mode 100644 index 00000000..914fce2e --- /dev/null +++ b/src/components/profile/UserResume.tsx @@ -0,0 +1,30 @@ +import { File } from 'lucide-react'; +import Link from 'next/link'; +import { useEffect, useState } from 'react'; + +export function UserResume() { + const [resumeLink, setResumeLink] = useState(null); + + useEffect(() => { + const storedResume = localStorage.getItem('resume'); + if (storedResume) setResumeLink(JSON.parse(storedResume)); + }, []); + + if (!resumeLink) { + return null; + } + + return ( +
+ + +

Click here

+ +
+ ); +} diff --git a/src/components/profile/UserSkills.tsx b/src/components/profile/UserSkills.tsx new file mode 100644 index 00000000..4871fbe9 --- /dev/null +++ b/src/components/profile/UserSkills.tsx @@ -0,0 +1,28 @@ +import { useEffect, useState } from 'react'; + +export function UserSkills() { + const [skills, setSkills] = useState(); + useEffect(() => { + const storedSkills = localStorage.getItem('skills'); + if (storedSkills) { + setSkills(JSON.parse(storedSkills)); + } + }, []); + + if (!skills) { + return null; + } + + return ( +
+ {skills.map((item, index) => ( +
+ {item} +
+ ))} +
+ ); +} diff --git a/src/components/skills-combobox.tsx b/src/components/skills-combobox.tsx index 3f724d5c..1a90f6ef 100644 --- a/src/components/skills-combobox.tsx +++ b/src/components/skills-combobox.tsx @@ -66,7 +66,10 @@ export function SkillsCombobox({ }, [debouncedComboboxValues]); React.useEffect(() => { - form.setValue('skills', comboBoxSelectedValues); + (form as UseFormReturn).setValue( + 'skills', + comboBoxSelectedValues + ); }, [comboBoxSelectedValues, form]); return ( <> diff --git a/src/components/user-multistep-form/user-multistep-form.tsx b/src/components/user-multistep-form/user-multistep-form.tsx index a4631ee1..0535585a 100644 --- a/src/components/user-multistep-form/user-multistep-form.tsx +++ b/src/components/user-multistep-form/user-multistep-form.tsx @@ -22,6 +22,7 @@ import { useState } from 'react'; import { useToast } from '../ui/use-toast'; import { validateUserBoarding } from '@/actions/user.profile.actions'; import { useRouter } from 'next/navigation'; +import { useSession } from 'next-auth/react'; const forms = [ { @@ -62,6 +63,7 @@ export default function VerticalLinearStepper() { setActiveStep((prevActiveStep) => prevActiveStep - 1); }; const { toast } = useToast(); + const { data: session, update } = useSession(); const handleFinish = async () => { try { @@ -76,7 +78,12 @@ export default function VerticalLinearStepper() { title: response.message, variant: 'success', }); - router.push('/jobs'); + await update({ + ...session, + user: { + onBoard: true, + }, + }); router.refresh(); } catch (_error) { toast({ diff --git a/src/config/path.config.ts b/src/config/path.config.ts index 9809adc6..60bd264b 100644 --- a/src/config/path.config.ts +++ b/src/config/path.config.ts @@ -15,5 +15,9 @@ const APP_PATHS = { PROFILE: '/profile', EDIT_PROFILE: '/profile/edit', ACCOUNT_SETTINGS: '/profile/settings', + PROJECTS: '/profile/projects', + RESUME: '/profile/resume', + EXPERIENCE: '/profile/experience', + SKILLS: '/profile/skills', }; export default APP_PATHS; diff --git a/src/lib/constant/app.constant.ts b/src/lib/constant/app.constant.ts index 9cec8870..f296e2ae 100644 --- a/src/lib/constant/app.constant.ts +++ b/src/lib/constant/app.constant.ts @@ -40,6 +40,10 @@ export const userProfileNavbar = [ { id: 1, label: 'My Account', path: APP_PATHS.PROFILE }, { id: 2, label: 'Edit Profile', path: APP_PATHS.EDIT_PROFILE }, { id: 3, label: 'Account Settings', path: APP_PATHS.ACCOUNT_SETTINGS }, + { id: 4, label: 'Experience', path: APP_PATHS.EXPERIENCE }, + { id: 5, label: 'Projects', path: APP_PATHS.PROJECTS }, + { id: 6, label: 'Skills', path: APP_PATHS.SKILLS }, + { id: 7, label: 'Resume', path: APP_PATHS.RESUME }, ]; export const socials: { href: string; diff --git a/src/middleware.tsx b/src/middleware.ts similarity index 79% rename from src/middleware.tsx rename to src/middleware.ts index d306cac6..3e578f39 100644 --- a/src/middleware.tsx +++ b/src/middleware.ts @@ -15,6 +15,13 @@ export async function middleware(req: NextRequest) { ) { return NextResponse.redirect(new URL('/', req.url)); } + if ( + pathname !== '/create-profile' && + token?.role === 'USER' && + !token.onBoard + ) { + return NextResponse.redirect(new URL('/create-profile', req.url)); + } return NextResponse.next(); }