Skip to content

Commit

Permalink
multistep-form
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashutoshpadhi629 committed Oct 11, 2024
1 parent e233092 commit f500dfd
Show file tree
Hide file tree
Showing 16 changed files with 955 additions and 4 deletions.
4 changes: 4 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,12 @@
"dependencies": {
"@aws-sdk/client-s3": "^3.645.0",
"@aws-sdk/s3-request-presigner": "^3.645.0",
"@emotion/react": "^11.13.3",
"@emotion/styled": "^11.13.0",
"@faker-js/faker": "^9.0.0",
"@fontsource/roboto": "^5.1.0",
"@hookform/resolvers": "^3.9.0",
"@mui/material": "^6.1.3",
"@prisma/client": "5.18.0",
"@radix-ui/react-accordion": "^1.2.0",
"@radix-ui/react-avatar": "^1.1.0",
Expand Down
32 changes: 31 additions & 1 deletion prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,16 @@ model User {
email String @unique
emailVerified DateTime?
skills String[]
experience Experience[]
project Project[]
resume String?
oauthProvider OauthProvider? // Tracks OAuth provider (e.g., 'google')
oauthId String?
blockedByAdmin DateTime?
onBoard Boolean @default(false)
}

enum OauthProvider {
Expand Down Expand Up @@ -75,6 +80,31 @@ model Job {
user User @relation(fields: [userId], references: [id])
}

model Experience {
id Int @id @default(autoincrement())
companyName String
designation String
EmploymentType EmployementType
address String
workMode WorkMode
currentWorkStatus Boolean
startDate DateTime
endDate DateTime?
description String
userId String
user User @relation(fields: [userId] ,references: [id])
}

model Project {
id Int @id @default(autoincrement())
projectName String
projectSummary String
projectLiveLink String?
projectGithub String
userId String
user User @relation(fields: [userId] , references: [id])
}

enum Currency {
INR
USD
Expand Down
2 changes: 1 addition & 1 deletion prisma/seed.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const prisma = new PrismaClient();

const users = [
{ id: '1', name: 'Jack', email: '[email protected]' },
{ id: '2', name: 'Admin', email: '[email protected]', role: Role.ADMIN },
{ id: '2', name: 'Admin', email: '[email protected]', role: Role.ADMIN , onBoard : true },
{ id: '3', name: 'Hr', email: '[email protected]', role: Role.HR },
];

Expand Down
108 changes: 107 additions & 1 deletion src/actions/user.profile.actions.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,18 @@
'use server';
import prisma from '@/config/prisma.config';
import { UserProfileSchemaType } from '@/lib/validators/user.profile.validator';
import {
addSkillsSchemaType,
expFormSchemaType,
projectSchemaType,
UserProfileSchemaType,
} from '@/lib/validators/user.profile.validator';
import bcryptjs from 'bcryptjs';
import { authOptions } from '@/lib/authOptions';
import { getServerSession } from 'next-auth';
import { ErrorHandler } from '@/lib/error';
import { withServerActionAsyncCatcher } from '@/lib/async-catch';
import { ServerActionReturnType } from '@/types/api.types';
import { SuccessResponse } from '@/lib/success';

export const updateUser = async (
email: string,
Expand Down Expand Up @@ -125,3 +136,98 @@ export const deleteUser = async (email: string) => {
return { error: error };
}
};

export const addUserSkills = withServerActionAsyncCatcher<
addSkillsSchemaType,
ServerActionReturnType
>(async (data) => {
const auth = await getServerSession(authOptions);

if (!auth || !auth?.user?.id)
throw new ErrorHandler('Not Authorized', 'UNAUTHORIZED');

try {
await prisma.user.update({
where: {
id: auth.user.id,
},
data: {
skills: data.skills,
},
});
return new SuccessResponse('Skills updated successfully', 200).serialize();
} catch (_error) {
return new ErrorHandler('Internal server error', 'DATABASE_ERROR');
}
});

export const addUserExperience = withServerActionAsyncCatcher<
expFormSchemaType,
ServerActionReturnType
>(async (data) => {
const auth = await getServerSession(authOptions);

if (!auth || !auth?.user?.id)
throw new ErrorHandler('Not Authorized', 'UNAUTHORIZED');

try {
await prisma.experience.create({
data: {
...data,
userId: auth.user.id,
},
});
return new SuccessResponse(
'Experience updated successfully',
200
).serialize();
} catch (_error) {
return new ErrorHandler('Internal server error', 'DATABASE_ERROR');
}
});

export const addUserProjects = withServerActionAsyncCatcher<
projectSchemaType,
ServerActionReturnType
>(async (data) => {
const auth = await getServerSession(authOptions);

if (!auth || !auth?.user?.id)
throw new ErrorHandler('Not Authorized', 'UNAUTHORIZED');

try {
await prisma.project.create({
data: {
...data,
userId: auth.user.id,
},
});
return new SuccessResponse('Project updated successfully', 200).serialize();
} catch (_error) {
return new ErrorHandler('Internal server error', 'DATABASE_ERROR');
}
});

export const validateUserBoarding = async () => {
const auth = await getServerSession(authOptions);

if (!auth || !auth?.user?.id)
throw new ErrorHandler('Not Authorized', 'UNAUTHORIZED');

try {
await prisma.user.update({
where: {
id: auth.user.id,
},
data: {
onBoard: true,
},
});
return new SuccessResponse(
'Welcome!! On Boarding successfully',
200
).serialize();
} catch (_error) {
return new ErrorHandler('Internal server error', 'DATABASE_ERROR');
}
};
18 changes: 18 additions & 0 deletions src/app/create-profile/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
import VerticalLinearStepper from '@/components/user-multistep-form/user-multistep-form';
import { authOptions } from '@/lib/authOptions';
import { getServerSession } from 'next-auth';
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');
return (
<div className="flex flex-col justify-center place-items-center">
<h1 className="text-xl font-bold md:text-3xl md:font-extrabold m-auto">
Hey {session?.user.name} let&apos;s get you set up and started!
</h1>
<VerticalLinearStepper />
</div>
);
}
10 changes: 10 additions & 0 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,18 @@ 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 (
<div>
<HeroSection />
Expand Down
1 change: 1 addition & 0 deletions src/components/auth/signin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export const Signin = () => {
const searchParams = new URLSearchParams(window.location.search);
const redirect = searchParams.get('next') || APP_PATHS.HOME;
router.push(redirect);
router.refresh();
} catch (_error) {
return toast({
title: 'Internal server error',
Expand Down
3 changes: 2 additions & 1 deletion src/components/skills-combobox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@ import { UseFormReturn } from 'react-hook-form';
import { JobPostSchemaType } from '@/lib/validators/jobs.validator';
import _ from 'lodash';
import { X } from 'lucide-react';
import { addSkillsSchemaType } from '@/lib/validators/user.profile.validator';

export function SkillsCombobox({
form,
comboBoxSelectedValues,
setComboBoxSelectedValues,
}: {
comboBoxSelectedValues: string[];
form: UseFormReturn<JobPostSchemaType>;
form: UseFormReturn<JobPostSchemaType> | UseFormReturn<addSkillsSchemaType>;
setComboBoxSelectedValues: React.Dispatch<React.SetStateAction<string[]>>;
}) {
const [comboBoxInputValue, setComboBoxInputValue] = useState('');
Expand Down
117 changes: 117 additions & 0 deletions src/components/user-multistep-form/add-project-form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import {
projectSchema,
projectSchemaType,
} from '@/lib/validators/user.profile.validator';
import { zodResolver } from '@hookform/resolvers/zod';
import { useForm } from 'react-hook-form';
import {
Form,
FormItem,
FormLabel,
FormControl,
FormMessage,
FormField,
} from '../ui/form';
import { Input } from '../ui/input';
import { Button } from '../ui/button';
import { Textarea } from '../ui/textarea';
import { useToast } from '../ui/use-toast';
import { addUserProjects } from '@/actions/user.profile.actions';

export const AddProject = () => {
const form = useForm<projectSchemaType>({
resolver: zodResolver(projectSchema),
defaultValues: {
projectName: '',
projectSummary: '',
projectGithub: '',
projectLiveLink: '',
},
});

const { toast } = useToast();

const onSubmit = async (data: projectSchemaType) => {
try {
const response = await addUserProjects(data);

if (!response.status) {
return toast({
title: response.message || 'Error',
variant: 'destructive',
});
}
toast({
title: response.message,
variant: 'success',
});
form.reset(form.formState.defaultValues);
} catch (_error) {
toast({
title: 'Something went wrong while Adding Projects',
description: 'Internal server error',
variant: 'destructive',
});
}
};

return (
<Form {...form}>
<form onSubmit={form.handleSubmit(onSubmit)}>
<FormField
control={form.control}
name="projectName"
render={({ field }) => (
<FormItem>
<FormLabel>Project Name</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="projectGithub"
render={({ field }) => (
<FormItem>
<FormLabel>Project Github Link</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="projectLiveLink"
render={({ field }) => (
<FormItem>
<FormLabel>Project Live Link</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="projectSummary"
render={({ field }) => (
<FormItem>
<FormLabel>Project Summary</FormLabel>
<FormControl>
<Textarea {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button type="submit" className="mt-4">
Submit
</Button>
</form>
</Form>
);
};
Loading

0 comments on commit f500dfd

Please sign in to comment.