Full Stack React Project ( AI Career Coach ) - Next JS, Tailwind, Gemini AI, Prisma, Shadcn UI 🔥
FULL TRANSCRIPT
this is that AI powered project that
will take your resume to the next level
by building this full stack AI career
coach called Sensei with features like
user onboarding to generate interactive
dashboard displaying in demand skills
and salary Trends in your industry which
is updated weekly an AI Resume Builder
that generates ATS optimized content
based on your industry and skills and it
is fully customizable as well this
awesome mock interview feature with role
specific questions to prepare for your
upcoming interviews which also displays
your performance over time and gives AI
generated Improvement tips as well an
intelligent cover letter generator that
analyzes job descriptions and creates
tailored content accordingly along with
this fully responsive and beautiful user
interface that we will be building using
nextjs and Shad CN UI so right of the
bad we have this professional and modern
looking landing page with this beautiful
scroll animation which I'll teach you
how you can Implement as well if you
further scroll down we have the list of
features that our app contains like
interview preparation industry insights
AI powered career guidance smart resume
creation and much more we have all of
the statistics related to our app how
our app works and also some testimonials
from our users along with some
frequently asked questions right here so
we have included all of the important
elements that a modern SAS website
contains and a call to action button at
the bottom and as I always say a modern
landing page like this can play a huge
huge role on if you'll be getting a call
back from that HR or that company or not
so so let's click on get started and we
are on the login page now we can go on
and login with Google or email address
and password so let me just quickly go
on and login with Google and there we go
we have successfully logged in and it
has presented us with this user
onboarding page where we can select our
industry so let me select technology and
I'm a software developer years of
experience let's say four let's select
our skills for example if I select
JavaScript react postgress nodejs we can
enter some professional bio and click on
complete profile and here we go we are
on industry insights now this is our
dashboard page and over here you can see
on the top left we have a user drop-
down with options like manage account or
sign out if I click on manage account we
can see more details about our user we
can update our profile over here like
first name last name or email addresses
Etc if you go to security you can see
different different places where the
user is logged in right so we have all
of these production great features
implemented inside of our app let's
close this and over here you can see
there's another drop down which will
display all of our AI tools we will come
back to this very soon but here in the
industry insights you can see it shows
us The Market Outlook based on what we
have selected on the user onboarding
screen how fast our industry is growing
how much is the demand or you know jobs
inside of our industry what are the top
skills over here and it shows different
different job roles with minimum median
and Max salary for each and every one of
them right these are all the top job
roles in the industry and if you further
scroll down it will tell you the key
trends that are going on right now in
the industry and the recommended skills
that you can learn learn and all of this
data is updated constantly right every
single week you can change it if you
want as well and it will be updated in
next six days and to implement this
feature we will be setting up a
background job a crown job which will be
running every single week and not a lot
of tutorial teach you this kind of
features and that is what makes this
project different now let's go to this
growth tools dropdown and let's select
Resume Builder and we are on this form
right here right we can select our email
addresses let me just select Anish over
here some random mobile number we can
enter our LinkedIn URL our Twitter
profile we can enter professional
summary along with the skills as well
now when it comes to work experience
let's say you're entering something in
the description right let's say worked
on an Ecommerce website and now you're
St what am I supposed to write further
in this right let's say an e-commerce
websites selling clothes so you can
click on this improve with AI button and
notice
it will generate a complete description
ATS friendly description with all of the
analytics and everything as well right
using Gemini Ai and I will show you
exactly how you're going to go on and
Implement a feature like this you know
so you can build any type of AI
application we can also enter over here
our title our company from which date we
started and if it's a current experience
you can click on this else you can
select the end it over here as well and
you can click on ADD entry button over
here and this will be successfully added
and similarly you can add more
experience if you want to add education
if you want to add projects it supports
links and everything as well right and
after you have built this rume
successfully you can go to this markdown
tab over here and your complete rume
will be rendered right over here so let
me just fill this form completely and
let me come back and there we go I have
created my complete resume right over
here now obviously you want to download
this resume as well right so click on
this download PDF button and this will
allow you to download this rume as a PDF
let me click on Save and there we go our
resume is successfully downloaded
awesome if you want to save this
markdown rume in your uh you know
database you can click on this save
button over here and if you want to
directly edit this resume you know how
to edit markdown right so you can click
on edit resume and you can change
anything directly over here and then you
can click on Show preview and it will be
updated over here and you can click on
save button and to manage all of the
forms inside of our app we will be using
react hook form along with the Zod
validation Library so that you are
following all of the latest industry
standard practices as well all right now
let's go on to the interview prep page
and over here you can see we have the
data related to the users's performance
over the time what is their average
score how many questions that they have
practiced what is the latest score and
if you scroll down you can see all of
the latest quizzes that user has
attempted if you click on it you will
see the result for that particular quiz
as well along with the Improvement tip
as well so let me just go on and start
start a new quiz there we go it tells us
that this will have 10 questions let's
click on start quiz and it's loading our
quiz it has generated 10 questions using
Gemini API so let me just go on and
randomly answer these questions for now
and you can see we have this show
explanation button like if you're not
able to think for any of this question
if you on show explanation it will show
you and give you the idea of the right
answer so let me just click on next
question and randomly select all of
these and we are on the last question
let's finish the quiz and we will see
the result right over here so you can
see I have scored 20% and it's given me
this Improvement tip that I need to
focus on solidifying my understanding of
core JavaScript concept asynchronous
programming blah blah blah for every
single question it will give you the
summary of your answer and the correct
answer and along with the explanation of
the correct answer so these are the
Enterprise grade features that you see
in the big applications right and if you
go back to the interview preparation see
it showed me my latest performance which
was 20% so the chart died and we will be
using recharts for rendering all of
these charts inside of our application
and you can see inside of our
postgressql database we have all of
these data stored like related to our
assignment our resume you can see the
complete resume stored in the content in
the markdown format the industry
insights for every single user right so
that all of this data is persistent now
let's check out the AI cover letter
Builder so you can see I have already
created two cover letters over here if
you click on create new it will ask you
the name of the company enter job title
for the job description obviously you
can copy it from the job portal and
stuff like that but let's say for now
I'm going to say nodejs and mongod DB
required right let's click on generate
cover letter and this should generate a
proper cover letter that we can provide
to that particular company to you know
apply for that job so you can see it has
given us this empty columns to fill our
name address phone number email and the
complete cover letter over here as well
right it has mentioned the back end
develop at roadside coder and stuff like
that amazing let's go back to the all
cover letters and also I would like to
mention this is not a spoon feeding
course I will also give you some
assignments to do as well along with
this course right because on this
channel We Believe taking action and
building projects yourself is what makes
you a better developer so before
starting up let me know in the comments
down below from where you watching this
video and what is the best feature that
you like about this app let's get
started all right so open vs code over
here and let's quickly open our terminal
and initialize a new nextjs app so I'm
going to say npx create next app at
latest and I'm going to press enter it's
going to ask us to proceed yes what is
your project name now if you name our
project anything it will create a new
folder over here but we are already
inside of this Sensei folder I'm going
to press Dot and press enter would you
like to use typescript no would you like
to use es lint yes yes Tailwind CS says
yes of course would you like your code
to be inside of SRC no we're going to be
using App router right so yep that's
exactly the next question yes we're
going to be using App router turbo pack
yes comes from nextjs 15 would you like
to customize import allias no and there
we go it is going to be initializing a
brand new nextjs app inside this folder
right here there we go let's go on and
run this so I'm going to say npm run Dev
and it
has started a brand new server let's
click on over here there we go our
nextjs app is running now where can we
find this code so if I go back and if I
go inside this app folder right here we
have two files one is layout. JS which
basically contains the code that wraps
all of our app so if you see over here
we have HTML body and then the children
and this children is this page.js file
inside of this you can find the complete
code that we just saw so let's remove
everything inside this return right here
and I'm going to have a Dev over here
which will say subscribe to Road Site
coder which you should if you haven't
yet so yep you can see this is what is
being rendered over here all right now
next what I'll do I'll go on and install
shat CN UI so just quickly go to Google
and search Shad cnii there we go and
shat CN UI is not a component Library
this is basically a beautifully designed
components that you can copy and paste
inside of your app so this is basically
pre-built components that are built on
top of Tailwind CSS that we can fully
customize right right the ones that you
can see right here so let's see how we
can install it if I click on docs over
here and click on installation and by
the way this library is super popular
noways click on nextjs and let's go and
use npm so I'm going to copy it up and
I'm going to go back to my terminal
let's open a new terminal over here and
paste it right here so npx Shad C in at
latest in it and it's going to ask us
few question base color Yes keep it
neutral CSS variables yes and it's going
to ask us this question how would you
like to proceed it's because we are
using react 19 right and some libraries
are not yet fully compatible with react
19 so we have to choose this use Legacy
pair depths option so that it works fine
with our application all right now next
let's go and install some component over
here so over here you can see they've
given the example of button so if you go
to a button right here yep you can see
we have different different types of
components over here right so for this
button to install it we simply have to
run npx shat CN at latest add button so
let's just copy and paste it right here
and I'll show you how Shad scen UI helps
us so basically it will create a
components folder inside of our app but
before that we have to choose Legacy
Pier depths again so you can see it has
created this components folder over here
and it has created this UI folder which
is reserved for the Shaden UI components
and it has created this button component
over here and you can see This is highly
scalable component with uh you know
multiple different variants and sizes of
the but so we can better see it over
here so if you scroll down this you can
see this is a primary button secondary
color destructive color outline variant
Etc right similarly we have ghost and
multiple different sizes for this button
over here as well and if you want you
can customize this here and you'll be
good to go so you see how easy it makes
our job so if I you know let's just add
a br tag to add a line break and I'm
going to add a button from Shad CN UI
make sure to import it from this/ UI
folder only and I'm going to say hello
and let's save it and let's go back and
check yep you can see we can see this
Hello message right here now as you can
already see inside of our app we are
using this button in multiple places we
are using this card component this is
also from Shard CN UI we are using um
let's say this accordion component which
is also from shat CNU right so we're
going to be using bunch of different
things inside of our app bunch of
different components from shat CN so
what I'll do let's just go on and
install them one by one so you can see
accordion right here we just simply have
to copy this lineup and go back to our
terminal and paste it right here the
other things that we will be needing is
a badge component button we have already
installed we're going to be needing a
alert dialogue for you know asking user
confirmation or cancelling some action
right we have to use the card component
a dialogue component for displaying
models like in our case we were
displaying result after the quiz is
finished right a drop down menu input
comp component from chat CN UI label for
those inputs or you know some other
places as well progress for showing our
progress in the dashboard we would be
needing a radio group component a select
component a sonor component for all of
the toasts that you saw on the bottom
left side of the screen the tabs
component and at last text area right so
press enter and this will successfully
install all of these components right
here inside this UI folder oops
something went wrong I believe I
misspelled something oh I think it was
the radio group so it has to be g r o u
and we will also be fixing this text
area right here and yeah I think uh that
should do it let's press enter would you
like to proceed use Legacy paer deps all
right so you can see it's installing all
of these dependencies right inside this
UI folder and it's asking us the button
already exists let's let's override it
and and yep there we go we can see all
of our components right here and we
don't need to touch this unless we need
to customize it and we don't need to
customize it right now right cool then
now out of the box shaten also provides
us with the light and dark mode and as
you can see our app is in complete dark
mode right so let's implement the dark
mode as well so you can click on over
here in this dark mode and click on
nextjs and it's pretty simple you don't
need to understand what's going on over
here so just install this next themes
over here so I'll just copy it up and
paste it right here and it's going to
provide us this code which will be used
for you know handling our themes so just
copy it up and inside of components we
have to create a component called theme
provider. TSX so I'll say
theme-
provider. TSX and in our case since
we're using jsx we have to say jsx right
so paste it right here and let's get rid
of these types we won't be needing them
Yep this looks good and after this we
just have to wrap whole of our app using
this theme provider object right here or
sorry theme provider component right
here right so I'll just copy it up and
go to our layout. JS file as I already
told you this layout. JS file is
responsible for wrapping h of our app so
instead of this children I'm going to
put this theme provider right here and
let's import the theme Provider from
this/ component / theme provider and
I'll put the default theme as dark and
yeah that that is all we need to do to
set up shat CN UI great let's work more
on this layout page over here so inside
of this let's plan what all the things
that will be inside of our app so we
will have a header component okay then
we will have a main component which will
contain all of the children components
so I'll put children inside of it then
below it I'm going to have a footer
component right let's just create this
footer right away and for this main tag
I'm going to give the class name of
minimum IM um height to be screen and
this footer let's just have a footer
component over here inside this I'm
going to have a d inside that I'm going
to have a paragraph tag which will say
made with love by roadside coder let's
go back and see yep we can see it right
here and you can see our app is now in
the dark mode as well great let's give
it some styling so if you have not work
with Tailwind CSS is pretty similar to
bootstrap you just have to say class
name over here and you can start giving
the class name which are inbuilt in
typescript and you can refer to
typescripts documentation and I will
also show you how you can use it right
here so like for example if we have to
add a background color of muted and by
the way these colors are predefined in
shat cnii so if you go to this global.
CSS you can see we have different
different types of colors that are
defined over here for muted it's this
muted foreground you can you know check
more about these in the shats and UI
documentation about what these colors
are exactly so I'll say background muted
and SL I'll provide the 50 opacity I'll
say padding vertical so py and if you
hover on these Styles you're going to
see we have padding top and padding
bottom to be 48 pixels you can see the
exact values right similarly for our da
as well I'm going to provide the class
name of container now what is a
container class so basically container
provides us with some break points like
for example in the smaller screens we
have Max width of 640 pixels in the you
know medium screens or screens less than
768 pixels we have Max withd this and
different different things right so this
helps make our websites more responsive
so I'll say MX Auto so that we can
display it at the very middle some
padding horizontal of four text is going
to be in the center and text Gray is
going to be 200 let's see Yep this is
exactly what we wanted amazing for our
header right here let's just quickly
create a header component as well so I'm
going to create a new file
called header. jsx R A fce and I'll say
header over here let's import our header
over here make sure to import it from
slash
components let's see yep we can see the
header over here as well now before
moving forward before doing anything
else let's start working on our user
authentication so click the link in the
description down below and you will land
on Clerk and this is the most
comprehensive user manager platform as
you have already seen in my previous
videos I love to use cler because it
provides us user authentication and user
management right out of the box so let's
click on start building for free and
let's go on and log in with Google and
you will see a similar interface now
here we can add the name of our
application so let's say Sensei we can
choose what are all the authentication
types that we are going to be adding
inside of our app like for example we
can add email password we can add Google
login as well or like username phone
number bunch of different things right
and I'll show you how easy it is to
implement inside of our application so
let's just choose both email and Google
and click on create application and we
are in amazing and you can see it tells
us exactly how we can go on and add
clerk inside of our application so I've
chosen nextjs over here so all we need
to do npm install at clerk / nextjs okay
I'll copy it up and go back to my
terminal and paste it right here now
what's next we are supposed to set up
our environment variable so that
app can connect to clerk so okay let's
copy it up and instead of our app I'm
going to be creating a new EnV file this
will contain all of our secret
environment variables and which we are
not supposed to share with the world
right and obviously if you try to use my
environment variable they won't work
because I would have deleted them after
the video okay after that we have to
create this middleware DJs file now what
is this file all about so this file runs
before our application is run right so
before our application starts we need to
figure out some things like for example
in this case user authentication so you
can see it is going to initialize the
clerk before our application is accessed
so I'll copy it up and I'm going to
create a new file
middleware do JS and paste it right here
just like this then next we are supposed
to wrap our app inside this clerk
provider function okay let's see so if
we go to our layout. JS this whole thing
this whole app is supposed to be wrapped
in clerk Prov provider so let me just
move it to the very bottom and let's
import clerk Provider from at clerk SL
nextjs like this and also by the way we
don't need both of these fonts right
here so I'm going to remove them and we
would be needing the font
enter and let's just add this font right
here so simply you have to do const
enter equals we have to import enter
from next SL font / Google subsets Latin
so this is basically just for changing
the font of our application if you don't
want to keep it that's fine you can use
some other font so over here in the body
I'm going to remove this let's remove
this class name and over here simply I'm
going to say enter do class name this is
just a onetime configuration don't worry
about it we're just adding the fonts
inside of our app and let's just change
the title to AI career coach or you can
also say Sensei AI career coach and you
can add your description according to
you right all right now let me show you
how easy it is to set up the user
authentication using cler so what I'll
do over here you can see we have this
example over here so I'm just going to
quickly copy it up and I'll go to my
header. jsx and inside of it I'm going
to paste it and import all of these
things from clerk so clerk / nextjs
signed in component user button
component all of them right now you're
going to notice if I go back to my app
we are getting this warning right here
hydration warning oh so this is actually
H this happens because like when we add
this theme provider inside of our
application to so you can check the shat
scen U documentation as well they have
recommended that you can simply add
inside of the HTML this thing suppress
hydration warning and you won't see this
error so what's going on over here so
basically we're saying that if the user
is signed out then show the signin
button and if the user is signed in show
the user button let's see you can see we
have this signin button over here or
sign in text if I click on it what's
going to happen it takes me to a
completely separate login page amazing
and we didn't even do anything right we
just took that code from Clerk and
obviously this is highly customizable as
well so if I click on continue with
Google it's going to allow me to sign in
and let's see we are in you can see we
have this user drop- down button right
out of the box coming from cler right
click on manage profile you can see we
can update our profile over here as well
update our profile picture like for
example if I go back inside this
configure tab in clerk you can add some
options over here like for example I
want users to be able to change their
name as well right so I'll enable this
and now if I go back I'm going to click
on update profile and now I'll be able
to change my name right here amazing and
you can also go to this security tab to
see where you are logged in and you can
delete your account as well right so
yeah all of these things comes right of
the box from clerk let's go on and
properly design our header for our
application so you can see see inside of
our deployed app we have this logo right
here right Sensei AI so you can get this
logo from my GitHub repository so just
click the link in description down below
and you will land on this GitHub
repository right here and if you go to
public folder over here you can find
different different things like for
example this logo that I've kept apart
from that I have three different types
of banner so that all of you know all of
you building this app don't have same
types of banner images inside of your
app right so I'm going to go on and
download both of these there we go I've
downloaded them and kept it right here
in my public folder let's get rid of
these SVG files we won't be needing them
so delete all right so right here in the
header component let's above it I'm
going to change it to header inside this
I'm going to have a nav tag over here
which will contain all of the navigation
items of our app like for example first
of all I'm going to add a link tag so
link tag in nextjs basically helps us to
navigate between different different
pages so this link tag takes a h riff
just like our anchor tag in HTML right
so this will be let's say slash for our
homepage and to add any image inside of
nextjs we use this image tag which comes
from next SL image and the reason for
this is this optimizes our images in the
server right and sends it to our client
so let's say SRC equals SL
logo.png let's save this this and see we
cannot see anything over here in fact we
have an error over here that missing
required width property so yep this
takes a width and height property over
here as well and we will also give this
the alt tag that is the description of
the image so let's just say Sensi logo
and width and height let's just give
this some class name of height to be 12
so this is basically the resolution of
the image right and here I'm going to
provide the Styles like padding vertical
to be one
width is going to be Auto and object to
be contained so that it doesn't get
deformed let's see now yep that is
exactly what we wanted below this link
let's add some action buttons right like
for example to be able to navigate to
our dashboard or industry insights or
the d drop down menu uh displaying all
of our AI tools and you can see so
inside of this div we will display all
of those things only if our user is
signed in right so I'll say signed in
and insert this I'm going to first
display the link tag to our industry
insights right so I'll have a link tag
which will have a button which will
obviously be coming from Shad Cen UI so
make sure to import it from / ui/ button
inside this we can add an icon over here
as well along with the text so you don't
need to worry about the icons because
Shad CN UI contains Lucid react package
which you know contains all of the icons
so just say layout
dashboard and you can see we're able to
import it from Lucid Das react and let's
give this some class name of height to
be four width to be four and below this
I'm going to say industry insights let's
see how this looks oh of course we need
to give the link tag a HF let's just
call it dashboard if you want to call it
something else again that is up to you
yep that looks good we need to obviously
this a little bit more so what I'll do
I'll go to this header right here I'm
going to say class name and our header
will be fixed on the top right I'll say
top to be zero and width is going to be
full let's give it some border bottom
and some background color to the
background from shat Cen right as
already told you these things are
already defined in the global. CSS file
you can go and check it out and I'll
keep the opacity to be 80 I'll say back
drop blur to be medium so that if
anything comes behind our header it is
you know it has the blurry effect I'll
say Z index to be 50 so that this
remains on the top and I'm going to say
it supports backdrop filter right and
then I'll give it the background of
sorry BG to be background color with the
60 opacity let's see how this looks like
this looks pretty good and you can see I
think whatever behind this I think there
was a button right it went behind this
header now and we'll come back to this
to fix it right now for this nav button
right here I'm going to give this some
class name to be container I've already
told you what container does mx Auto
said everything is in the middle padding
horizontal to be four height to be 16
and I'm going to give this some display
Flex so that everything is in one single
line and items to be Center so that we
vertically align it and horizontally I
want justify between so that they are on
Far ends of each other let's see yep
that is what we wanted now one thing
that I want to do over here is make this
website responsive out of the box right
so when this becomes smaller we don't
want to show this whole button right
here so what I'll do let's just put it
into a span tag right here and over here
I'm going to give this class name of
hidden by default and when it's more
than the medium screens I'll say display
to be block so now you will see if it's
less than medium screens yep that is
exactly what we want to do great now
after this we're going to be showing a
drop- down button which will display all
of our growth tools right so after this
let's go back to shat ceni documentation
and over here we have to find the drop
down menu component and we already
installed it so if I click on it you can
see this is how it will look like okay
so let's take this code right here I'm
going to copy it up and paste it right
here so let's import them one by one and
make sure to import it from from /ui
folder and not the radx UI right so
sometimes we make this mistake and then
it won't work so let's import all of the
separator and yeah I think they would
have been imported now oh I think I did
not import this drop down menu item now
it would be important yep if I click on
open yes we can see the drop down all
right let's uh configure it according to
us we would need the three items over
here and we won't be needing the
separator and the label okay for the
trigger here I'm going to Simply add a
button just like we have done right here
so I'll just take this button and paste
it over here instead of this layout icon
I'm going to add the Stars icon over
here and instead of this industry
insights let's just say growth tools and
below this let's add an icon for you
know indicating the Chevron down key you
know indicating a drop down over here so
let's see um yeah that is exactly what
we wanted let's create some gap between
both of these so over here in the div we
would need to give some class names so
I'm going to say display flex and items
to be centered so that they are
vertically in between and some space X
tob2 so that they have some space
between them and when the screen size is
bigger we will add Space X to be 4 yep
that is what we wanted all right so now
over here for each and every one of
these I'm going to add a link tag which
will take us to that particular you know
growth tool like for example Resume
Builder cover letter Builder or you know
interview preparation page again inside
the link tag I'm going to have two
things one uh icon and the name of the
growth tool so over here we just going
to get rid of this class name inste of
this Stars icon I'm going to say file
text icon so import it from Lucid react
and I'll say build resume let's see if I
click on it oh yeah of course this again
needs the HF tag which will be slash
resume and I'll give this the class name
of display Flex items to be Center and
some gap of two between them so now we
should see this if I click on it yep
build resume similarly I'm going to do
for both of these as well so let's just
copy it up and paste it right over here
and then we will change it for the cover
letter I'm going to add a pen box icon
and for the interview prep page we're
going to add a graduation cap icon so
let's just import both of these and now
let's see yep that is exactly what we
want let's change the link so for this
one I'll say AI cover letter and for
this I'll just simply send it to the
interview page okay now after this we
need to display our you know login
button and user button so let's move
this back inside of over here inside
this div and when the user is signed out
we don't want to show this sign in
button instead this will have some
children inside of it where we will
render the button from Shad cnii so I'll
say sign in also by the way we want to
differentiate between both of these
variant right so for my dashboard or the
this uh industry insights let's just say
variant to be outline and let's go back
and Y that looks much better and for
this thing right here okay sign in
button I want this to have the variant
of outline as well so if I go now and
try to log out let's click on sign out
over here you can see just the sign in
over here and growth tool wise it's
showing up over here I think we need to
move it inside of this signed in so just
take this signed in from over here and
put it right here now it should not show
up just the signin button okay now for
our user button over here if we quickly
go on and sign in again there we go you
can see it is very small right the
circle is very small so we can customize
it actually over here in the user button
we have a appearance prop inside of this
let's just say elements we can choose
the element and we need to choose the
Avatar box which will be width to be 10
and and height to be 10 user button pop
card is going to be Shadow Xcel and user
preview main identifier is going to be
font semi bold so you will see this will
have some Shadow and some font to be
semi bold okay and we can also Define
when we click on sign out where this
page goes right so for now I'm going to
keep it slash only if you want to keep
it something else I've kept it right
here looks like this is deprecated but
don't worry it will still continue to
work all right now let's try to uh click
on this industry insight we're going to
click on it and this will take us to
the/ dashboard page and again this page
does not exist but still we are logged
in right now right if we log out you're
going to see it took us to the slash
page right that's fine but now if I try
to go to slash dashboard page it should
not allow me to land over here because
we are not logged in right now right
even though this page does not exist it
should us it should route us to our home
page right or maybe our signin page
where we can sign in so let's add this
protection to our app so I'll go to the
middle .js file and over here on the top
I'm going to say cons is protected route
and we can add the different routes that
we want to add over here so we'll import
create route matcher inside this we will
give an array and we can Define all of
our routes right over here so our routes
will be dashboard resume interview AI
cover letter on boarding right all of
these and I've added dot asteris over
here that means anything that comes
after it
will also be a private route okay now
after it inside this export default
clerk middleware we're going to be
adding a call back and this will be an
asynchronous call back which will take
the Au and the request object and we
will work with these to you know add the
production to our app first of all I'm
going to say await o and this will give
us our logged in user's ID so const user
ID then after it right that is user is
not logged in right now and is protected
route that is this thing right here
we're going to pass the current route so
I'll pass this request object if request
object does not contain any of these
then we will route them back to the Lo
login page right so I'll say await or
and this will give us something which
will be redirect to sign in and we can
simply take this and in the next line
I'm going to say return redirect to sign
n that's it we're going to call this
function and this this will continue to
work this will reroute us back to the
signin page and then in the end I'm
going to say return next response. next
that is whatever comes next we can
continue our app after this and make
sure to import it from next SLS server
okay now let's try to do this again if I
try to go to/ dashboard you can see it
sent us back to this signin page but
wait you might be thinking what is this
URL over here this is a weird sign-in
page right I want the sign-in page to be
inside of my application and and not go
to some third party URL right and that
is exactly what we will be implementing
next But first you can see our app is in
dark mode right and this is light mode I
want this to be in dark mode as well so
what I'll do I'll go to my layout. Js
and in the clerk provider I'm going to
say appearance and inide of this it
takes a base theme and this will
basically be dark so I think we need to
import it or probably install it from uh
some package let me me just check inside
of the clerk Yep this is what we want
npm install at clerk SL themes you can
find it in the docs right so I'm going
to paste it right here and P install at
cl/ themes and this will install it and
from here we can you know get a lot of
themes like for example you can see we
have different different types of themes
right here and this is what I need all
right so this has finished installing
let's try to import it yep from at clerk
/ themes and now if I go back to my app
looks like it won't work on this page
but it will work on the component when
we go on to add this signin component
inside of our app right so okay let's
try to do that and how are we going to
do that we have to define the custom uh
you know signin page for our application
and to do that we have to go to the EnV
file and we have to add four environment
variables over here actually two
environment variables other two are
going to be for something else so first
one will be next public clerk signin URL
this will Define the signin URL for our
app and the second one will be sign up
URL sign up right now what will happen
when the user signs in or signs up so
I'll say next public clerk after
signning URL to be SL onboarding and
after signup URL to be/ onboarding as
well all right now you're going to
notice if I go back to my local host
3000 I'm not dashboard I mean just the
Local Host 3,000 if I click on sign
button this will take me to/ signin but
this page does not exist so what should
we do inside of our app now we going to
be creating a brand new route right and
don't worry I'll show you very soon the
in-depth ways to create you know routes
inside of our next CH but for now just
try to understand what I'm trying to do
over here if I want to create a new
route I have to create a new folder
that's all if I let's say sign Dash in
this is now a new route inside of our
application like if I say new file page.
jsx and this very important you have to
create a file called page. jsx for it to
be recognized as a route so I'll say
page and um let's say sign in right
let's see if I refresh this now I think
this is in like behind our header right
now if I just remove my header from over
here let's get rid of this header y you
can see the sign in is behind this
header let's take care of this as well
we don't want things to be uh you know
overlapping our header so what I'll do
over here I'll create a sign up folder
over here as well sign Dash up and I
want to provide some common styles to
both both of these so what will I do I
want to grip them somehow right and to
grip them I'm going to create a new
folder called o but this won't work
right this will create a new uh route
and I don't want to Nest another route
like / signin so what I'll do I'll wrap
it inside of parenthesis and this will
essentially ignore this folder as a
route right so if I move both of them
inside of it move now this won't be
considered as a route right here and I
can provide it a
layout. Js file and whatever we write
inside of this file will directly affect
both of these right so I'll say R A fce
let's say o layout and I'll take the
children over here and I'll render them
right here so this sign in and sign up
will render right over here and I can
Pro now provide some common Styles like
for example I'll say class name display
Flex justify to be Center and padding
top to be 40 let's check it out if I go
back and ref refes this yep you can see
sign in right over here now instead of
this sign in I'm going to display the
clerk's signin component right so if you
go to the clerk's documentation you will
find that there's a convention on
creating these signin pages right what
you have to do you have to create this
signin route first then inside of this
you have to create a new folder which
will contain a catch all route and we
will discuss what this catch all route
is basically you have to write two
square brackets inside of it dot dot dot
sign Dash in and this is basically an
optional route if you have anything over
here it will catch it like for example
if you go back you can see we have all
of these options over here right so
these are for clerk to be managed so
that's why you have to create this scat
all route over here and let's move this
page. jsx inside of it move now inside
this page. jsx simply all we have to do
is to import a sign in component from
clerk that is all let's get of this
import Port react and now let's see yep
you can see such a beautiful sign in
form right here and this is right inside
of our application if I go back and try
to click on sign in yep you can see we
are landing on the signin page great
let's do the same with our sign up page
as well so inside of the sign up page
I'm going to get a new folder and I'll
add a catch all route for sign Dash up
inside of this I'm going to add this
page. j6 file so I'll create a new file
page. jsx paste it instead of sign in
I'm going to say sign up right let's
remove this sign in over here and let's
see if I click on sign up it should take
me to sign up page all right we can you
know fill up this form and create a new
user all right great so we have
successfully set up our user
authentication and now let's go on and
connect our back end or you know set up
our database basically so click the link
in description down below and you will
land on neon and this is a postgress SQL
database and you already know how
important it is to be knowledgeable
working with a post SQL database right
so I'll tell you everything about how
you can get your database set up and
also use it alongside with Prisma orm
which again is a super trending
technology nowadays right so click on
get started and let's log in with Google
and we are in now we're going to create
our project so let's name our projects
and say database name we can just keep
it empty and click on create project
there we go we are in let's go on and
set up our database so all we need to do
we need to go to this overview tab over
here and this roles and databases and
click on ADD roll let's add rule of
roadside coder that I'll be the one
managing this database create and then
below it we need to add our database
right if you want you can copy this
password over here as well let's click
on ADD database and let's name the
database
Sensei and the owner will be roadside
coder let's click on Create and yeah
with that we have created our database
successfully let's go to our dashboard
over here let's choose the database and
the owner and we can simply copy this
connection string from over here right
and then go back to your app and inside
of EnV file I'm going to create a datab
basore URL equals and I'll assign it
right over here so basically this
connection string will enable us to
connect our project and to communicate
with neon DB using something called as
Prisma omm which I'll show you in just a
moment but let's set go on and set up
the service which we will be using for
managing our background jobs like you
know fetching the industry insights
every single week so click the link in
description down below and you will land
on inest and click on sign in over here
quickly I'm going to sign it with Google
and there we go we are in let's click on
take me to onboarding and you can see
over here they've given us this command
so just copy it up and go back to your
terminal just run npm install injust
first so that it installs the injust
inside of our application and then we
can run npx in/ CLI at latest Dev
proceed yes there we go it has
successfully ran inest on our local
machine and click on this Local Host
link right here and there we go our
inest server is running in our Local
Host okay so let me just open the injust
docs over here real quick so we have
already ran this Command right here and
we can see our injust running now we
have to create an inist client and to do
that we have to do new inist and provide
the ID of our app okay so what I'll do
I'll go back and insert this lib folder
over here I'm going to be creating a new
file and actually new folder for
inist and I'm going to be creating a new
file called client do JS andert this I'm
going to Simply paste this code so I'll
just copy it and paste it right here
right let's name our app Sensei and this
will also take the name so I'll just say
Sensei just like that and this will take
more things like for example later on we
will go on and configure our credentials
for the Gemini API key right so yeah we
will come back to this later let's go to
the docs and see what else so now we
have to set up this/ API SL inest route
inside of our application now how does
inest actually works so in nextjs we
have something called as this API folder
right and inide this we can as the name
suggest build our apis right it can be
public API can be private API so we have
to build a public API for inest to
connect to our application right so you
can see we just have to say/ API SL
inest so I'm going to say new folder
inist and so that this AP works we have
to create a route inside of it route
file so I create new file route. JS
inside this route. JS file we have to
add this code right here so just copy it
and paste it right over here and also
we're going to remove this because our
inest client will be coming from at lib
slin client so what is going on over
here so we have created this API with
get post and put requests and this serf
function which also comes from inest /
nextt and now over here we can Define
our functions which will be running in
the background let me show you how you
can quickly go on and create the
function so over here they've given this
example in the code of a simple hello
world function right let me just copy it
and explain you how this works so um
let's say I'll go insert this inest
folder and create a new uh like file
called functions do JS and paste it
right here now this function takes an ID
of hello world and the event of test/
hello. world now this event right here
is something which will trigger this
particular function over here right so
this is very important over here this
first object takes more things like for
example if you want to add the retries
that is how many times this function
will retry before it fails in the
background it also takes things like
rate limiting throttling ability to bash
the events debouncing and much much more
right so you can find all of these
things in the official NES documentation
and learn more about it okay so now what
we are doing inside of it we're simply
saying that make this function wait for
1 second and then we will return a
message called event. data. email which
will be provided by us right so this
takes the event and the step so injust
functions are executed in different
different steps over here right and over
here we just have added a step of sleep
so now if I just take this and where did
it go I'll go back to my route. ts and I
can import this right here right so
import hello world from in just SL
functions and now if I go back to over
here in in my development server inside
the app you can see it has all autod
detected it inside of the functions we
can see this hello world function and we
can manually invoke it from over here
right obviously inside of our app we
won't be manually invoking it and you
can see it's asking us for some data if
we want to provide let's say email and
and let's say example at
gmail.com click on invoke function it
will wait for 1 second and yeah you can
see it has already completed the
function and provided us this message
hello whatever the email was and you can
see we have this wait a minute step run
over here right we can see the data that
we have sent to inest yep right here
email so this is a very basic example of
an injust function again we will be
coming back to this and build our
industry insights function which will be
fetched every single week right so let
me just um get rid of this for now and
we will come back to this very soon and
if you're someone who's preparing for
frontend interviews you can definitely
go and check out my front-end interview
preparation course this is the only
thing that you will ever need to prepare
for your fronted interviews and not only
this I've covered every single topic of
reactjs like react router D Redux all of
the hooks class based component function
based component react even the
performance optimization
which is asked from senior developers a
lot tons of machine coding questions as
well and obviously in-depth interview
preparation course on JavaScript as well
and not just this along with this course
you get an active Discord Community
where you can ask your doubts whenever
you get stuck and we will be there to
help you so click the link in
description down below because right now
we're having the biggest sale of the
year for a very limited time so don't
forget to check it out or you can also
scan this QR code on your screen to go
directly to the the course page and now
let's go on and build our beautiful
landing page right here as I've already
told you having a beautiful landing page
like this plays a huge role on if you'll
be getting that call back from your
recruiter or not right and you can see
this looks super professional with all
of this you know grid background and
everything with all of the FAQs provided
over here as well so cool then let's get
started we have already imported this
banner. jpeg from my GitHub repository
which we will be needing while building
our app right all right so I've opened
my page.js file over here let's remove
everything inside of it and let's see
what are all the things that we will be
adding inside of it so first of all
obviously we have a hero section this
hero section right here then we will
have the feature section the statistics
how it works what our users say and
obviously frequently asked questions
okay so let's first add this background
that you can see over here right this
grid background to our app so I'll go to
my Global start CSS file and right on
the bottom I'm going to create a class
name called grid Das background inside
this I'm going to add a position fixed
top to be zero left to be zero width and
height to be 100% for our page right
because this is going to be fixed and
we'll be inside whole of our app okay so
to create those lines I'm going to be
adding this background property over
here of linear gradient so basically
what we will be doing we will be
creating a line of one pixel with the
opacity of of 10% or
0.1% right and it will go from left to
right similarly we will create another
line or linear gradient which will go
from top to bottom right and we will do
it with the gap of 50 pixels right so
I'll say background size to be 50 pixel
and 50 pixels I'll say pointer events To
None because we don't want to select it
and Z index to be minus one so let's
take this grid background and go back to
this page. jsx I'll add a div over here
let's actually rename this file to page.
jsx and give this the class name of grid
background and let's see yep that is
exactly what we wanted let's add a
radial gradient over here right if you
notice this is fading on the sides but
it's visible in the middle so I'll go
back to global. CSS and below this I'm
going to say dot grid background and
I'll add before sudo element and inside
of it let me just quickly get the Styles
and I'll explain you so content is
nothing we don't want to display
anything the position is going to be
absolute for this gradient top zero left
Z width 100 and height 100% right that's
like covering the whole page but we're
saying add a radial gradient property of
circle and transparent and it will be 9%
opacity right so now you will see yep
just like this this is exactly what we
wanted and you know don't stress much
about this in real world you will most
probably be searching in chat jpt or
Google to find a property like this
right you won't be having to create this
from scratch anytime soon all right so
coming back to this page. jsx we will
first be having the hero section so
right below it I'll have a hero section
component now one thing that you need to
understand in nextjs is there are two
types of components a server component
and a client component like for example
this page this does not have any flag on
the top so this is a server component if
I were to make this a client component I
would have to add use client now what is
the difference between both of these so
client component can have logic like
hooks right and which which cannot work
on our server right which cannot work on
our back end so that's why we have to
make them work in our front end right
that's why they are client component and
the server component are basically those
component which generally are rendered
on a server and contains the information
which is crucial for probably the SEO
let's create this hero section component
um over here so I'll get a new file
called hero. jsx or
afce hero section okay let's import it
right here just like this now uh over
here let's just wrap it in a section tag
and start this I'm going to have a Dev
which will contain another Dev which
will have our H1 and we will simply say
your AI career coach for professional
success and this section right here will
have class name of width full some
padding top of 36 right we can see this
and in Media medium screens like screens
more than medium screens we're going to
have padding top 48 and padding bottom
to be 10 let's see Yep this is what you
wanted below this H1 we are also going
to be having a paragraph tag which will
say Advance your career with
personalized guidance interview prep and
AI power tools for job success now below
this div over here we will display the
two buttons for getting started and
watching the demo of this so I'll have a
d which will contain a link tag which
will contain a but Buton from shat cnii
which will say get and the link will
have the hrf of/ dashboard when we click
on get started okay for this button
let's just provide the size of large and
class name of padding horizontal to be8
Let's duplicate this link just like this
and if you have any demo video you can
add the link to it over here I'm going
to Simply add the link to my YouTube
channel is that because that's is the
place where this tutorial exists and
this button will have the variant of
outline that's all let's see link is not
defined okay let's import link from next
SL link and there we go we can see it
now below it we will add that Banner
image that we copied from GitHub right
so below this div right here I'm going
to have another div which will contain
another div which will contain the image
and the reason why I'm creating two divs
over here is because we will be adding
the logic to this uh image right here so
if you notice when we scroll down see it
tilts forward right so so this is the
logic that we would be needing to add
and I'll show you how you can add that
as well right so but first of all inside
this image I'll say SRC let me see if I
have imported image from next / image
yep so SRC will be/ banner. jpeg I'll
provide it the resolution of 1280 to 720
and the all tag of dashboard preview oh
you know instead of that let's just say
Banner Sensei below it I'm going to have
the class name of rounded large Shadow
tob2 XL and some border with margin
horizontal to be Auto and let's just add
the priority tag so that it loads first
right yeah like this all right now that
we have created the basic structure of
our app let's just add some stylings
over here first of all this div which
contains both of these things we will
add some gap between them so I'll say
space y to be six and text to be Center
and for this particular div I'm going to
say space y to be six and margin
horizontal to be Auto let's see yep that
is what we want now for this particular
title we will be adding some uh
gradients and all if you notice just
like this and we will be using this
gradient throughout our application so
we want to create a custom Tailwind
style for this so what I'll do I'll go
to global. CSS I'll say add layer
utilities and inside this let's first
create a gradient class so I'll say
gradient inside this I'm going to say
add apply and I'm going to apply a
background gradient so I say BG gradient
which goes from from top to bottom right
so I'll say gradient to bottom and we
can assign either two colors or three
colors so I'm going to say from Gray 400
via gray 200 to gray 600 if you want you
can remove this from the middle over
here as well but I want three different
colors over here right now similarly we
can reuse this class over here so I'll
create a gradient title class over here
or style over here and I'll use the
whatevers that we have added over here
gradient font extra bolt tracking
tighter text transparent background clip
to we text you know so that we can show
this background and clip it inside of
our text just for our text some padding
bottom and some padding right right so
we're just going to take this gradient
title and provide it to this H1 so I'll
say class name to be gradient title and
now you will notice which I don't think
you can notice because we need to
provide some uh size over here as well
so let me just add it so over here
alongside it I'm going to add text 5xl
font to be bold in the bigger screen
text is going to be 6 XL in smaller
screen it's 5 XL and in the larger
screens it's going to be 7 XL and in the
extra large screen it will be 8 XEL so
right we will we can customize every
single aspect of our app using Tailwind
CSS breakpoints right and of course
gradient title so now if I go back Yep
this looks good for our paragraph tag
right here let's come to over here and
I'm going to say margin horizontal to
Auto and Max width is going to be 600
pixels so that we can break it down into
two lines
text muted to foreground so that it's a
little bit lighter and in the larger
screens I'll say text Excel okay that
looks much better all right now for both
of these buttons right here let's
provide some gap between them so I'll
say for this D I'll say display Flex
justify Center and Space X to be four
like that now for our image over here we
need to add that logic off so that you
know this image is tilted and when we
scroll down it's going to come forward
and tilt to the normal size so for this
div I'm going to be providing a custom
class called hero image wrapper some
margin top of five in the smaller
screens and in the medium screens it
will be margin top of zero yeah we will
be getting a reference to this div right
here so that we can provide that logic
so for that we will be using a use ref
hook so I'll say use ref and import it
from react and I'll show you what use
ref exactly does and we will take image
ref over here and now you will notice
this will give us error because we are
using hooks over here right so it will
tell us to use this use client directive
so yep let's just add it on the top I'll
add use CL and this will continue to
work just like this okay so now what
will we do with this image refi we will
take this and say ref equals image refi
now through this image refi variable
right here we will get the access to
this div's HTML right we can manipulate
and we can do whatever we want and by
the way by default we can keep it as
null over here and also let's give this
the class name of hero image now what
will we do we will first of all take the
use effect hook over here right simple
use effect hook which will run when our
component is loaded we need two things
over here first of all the scroll
position where we are currently in our
uh you know window and the scroll
threshold that is after this has been
cross we want to tilt it back right tilt
the image back so first of all to tilt
the image back we first have to tilt it
right so we have already added these two
classes hero image rapper and hero image
so let's go to our global. CSS and add
this so over here I'm going to add hero-
image- wrapper and here I'm going to add
a perspective class with 1,000 pixels so
now you will see nothing will happen
because we have added just perspective
property over here but we have to rotate
it now so now I'll say do hero- image
transform rotate X by 15° and scale is
going to be 1 now let's see yeah you can
see now it has been rotated but if we
were not to add this perspective style
notice what will happen see it wouldn't
work right so we have to add this
perspectives for this transform to work
and when we transform when we perform
the transform I want the transition of
0.5 seconds right so that it eases out
with the transform and we also need to
tell Will Change to be transform
right so this provides a rendering hint
to the user agent stating what kind of
changes the author expect to be
performed on an element right so we just
adding transform animation that's why we
have added it over here now when we want
to put it back to normal we are going to
trigger another class light over here so
I'm going to say dot scrolled scrolled
right so this will simply have this
transform actually so I'll just
duplicate it and I'll put it right here
but instead of 15° it will be 0° scale
to be 1 and
Translate Y to be 40 pixels I want to
shift it a little bit right that's all
we need to do we just have to add this
scrolled class now programmatically so
now in the use effect hook I'm going to
say this I'll say if scroll position is
more than the threshold position right
we have crossed this 100 pixels of
threshold then first of all I'll get
reference to this image ref over here so
I'll say on the top over here
image element equals image ref do
current now we have the access to it so
I'll take this image element I'll say
image element do class list and I'll add
the class of scrolled and um yeah we
need to wrap all of this code inside of
a function so I'll say handle scroll so
that we can you know check the scroll
position so I'll add a app Arrow
function over here wrap this whole thing
inside of it and then after this I'll
say window. add event listener and I'll
add the scroll event inside of it which
will trigger the handle scroll so now if
I check if I scroll down yep you can see
it comes back to normal amazing and also
we need to remove the event right when
the component is unmounted so I'll say
return window. remove event listener
scroll handle scroll and even though it
is working but let's just add the else
condition over here as well else I'm
going to say remove the scroll if the
threshold is not less than that right so
let's see yep amazing this is working
flawlessly great so now that we have
created our hero section let's move
forward to this feature section over
here so I'll go back to my page. jsx and
below this hero section I'm going to
have another section right here for the
features so I'll add a d and a h two tag
which will say powerful features for
your career growth okay now to render
all of these features right here we
would need all of this data right and
all of this data over here as well so if
you want you can create your own data or
what you can do I have added this data
inside of my GitHub repo so inside of
this data folder over here you can find
these four files FAQ features Industries
and testimonials make sure to download
all these four files right so let me
just do that real quick all right I have
downloaded Ed them and put them inside
of this data folder right here right
don't worry about it right now we're
going to be using one by one so first of
all we would be needing this features
and simply what we're doing over here is
we have the icon title and description
these three are what we will be
rendering over here right and we will be
mapping through this features array so
I'll go to page. jsx and after this H2
I'm going to have another div which will
render my features so features. map and
we will take each and every feature and
the index and inside of this we will
render all of these features inside of a
shat CN UI card so how do we create a
card in shat CN UI let's see I'm back in
this documentation and over here in the
card Yep this is what we want so let's
just copy this code right here and I'm
going to say return and paste it over
here okay we won't be needing this card
header so let me just get rid of it and
import this card card content and card
footer as well I don't think we will be
needing it this is all we need first of
all insert this card content I'm going
to have a Dev right here which will
render the feature do icon and I'm also
going to be having a H3 tag which will
render the feature do title and then a
paragraph tag which will render the
feature. description let's save this and
see if we can see anything over here
features is not defined okay we need to
import it just like this from data
folder and if you scroll down yep that
is what we want let's just go on and
style it first of all for this parent uh
section right here I'm going to say
class name to be width full padding
vertical to be 12 in the bigger screens
padding vertical is going to be 24 and
in the larger screens it's going to be
32 and background is going to be this BG
background color from shat C and UI
right so let's see yep we have some gap
between them and yeah it gives us this
error each child in a list should have a
unique key prop so that is what we have
to add over here in the card I'll say
key to be index okay now for this div
right here I'm going to say class name
to be container margin horizontal to be
Auto some padding horizontal to be four
and in the bigger screens it will be PX
to be six for this H2 I'm going to say
text to be 3 Exel font is going to be
bold and tracking tighter so that the
you know words a little bit closer to
each other text is going to be Center
and margin bottom to be 12 so yep that
is what we can see over here and now for
this D we will be giving a display grid
right here right so I'll say class name
to be display grid in the smaller screen
The Columns will be one medium screen
columns will be two and larger screens
columns will be four we can see all of
them right so Gap is going to be six
between them and Max width is going to
be six XEL and they will be in the
middle so I'll add margin horizontal to
be Auto just like this let's just add
some styling to each and every card so
for this card right here I'm going to
say border to be2 when we hover on it
border is going to be primary color that
is we can see the you know border around
them transition color so that when we
hover on it we can see the colors
transition and 300 millisecond is going
to be the duration so if I H on it yep
just like this now for each and every
card content over here I'm going to be
saying padding top to be six some text
Center display flex and flex to be colum
so that they are on top to bottom over
here and items to be Center horizontally
for this internal div I'm going to be
saying display Flex Flex to be column
items to be Center and justify to be
Center so that you can see everything is
in the very middle now for each and
every one of them first of all for the
H3 tag I'm going to be saying the class
name is going to be text Excel and font
bold and margin bottom to two so there's
some gap between these and I'll make
this paragraph a little bit muted color
so I'll say text muted foreground so
like this now we can clearly see them
right here all right now after it we
want this feature right here this
Statistics feature right so let's build
that section so over here below this
section you know what let me just copy
this section up from over here and paste
it right here and let me remove these
features from here let's also get rid of
this H2 right here instead of back BG
background I'm going to say PG muted
oops
muted to be 50 opacity padding vertical
to be 32 let's just remove this in the
larger screens I think this should be
enough and over here inside this D we
can add four different stats now when I
was building this app I had written the
HTML for it and I forgot to create the
separate data for it so let me just
bring this in it would look something
like this so for each and every D we're
going to have the stat and the subtitle
for it right like 50 plus Industries
covered we're adding display flex and
flex column so that these are on top to
bottom item Center and justify Center so
that they are on the middle and space y
to be two so that we have some gap
between them right and simply for 50
plus we will have text 4XL and font bold
and this one will be muted and similarly
we will have three more stats over here
which again if you want you can create
your own or you can copy just like I
have written like 1,000 plus interview
questions 95% success rate 24/7 AI
support right so let's see if we scroll
down yep that is what we want right so
after this section uh you know what one
thing but while creating a landing page
I would say the development is the
easier part the main thing is to
understand how you can create the design
right and what are all the elements that
you can separate separate elements that
you can create to build an app like this
so you can use there there are variety
of websites available like behance and
dribble which we can go and take the
inspiration of design from right just
don't copy it take take some inspiration
and add something on your own as well so
below this section let's just again so
this is going to be pretty similar to
this feature section right so I'm going
to take this section and uh copy it
right here and on the top instead of
this we can write how it works and we
can also add a paragraph tag over here
which will say four simple steps to
accelerate your career growth and you
know what let's just wrap this whole
thing inside of a d so that this is
separate from the below section
and let's remove all of this and I'm
going to say margin bottom to be four
and for this div over here I'm going to
say text to be Center Max width to be
3XL and some margin horizontal to be
Auto and margin bottom to be 12 so let's
see if I go down yep we can see this we
just need to change this thing right
here and let's remove everything inside
of over here in this features. map
instead of this I'll take the how it
works and make sure to import it that we
copied inside this data folder over here
right how it works. JS I'll take each
and every item and the index so I'll
take each div over here and actually
since there are I think how many items
are there so there are four I think we
don't need to change this grid over here
so inside this div I'm going to Simply
render my item do icon and let's see how
it looks if we scroll down okay we need
to add a key prop as well so key to be
index and for this div let's just add
add a class name of width 16 height 16
rounded to be full background color is
going to be primary with 10% opacity
flex and items to be Center justify
Center and now if you see yep we can see
it right here but we cannot see the icon
over here so what did I do wrong it
should be icon with the capital I no I
think it should be small I over here
that's better now if I see yep we can
see the icons let's get this Dev some
class name of display Flex Flex to be
columns items to be Center text to be
Center and space y to be four so that
these are in the middle yeah that looks
good now below this D I'm going to have
the H3 and the paragraph tag as well
which will contain item. title and item
do description which will be a sem font
semibold and text to be Excel and again
paragraph is going to be muted just like
what we've been doing up until this
point right just like this great after
this what do we have we have what our
users say right again this will be very
similar to what we how we have been
creating this featured section right so
I'll just copy this feature section over
here again and go down after this
section and I'll paste it right here
right first of all over here I'm going
to Simply say what are users say and
instead of this features I'm going to be
mapping my testimonial right so I'll say
testimonial I'll take one single
testimonial over here for this card
simply I'll say background is going to
be this background color that we have
defined like that shat scene UI has
defined now inste of this card content
let me just remove everything and write
it from scratch but first of all let's
see if we can see anything over here yep
what our user say with three cards of
our testimonial let's just first give
this card content just the class name of
padding top to be6 now inside of this
card component over here I'm going to
have a d and let's plan how we're going
to be building this so if you see over
here we have this Dev first and then we
have this Dev for containing this uh
block code right so inside this div I'm
going to have first div for that one and
then we will have a block code for our
code right and then inside this div
we're going to have two things first the
image and then the information about the
user right so I'll have a div for the
image and then another div for the info
about the user so first of all for the
image so I'll add a image tag over here
import it from next SL image and inside
of this I'm going to have the SRC of
testimonial. image width and height of
40 and all tag of testimonial. author
let's see if I go back to our app um
okay so we're using this random user. me
over here right if you go to the data
we're using this image with random user.
me API so we need to add this to next.
config.js we need to tell nextjs that we
are using this so insert this I'm going
to say images and remote pattern I'll
say protocol https and host name to be
random user. me so let's see now if I
refresh this let's scroll down okay we
cannot see the image yet y now we can
see great let's add the class name of
rounded full object cover border tob2
and like border color to be primary and
20 opacity so let's see yep like that
now after this inside of this T over
here we're going to have three paragraph
tags first one we'll have testimonial
author testimonial role and testimonial.
Company right this will be semi- bold
this will be text small both of them and
this will muted and this will be text
primary color so let's see yep that is
what we want and for both of these for
this parent da over here I'm going to
give the class name of display Flex
items to be Center and Space X to be
four let's see now sorry I had to give
this class name over here not there and
now let's see yeah that's better for
this image Dev I'm going to give the
class name of relative height and width
to be 12 and flex shrink to be zero so
yeah it will take the space available to
it for this parent div right here I'm
going to give the class name of display
Flex Flex column because the block quat
will be in the bottom of it right in
like below it and space y to be four
like this okay now you can't see much
over here right now but for this parent
Dev of this testimonial do map let's see
okay okay I've have given grd columns 2
and over here I think it should be grd
columns 3 because we just have three
items over here right so let me just
remove it and let's add gap of eight
between them and yeah now we should see
them in the middle yep something like
that okay so now let's work on this
block code so here simply let me just
bring in the code real quick we will
just have a paragraph tag which will say
text muted foreground and it will be
italic since this is a quote right and
position relative and insert this we
will first add this quote Right double
inverted commas so to write that you can
write something like this and we will
just style them text 3 XEL text primary
and Position will be absolute minus top
four and minus left to be2 similarly for
this one as well minus bottom to be four
and then we'll simply insert our
testimonial quote between them like
which looks something like this right so
yeah let's see yeah that's exactly what
we wanted in the it as well and let's
just add some background to this so what
I'll say background muted to be 50 over
here okay yeah that looks much better
now I think the only thing remaining is
frequently asked questions and this CTA
at the bottom let's just build it real
quick this will be very easy just like
what we've been doing up until now let
me just copy this section above it this
how it works section and let me just
paste it right after it let me just get
rid of everything inside this map
because we will be mapping through the
fa cu's over here this won't be a grid
so let me just remove everything over
here in like apart from these two things
on the top instead of how it works I'm
going to say frequently asked questions
and instead of this paragraph I'm going
to say find answers to most common
questions about our platform let's see
okay yeah let's just render the FAQ real
quick so we have created this fuuse data
of question and answer over here so
inside this I'm going to say return and
I'm going to return a accordion actually
over here not
accordion accordion item we will wrap
this with the accordion so let me show
you so if I go back to shat CN UI we
have this accordian component right here
just like this and to use it this is the
code I'm going to take it and paste it
right below it and we will take the
accordion item and first of all let me
just wrap this with the accordion and
over here is what we will be returning
the accordion item right let's go back
and see accordion is not defined
obviously we have not imported it so
let's import accordion from /ui folder
accordion trigger accordion content all
of this has been Tri like imported I
guess let's see yep but obviously we
have to provide the key as well right
and also it takes this value right here
so I'll say key to be index and value to
be item and whatever the index of that
item is right so now we can access every
single
separately and instead of this we will
add the dynamic value that is FAQ do
question and over here as well FAQ dot
answer let's make it full width so I'll
say class name to be width full so page
crashed let me just reload it FAQ is not
defined oh sorry FAQ over here not the
item yep just like this this is exact
what we wanted the most asked questions
about our app and then in the end we
want to have a this beautiful seat here
right which most of the SAS companies
nowadays have to make the conversion
even better so again let me just um
duplicate this section I'm going to copy
it and paste it right here you won't be
needing this accordion now let's get rid
of this div all together for this H2 I'm
going to say ready to accelerate your
career and for this paragraph I'm going
to say join thousands of professionals
who are advancing their career with AI
powered guidance now obviously we will
be changing these Styles a little bit so
instead of this section over here I'm
going to say width to be full instead of
this div I'm going to be saying margin
horizontal to be Auto padding vertical
to be 24 some gradient so if you
remember we created the gradient
Tailwind CSS class right so we will be
using over here for our background color
and rounded to be large so let's see yep
something like this we have to enhance a
little bit more for this div over here
I'm going to be saying display Flex Flex
column so that we are aligning them from
top to bottom items to be Center justify
Center so that they are in the middle
space y to be four between them text to
be Center and maximum width is going to
be 3 XEL which is around 768 pixel right
and obviously we want them in the center
so margin horizontal to be Auto for this
h2 tag I'm going to be saying in the
smaller screens three XL some font bold
and tracking tighter so that texts are
closer to each other text color to be
primary foreground in the smaller
screens it will be text 4 XEL size and
medium screen 5 XEL so let's see yep
that is what we wanted let's do the same
for the paragraph tag as well so margin
horizontal to be Auto and Max width is
going to be 600 pixels text primary
foreground but with 80% opacity and in
the medium screens text Excel like this
now in the bottom below this P tag I'm
going to be Simply Having a link tag
which will take us to the slash
dashboard page a button with size large
variant secondary and class name to be
height 11 margin top to be five and it
will bounce right this is a native
Tailwind class of animate bounce and I'm
going to Simply say start your journey
today and I'm going to add a arrow right
icon which will come from Lucid react so
link is not defined let's import link
from next SL link let's see yep there we
go this is exact ex L what we wanted
amazing so we have successfully
completed our landing page Pat your back
now let's go on and dive into juicy
stuff that is designing our database
right designing the tables inside of our
database using Prisma so first of all
let's just first go on and install
Prisma inside of your app and then what
we will do we will go on and design our
database so I'll say npm install
dashd Prisma so let me show you what
Prisma is all about so if you just
Google Prisma right here just open it up
and in the documentation let's see this
example right here of create right like
if you were to create something using a
SQL query it will be super long right
like insert into this table blah blah
blah blah it will be a headache but
Prisma makes it super easy like you can
see Prisma and let's say if you want to
insert into user table do user do create
and simply providing our data right here
super easy so the Prisma has been
installed I'm going to say and PX Prisma
in it and it will initialize our project
with Prisma over here so you're going to
notice it has created this Prisma folder
with schema. Prisma and this is the
place where we will be creating our
tables and you can see it has picked up
this database URL that we have added
inside of the EnV file and it will use
it to connect to our post G SQL database
so this is how our database will look
like so first of all we have a user
table where we will be storing all of
the information of the user now you
might be thinking we are already using
clerk for storing uh the user info right
but we will also be storing a copy of
that information inside of our database
along with few more uh things like
industry bio experience skills Etc right
so that we can access all of these
details fast and we can query the other
tables along with the user table faster
all right so as you can see we'll be
storing the ID like obviously this ID
will be generated but we will be storing
the clerk user ID over here and the
email the name the image URL and the
industry so this industry will be a
foreign key to this industry table right
here we'll come back to this apart from
that we will obviously have a created ad
and updated ad that we generally do in a
database table then we will have the bio
of the user so bio experience and skills
these are the three questions that we
will be asking the user when user is on
the onboarding screen right so we will
also be asking their um you know
industry so we will have to uh you know
categories for this one like the first
category will be for selecting the uh
industry like for example Tech Finance
etc etc and then the sub industry like
for example in Tech are you in data
science uh front end development backend
development Etc right so that is what we
will be storing over here in this
industry table and we will be generating
the industry insights and storing them
right here and as I already showed you
in the intro that we will be updating
this uh particular information every
single week right so for for example if
there are two users who are uh you know
in Tech and they are front end
developers this information will be
updated for both of them right along
with the salary ranges the growth rate
demand level what are all the top skills
that they can learn how does the market
look like in the future right key trends
what are the recommended skills that
they should be learning right so this is
the whole package that is being provided
to the user in terms of the insights
when it comes to their industry okay so
that's that apart from that the user
table is connected to these three things
these two like these three AI tools that
we have inside of our app right first
one um let's start with the resume right
so we can build our rum we will store
the markdown of the rume over here in
the content if we want we can generate
an ATS score over here I've just kept
this uh field over here even though this
is not the part of our application so
that if later on we decide to add this
feature we can add this feature right
here right but right now it's not our
priority because we are just generating
the
and alongside it we can also provide
user the feedback on this right so if
you want you can ignore these two Fields
over here as well right the major field
over here is this user ID that this
belongs to and the content that is what
is the markdown content inside this
resume and again the updated at and
created at now we will also uh like user
will also be taking the mock interview
inside of our application so we'll be
taking like we can configure it we can
ask them five questions 10 questions Etc
right so we'll be storing information
about every single assessment like what
are all the questions that were involved
in that particular assignment along with
the answers what category does it
belongs to the score of that particular
quiz if user gave any wrong answers we
can ask like we can provide them an AI
generated Improvement tip over here as
well and of course the user ID as well
right apart from that when it comes to
generating a cover letter we will have
the user ID the content of that cover
letter that is generated right the job
description from which this was
generated
the name of the company the job so
basically these three things are the
ones that we will be using to generate
our cover letter right so yeah this is
the basic structure of our database now
let's go on back to our code and let's
design all of these database models
inside of our schema. Prisma file right
here so to create a model we have to say
model and the name of that model like
for example in this case user right so
what were the things that we discussed
first of all it was the ID which will be
generated uh automatically so let's say
string and if we want we can provide it
some default value as well which we will
do it over here so I'll say this is an
ID you can read the description about it
right defines a single field ID on the
model and then I'm going to say add
default and we can provide the type of
ID this will be so I'll say uu ID so it
generates a global unique identifier
based on The UU ID spec apart from that
we will also have the clerk user ID as I
already showed you which will be unique
we will have the email name the image
URL of the user and name and image URL
I'm keeping optional over here then I'm
going to have the industry which will be
a string and as I already showed you
let's see user belong in the tech
software engine like software
development industry right so we will
combine both of them that usable from
Tech and inside of this we will have the
software development then after this we
want to connect this industry to the
industry table right so we will be
creating a industry table over over here
as well so I'll say industry insights
and obviously we'll come back to this
later but over here I'm going to say
industry insights is going to be the
type of Industry insights
table and then we will Define the
relation between this user table and
Industry table so inside the industry
table we have a field called industry if
you remember this thing right here and
we will be connecting this to the
industry field inside of the user table
that is this field right here then after
that we will obviously have the created
at and updated at Fields as well okay
then on the onboarding screen we will
ask users some questions right so I'm
going to say profile fields and I'll add
bio which will be type of string
experience which will be of type integer
and the skills which be which will be
array of skills right or string then
after that we will also store the users
resume and the assignments that they
have uh attempted first of all the
assessments the resume and the coverlet
and obviously these three will be uh
related to the separate table that we
will be creating so let me just um
create it real quick over here I'll say
model assessment let me duplicate it
twice I'll say model resume and model
cover letter so each user can have more
than one cover letter and more than one
assignment but a user can only have one
single resume all right so let's check
this assignments or the assignment table
right so let me just bring it real quick
and then I'll explain you what's going
on over here so obviously in the
assignments table we will have the ID
the user ID that this belongs to and
then we will Define the relation between
both of the table so since this is a
foreign key I'm going to say inside of
this table we have user ID Fields user
ID which references the ID of this user
table that is this particular ID of the
user table which we are storing right
here then we will obviously store the
quiz score that we got after attempting
the quiz the question so this basically
be a Json so it it will contain the
question answer the users's answer and
what is the correct answer right so all
of these information that we'll be
storing over here we will have the
category of that quiz that is if it's a
technical quiz behavioral quiz Etc what
is the you know a generated Improvement
for that particular quiz if any so this
is optional and obviously created at an
updated ad and then we are saying that
Index this table with the user ID so
that this remains unique then comes
resume again very similar to what we
have already seen ID the user ID and we
are referencing the user table over here
the content which will contain the
markdown of the resume we can if you
want you can skip both of these right as
I already mentioned I've just kept it so
that later on if I want to update
something inside this project I can add
the atss scoree feature over here as
well right so you can remove that and
then we have created it and updated it
over here as well right when it comes to
cover letter we will have the ID the
user ID again referencing that user
table the content of the markdown
content of that cover letter the job
description from which this was created
company name job title and obviously
created at and updated at and again we
are indexing this with the user ID now
let's see the industry insights this is
uh you know quite complex first of all
obviously we will have two things over
here right the ID of that particular
table and the industry that the unique
industry like I already gave you the
example tech software development right
then we will have the list of all of the
users in the industry so we will write
them just like this then after this we
will have more things like salary ranges
which we will get in this particular
format we will get the role that this
industry has like for example data
scientist or backend developer then what
is the minimum and the maximum salary
and what is the average salary so let me
show you something like this so over
here you can see we have multiple
different uh information about that
particular industry right mobile app
developer devops engineer backend
developer Etc then we have the growth
rate demand level and the top skills
right so again you can see the growth
rate the demand level and the top skills
over here right this will be an array so
I've added string array growth level
will be in the float format then next we
will have the current market condition
right and what are the key trends right
now so this can be positive neutral or
negative key trends can be array of
current industry Trends then we have the
array of recommended skills and last
updated and the next update that will be
happening for this right so which will
be next weekend so also you can see we
have a lot of these enums over here
right that this should only have these
many values this should only contain
these many values right I mean either of
these values so let's create the enim
for this so for demand level and Market
Outlook I'm going to say enum demand
level and I'll say high medium low and
Market Outlook can be positive neutral
or negative right so I can just take
this and provide it right here and same
for Market Outlook as well over here
right this is much more better and much
more robust and yeah I think uh that
should be it let's just open our
terminal and I think there's some error
over here so industry Insight I think
that should be it not insights and now
we have to say npx Prisma migrate Dev
and we have to provide the commit
message which let's say create models
and press enter so what this will do it
will push all of these schemas to our
database that is our neon database and
we can see it right there so let's just
open the neon database there we go this
is pushed and now if we go back to this
tables tab inside of our database let's
just select Sensei and uh let's see yeah
you can see all of these tables have
been created successfully amazing so now
what I want to do first thing first
let's just store our user inside of our
database right so to query Prisma inside
of our app I'll be creating a file over
here in this lib folder there I'll say
new file and I'll name it Prisma do JS
and inside of it I'm going to say
export const DB equals new prisas Prisma
client and let's import it from at
Prisma / client now if we were to use
this uh new Prisma client to query our
database every time the nextjs uses hot
reloading that is it refreshes our app
it will create a brand new Prisma client
over here as well so we don't want that
right so what we want to do we want to
store it inside of our Global variable
so I'm going to say if
process.
env. nodecore EnV is not equals to
production right so I'm going to say if
we are not on production that is we are
on the development I'm going to say
Global this. Prisma equals DB and every
time we export this I'm going to say
Global this. Prisma if this exists then
take it else we will take the new Prisma
client all right let me just add an
small explanation over here for you guys
so you can refer it later on cool then
so now to store our user inside of our
database I want to run that function
every single time my app runs right we
have to check if the user is added
inside of our database or not so what
I'll do I'll create a new file over here
called check user.js I'm going to say
export const check user equals if sync
and this will be an arrow function and
ins start of this now we will first
check if the user is logged in or not so
we will take this current user from at
clerk / nextjs SLS server make sure to
import it from this only right so we
will take this and then we will check if
user is there or not so I'm going to say
if user is not there then return null
now we will try to query our database so
I'll have a try catch block over here
inste this I'm going to say await DB and
import that DB that we just now created
do user so we're going inside the user
table and then I'm going to say find
unique we want to find one single
database which like we want to find one
single entry which is unique to you know
particular users ID so we have already
like we will be storing uh clerk user ID
right I've already showed you that so
I'm going to say where the clerk user ID
is equals to user do ID the user ID that
we got from this object user object over
here right and and then we will store it
inside of the logged in user so
basically we are checking if the user is
already stored inside of our database or
not so if it is if the user is stored
here inside of our database then we will
simply return the logged in user and we
don't have to do anything right but if
user is not stored inside of our
database then we do need to store it so
I'm going to say const name equals user.
first name space user. last name and
then to create this inside of our
database let me just copy this whole
thing up and paste it right here instead
of logged in user I'm going to say new
user and I'll say db. user. create
instead of this where I'm going to
provide the data that I'm supposed to
store so data will be clerk user ID
which will be user. ID the name of the
user that we just now created the image
URL which will be user. image URL and
the email right so user. email addresses
user can have more than one email in
clerk but we will just take the first
email address and we will store that
inside of our database and then we will
simply take this new user and return it
so return new user over here that's all
and in the console like in the catch
block I'm going to say console log and
log the error do message over here that
is all we need to do right now we will
just take this check user we will go to
our header component and making sure
this is a server component and let me
just get rid of this yeah and this
should be an asynchronous function since
this is a server component and I'll say
await check user right here every single
time we land on our app we will check if
the user is like part of our database or
not so import this check user and if
they are it will simply return it from
over here else it will create it let's
go on and check it out so now let's go
on and sign in continue with Google and
we are in this should have posted the
user inside of our database let's see if
I refresh this uh user table over here
there we go we we have the user
information inside of our database along
with the email name image URL and all of
these things obviously the industry is
empty right now and we can also see all
the tables that our user table is
connected to over here but we cannot see
anything since we don't have any data on
them right now cool then let's go on and
um you know create our onboarding page
but before that I want to explain to you
guys how routing Works in nextjs so as I
already told you guys for every single
uh route that we create like for example
over here in the O we have this layout.
JS file right so we can have a layout.
JS file and we can also have a page.js
file and page. jsx is what makes a route
possible right so for example for the
internal routes of our app we will have
the main folder right so I'm going to
group them in the main folder and
obviously this is optional you can do it
without this main folder as well but
since we want a consistent layout for
our application we will be creating a
layout
.js file over here which will wrap all
of the routes inside of our main file
like or sorry main folder like for
example I'm going to be creating a new
folder for on boarding over here and
inside this we will have a page. jsx
file let's say r fce on boarding page
right simple as that and in the layout.
JS I'll say R fce if I want this on
boarding to fall under this layout I
want to create it inside of this Main
folder right so I'm going to say main
layout and then I'm going to take the
children over here that we will be
rendering so I'll be rendering these
children right here and then if I want I
can provide them some Global Styles just
for this uh routes that fall under this
main folder right so I'll say class name
to be a container MX Auto so they are in
the very middle some margin top to be 24
so that our header does not overlaps
them and some margin bottom to be 20 and
also since all of these are protected
routes right I'm going to be adding the
logic over here so that when the user is
already onboarded we will check if like
we will land on the onboarding page and
then we will check if the onboarding
process already completed then we don't
need to do it again right so we will
redirect user after on boarding right we
will come back to this later on but
first so what are the different types of
routes in nextjs let's actually go to
next js's documentation so I'm going to
say next js. RG let's go to docs and
over here we have this project structure
page over here right okay so let's see
so since we using App router we are
inside this app folder right and there's
something called as Pages router as well
which is old next Chase right so we
don't need to worry about that so if we
come down you can see it is explaining
all of the different different files
that we have inside of our folder and
yeah this is the main thing so we have a
layout file and the page file we can
also have files like loading not found
page right the error page for each and
every single route we can also have the
nested routes right like for example if
you have created a onboarding page like
if I show you inside of our deployed app
we have the interview prep right so it
will be slash interview so this can be a
page in itself but if you click on start
new quiz you can notice it goes to/
interiew SL mock right so this will be a
nested page so let me just create it I'm
going to say new folder interview and it
can also have a new folder mock right so
this will be a uh page now so I'm going
to create a new file page. jsx R A fce
I'm going to say mock interview page and
they should open that and we can also
create a page file inside of this
interview folder so I'll say page do jsx
and let me just copy this whole thing up
over here and simply instead of mock
interview I'll just say interview page
notice this is a route on its own right
with the page. jsx and this mock is also
a separate route right okay apart from
this we can have Dynamic route segments
over here as well right so what is a
dynamic route if I go back to the app
and let's go to the cover letter so you
can see we have different different
cover letters over here right like we
can have the list of cover letters so we
I've created this one cover letter so
this is ai- cover- letter if I click on
this job like over here in this I button
it will take me to one single cover
letter so you can see this is an ID over
here right so this is going to be
dynamic every single time so we have to
create a dynamic route over here as well
so how do we create a dynamic route
let's create a route called AI cover
letter and then inside of it if I want
to create a dynamic route I have to wrap
them inside the square brackets and
let's say ID right so this will be a
dynamic route I can create a new file
called page. jsx over here let me just
add this same name code and I'm just
going to Simply say cover letter and now
inside of it we can access that
particular ID by saying params and let's
just console log perams do ID over here
or you know what I'll just display them
right here I think we need to do a wait
params do ID and const ID equals this
and this should be an asynchronous
component if you want to do this let's
take the ID and I'll say cover letter
colon ID right here right so this can be
a page on its own we can also create
this AI cover letter page so I'll say
page. jsx over here r a f c AI cover
letters page right so this can be a
separate page on its own let's try it
out so if I go back and I'll say AI
cover letter you're going to notice see
we are on this AI cover letters page and
if I try to go to some gibberish over
here and press enter you will see we
will go to that part cover letter page
so cover letter and the ID that has been
entered on this URL right here apart
from that we have this catch all route
right which like for example if you were
to enter like let me show you if I click
on it see we have SL shop SL A/B SL C
right we can have multiple different
routes after it but we don't know how
many routes are going to be there right
so that is where we create this catch
all route so it can catch all of the
routes after this particular thing like
after whatever this written before this
catch all route right so we not going to
be needing that you've already seen the
optional catch all route so this thing
right here that we used in clerk right
so over here so this is actually
optional we don't need to provide
anything over here and it will still
work fine then we will have these
grouping conventions that I already
showed you we created the O folder the
main folder right for grouping the
routes but in this case next just
ignores this and goes inside of it but
if we create a folder with let's say
underscore folder right next just we
completely ignore this folder and
everything inside of it so the best way
to use this is by creating a components
folder right like for example if I let's
we are going to be working on the
industry insights right so let's say if
I create a new folder called
dashboard we will have a page. jsx file
over here like RFC I'm going to say
industry insights page and then if you
want to use any components over here I'm
going to create a new folder called
underscore components and everything
inside fit along with this folder will
be ignored and will not be considered as
a route and then we have more types of
routes like parallel and intercepted
routes metap file conventions right so
we don't need to worry about all of
these things this is the major things
that we need to worry about for this
particular project right let me show you
how you can create a not found page so
currently we have a default not found
page right like if I go to some
gibberish over here it will show us not
found yep you can see 404 page cannot be
found but if you were to create a our
own not found page so over here inside
this app folder you can create a new
file called not- found and it has to be
exactly called this not found. jsx and
let like you can create anything over
here let me just give you a quick
example this does not really matter so
I've created this not found page over
here with simple H1 of 404 H2 of page
not found and then I've written a
message over here oops the page you were
looking for does not exist or has been
moved and then I have a return home
button Buton over here which will take
me to the homepage right simple and I've
given this gradient title if you
remember we created this some time ago
our custom title so if I go back now you
can see this looks much more beautiful
right so it's a custom 404 page if you
don't want to create it that's fine
nextjs is anyways handling it by our
default 404 page all right then let's go
on and start working on our onboarding
page so if I go to/ onboarding we will
just see this onboarding page message
over here so now if I go back over here
inside of the main folder we have this
onboarding page. jsx and over here first
of all we will check if the user is
already onboarded if they are we will
redirect them to the dashboard page and
I showed you we have added this
condition over here in the layout. JS
right so over here basically we will say
redirect to onboarding if user is not
onboarded then we will redirect them to
onboarding but if they are already
onboarded then the onboarding page will
send them back so we will come back to
the logic but over here instead of this
div I'm going to have a main tag and
inside of this I will be rendering my
onboarding form so I'll say on boarding
form so the reason why I've created a
separate component over here is because
this is going to be a client component
and client components need to use that
use client directive right we cannot use
hooks inside of This Server component
and the reason why I'm not creating this
as a client component is because we will
be making a API call over here on our
server which will check if user is
already onboarded or not and that will
be much more faster when compared to
client component right so to this
onboarding form I will send the data of
the industries so we have created this
Industries data right inside this data
folder yep there we go Industries and
now you can see this data so basically
for this data we have the iD Tech name
technology and the sub Industries inside
of this and similarly for each and every
Industries like Finance Healthcare
manufacturing etc etc we have created
the industries and sub Industries over
here as well so that all types of users
can use our app okay so let's import
this I'm going to send this as
industry or you know what industries
equals Industries and make sure to
import it from at data/ Industries and
obviously this onboarding form does not
exist yet so let's just create this
component just like I showed you we will
be creating a new folder UND score
components and I'll name this file as
onboarding Das form. jsx R A fce I'll
say onboarding form over here for this
particular component and this will take
the prop of the industries that we are
sending from right here okay now before
uh building the UI of our onboarding
form let's first create the apis for the
onboarding form right so the back end
part of these things so where do where
will we uh you know be writing our apis
the older way of writing it is in the
API folder over here but the new nexj I
think it was from nextjs 13 something
version that nextjs has introduced
server actions so we have to create a
folder over here called actions and
inside this we will be creating the
server action this will be like simple
functions which will be running on our
server so let me show you so if I create
a new file over here let's say user doj
yes since onboarding has more to do with
user right and I'm going to create a
function over here export async function
update
user right this will be our server
action and this will take some data
let's say onboarding data if will come
from the UI right and also very
important you have to write use server
on top of this so that this runs on our
server and our server components as well
right so before updating our user we
need to check if user is logged in or
not right so I'm going to say await o
and this Au will be coming from at clerk
nextjs SL server right if we get the
user ID then we will check if user ID
does not exist then obviously the user
is not logged in and we will throw
unauthorized then we will check if user
exists inside of our database or not so
I'll say const user equals a wait DB
just like we have done earlier db. user.
find unique and I'll say where we will
will'll find them using the clerk user
ID right so clerk user ID is equals to
user. ID pretty straightforward just
like we have done over here sorry not
user ID this user ID over here not user.
ID and again if user does not exist
inside of our database we will throw the
error over here cool then now that we
have that out of the way we can make the
uh you know connection with our database
over here inside the T TR catch block so
we have the data right here right what
will this data contain let see inside of
our Prisma schema right here so we will
be sending like let's see our user table
Yeah we will be sending the bio the
experience the skill set and obviously
the details about our industry okay so
now after this we will be performing two
operations over here first of all we
will find if the industry exists right
if the industry exists then we won't do
anything but if the industry does not
exist we will create it with the default
values for now but later on we will add
the you know AI logic to generate the
industry insights right away right and
the third step will be to update the
user right so we are performing three
API calls over here so this can be a
time-taking process so instead of using
just normal API calls over here we will
be using something called as transaction
that comes from Prisma right so I'll say
const result equals await DB Dot and
I'll say dollar sign transaction action
right so now what is the uh you know use
of transaction so transaction basically
makes sure that all of these three
completes if any of these fails the
transaction will fail as well and this
will give us the error right so first of
all we will take a call back over here
the asynchronous call back and the
second thing that we can provide it is
the timeout value now this is optional
by default it is 5 seconds but we are
providing 10 seconds over here because
later on we will be adding this AI logic
right so let me just take this first of
all and add it inside of this call back
so yeah this is the call back we are
providing it 10 seconds to Pro you know
complete this operation to generate all
of the AI insights regarding that
industry and then in the end we will
return the result. user that's all in
the catch block I'm going to say
console. error error updating user and
Industry and we'll provide the error.
message and we'll throw a new error that
failed to update profile that's all
let's dive into this now so to perform a
transaction we will take a TX over here
for one single transaction and we will
have three transactions over here right
so first for this step I'm going to say
cons industry insights or let's just say
let over here because we'll be
overlapping this value so industry
insights equals await TX do industry
insight. find unique we will try to find
if that particular industry already
exists right like for example front end
developer and I'll say where here data.
industry so we'll be getting this
industry from this data object that
we'll be getting over here right so I'll
check if this already exists and now if
the industry does not exist we will
create it with the default value so let
me show you I'm going to say if the
industry does not exist the industry
insights oops sorry I think it should be
industry Insight does not exist we will
simply say await tx. industry insight.
create and we will create all the
default value that we are supposed to
have in inside of our database right so
you've already seen this schema. Prisma
file over here this industry inside
right so we are providing all of these
values as default so you can see the
industry the salary range the you know
empty array growth rate zero demand
level medium top skills Market Outlook
blah blah blah and this is a temporary
thing for now right again when we later
on understand the AI stuff we will be
adding the AI generated industry
insights right here cool then after this
we will update the user so I'll say
const
updated user equals await
tx. user. update we will update the user
object that is already inside of our
database right so first of all we will
try to find that user using the user. ID
and then we'll provide the values that
we are getting that is industry
experience bio and the skills right all
these four values right here then in the
end let's return this updated user and
and this industry insights notice I have
overlapped this industry insights
variable over here so after this I'm
going to say return the updated user and
the industry Insight that's all that is
all we needed to do to create this
update user server action over here okay
and then after this let's just create
another server action for fetching the
onboarding status right so I'll just
copy it and paste it right here and
rename it to be get user onboarding
status we won't be needing any data we
are just going to be first checking if
the user is logged in or not so I'll
just take all of these things that we've
already written and I'll just paste it
over here first and then I'll say try
catch block oops try catch and this will
be fairly simple right first of all I'll
fetch the user so I'll say cons user
equals a wait DB do user do find unique
and set this I'm going to say where
clerk user ID is this ID or you can you
know also um search using this ID equals
user. ID as well from this our database
but this will also work and then I'm
going to say select the industry as well
because when we get the user object the
industry object might not be populated
we just get the ID so we want to
populate the industry as well and then
in the end we will simply return is
onboarded flag and I'll check if the
user industry exists then obviously this
will be true else this will be false and
in the catch block we will simply say
console. error error checking onboarding
status and provide the error do message
and throw new error fail to check
onboarding status that is all cool then
this these are the two API that we will
be using right here right so let's go
back to our onboarding form and let's
start building this but you know what
since we have already created uh this
get user onboarding status API why not
we just go on and add it to our page.
jsx over here right we have this
condition so I'm going to say cons
and actually I will say first await get
user onboarding status let's take it
from over here at action SL user and
over here I'm going to say const this
will provid us is onboarded you can see
it is suggesting this is onboarded and
this will be an asynchronous function
since this is a server component and if
the user is already onboarded we will
route them to the dashboard page right
that's all let me just copy it and go to
our dashboard page. jsx and I'll add it
right here as well and let's make it an
asynchronous component and I'm going to
say if user is not onboarded then we
will push it to on boarding page right
and yeah that should do it let's just
import this and import the
redirect and now if we try to go to
dashboard yeah you can see we are in the
like let's do this again if we go to
dashboard we are directed to the
onboarding form great so now if the user
is not onboarded we will be redirected
to the SL dashboard page let's go back
to this onboarding form over here and
now we will be creating the form for
basically onboarding the user so for
that we will be using two very popular
libraries react hook form and Zod so I'm
going to say npm installed react hook
form space Zord and at hook form SL
resolvers so this library is for making
sure that these two work fine with each
other so I'll press enter there we go it
has successfully installed and now since
this is going to be a client component
I'm going to be adding a use client flag
over here so inside this onboarding form
I'm going to say use form and use form
comes from react hook form over here
right so this is a hook that we'll be
using and for creating our form we will
also be creating the schemas right so
schemas that will monitor our form if
the user has entered the correct
information or not right so to create
the schemas I'm actually going to be
creating a new folder over here called
lib and inside of it I'm going to be
adding a new file called
schema. JS and here we'll be creating
all of our schema right so let's see how
do we create an schema using Zod and by
the way if you want to get more
information about react hook form and
Zord I've created a completely separate
video on this topic as well so you can
check it out from Link in description
down below so let export const on
boarding schema equals Z and this will
be imported from Zod Z doob and this
will take an object inside of it right
so first of all the
industry I'll say industry and Z do
string it will be a string and if it is
not a string we will provide the error
to the user right so I'll say requir
error and I'll say please select an
industry similarly for sub industry as
well if the user has not selected it
I'll say please select a specialization
similarly for our bio we'll say this
will be a string and Max 500 character
vors and this will be optional then we
will be adding the you know inputting
the user experience right so experience
will be Z do string but we will add some
conditions over here not conditions
actually we will transform it so we will
be inputting the experience in the
number of years right so it be provided
to us as a string let's say four or five
right so we will transform it into the
integer value before storing it inside
of our database so we will use this
transform over here and parse int
function then after this we will add
this conditions over here that means
minimum it can be zero or 50 years of
experience Max then we are going to be
having skills from the users right so z.
string and user will be inputting this
in form of the comma separated values
right so I'll Transform this into an
array over here so I'll say value do
split split it with the comma and I'll
map through each and every one of these
trim any space around them and I'll say
filter out if there's any you know empty
spaces over there so we will have the
array of strings over here cool then so
we will just simply take this onboarding
schema go back and inside of this use
form over here we will have an object
and we will say resolver and inside this
we will import the Zod resolver so this
Zod resolver will come from the you know
this library that we installed the hook
form resolvers so make sure to import it
right here Zod resolver from Hook for/
resolvers SL Zod right and this will
take our onboarding schema which will
come from that schema file that we just
created right here all right so now this
use form will return us a few things
right first of all register and this
will be very important to connect our
form with the logic right I'll show you
in just a moment it will provide us the
handle submit for submitting our form
the form state which will contain things
like errors or is dirty is loading is
submitted Etc right before now we will
just take the errors then we will take
the set value and watch to watch any
particular field if it changes right
like if we are going to be selecting the
industry only after that we are going to
be displaying the select box for our
specializations right okay uh apart from
this we are also going to be having a
use state so use State hook which will
be storing our
selected industry and this will be null
by default we will also be having a hook
called use router which will be coming
from next / navigation make sure to
import it from this only and this will
help us to navigate to some other page
so I'll say router equals use router
that's all let's go on and create our UI
over here so I'll WRA this whole form
inside of a card right so card comes
from uh Shad CN UI so let's go back to
the docs over here and card right here
so I'll just take this card code from
here and paste it over here let's import
the card card header card title card
description we'll need the card count uh
like content and we won't be needing the
card footer so let me just remove it
let's see if it's working fine yep we
can see it all over here for this parent
D let me just provide some class names
so display Flex items are going to be in
a center and justify Center as well so
that we have the onboarding form in the
center of the screen background will be
of this background color defined by Shad
CI so let's see yep like that for the
card component over here I'll say class
name width to be full and Max width is
going to be large which is around 512
pixels margin top is going to be 10 and
margin horizontal is going to be two
like this for the card title I'll say
complete your profile and card
description I'm going to say select your
industry to get personalized career
insights and recommendations let's just
provide the gradient title to this card
title so I'll say class name to be
gradient
title and text to be 4 Excel okay let's
see yep that's more like it now inside
the card content is where we will be
creating our form let's remove this and
I'll have a form tag over here inside
this form tag first of all we will have
the industry and then the uh you know
specialization select box so we're going
to be using a select tag so let's go
back to Shad CN UI and let's use this
select Yep this is the select one let's
see the code for this this is going to
be the code so just copy it and paste it
right here let me just import all of
these from at component /ui select
trigger select value select content and
select item let's see how this looks
okay yeah something like that let's wrap
this select inside of a div and we will
also be having a label right here so
I'll just say label from shat cnii
inside this I'll say industry label will
have HTML for the industry that is this
particular select tag let's map all of
the industries so we are getting this
Industries over here right Industries
array so let me get rid of two of these
and over here I'm going to say
industries. map and let's have a call
back over here and I'll move this inside
of it return let's take one single
industry and right here I'll say
industry. name and in the value
industry. ID and and the key will be
industry. ID as well let's see label is
not defined okay let's import the label
oops not from over here make sure to
import it from slash
components so let's see okay yeah that
looks good why is this theme over here
instead of this let's keep selecting
industry and the in the trigger I'm
going to say ID equals
industry for this HTML 4 right so let's
see yep that's more like it if I select
anything I'm able to select it great for
this de let's have class name of space y
to two so that we have some spacing
between both of these oops not here
actually I want to keep it on the D yep
something like this now on our select
tag I'll say on value change I'm going
to say set value and we'll change the
value of this industry right so we have
taken this set value from over here if
you remember right so I'm saying set
value and take the current value that is
selected and provide it inside of our
form and and then we will have this
selected industry updated right so I'll
update the set selected Industries and
I'll find the particular industry and
provide it inside of it so that we can
you know display the other the
specialization select box over here and
I'll say set value to be sub industry to
be empty right so we will now display
the sub industry form but before that if
user has you know not selected the
industry and just press on the submit
form button then I'm going to display
the error so we have taken this error
from over here right so I'll say error
do industry if it exists I'll display a
paragraph tag with text small and texted
500 and display the errors. industry.
message over here all right pretty cool
let's just take this D and I'm going to
duplicate it right below it to display
our sub Industries but first uh okay I
have some uh mistake over here let's
just fix it okay this will be labeled
for sub
industry ID sub industry here the value
change will be pretty simple on value
change will just be set value to set the
sub Industries value and set this
Industries I'm going to say sub industry
do SUB industry so sub industry is our
state if you remember value can be um so
we don't need to add the ID over here
this will just be a simple name right so
we can just put indd on all of these
three places so let's see let's select
anything let's select technology and
yeah now we're able to see it but we
should not be able to see it right away
so what I'll do first of all instead of
this industry I'll say specialization
and I'll put it inside of a curly braces
and also over here instead of Industry
it should be sub industry right now
we'll watch this field if it's selected
or not and then add the condition there
so before return I'm going to say const
watch industry equals watch this
industry field over here if it's
selected only then we will show it so
I'll say watch industry and over here
right so let's see if I just refresh my
page now we can just see this if I
select technology only then we are able
to select the specialization let's
provide some gap between these so in the
form I'm going to say class name to be
space Y and I'll also say on submit I'll
call the handle submit function that is
provided to us by that use form hook and
this actually takes another call back
over here so let me just create a dummy
on submit so const on submit like this
let's keep it asynchronous and this will
provide us the values so let me just
take it and just provide it for now we
don't need to worry about it for now
right so okay so we have this select
Industries and specialization done now
we need to ask user how many years of
experience do they have so I'll just
copy this div and right below it let's
get rid of this select tag and over here
this will be for the experience right so
I'll say years of experience over here
and after this I'll have the input tag
so input which will be coming from SL
components that is shad CN UI I'll say
ID equals experience type is going to be
number it can be minimum zero maximum 50
and placeholder can be enter years of
experience right and now if you remember
we have taken this register from our use
form hook right so we'll be using this
now over here so I'm going to say in the
curly Braes dot dot dot register and
then provide where like which field does
this belong to so if you press double
codes over here I'll say experience
experience yep something like this let's
take this experience and replace it over
here in the errors as well cool
similarly we will do the same thing for
our skills so I'll just copy this do and
paste it right here I'll replace with
the skills over here I'll say skills as
well with the capital S over here I'll
just have two things the ID skills and
placeholder can you know denote some
skills like python JavaScript Etc and
over here as well I'll say skills here
as well instead of experience I'll say
skills right and let's just give users
some message below it that they should
only enter it in comma separated values
right so I'll say separated multiple
skills sorry separate multiple skills
with commas and this will be a small
text with muted foreground so let's see
yep something like this let's copy this
up and again paste it right here now we
will add the professional bio field so
I'll say bio this ID as well bio
placeholder can be tell us about your
professional background let's have a
height of 32 so this will not be the
input actually this will be text area
from uh shat CN so import that instead
of skills I'll say bio over here let's
get rid of this paragraph tag instead of
skills again I'll say bio over here then
I think that is all we needed to do
let's see our form yep that looks more
like it let's add the submit button on
the bottom so I'm going to say button
which will be imported from Chad CI type
submit and class name will be width full
and I'll say complete profile right so
let's see yep that's more like it and
now let's try to submit our form right
so let's see over here in the onsubmit
let me just console log this simply for
now so I'll say values let's see if I go
to inspect in the console let's try to
select the industry sub industry can be
software development years of experience
let's say four skills let's say
JavaScript react etc etc professional
bio can be anything right let's click on
complete profile and yep there we go we
are getting this object right here let's
not provide the years of experience and
you can see we are saying expected
number received n if I click on complete
profile it won't allow us to complete
our profile now if I just refresh it and
just click on complete profile you can
see we are getting these errors over
here if I just select this we are
getting the error please select our
specialization right so this is made
possible by using Zod so now we are
getting all of these values over here
right in this values object how do we
make the API call to our back end so we
have to call this update user API right
this is not a server component so we
cannot just use the await and call it
directly to use it inside of our client
component we have to use a traditional
way of you know using using something
like fetch right fetch or axio or
something like that and to manage that
we would have to manage some separate
states like loading error and maintain
data State etc etc right we have to use
that again and again so what if we
created a custom hook that will manage
all of this for us so what you can do
you can create a hooks folder over here
ins start this I'm going to create a new
file called use fetch. JS right so this
is the custom hook that we will be
creating JS or jsx whatever you want to
call it and if you don't know about what
you know hooks are these are basically
just like a normal functions but with
super power of react right and they are
written by using use keyword in the
beginning so let's say for this one I'll
say const use fetch equals whatever this
is right this is basically a normal
function and I'm going to take a call
back inside of it which is going to be
basically a API call in this case our
create project call
so what will this contain it will
contain the three states that I just
discussed data loading and error right
all of these three states which we
create again and again when we have to
do an API call let's create a FN
function over here and this is going to
be an asynchronous function which is the
only thing that we would be needing to
call in the end when we have to trigger
an API call and we will write all of our
logic inside of it which we're going to
reuse again and again right right and in
the end I'm going to return data loading
error and this function right here and
also if you want to you know manipulate
this data from outside I'm just going to
send this set data as well and I'll say
export default use fetch what's going on
over here uh let me just import use
State just like this okay now insert
this function over here first of all
before fetching our API we're going to
say set loading to true and set error
Tel right oops sorry it is export
default not export delete yeah so set
loading and set error by default values
this will be true and this will be null
then inside of this I'm going to have a
TR catch block for handling our errors
and all inside the tri block I'm going
to say const response equals a wait call
back whatever this call back is and
let's if you want to provide it with
some extra arguments I'm going to take
this args from over here and provide it
inside of this ARX then I'll say set
data to whatever the response that we
get from it so I'll say response and set
error to be null but what if we do get
any error over here then I'll say set
error to be error now after this set
error I'm going to be displaying a toast
as well to the user right and how do we
do that so I'll go to shat CI docs and
we have something called a serer that we
installed so if I click on show toast
you can see we are able to show these
toast so this is exactly what we will be
using
so to configure it we have to first you
know add this toaster to the code so
inside of our app in the layout. JS
right below main I'm going to add this
toaster import it from sonor and this
also takes Rich color I believe yeah
Rich colors which will display different
different colors for different different
types of toasts right warning success
etc etc and now simply over here I'll
say toast import it from sonor do error
and I'll say error. message and then in
the end finally I'm going to say set
loading to false that's all now let's
take this use fetch hook and go back to
our onboarding form and let's right
before it I'm going to say use fetch
oops use Fetch and import it and this
will take our server action that is
update user and we'll provide us a
couple of things like three things right
data function loading and error so error
we won't be needing I'll just say
loading and rename it to update loading
function rename it to update user
function data update it to update result
right these three things are the ones
that we are going to be needing and
let's call this update user function
inside of our onsubmit function over
here so simply I'll have a try catch
block over here first of all we will get
two things inside of uh like we will get
multiple things out of this values right
so if I show you see we are getting the
industry and sub indust industry so what
I'll do I'll combine them to form the ID
so what I'll say const formatted
industry is equals to value. industry-
value. sub industry and I'll take the
sub-industry convert it into the
lowercase and replace it replace the
space with the dash so it would look
something like this so Tech Das
software-development something like this
let's remove this and then after that
I'm going to call that function so I'll
say await whatever the values that we
are providing and the industry key with
this formatted industry variable and in
the catch I'll throw the error of
onboarding error this error we actually
don't need to handle catch over here
since we're already doing it inside of
use fetch but again it depends on you
right so cool then that's that after we
have submitted the form I'll run a use
effect hook so use effect which will run
when the component is loaded but will
run when something inside this
dependency array changes so I'll put the
update
result and update loading these two
things inside of it so that it will only
run when these two things change right
so first of all I'll say if update
result do success is true or update
loading is false right that is it has
fetched and we are no longer loading
right then what will we do first of all
we will show profile updated
successfully and let's import this toast
from soner then we will use this router
over here and I'll say router. push it
to dashboard and router. refresh right
we will refresh the page and yeah I
think uh that this should do it let's
just try it out I'll come back to this
and let's say industry technology
specialization software development
years of experience 4 let's select the
Industries or let's enter the skills
over here professional bio I am the best
complete profile and oh we have to add
the that loading indicator over here as
well right and anyway this failed right
here good thing now let's just add the
loading indicator to our button so this
button in the bottom whenever this is
loading I'll first of all disable it
right so I'll say disabled update
loading and over here instead this
complete profile I'm going to say update
loading is true show this loader right
here imported from Lucid react and I'll
spin this loader and show Saving else
I'll say complete profile okay let's see
what the error was go to terminal by the
way server components will display their
errors in this over here and the client
components will display in the inspect
so of course this is a client component
so let's just go to our use form hook
use fetch hook and uh so we are
displaying error. message over here I
think we are not properly handling the
errors so in the actions user.js in the
update user I'll say fail to update user
profile and I'll also add this error do
message message plus error do message
let's see now it should give us the
exact reason of failure so if I click on
complete profile okay uh what is it
invalid value of argument demand level
expected so are we not sending the
demand level let's see over here demand
level is being provided it is medium
over here instead of I think it should
be Capital medium instead of small
medium and it should be neutral
something like this probably with
respect to our enum also one thing I uh
made a mistake over here I realized so
we're returning result. user it should
not be this we should be returning
success to be true and apart from that
whatever the result is right so now
let's see let's select the industry
technology specialization software
development let me just open that uh
Network tab over here as well years of
experience for skills to be these
professional bio and yeah let's just
click on complete profile let's see if
it works okay profile completed
successfully and now we are routed to/
dashboard page amazing let's go back to
our database and refresh on this
industry insights table yep we can see
the industry insights have been added
those default values have been added for
now in the user we can see in the
industry we have this tech software
developer and this is connected to the
user table we can see in the end right
over here amazing so we have
successfully created our onboard cing
flow awesome let's go on and generate
all of these industries insights using
Ai and we will be displaying them right
here just like this right cool then
let's go first of all we would be
needing the Gemini API key so just
Google Gemini API key and select this
first link and you will land on this
page let's click on get a Gemini key in
Gemini studio so yep so click on this
create API key and over here just select
this Gemini API and click on create API
key in existing project and by the way
this is free to use so you can see over
here I've been proved my API key I'll
just click on copy over here and I'll go
back to my project in this EnV file and
I will add Gemini uncore API uncore key
right over here great let's close this
en EnV now and don't worry about the
pricing and stuff because they have a
very generous free plan as well if you
want to see I think you go to ai.
gooogle dodev SL prising and you can
find the pricing for all of these models
over here we will be using Gemini 1.5
flash so this has 1 million token per
minute limit over here let's go on and
create our API first over here so I'm
going to create a new file called
dashboard. JS to create our user
insights API over here to generate the
AI insights so let's first of all create
the server action to get the industry
insights from our database right so I'll
say use server just like we did earlier
since we're making a server action and
I'm going to say export async function
get industry insights all right similar
to before we will first check if the
user is logged in or not so simply I'm
going to copy this thing and paste it
right here let's import o from clerk sl/
server and also DB as well now after it
I'm going to check if the industry
insights exists inside of this or not so
basically this thing right here what we
have done over here right let me just
copy it up and paste it right here oops
I'll paste it right here but this will
not be as easy as this right so I'm
going to remove everything inside of
this and I'll say if user. industry
Insight does not exist now we are going
to be generating them using AI right so
let's just create a function on top of
it export cons generate AI insights
equals and this will be an
asynchronous arrow function this will
take the industry as an input and this
will generate the AI insights for it so
I'm going to take this and I'll come
back over here I'll say const insights
and we will say await generate AI
insights and we will provide user.
industry over here for which we are
supposed to generate the AI insights and
after that simply what we have done
earlier over here we have done this
industry insight. create right so that
is what we will do over here going to
say const
industry Insight equals a weit DB do
industry insight. create and here we
will provide it with the data which we
will be receiving from right here right
so first of all I'm going to say
whatever the users's industry is so I'll
say industry is equals to user industry
I'll take this insights and I'll spread
these over here and then I'll tell the
user when the next update is going to be
happening over here right so I'll say
next update equals new date date do now
and I'll add one week time to it right
so after one week we will be having a
next update for this thing cool then
after this we will simply return the
industry Insight but if let's say we
already had the industry insight over
here then we don't need to do anything
we will return the user. industry
Insight over here also we will be
getting this generate AI Insight right
so this is exactly what we will be using
over here as well we will replace this
code with this code right here very soon
so inside of it let's see how do we make
a generative Ai call right first of all
we need to install a package to use
Gemini API right so I'll say npm install
at Google SL generative d a and press
enter there we go it has installed now
on the top I'm going to say new Google
generative Ai and make sure to import it
from at Google / generative Ai and this
will take our API key so I'll say
process. env.
Gemini
apore key I'll say cons gen AI equals
this okay now that we have this instance
of this generative AI class I'm going to
say const model I'll Define what model
I'm going to be using over here so I'll
say gen. getet generative model and
insert this this will take an object
I'll say model and we can choose the
model that we're supposed to use over
here so I'm going to say Gemini D 1.5
Das flash so I'll be using Gemini 1.5
flash cool then we will take this model
object over here and we will go inside
of this function and we will prompt it
right so first of all let me just write
my prompt over here real quick just like
this and I know it's way too big so if
you want you can go on and you can copy
it up from my GitHub repo you can simply
go to this dashboard. JS and I've
written the complete prompt right here
right so what what is this prompt all
about so I'm saying that analyze the
current state of this industry that we
have over here and provide insights in
only the following Json format right
this is the Json format that we want
without any additional notes or
explanation so for the salary Rangers
I've already showed you right what are
all the types of these things is so so
I've just provided over here this for
this one enum of high medium low
positive negative etc etc right and in
the end we have again mention return
only the Json no additional text notes
or markdown formatting we're supposed to
include at least five roles for the
salary ranges and the growth rate has to
be in the percentage include at least
five skills and train awesome now how do
we make use of this prompt so after this
I'm going to say const result equals a
wait mod model dot generate content
right this will allow us to prompt the
model so I'll say prompt I'll provide
the prompt over here and this will
return us some response so I'll say
response
equals result do response okay now that
we have the response we need to fetch
the text from inside of it so let me
show you how the response will be so
there we go I've opened the
documentation for the Gemini and here we
go in the response body this is how you
will get the response after the code
that we have written and if we just want
the text like for example over here you
can see we have the text string over
here right if we want this we have to do
something like this so over here if I go
to the top see they've mentioned
response. text this function is what we
will have to use right so if I go back
I'm going to say
response. text and I'll call this
function right here and this should give
us the text response provided by this uh
API now this is actually a little bit
weird so it provides us a response in
the following format like for example in
starting it will add something like this
right and in the end as well it will add
something on the Json so we need to
clean this response right so what I'll
do I'll say const cleaned text equals
text. replace we'll take this text and
replace it whatever this these things
are over here and the Json and
everything that is additional things
that are written over here I will remove
all of them with nothing right and just
keep the normal response normal text
response over here and I'll trim it if
there's any spaces around it as well
right that's all so then we should get
some object like this now all we have to
do is to parse it and convert it back to
an object you know right now it's a
string so I'll just say return json.
pars and clean text there we go and I
believe that that is all we need to do
over here and this function should now
work this get industry insights function
should now work all right so what do
I'll go back to this dashboard page. jsx
and actually first we need to create a
layout file over here as well to show
the loading indicator and stuff so I'll
say layout. JS so I'm going to say r a
fce layout let's take the children oops
children over here and I'll render them
right here okay but before them I'm
going to have a div over here which will
render a H1 tag which will simply say
industry insights let's give it some
horizontal padding and this D the class
name of display Flex items to be Center
justify Center and margin bottom to be
two so this is on the very middle of the
screen right and for this H1 tag I'm
going to say text to be 6xl font bold
and gradient title so that is is
displayed in our gradient format right
and regarding this children over here
right we will be making an API call
inside of page. jsx so that will be fet
that will be fetched on our server right
so we have to something called as
suspense that is until that is fetched
we want to show some loading indicator
right we will put the children inside of
it let's import suspense from react and
I'm going to say fall back and over here
we can show some fall back UI which can
probably be a loader right so let's
import a like let's install a library
over here npm install react Das Spinners
right so this will install a bar loader
as well which I want to use over here
like let me show you Yep this this thing
is what I want we have more types of
loaders over here if you want you can
use some other loader that's fine let's
close this and if I go back it's
installed and over here I'm going to say
bar loader let's import it from react
Spinners and I'm going to give it some
props like class name to be margin top
of four width to be 100% and color is
going to be gray cool then okay so the
default export is not a react component
in dashboard / page let's see what's
wrong so print page. jsx it is a react
component what's going on let me refresh
this there we go it's working we can see
the industry insights heading over here
great also I forgot to do one thing if
we go back to this user.js I mentioned
that we will be replacing this uh with
the AI generation later on right so I'll
just uh remove this for now and what
I'll do I'll go back to my dashboard. J
so we're doing this over here right so
let me just copy it and paste it right
here and instead of const I'll just
remove the con from here and yeah that
is all we need to do and this will fetch
our AI insights let's just import it and
now what I'll do let's go back and
delete the industry insights the empty
uh industry insights that we kept over
here right so let me just go on and
delete one record and now go back to our
app refresh it this should take us to
our onboarding page yep like this so now
let's select technology I will choose
software development years of experience
let say four I'll choose the same thing
and professional bio okay let's click on
complete profile and let's see if this
you know fetches the AI insights or not
okay looks like something failed
argument industry might not be null uh
let's check okay so over here instead of
user. Industry we're supposed to provide
this data. industry right because that
is where our industry exists for now so
make sure to replace on both of these
places and now let's try it again I'll
click on complete profile there we go
profile completed successfully and now
let's go back to our database and
refresh this page let's see if we have
the AI generated insights or not there
we go amazing we can see all of these
salary ranges over here for different
different uh roles if I go back uh
forward you can see growth rate demand
level high top skills let's click on
this yep we have some top skills Market
Outlook positive and other things have
been populated as well amazing so Yep
this is how easy it is to generate stuff
using AI apis like Min let's continue to
build this page now I'll go back to my
code inside of this where is it yeah
page. jsx now over here first of all I'm
supposed to fetch the industry insights
right so apart from this I'm going to
say con insights equals await get
industry insights this is the server
action that we just now created if I go
to it you can see it takes us to the
dashboard. JS page right so this is
where we will get all of the insights
over here inside this D I'm going to be
creating a component called
dashboard view so again we're creating
this separate component because we want
this to be a client component and this
right here is a server component so I'll
say dashboard view insights equals
insights okay this obviously does not
exist first of all I'll just give this
some class name class name of container
and margin horizontal to be Auto so that
they are in the middle okay let's get
this dashboard view component so insert
this components right here I'll say new
file Dash
board- view. jsx r a fce dashboard View
and this will take the
insights all right also here we will be
using like we will be displaying charts
and stuff as well right so if I go to
our deployed app like this right so for
this thing we will be using something
called as recharts library right so also
I think we have some error over here
let's make this component use client
first and then we'll come back to that
error also we need to install the
recharge library right npm install
recharts press enter let's go back and
see I think that error was temporary no
it still exists so it's saying that uh
unique constant failed on fields
industry what's wrong let's see I think
this error is over here in the
dashboard. JS file so while fetching the
user from our DB we're supposed to
include the inter industry insight as
well right so I'm going to say wear this
and include industry insight to be true
now this should work this should fetch
if the industry Insight is there or not
okay yeah now it's a separate error
dashboard view is not defined so
dashboard view let's import it so let's
go back to the page. jsx and over here
I'll import dashboard view component now
let's go back there we go it is rendered
great now let's prepare our data that we
are supposed to display right over here
like this like Market Outlook industry
grow demand level top scale uh
categorizing you know salaries by the
role all of these key trends and
recommended skills right so I'll go back
to the dashboard view component and
inside of it first of all for the salary
data so let's see how we structured it
so it looks something like this right
roll minimum maximum for each uh you
know role and the location as well so
I'll simply over here I'll say const
salary data is equals to insights do
salary ranges do map and I'll map
through each and every one of them and
I'll categorize them in name Min Max and
medium right so the reason why I'm
structuring them in this way so that
recharts is able to render them let me
show you so if I go to recharts library
and over here we would need this simple
bar chart so over here simple bar chart
you can see this is kind of similar to
our example it's just our example has
three values so you can see name and it
has one value second value and the
amount right the this thing amount over
here so yeah we will come back to this
but let's prepare our other data as well
so apart from salary data let's
structure the demand level so what about
the demand level so it can be high
medium or low right so I'm going to say
I'm going to create a get demand level
color function over here which will take
the level and we'll have a switch case
first of all we'll convert it into the
lower case right so I'll say if it's
high it's green medium yellow yellow low
red by default it will be gray 500 okay
then after it for our Market Outlook it
can be positive neutral or negative I'm
going to create a similar function over
here if it's positive it will have the
trending up icon from Lucid react for
neutral it will have line chart else it
will have the trending down right and
respective colors for each and every one
of them okay and since Market Outlook
will just have one single value so let's
just U you know calculate the icon and
color right below it so I'm going to say
Outlook icon is equals to and I'll take
this function over here and I'll provide
the insights. Market Outlook to it same
with the color as well and I'll take the
icon and the color respectively now we
want to format the date because we are
also supposed to display this thing over
here so if I go to deployed website
we're supposed to display this last
updated right and it's optional if you
want to add the next updated or not I'm
simply rendering it right here next
update in these many days right okay so
for last updated I'm going to something
like this I'll use the format function
which will come from date- FNS Library
which is an awesome Library if you want
to manage dates inside of your
application so I'm going to say npm
install date- FNS and press enter I
would highly recommend you to you know
read more about it from their
documentation so they have a few
functions that we will be needing inside
of this project first of all the format
which will be used for formatting our
dates right we have provided this date
of last updated and I'm saying format it
in this format right here next to
calculate the distance between the
today's date and the date when this will
be next updated I'm going to something
like this const next update distance is
equals to format distance to now so we
will also be taking it from date- FNS
and I'll provide it with the first date
that is the next update date and it will
calculate the distance from now to this
this particular date right so let's just
import both of these so on the top I'm
going to say import format and format
distance to now from date- FNS great and
also this add suffix basically adds this
x ago or X days remaining something like
that in our local language right that's
why we need ad suffix over here cool
then so I guess we have prepared our
whole data let's start rendering them
right over here right so instead of it
first of all you can see we have to
render this last updated badge so we
have uh already installed badge from
shat CN UI if you want to see this you
can go right over here in the badge and
this is how you use it right so let me
just copy it up and inside of this div
I'm going to have another div with the
badge which will simply render last
updated to be this last updated date
let's just give this the class name of
display Flex justify between and items
to be Center and I'll provide this
parent Dev as well some space y of six
between everything inside of it right so
let's see how this looks okay I have not
imported badge so import badge from SL
components folder yep something like
this let's add those cards over here for
our Market overview and for cards
obviously we'll be getting cards from
Shad cnii so in the Shad cnii docs I'll
go to card and I'll copy this thing up
copy it up coming back and below this de
I'm going to have our D which will
contain all of them so let's paste the
first card and we are not going to be
needing the footer let's import the
header the title not going to be needing
the description let's import this card
card content and yeah let's see how does
this look yeah pretty cool for this one
the title will be Market Outlook and
let's display the icon below it so it
will be this Outlook icon and I'll make
it this Outlook color as well so I'll
say Outlook icon
and class name will be H1 like height to
be four width to be four and I'll give
this the color that is according to the
Market Outlook so let's see yep
something like that and inside of the
card content I'm going to be adding two
things first the div which will say how
the market is right like positive
negative Etc and the next update so next
update distance is in 7 days and you can
see it was last updated today so it
shows this thing right here okay for
this card header I'm going to say
display Flex items to be Center justify
between so that they are in the very
center space y to be zero between them
and padding bottom to be two so let's
see yep something like that and for the
card title I'll say text to be small and
font to be medium and actually I'll say
Flex row over here so that this is in
one single row okay yeah that looks good
let's make space for four different divs
over here right so I'm going to give
this display grid for the parent div and
grid columns 1 for the smaller screens
in the medium screens I'll say grid
columns to be two but in the larger
screens so I'll say I'll say grid
columns to be four and some gap of four
between them so now you will see yep we
have one single card and we have space
for four more cards over here let's just
duplicate this card three more times so
1 2 3 Let's see yep something like this
now let's start updating each card one
by one let me just put some gap between
them so that it's easier to
differentiate so for the second card I'm
going to render the industry growth and
this will have the trending up icon
right to demonstrate the industry growth
and I'll render over here the percentage
at which the industry is growing so I'm
going to say insights. growth rate. to
fix and I'll add the percentage over
here all of these things are already
provided to us using the AI insights
right and then after this we will render
the progress component so if you go back
to shat scen ui's documentation over
here we have a progress somewhere around
here yep this one so this is what we
will be rendering so let's take this and
I'll paste it over here make sure to
import it from SL components and value
will be this thing insights. growth rate
so I'll copy and paste it over here and
simply I'll say class name to be margin
top to two yep something like this let's
see how it looks like I come over here
yep this looks good after this for the
third card I will render the demand
level and simply over here I'll say
insights do demand level and after this
instead of this P tag I'm going to have
a div over here which will just display
the demand level so let me just bring in
real quick so over here I'm going to say
a div with the class name of height two
width full rounded full and margin top
to be two so we are just going to be
showing over here a color right so we
using this get demand level now if you
want you can also use over here this
progress bar right you just have to keep
the value to 100% and change the color
so it would look something like this
right and let's change this icon over
here as well instead of this Outlook
icon I'm going to say briefcase icon
with the text muted foreground let's see
I have not imported it so import it and
let's see yep something like this then
for the fourth card let's display the
top skills over here so instead of
Market Outlook I'll say top skills and
here I'm going to going to render the
brain icon from Lucid react and then
right here I'll just get rid of it and
we will have our div and inside of this
div I will render all of the skills
right so I'll say insights. toop skills.
map and I will render a badge icon over
here simply like the a badge over here
with this skill and variant is going to
be secondary and instead of this class
I'm going to say display Flex Flex to be
wrap so that they can go to uh you know
next line and GAP to be one between them
let's see yep something like this this
looks pretty good so far now below this
we are supposed to render the chart
right the graph over here so let me just
take this card and duplicate it one more
time and we are not going to be needing
these class names over here now let's
get rid of this insert the card header
simply we will have two things right the
card title salary ranges by rule and the
card description let's import displaying
minimum median and maximum salaries in
thousands right in obviously dollars
let's remove everything inside of this
card content over here let's see how
this looks okay there we go we are
supposed to display the chart below it
so let's go to the recharge library and
over here this is what we will be
getting this responsive container right
so I'll just copy this whole thing up
till this point and insert this I'll
have a div which which will have a class
name of height to be 400 pixels and to
give it in pixels you have to wrap it in
this square bracket and now paste it
right here right so import responsive
container this bar chart from recharts
this cartasi grid
xais y AIS import the tool tip we don't
need the legend but we would be needing
these bar component over here right so
import the bar component and obviously
I'll come to these one by one so let's
see from the beginning so this
responsive container is basically to
make sure this is responsive in the
smaller screens as well the chart is
responsive in smaller screens right so
this would need some data over here the
bar chart so we will provide it this um
data of salary that we have created
earlier so salary data if you remember
this salary data right here we'll
provide it in the data field and that's
all let's remove this width height and
margin from over here then in the
Cartesian grid we will leave it as it is
x-axis data key will be the name y AIS
we will leave it as it is in the tool
tape yep so over here if you go back to
our deployed app we have this in the
tool TI right the name of the role
minimum salary medium salary and the max
salary right so this tool tip actually
takes something called as insert this we
can have a call back function so
something like this and we can take
things from our data inside of it which
are active payload and label so what is
active active is basically if we are
hovering on any one of the bar charts it
is true right and if it's false then
tool tip is not displayed okay what is
payload payload contains basically all
of the data that is inside of over here
in the salary data and the label will
basically be the name so this thing
right here so this name right here right
cool then let's go back inside of this
call back and simply I'm going to check
if we are hovering on this that this is
active and payload has something and
payload do length has something right
then we'll do something inside of it
what will we do I'm going to say return
a Dev over here with the label and then
after that we have the payload right so
in the payload we have three things the
minimum salary the maximum salary and
the median salary so I'll render them
one by one the name like the median Max
etc etc and the value right in K like in
these many k dollars,
right if not I'm going to return null in
the very end and obviously we're going
to provide them some styles again you
are free to customize them as you want
I've just provided some background color
and some Shadow to it and some font
medium to this P let's see how it looks
so if I come over here okay it's saying
page not found I believe we have to do
more over here rectangle is not defined
okay yeah of course we have not
configure this bar at the bottom so let
me show you quickly what will we do over
here so it takes three things data fill
and active bar so each bar represents
like min max or median so over here I'm
going to display this so first will be
data key Min fill you can you know
provide any color I've provided a shade
of gray over here all of these are the
Shades of Gray and the name me m salary
to be this thing data key is main over
here it's important that it is what you
have written right here right min max
and median so now this should render yep
something like this you can see and when
we are hovering on these we can see the
label over here as well this looks
beautiful now after this inside of our
deployed website we can see we're
supposed to render the industry Trends
and recommended skills this will be easy
right let's go back to our code below
this card I'm going to actually
duplicate this card again and let's
remove everything inside of the card
content and for the title and
description I'm simply going to say key
industry Trends and current trends
shaping the industry and also we will
have two cards over here let me just
duplicate it and put both of these cards
inside of a d so that we can you know
create a grid of these two cards so I'll
give this the class name of display grid
grid columns one in smaller screens
above medium screens it will be two
columns and gap of four between them
let's see if we come down yep something
like this now we have the key trends so
we should be displaying them in points
just like this right so we'll be using a
ul and Li tags so inside of the card
content I'll say UL tag let's give this
the class name of space y to be four and
inside of this UL tag I'm going to say
insights. key trends. map we will take
each and every Trend and we will render
inside of a list item right with display
Flex items start and Space X tob2
between like both of these three like
both of these things right first it's
going to be a DOT over here and then a
span we have added this dot because by
default chat cnii you know removes all
of these styles from CSS so yeah we will
render the trend and if I go back yeah
this is our app so you can see it has
been rendered right here for the next
recommended skills right so I'll just
provide the title and description over
here and skills to consider while de uh
developing inside of this card content
again we will render the recommended
skills array as well everything is like
absolutely the same right we just at
this point we just repeating stuff so
we're just rendering all of the badge
just like we did earlier for this thing
right here top skills so yeah you can
see recommended skills has been rendered
and yeah with this we have completed
this industry Insight page now one very
crucial thing remaining over here and
that is we need to make sure that we
fetch it every single week right so now
we have to come back to inest and we
have to configure our background job for
this so what I'll do I'll go to my let's
just compress it and go to this lib
folder inside the inist and client.js
and actually functions. JS not client.js
and here we will be creating our
function which will be making the use of
gini API and fetching the industry
insights so here I'm going to be
creating a new function Sol export const
generate industry insights equals inest
do create function just like we did
earlier similar to this we will be
creating a new inest function and you
know what let's just go on and run our
injust client so npx inest CLI at latest
Dev right so press enter okay let's open
this Local Host link and there we go if
you go to the apps you can see it shows
no functions registered so never mind
we'll come back to this so over here
inside of this create function as I told
you the first object takes multiple
things like ID name etc etc right so
I'll just provide the name which will be
generate industry insights then we will
have another object over here where we
will be creating our Cron job now what
is a Cron job so cron Expressions look
something like this right now you might
be confused what is this all about so
this basically means that this job this
function will run every single Sunday at
midnight now how do you know what Crown
to write over here first of all each and
every one of these mean something right
so if I show you I've opened claw AI
over here and I've simply asked it to
explain what cron is right so this will
be more easier for you to understand so
each and every one of these mean
something like first one means the
minute then the hour then the day of the
month then the month and the day of the
week right it can be 0 to 6 like if we
want to run it on Sunday we would put
zero over here right like just like
they've given this example over here we
can Define exactly on that particular
day what minute this will run so I'll
put zero what hour I'll put zero that is
midnight day of the month I will put
star that means every single day of the
month and what which month that means
I put a star over here that means every
single month of the year right so this
is how you write Chon expressions and
again if you want to you know write cron
for anything simply come over here to
CLA or you know chat GPT and ask it to
generate a cron expression to you that
is much more easier right so coming back
to this after this we will have a call
back function right so I'll take the
call back function this will be an
asynchronous call back function and
again we will take the steps over here
so as I already uh showed you earlier we
have different different steps for a
particular job right so first of all we
will fetch all of the industries over
here so we want to refetch all of the
data for the industries right so we want
to uniquely fetch the industries so I'm
going to say const Industries equals
await step. run so this is the first
step that we are running and this is
called as fetch Industries and start
this we have provided this call back
what it's doing over here it's taking
this DB let me just import DB over here
it's saying db. indust insights. find
many and we are selecting all of these
industries right over here okay so
inside this Industries we will get all
the industries cool then we will Loop
through all like each and every one of
them so I'll say for const industry
industry of Industries right so
basically we are taking out this
industry from each and every object of
these industries right inside this for
Loop now again we will be running that
API call over here so in just actually
provides some functions right out of the
box for making the uh you know API calls
so let me show you in their
documentation yep over here in the AI
inference you can see they have this
step. a. RP and step. a. infer these two
types of functions what we will be using
is step. a. RP over here if we further
scroll down you can see not this one if
we further scroll down you can see this
is an example of step. a. infer simply
we are providing this unnamed and then
we're sying step. a. models this is an
example of open AI model but if you want
to use Gemini it also supports Gemini
right and we can provide the exact model
name for it and then we can provide the
prompt and you might be thinking what uh
you know what is the benefit of using
these right r or infer so basically
these functions provide us additional
information about our API right like
metrics data sets and adds the
monitorings to our calls right so it is
much more easy easier to Monitor and get
the information from these apis so this
is what we will be using so first of all
we will be providing it any name you can
name it whatever right then we'll be
providing it a call back function we'll
be fetching the content and then the
prompt right so if I go back now let's
bring our prompt back so we had written
it over here in the dashboard yep let's
take this prompt from over here and I'll
paste it right here after this we'll be
making use of ai. trap right so I'll say
con
response equals await step. a do WP and
you can read more information about it
over here as well now what should we
call it let's call it Gemini then let's
have a call back function over here
let's make it an asynchronous since
we'll be fetching the API data then the
third thing will be our prompt right so
how do we get the prompt inside of this
function we'll get it from right over
here and inside of this simply we will
be using the same thing this await
model. generate content so I'll paste it
right here instead of prompt I'll
provide p over here and I'll say return
cool so this should give us the response
that we need now from inside of it we
are supposed to fetch our text right so
after this I'm going to say const text
equals r s.
response. candidates you you might
remember earlier in the video I showed
you what response jini AP provides us so
candidates zeroth index we'll take the
content we'll take the parts zeroth
index and then we will get the text if
you want to get more information about
it you can just you know simply console
log and you will realize it's this thing
right okay if this is not there we'll
just take it as an empty string and then
obviously we're supposed to clean it as
well just like we did earlier over here
clean text so I'll paste it right here
cons clean text equals text. replace
this and trim and yeah that's all after
this we will do json. and provide it
with this clean text cool then now all
we need to do is need to update our
database right so I'll say a wait step.
run in this step we will be updating our
database so I'll say update let's keep
it inside of back Tex actually update
this particular industry so industry
insights and then we will provide the
call back after it so it will be an
asynchronous call back it will be a
simple API call to our database just
like we have done over here this thing
right here I'll just copy it up and
paste it right here so db. industry
insight. update and I'll say where
industry is this and the data can be
I'll say insights and I'll update the
next update so whatever the date do now
is and I'll add one week to it and I'll
add the last updated as well which is
going to be the current date which I
think will automatically generated by
our DB as well right so yeah I think
that should do it the only only thing
remaining over here is this how do we
provide the API key to inest right so we
have to basically go to our client.js
and over here after the name I'm going
to provide credentials inside of the
credentials it will take Gemini and the
API key which is our Gemini API key yep
that is all let's go and try it out so
I'll go back to my inest de oh sorry I
have not provided it to our API right
arang just API so instead of our app the
API route. JS yep let's provide this
function over here generate industry
insights and now if we go back and
refresh it you can see one function
found let's go to the functions yep
there we go let's click on invoke and
invoke function and let's see how this
is running okay it has fetched the
industries but something went wrong with
Gemini let's see what went wrong model
is not defined okay let me just cancel
this job and see what's the error so
model yeah this one we need to have the
model right just like we did over here
yeah we need to copy it up and actually
and I realize uh this will get the
Gemini API key from here as well but
that credentials that I showed you that
we added that can work in you know cases
when we are using ai. infer right which
uses the model like this like this right
in this case we don't need to create
models separately it's just taking it
directly from step. a. models right so
yeah let's just copy it up and paste it
right over here on the top let's import
Google generative AI over here and now
we should uh be good to go let's rerun
it let's close this one and open this
okay fetch Industries it worked let's
see if Gemini works or not okay looks
like Gemini has fetched the data and you
can see this is the exact content that
we get in the response and see this is
what we were trying to clean from our
response and if we go further yep we
have updated all of the industries as
well amazing so you see how easy it was
to set up a background Cron job you
might have thought I'm pretty sure that
this would be something very difficult
but see it was a piece of cake I would
say with the help of inist creating a
background function is super easy and I
have actually uh you know discussed in
just in one of my previous videos of
Finance platform as well so you know
after you have done following this
project you can go and follow that
project as well all right then so we
have successfully created our industry
insights feature now this is my most
favorite feature that is the interview
prep we are going to be creating the
interview prep page which will be this
slash interview page right here so first
of all I'll go to this actions folder
and create the apis for generating the
quiz and saving the results of the quiz
so I'm going to say interview. JS inside
of this I'm going to be creating a
function called generate quiz the server
action and I'll also use the use server
at the top this is very important don't
forget it now inside this what will we
do first of all obviously we need to
check if the user is logged in or not so
let me just take it from right here let
me just take it from this server action
and just paste it make sure to import o
from clerk sl/ server and I'll also
import DB from the Prisma file okay now
after it we're going to have to write
the prompt for you know generating our
quiz so let me show you how I'll do it
so here it is I'm going to say generate
10 technical interview questions for a
user. industry like whatever the user
industry is I'm going to say generate 10
interview questions for this and since
we already have the user skills right
then we will say if the user uh skills
are there I'm going to say with
expertise in whatever the user skills
are right else we will give empty string
and then I'm going to say each question
should be multiple choice with four
options and we have to return the
response in the Json format only no
additional text and the format will be
this like the question like the array of
questions with the question options
correct answer and the explanation of
that answer right so yeah pretty
straightforward if you don't want to
write it over here you know just you can
go to my GitHub repo and open this
interview JS file and copy this prompt
from there after this we will write the
same thing that we have done earlier
over here I believe in the dashboard JS
right y to structure the response I'm
just going to copy this up up and paste
it right here so result will be model.
generate content on obviously we need
this model as well right so I'll take it
from right here and put it on the top
right over here and let's import the
Google generative AI from at Google /
generative Ai and then over here I'm
going to say model. generate content
with this prompt response will be
result. response and obviously we are
supposed to clean the response as well
right so I'll just copy both of these
just like this instead of learning this
I'm going to say cons quiz equals json.
bar clean text and then in the end we
would get the array of all the questions
right so I'll simply say return quiz do
questions and I think we should keep it
in a TR catch block so let's keep it in
a TR catch block I'll move this part
I'll cut it and move it right after the
return and inside of the catch I'm going
to say console. error error generating
quiz and throw error fail to generate
quiz questions okay now after we have
attempted the quiz right we also want to
save the results of the quiz right so
I'm going to be creating a new server
action called save quiz result which
will take all of the questions that is
this array that was provided to us all
of the answers by the user and the final
score of the user right we will
calculate these things in the front end
and then send it to this save quiz
result server action again same thing we
will check the user Authentication first
so I'm just going to copy it and paste
it right over here now we first of all
need to structure what we are going to
be sending to our DB right if we refer
back to our schema so right here schema.
Prisma we have this assignment right so
inside of this we will be storing it
inside of this questions like array of
questions answer user answers and what
is the correct answer and also the quiz
result so if I go back I'm going to say
const question
results and I'll take each and every
question like questions do map and we
will return an object from inside of it
I will take each and every question over
here and the index of that question and
this array that we will be forming will
contain first of all the question right
so I'll say question equals Q do
question then the answer the correct
answer that is not the users answer so
I'll say Q do correct answer then I will
take the users answer right so we have
the users answers array over here so
we'll simply use the index so I'll say
user answer equals answers and this
Index right here if it's the correct
answer or not so we will compare both of
them so I'll say Q do correct answer if
it's equals to answers index then this
will be true or false accordingly this
will help us to you know render rui even
better with less calculations and
obviously the explanation for each and
every question and yeah this is what we
will be storing inside of our database
but first of all for each and every quiz
if you might remember we have this
Improvement tip as well right so
Improvement tip will obviously be based
on the questions that are answered
incorrectly by the user right so we
would fetch all of the answers that are
been uh you know like the all of the
wrong answers so over here what I'll do
I'm going to say con wrong answers
equals questions question results do
filter and whatever question is not
correct that is not Q do is correct we
will take it from this particular array
right and now we will take this wrong
answers and we will feed it to Gemini
API so I'm going to say if wrong
answers. length is more than zero only
then we will generate the Improvement
tip so let's fetch all of the uh you
know text from inside of this array so
I'll say con wrong questions text equals
wrong answers. map and for each and
every child I'll form a text right it
will be question whatever the question
is the correct answer and the users
answer right three things and this is
what we will be providing for each and
every question we will be providing for
uh the AI the Gemini Ai and then we will
join all of them so after this simply
I'm going to form my prompt and again if
you want you can get this prompt from my
GitHub repo as well or you can just
watch this video and copy from over here
so the Improvement prompt the user got
the following whatever the industry is
let's say software development industry
technical interview questions wrong and
then we will provide this thing that we
generated right here right and then I'm
going to say based on these these
mistakes provide a concise specific
Improvement tip focus on the knowledge
Gap revealed by this wrong answer and
keep the response under two senten right
we don't want to keep it longer so that
our UI breaks don't explicitly mention
the mistakes instead focus on what to
learn and practice cool then I think
this will help the user a lot then
simply I'm going to have the TR catch
block and we will make the call to
Gemini API so I'll just go up here and
just take this just like we've been
doing up until this point and I'll come
back and paste it right here so result
await model generate content and let's
take this Improvement prompt and provide
it to it and actually since this is not
going to be a Json so we won't be
needing this cleaned text over here and
neither this json.parse let's just
simply trim what we get after it right
and let's see we should take a variable
over here for the Improvement tip as
well right so before this I'm going to
take let Improvement tip equals null and
I'll update it after it like after we've
got this so instead of this text I'm
going to say Improvement tip equals
response. text. trim and yeah that's all
uh if there's any error we will just
console log the error and then outside
of it let's store all of this inside of
our database right so I'll have another
Tri catch block and obviously this thing
is optional since it depends on the
wrong answers if wrong answers are there
only then we will make this Gemini API
call so now simply over here we will
feed it to our assessment table so I'm
going to say const assessment equals
await db. assessment. create and we will
provide the data from over here that is
user ID quiz score questions result so
if you remember this array that we
created questions result the category
and the Improvement tip right cool then
after this we will simply return the
assessment and in the catch I'm going to
provide the console error that error
saving quiz result and fail to save quiz
result and yeah that should do it let's
go on and build the UI for it so I'll go
to the interview and over here in this
route let's just create a layout. JS
file so I'll simply go to dashboard uh
layout page probably and just copy it up
and paste it right here let's just get
rid of this div on the top and yeah that
should be it this will be more useful
when we fetch all of the you know
results and the stats in the interview
page but let's go on and build this mock
interview page now so over here first of
all I'm going to have a div which will
contain the link to go back right so I'm
going to have a link tag inside of it
which will say hre and this will take me
back to the interview page and then I'm
going to have a button inside of it with
variant link let's import this button
from components and this will have some
class name of Gap two and padding left
to be zero because we will have an arrow
left and back to interview questions
text over here so let's see so default
export is not a react component let me
just refresh it yep there we go let's go
to// mock over here and then you'll see
that link Arrow left is not defined
obviously we have not imported this icon
right here and also uh let's just add
some class name of display Flex Flex to
be column space y to be two and margin X
to be2 because right below this link we
will be adding our heading over here as
well so let's see yep there we go
something like this back to interview
preparation cool then so below it I'm
going to have a Dev over here which will
contain an H1 tag and a paragraph tag
just like we've been doing up until this
point H1 tag will be text 6 Excel font
Bold and the gradient title class that
we created three specific questions and
this will be a text muted foreground so
let's save it yep something like this
sorry below this div is where we'll be
rendering our quiz interview question
opponent folder over here quiz okay
let's see oh we have not imported I
believe so let's just import it over
here as well and yep we can see this
quiz right here all right so this is
let's go ins start this quiz. jsx for
each and every quiz let me just first
you know let's just have a look at the
deployed app and go to the interview
prep section let's start a new quiz so
over here you can see we have a box
saying ready to test your knowledge and
as soon as we click on start quiz it
will obviously make the call to Gemini
and will generate five or 10 questions
whatever for us there we go now notice
for each and every question how many
states would be required first of all
when we are selecting it we need a state
to store uh the answer right another
state for you know storing whatever the
current question is and also these
explanation with respect to each and
every question right not exactly the
explanation actually uh if you're
supposed to show the explanation over
here or not right if it will be a true
or false state so if I go back I'm going
to take three states over here and
obviously since we're using States we
need to make this a client component so
use client let's import use state from
react and yeah you can see I've taken a
current question all of the answers
array and the show explanation
Boolean okay now we would need to fetch
the data right so again we have already
created that use fetch hook so I'm going
to be using the use fetch hook let's
just import it and this generate quiz
server action that we created and we
will take the loading rename it to
generate quiz generating quiz and
function rename it to generate quiz
function and data to quiz data right and
we will use this function right here to
call this and you know generate all the
questions using AI let's just first
create that UI where we will you know
show that box to the user so that they
can click on start quiz button and start
the quiz so I'm going to say if no quiz
data is present then I'm going to return
a card over here right so let's just get
the card from Shad ceni from over here
let me just copy it and paste it over
here we won't be needing the card
description for the title let's say
ready to test your knowledge inside the
card content I'm going to say this quiz
contains 10 questions blah blah blah
you've already seen this right text
muted foreground and in the card footer
let's just display a button saying start
quiz and this will be width full so
let's see card is not defined obviously
we have not imported all of these three
uh things so card header card title card
content card footer and is button there
no let's import the button as well and
let's see yep something like this let's
create some Gap between them or actually
on the sides so I'll say MX to be 2 that
is margin horizontal to be two and for
the uh page. j6 let me just give some
class name over here which will be
container margin horizontal to be Auto
some space between these and some
padding vertical to be six so now you'll
see yep we have some spacing between
them cool let me just close this now
when we click on this button start quiz
button we want to call a function right
so I'll say on click
generate quck quiz function which you
already know comes from right here cool
so when we click on it this will
generate the quiz let's just show the
loader as well I'm going to say if the
quiz is being generated like if
generating quiz then return bar loader
let's import this bar loader and same
things that we have already given it
earlier and also as soon as the quiz is
generated what I'll do I'm going to say
inside of the use effect let's just
import the US from react I'm going to
say if the quiz data changes then set
all of the answers in this set answers
array cool then so let's just work on
the UI so first of all uh you can see
the current question is zero by default
right so that is where we will be
starting our quiz from from the zeroth
index so after this I'm going to say
const question equals quiz data and
whatever the current question is it's on
the zero so that will be our current
question so insert of this div is where
we will be writing our UI and instead of
this div actually I'll just take this
card and paste it right right here yep
and let's update it so for the card
title I'm going to say question whatever
the question is 0 + 1 so obviously we're
going to start from zero so 0 + 1 of
whatever the total length of the
questions is inser the card content I'm
going to render my question so I'm going
to say question do question and it will
be text large and font medium let's
remove this button from card footer we
will come back to this card footer and
after this after entering this question
we will render all of the four options
over here as well right so for that
we'll be using radio group that we have
installed so let's see we have this
radio group over here yep something like
this so let's just copy this code from
over here and paste it right after this
so insert this let me just remove both
of these options over here and yeah so
radi inside of the radio group we will
render the options from inside of our
question so I'll have uh question.
options. map over here let's take the
call back inside of it we will take each
and every option and the index of that
option let's move this div inside of
over here and I'll say return let's give
this the class name of space y to be2
and since we're using the map let's just
give this the key of index now for this
radio group item we're taking each and
every option right so I'm going to say
value will be option and the ID will be
option and whatever the current index is
similarly for this label as well I'm
going to say HTML for and whatever the
ID of this uh radio group so when we
click on this it selects the radio group
right when we click on the label let me
just import all of these one by one so
make sure to import from SL components
only radio group item from SL components
and radio group as well let's see how
this looks like so I'll go back to our
app and click on start quiz so it is
loading the quiz I believe and let's see
and there we go our quiz is loaded
successfully amazing now we just have to
work on this UI right now right so let's
focus on that let's add the logic for
selecting a particular option now for
this radio group I'm going to say on
value change and I'll call this handle
answer which we will just go on and
create in just a moment and the value
will be answers of the current question
so if you remember this is an array
populated with null so on value change
we will provide the users answer to this
handle answer uh you know function so
let's just go on right below this use
effect I'm going to say const handle
answer equals
whatever the user's answer is I'm going
to take that and notice in start of it
first of all I'll just make a shallow
copy of the state of the answers right
so I'm going to say new answers equals
whatever the existing answers is then
I'll take this copy and for that
particular question for like whatever
the index is right I'll update the
users's answer and then I will update
the array back right this state back to
this simple enough then after this I'm
going to be adding the Logic for you
know displaying the explanation as well
so after the radio group actually I'm
going to say show explanation so if the
show explanation is true only then we
will be showing the explanation so let's
take a div over here andert this I'm
going to have a P tag which will say
explanation and then another P tag which
will show the question do
explanation oops explanation let's keep
it font medium and for the explanation
keep the color as muted foreground for
the div containing this I'm going to say
margin top to be four padding four
background muted and rounded large so
let's just make it true so that you can
see what are we getting over here oh
sorry I think I lost the UI uh you know
what let's just um let's just do it
again let's click on start quiz yep you
can see we have the explanation right
here let's give some spacing between
both of these so I'll go to this card
content and I'm going to provide the
class name of space y to be four okay
now in the footer and by the way let's
just R it back to show explanation now
in this card footer right here we will
display the button for you know for
triggering the show explanation only if
the user has selected any option and
also the ability to you know go to the
next question so pretty simple UI if the
show explanation is not true I'm going
to display a button over here with
onclick set show explanation to True
when we click on it it will be true
variant will be outlined and if that
particular question has not been
answered I will display like disable
this button right and after this I'm
going to have a button for going to the
next question so I'm just going to copy
this button and paste it right here so
it's going to say if the current
question is less than the last question
right if we are not on the last question
we will show the next question else we
will show the finished quiz and this
will also be disabled if the question
has not been answered let's keep the
class name to be margin left Auto and on
click I will trigger a function called
handle whoops handle next we'll create
this function as well so I'll go over
here below let's say handle answer I'm
going to say const handle next let's
create a arrow function and now notice
inside of it I'm going to say if the
current question is less than the quiz
data do length minus one that is it is
not the last question right then simply
I'm going to say Set current question
question to current question plus one
and also I'll make this shed explanation
to false so that we don't see the
explanation for the next question right
away else we will trigger the
Finish quiz function which obviously
does not exist yet so let me just create
a dummy function for now finish quiz
something like this and we will
basically be calculating our score
inside of this finish quiz function and
then we will make the API call for
saving the result of our quiz so you
know what let's just take the other
server action as well let me just import
that right here save quiz result let me
just import it from SL action SL
interview so over here we'll take
loading function data and set data now
this set data as I already explained you
can be manually used for setting the
data of our quiz so basically when our
quiz is done we can take this set data
and you know set it to null after the
new quiz is started you know so that our
result data state is empty all right so
let's take this save quiz uh result
function and uh let's go inside this
finish quiz now first of all before uh
you know making this API call we have to
also calculate the score of the user so
I'll say const score let's just keep it
Zero by default for now and I'll have a
try catch block and insert this I'll say
await call this save quiz result
function and we will send the quiz data
we will send the answers the users
answers and obviously the score then we
will show the success toast so I'll say
toast. success and make sure to import
toast from sonor and set the catch block
I'll say toast. error if you know
anything fails over here now let's
calculate this score so for calculating
this score let's just have a function
called calculate score and this will be
a pretty straightforward function let me
show you so if I put it right here so
yeah calculate score we will start with
zero right the correct answers are zero
so far and then we will Loop through all
of the users answers we'll go inside of
it I'll say if the answer is equals to
the actual correct answer right whatever
the user has answered I'm going going to
go insert the user data dot like off
index and take the correct answer will
compare it if it is equals to it then
I'll say correct Plus+ and then in the
end I will return the number of correct
answers divided by total ansers into 100
that is we are calculating the
percentage over here and that is what we
will be sending to our back end to
displayed all right so also let's see
let's make use of this saving result
over here this loading indicator so I'm
going to go to the very bottom where we
had our button and I'm going to say this
will be disabled when we are saving our
result and also let's just display a bar
loader over here when we are saving our
result right cool then let's go on and
try it out um what I'll do I'll just uh
for now I'll just console log this
result data over here just to see what
do we get inside inside of it so result
data okay let's go back to our website
I'll click on start quiz and I'll open
my inspect over here as well console and
let's see so I'll just randomly answer
all of the questions over here and let
me just do this real quick okay there we
go I'm in the ninth so now after this it
should display a finished quiz and let's
just randomly choose something and click
on finish quiz and now it's not
displaying the loading indicator we'll
check what's wrong over here let's see
what happens okay quiz completed and
there we go we get something over here
we get all the questions the quiz score
10 did I answer everything correctly and
I don't think so that would be the case
but let's see what do we get over here
okay this is is correct false this
is is correct false over here okay yeah
you can see it has created the complete
structure for every single thing like uh
what is the correct question was what
user answer was right I shouldn't have
displayed the quiz score to be 10
probably I did something wrong over here
let's check probably we will check it
later on let's just first uh render all
of the results and then we can check it
so also uh apart from this bar loader
let's just display this loader to from
Lucid react I think this will be better
uh in showing the loading indicator
right and also let's just check if this
has been entered inside of our database
or not so I'll go to the assessment
Table and there we go I think we have
something over here but this is is wrong
most probably quiz score to be 10 this
shouldn't be that the case so let's do
one thing I'll go back to the code and
above this return and above this not
quiz data I'm going to say if the result
data is there I'm going to return a d
with the quiz result component and this
component does not exist we will build
it right now we will send it the result
data in the result and also we will send
it a prop on start new where we will
trigger this start new quiz function and
this will be pretty simple we will just
you know null everything inside this
function so I'll just say const start
new quiz equals set currect question to
zero set answers to empty set show
explanation to false H we're going to
generate know a new quiz when we trigger
it and set result data to null right we
just resetting every single thing over
here let's just create this component
first of all so I'm going to go and
start the components create a new file
called quiz result.
jsx r a fce quiz result let's import
this component right over here so I
actually tried it out once again and uh
looks like this is working now now the
quizes score is not being generated
fully not sure what went wrong earlier I
tried generating it with just three
interview questions let's just put it
back to 10 maybe some bug but it's
working fine now so yeah we're good so
let's start working on this quiz result
component so this quiz result component
we'll be using in multiple places right
over here in this quiz component as well
and also on the page of the interview
page like the where we'll be displaying
all of the stats and stuff as well right
so there we don't want to show this
start new quiz button so I'm going to
say I'll have a result I'll have highest
hide a start new flag over here as well
we won't be using it right here but
we'll be using it somewhere else and an
on start new function over here as well
okay so first of all I'm going to say if
the result is not present obviously
return null but if it is present then
first of all I'm going to say quiz
result as the title and I'll display the
trophy icon from Lucid react and simply
I'm just going to say display Flex items
to be Center some Gap to be two between
both of these text to be 3 Exel and
gradient title then after it let's just
take a card content component from Shad
CN uui and inside of this we can render
the quiz result right so first of all
the overview of the score I'm going to
have a Dev with the H3 of result do quiz
score and whatever the percentage is
right this will be in the percentage so
I'll say two fixed one and percentage
and we will also render the progress
component just like we've done in the
you know insights page as well similar
to that we'll be rendering result.
quizes score over here after that we
would have also gotten the Improvement
tip generated by AI so if you see yeah
in these results yeah you can see the
Improvement tip focus on mastering blah
blah blah we cannot see it fully right
now yeah you can see this tip was
generated using using AI so we will
render this now so after this I'm going
to say result do Improvement tip if that
is present then inside of a Dev I'm
going to be rendering uh P tag with the
Improvement tip title and the result do
Improvement tip and obviously we will
say text muted foreground for this and
font medium size for this then after
this we will display question wise uh
you know explanation like what went
wrong if and what went right so I'm
going to have a div over here with the
H3 tag which will say say
question review then below this I'll
take the result and map through it so
result do question questions actually do
map so we will map through each and
every question and we'll render them
over here let's give this div some space
y of four and the H3 of font medium and
also this paren Dev I'll just give this
margin horizontal of Auto that this is
in the very middle and now over here we
will have a d inside this we're going
have another day which will display the
question and if that question is correct
or not so I'm going to have a P tag
which will display the question so Q do
question and if it's correct then we
will show the check Circle so it will
look something like this and if it's not
correct I'll show The X Circle which
will look something like this and both
of them will be imported from Lucid
react and for this one I'll put the text
green of 500 and this one textured of
500 at some generic styles of height
five width five and flex shrink of zero
this div is going to be having a class
name of display Flex item start justify
between and some gap of two between them
and this parent div will have the class
name of some border rounded large and
padding to be four space y to be two
because after this div right here we
will be displaying more things right
like our answers the correct like your
answer and the explanation of the answer
so first of all I'm going to say your
answer is this and if it was not correct
only then we will display the correct
answer over here and also we will
display the explanation after it
regardless of the fact the answer was
correct or not so I'll have a d and I'm
going to say explanation and just render
the explanation just like we've done
earlier right and yeah after after this
de actually after this card component
sorry card content I'm going to say if
hide start new is not true then we will
display this start new quiz button over
here so I'll say up button on on click
on start new that we are getting from
right here and yeah I think uh this
should do it this should display all of
the results so let's go back and how
about we attempt a new quiz over here so
I'll click on start quiz and it's
loading it and there we go so we have 10
questions over here let's just randomly
answer all of these one by one there we
go we are on the last question now let's
click on finish quiz it is loading it
and let's see progress is not defined oh
man okay let's just import the progress
over here let's see if there's something
else that we have not imported I believe
I have imported this how about card
footer let's import card footer button
let's import button card content must be
already imported trophy is this imported
yeah this is imported let's try it out
again anyway let's me just put this to
three questions now instead of 10 10
questions every single time this will be
easier for me to test it out so again
let's randomly answer them one by one
and also let's just click on show
explanation and yeah this is working
fine and okay let's finish the quiz and
hopefully nothing goes wrong now Q is
not defined wow where is this coming
from so quiz result so we are mapping it
and obviously we have not taken the que
over here and also the index which we
will be using as a key so key equals
index and hopefully that should be it
let me just try it out again let's click
on finish quiz and there we go we are
getting the quiz results just just like
this amazing so you can see we have the
Improvement tip on the top I believe
this would need some spacing between
these so for this card content I'm going
to put this class name space y26 I'm not
going to save this right now because
this will you know refresh the page so
apart from that let's see the question
review yep we can see we have answered
all of them incorrectly and we have the
option to start a new quiz over here as
well great so let's just click on start
new quiz and let's start to actually
attempt the quiz this time and there we
go looks like like I have answered all
of the questions correctly now great if
you want you can wrap this quiz results
into a card component over here as well
instead of this div so if I just say
card and if I go back now yeah I think
div was much better so I'll revert it
back to the dev and cool then next what
I'll do we can click on back to
interview preparation and this is the
page where we can render all of the
stats related to the you know assessment
that user has attempted and all of the
quizzes that user has attempted in the
past so let's build this so I'll go to
this interview. JS again and let's
create the server action for this so
it's going to be pretty straightforward
I'll say export async function get
assessment and inside this first of all
let's check for the user authentication
uh sorry not authentication
authorization I'll just copy this and
I'll paste it right here to check if
user is found or not then I'll have a
try catch block over here
and here let's fetch all of the
assessments so I'm going to say const
assessments equals await db. assessment.
find many and I'll say where user ID is
this whatever the current user logged in
is and sort them in the ascending order
with respect to the created and return
the Assessments in the end and in the
catch I'll again handle the console
error error fetching assessments cool
then let's go to our page. jsx and start
building it so let's plan this uh
interview page first so so what are the
three things that we will be having over
here let's go and check inside of our
deployed website right here so first of
all we will have these cards which will
show us the statistics then our
performance Trend graph and then after
it we will render all of these quizzes
and when we click on it we will just
reuse that uh you know quiz results
component that we created earlier okay
so I'll go back first all over here
let's just display a H1 tag so I'm going
to have a de and start of this I'm going
to display the H1 tag just like we've
been doing up until till now texx XL
gradient title and font bold and also
let's just give it some margin bottom of
five over here let's save this and check
it out Yep this is what we want now
below this I'm going to have a div and
we will display three components over
here that we already saw right so let's
just create uh a new component for each
and every one of them so I'll say stats
card and by the way we just now created
this API endpoint like server action
right so let's get all the assessments
over here so I'm going to say const
assessments equals a weight get
assessments let's import it and also
let's make this an asynchronous
component since this is a server
component and I'll take this assessments
and I'll provide it to these stats card
over here like that similarly I'll
create two more components and they will
also take the assessments one will be
for displaying the performance chart
another will be for displaying the quiz
list
simple enough let's go on and make these
components so inside of the components
folder I'm going to say stats cards. jsx
R A fce stats cards second one will be
performance chart. jsx let's paste the
same code over here and I'm going to
rename it so
performance chart and then the third one
will be quiz list oops quiz list.
jsx I'll rename it to quiz list okay
cool let's just uh import all these
three over here stats cards performance
chart and quiz list cool let's just see
if it's fine yep we can see all these
three over here let's first work on this
stats cards so since we have to display
three cards over here inside of it right
so let's just use display grid over here
so I'm going to say class name to grid
Gap to be four between them and if the
screen size is more than medium I'll say
grid columns to be three let's take the
assessments over here from the props and
now let's see what are all the types of
Assessments that we'll be displaying
we'll be displaying the average score
the questions that we have practiced up
until this point and the latest score
that we got in the last quiz right in
the most recent quiz so for calculating
the average score it's going to be very
simple we'll be taking all of the
assessments over here right first of all
we'll check if we have any assessments
or not if we don't we'll just simply
return zero but if we do I'm going to
say assessments. reduce I'll take each
and every assessment and the sum like
this is an accumulator and this is the
each and every value if you know about
reduce I've created an in-depth video on
map filter reduce if you want to check
it check it out you can you know go and
search my channel so I'm going to take
an accumulator and the assessment so by
default sum will be zero and we will be
adding assessment do quiz score to it so
we will get all of the total and then we
can simply say total divide by
assessments. length and we will get the
percentage for it now if you want to get
the details of the latest assessment
simply I'll just say if assessment.
length first of all we'll check if it's
there or not else we will just return
assessment of zeroth index and we will
get the latest assessment then how do we
get the total number of questions I'm
going to say const get total questions
and inside of it again we will have the
check for it and I'm going to say
assessments. reduce this time instead of
this uh quiz score I'm going to say qu
questions. length right so we will take
each and every quiz and add the
questions length inside of it right so
cool then we got all of these three
stats right here now insert this div I'm
going to be using three cards so we have
already created similar cards right if
we go back to our industry insights see
we have created similar cards over here
as well right so these are very similar
to what we will be building over here so
what I'll do I'll just go back to
dashboard right here and let's go over
here dashboard View and let's just take
this first card and let's copy it up
let's go back and I'll paste it right
here let's just import all of these
things card card header card title
instead of Outlook icon I'll just say
trophy icon card content and yeah let's
work on this I'm going to say instead of
Market Outlook I'm going to say average
score and instead of this I'll have
simply H4 withd for and text muted
foreground and here we can display the
average score right so this thing get
average score so I'll just take this and
put it right here call this function and
put a percentage after it and on this
line I'm going to say across all
assessments so let's see if this looks
good I'm going to go to the interview
prep page yep that is exactly what we
want let's see if the UI is similar to
this yep it's exactly the similar to
this one and these other two cards will
also be same so let me just duplicate
this card right here two times in the
second one we want to display the
questions practiced so instead of
average score I'll say questions
practiced instead of trophy I'll have
brain as the icon Let's uh render the
total questions over here so I'll say
get total questions and below it total
questions okay for the third one I'll
display the latest score so instead of
average let's say latest score here
instead of get average score I'm going
to say get latest assessment and I'll
take out the quiz score from it fixed to
one decimal point and I'll say
percentage instead of across all
assessment I'm going to say most recent
quiz right let's check it out yep there
we go looks good let's start working on
this performance chart now so I'll go to
this and this will be using like making
the use of recharts right so let's see
what chart will be uh building over here
so it must be a line chart right so
let's see I'll go to the rechart
documentation and open this simple line
chart component so you can see this is
how we will be using it let let's come
back to this first let's prepare our
data for rendering the chart it will be
very similar to how we did earlier so
first of all let's just create a state
over here and also we need to input the
assessment right so I'll say assessments
and let's create a u state for chart
data chart data and set chart data and
now after it I'm going to say use effect
and I'll run this use effect as soon as
we render this component right so as
soon as the assessments changes we're
going to write the logic inside of it so
I'll say if assessments has anything
inside of it I'll say const
formatted data equals
assessments. map and I'll take each and
every assessment and go inside of it and
I'll format it so we want two things we
want the date and we want the score for
that particular quiz right so that's
what we'll write over here so instead of
this curly braces actually I'll have a
round bracket or parenthesis and then
inside of it I'm going to create an
object like this and I'll have a date
and I'll format it so for formatting it
we will be importing format from date-
FNS just like we did earlier and we will
provide the date of when that assessment
was created and we will provide this
formatting right month and date like for
example January 2 or something like that
right and this score for that particular
quiz and that is how we will get this
formatted data and let's just set this
data after xile say set chart data to
formatted data cool let's just take the
card component from shat CN UI uh to
render this what's going on oh sorry
this is a client component right so I
have to use use client over here
directive so that uh it does not break
let's go to shat CN UI and I'll take the
card component this one let's copy it up
and paste it right over here we don't
need this div over here get rid of it in
the card title I'm going to say
performance Trend and let's make this a
gradient title right just like we've
been doing up until now so I'm going to
say class name is going to be gradient
title text 3 XEL and in the larger
screens text is going to be 4 XL let's
see how this looks card is not defined
of course we have not imported all of
these things card card header card title
card description card content and no
need for the card footer let's see now
yep that is what we want let's provide
the description to it and that will
simply be your quiz course over time
okay so now instead of this P tag over
here let's just take that chart from
recharts so simple line chart and let's
just copy this responsive container from
over here till right here let's provide
a div over here first of all and I'll
give the class name of height to be 3
100 pixels and then inside of it I'm
going to paste that responsive chart
let's make sure to import all of these
things line chart from recharts make
sure to import it from recharts only
cartasi grid xais y AIS tool tip from
recharts as well we won't be needing the
legend and we will just have one line
right so I'll get rid of this one I'll
just have this line right here okay
let's see I think currently it will be
breaking since we have not provided dat
yep we have not puted the data so let's
see over here in the line chart let's
get rid of this and this instead of this
data right here I'm going to say chart
data right then uh for this Cagan grid
let's keep it as it is data key will be
the date so we have date and the score
right you can see date and the score so
on the x-axis I'm going to keep the date
and on the y axis it will go from 0 to
100 right so I'm going to say domain it
goes from 0 to 100 and it will map the
scores over here now for the this tool
tip if you remember uh let me show you
so yeah you can see it is starting to
show up see we have this tool tip over
here with score % Etc right we did this
earlier when we were working on the uh
insights so it takes something called as
content oops content inside this we will
have a call back function and this call
back function will take active and
payload the these are the two things
that we will be needing right now so
I'll say if it's active that is we are
hovering on it oops Act
and payload has something inside it it's
called payload do length let me just put
a optional chaining over here so that it
doesn't break and inside of this I'm
going to return a very simple de which
will contain the score and the date so
I'll have two paragraphs score payload
do value percentage right and the date
of from inside of that payload and let's
provide it some background as well so
I'm going to say BG background border
rounded large padding to be two and
Shadow to be medium let's check it out
this looks like if I go back to my app
okay yeah again we have not rendered the
line over here right so yeah we have to
configure this so line will display the
score right so uh data key can be score
stroke I'm going to provide the primary
color from shat cnii if you want you can
provide it some other color as well and
instead of this active dot over here I'm
going to say stroke width and let's
provide it to be two let's check it out
now there we go it looks awesome with
all the tool tool tip and everything
right we can see over the time how we
have performed in the quiz we can see
the questions practiced latest score and
the average score as well awesome let's
render the you know list of the quiz
right here and also let's provide some
gap between these so I'll go to the
page. jsx and in this div right here I'm
going to say class name space y to be 6
and let's see yep we can see the SPAC is
between them all right let's work on uh
the quiz list so let's take the
assessments over here and this will be
pretty straightforward we will render
the card for each and every quiz and
then when we click on it we have to
display a dialogue box for the results
and also have the option to start new
quiz right so for routing to mock
interview page let's just have a use
router hook over here imported from next
/ navigation we will be using it in just
a moment and we will also create a state
over here for the selected quiz right
the one that we want to open the
dialogue for so let's import use State
as well from react let's take the card
component right here so let me just have
a fragment and inside of it we'll have
the card component so let me just take
it from shat CN UI again and paste it
right here let's import the card card
header card title card description card
content and again we won't be needing
the card footer over here now I've used
these react fragments over here because
this is where we will be having a
dialogue for our results right so we
will come back to this first let's build
this card for the card title and
description I'm going to say recent
quizzes and let's provide it gradient
title text 3 Exel and in the larger
screen text 4 XEL and the description
will be review your past quiz
performances let's see how this looks uh
what's wrong oh we are using hooks over
here so this should be a use client
component and now let's see okay yeah
recent quizzes review your past quiz
performances and also what we want to
display over here is a button to start
new quiz right so I'll have a div and
wrap both of these in this div and I'll
have a button after it and then button
will simply say start new quiz and on
click we will say router. push and push
it to/ interiew SL mock let's see button
is not defined obviously let's import it
from Shad CNC okay so we want it to be
this side right here right so over here
in the card header I'm going to say
display Flex items to be Center and
justify between so that they are on Far
ends okay I think we need to do Flex row
over here as well yep something like
that start over here in the card content
we will be rendering all of our
assessments so instead of this paragraph
tag I'm going to have a div with the
class name of space y to be4 and here
I'll take the assessments do map and
over here
I'm going to render each and every
assessment I'll take the assessment and
the index and here I'll say return and
let's just take a card component over
here again so I'll just copy the card
component and paste it right here let's
get rid of the footer again in the card
title I'm going to say quiz whatever the
number of the quiz is right I + one in
the description let's just have two
things first thing will be the score of
that quiz and when that quiz was
conducted right so I'm going to say
under with the format from date- FNS and
new date whatever the date is and I'll
format it in this format right here
right let's see yep we can see all of
our quizzes being rendered right here
cool over here in the car description
I'm going to say display Flex justify
between and width to be full so that
these dates are right over here inside
the card content simply let's just
display our Improvement tip right so
that this is easily accessible so I'll
say as assment do Improvement tip with
text small and text muted foreground so
yeah this looks good we can easily see
these right here and now let's add the
logic for having the dialogue so first
of all let me just add some hover effect
on it so on the card on each card I'll
say cursor pointer on Hover we'll say
background muted to be 50 opacity and
transition will be added on the color so
y something like that on click we want
to select that particular quiz on inside
of this state right here right so I'm
going to say on click set selected quiz
to be that particular assessment and
obviously we need to provide the uh key
over here as well since we're doing the
map right so Al key equals assessment.
ID okay so let's see now how does the
dialogues work inui so I'll go over here
in the dialogue and if you click on edit
profile you can see it's like a model
pops up just like this so let's copy
this dialogue up and I'll go back and
right here I'm going to paste this
dialogue code so we have this dialogue
header and a trigger which you know
opens the dialog I guess we won't be
needing a trigger because our triggers
are right here and we will make sure to
open the dialogue when we have something
inside of this selected quiz right and
if we close this then we will remove
everything inside of the selected quiz
so I'll say on open change set selected
quiz to null simple as that let's try it
out so obviously we need to import uh
this dialogue as well so make sure to
import from slash comp components dialog
header dialog title we won't be needing
the description so let's get rid of it
and let's see how it looks I have not
imported alert dialog header okay let's
import that as well and now check if I
click on this quiz yep you can see we
are rendering it over here let's instead
of this alert dialogue header let's just
take the simple dialog header right
let's import it from Shad CN UI and
remove this alert dialog header so we
actually accidentally imported from
another component and we won't be
needing any title over here actually and
this is important to keep this tag over
here because shat seni might throw us
some errors if we don't provide it off
the accessibility right so now we will
just simply we would need the quiz
result component because that is what
we're supposed to render over here right
and here we will give the result which
is selected quiz on start new we will
push it to sl/ Mock and we will also
provide the hide start new so you might
remember we added this flag over here
right so it was for this place hide
start new and now we won't be seeing
start new quiz button over there so if I
click on any of these Yep this is
breaking for some reason I think we need
to add some styling over here in the
dialog content I think it's because of
this so I'll simply say Max WID to be 3
XL Max height to be 90 VH and overflow y
to be Auto so we can see the complete
result on this screen itself so if I
click on it now yep looks beautiful and
you see how awesome this feature is the
complete entent feature I mean this
feature in itself is a whole app right
but this is a part of our complete
Sensei app so great then let's move on
to the next big feature which is to
build our resume we will start by
creating the server actions for it so
I'm going to come to this actions folder
over here and I'm going to create a new
file called resume. JS and see most of
the things in this are going to be
performed in the front end part because
we will be building a Resume Builder and
all all of those these things is are
going to be on the client right but
after we have built the resume we want
to save that rume right so we will
create This Server action export ASN
function save rume and we will provide
it with the markdown content to save the
resume so cool let's start with simply
user authorization right if user is
logged in or not I'm going to import the
AU from clerk /n/ server and I'm going
to import this DB from Prisma file now
after this there can be a possibility
that uh rume already exists inide of our
database right for that particular user
so we'll be using uh a function called
upsert so let me show you so I'm going
to say con resume equals a wait db.
resume. upsert so basically this means
update or insert right whatever's
suitable first of all I want to search
the resume for that user so I'm going to
say where user ID is user. ID if rume
already exist we are going to update it
with this content just update the
content and if it does not exist we will
create for that particular user ID and
we will insert the content simple enough
and then we will refresh all of the data
inside of the/ resume path so I'm going
to say revalidate data let me just
import it from next SL cach and then in
the cat I'm going to throw the error
error saving resume with error do
message now similarly we will create a
function to get a rese if a res is
already created right
so it will be a very simple function so
let me just bring it real quick we will
just first check if user is logged in or
not and then I'm going to Simply say
await db. res. find unique where user ID
is simple enough right now for the third
thing let me show you if I go back to
the deployed app and you can see I'm in
this Resume Builder feature and I have
my rume right here if I go to form and
let's say if I want to add a work
experience over here right I'm going to
say frontend developer organization can
be let's say Google date and whatever
you can set these on your own and then
if I want to add the description let's
say if I worked on creating a Chrome
extension right or let's say basically a
Chrome browser I worked on Chrome
browser in Google right so now we are
blanked right what do we write in your
resume so we can add this feature of
improving with AI so if I click on this
button see it has improved this
description with respect to the uh you
know our the little description that we
have provided it so this is an awesome
feature let's try to build this right so
I'll go back to my app and I'm going to
create a new server action over here
called improve with AI now in our
current application I have added this
feature just for this description right
here right improve with a and I've left
this professional summary like this can
also be improved with AI right so
professional summary or if you want you
can add it in the skills as well so I
would highly encourage you guys to
follow the similar patterns to implement
it over here as well right so but it's
pretty simple let me show you how you
can do that so first of all obviously we
need to check if user is logged in after
that we want to write the prompt for it
so you know what uh instead of writing
it out over here let me just go back
into my GitHub repository and let me
copy this whole prompt if you want you
can also copy it from right here or of
course you can also add your own
variation to it so yeah this is the
prompt as an expert resume writer
improve the following we're going to
provide the type like if it's for the
experience if it's for the project if
it's for the education right this
feature is on all of these three places
so over here education projects all of
these have the Improv with AI so we'll
provide the type for the description for
a user who is in this industry which
let's say for example software
engineering or front end developer Etc
right make it more impactful
quantifiable and aligned with the
industry standards and then we will
provide the current content that user
has provided then the requirements are
following use action verbs include
metrics highlight relevant technical
just like we should be writing a resume
description right so this will help us a
lot in building a resume and format the
response as a single paragraph without
any additional text or explanation
simple enough and then after this let's
make an API call to Gemini right so let
me just uh do one thing let me bring in
my import for the Gemini yep these two
things and I'm going to put them on the
top right here right let's import this
Google generative AI over here and then
I'm going to use this model variable to
call it so inste of the try I'm going to
actually just go back and simply do what
we have already done right so yeah this
thing right here let me just copy it up
and I'll paste it right here so const
result equals model do generate content
then result do response and then I'll
get the text and actually I'll just say
dot trim if there's any spacing outside
of it let's call it improved content let
say return improved content all right
and in the catch block simply I'm going
to throw the error error improving
content if there is any error that's
going to happen right so yeah these
three are my server actions now let's go
to inside of this app folder Main and
yeah we have not created for resume so
I'll say resume and I'm going to create
a page doj SX right here instead of it
I'm going to say R A fce let's call it
resume page and now over here if there's
any resume pre-existing I'm going to
fetch it so I'll say const resume equals
a wait await get resume so this F
function that we just now created server
action make sure to make it an
asynchronous component since this is a
server component and yeah we will get
the info right here obviously does there
is no resume right now so what I do
instead of this resume page I'm going to
go inside of it and create a component
called resumé Builder again this will be
a client component so that we can use
the hooks and stuff inside of it let's
get a new folder over here underscore
components and I'm going to create a new
file resume builder. jsx now this is a
very you know logic heavy component so I
need you guys to focus properly we will
be sending this the initial content so
I'll say initial content oops contenter
this I'll say resume dot content right
I'll put a optional chaining over here
just in case if our resume does not
exist which currently it obviously does
not exist for this div I'm going to
Simply give class name container margin
horizontal to Auto and padding vertical
to six also since we are you know
loading the resume right here I would
recommend you guys to add a layout file
just like we have added right here or
like um it's pretty simple right you can
just copy it
and paste it right here inside of it
right simple enough and now it will show
a bar loader whenever this rume is being
loaded right here let's import this rume
Builder component am I not exporting oh
sorry R fce Resume Builder and I'll take
the
initial content right here well let's
just import it right over here like this
and go back and see yep we can see the
res Builder right here okay let's go
inside of our Resume Builder component
and start building our Resume Builder
first of all this will be a client
component so I'm going to say use client
directive over here so first of all
inside of this div I'm going to have
another div which will contain our H1
Resume Builder just like we've been
doing up until now I'm going to give
this the class name font bold gradient
title text to be 5 XEL in smaller
screens in the medium screens it's going
to be 6xl below this I'll have a div
which will contain our buttons like
saving the resume or you know
downloading the PDF Etc so I'll say
button import it from shat CN UI this
will be save and I'm going to have an
icon before it save icon from Lucid
react let's see how it looks yep
something like this let's give it the
variant of destructive so that it's red
color let me just duplicate this button
right here and this one will before
downloading the PDF and let's import
this download button from Lucid react
something like this and I don't want
this to have a destructive variant this
div can have a class name of Space X
tob2 and this parent div for both
heading and buttons can have display
Flex Flex column but in the larger
screens it's going to be Flex row that
is on one single row justify between so
at the AR far ends item Center and GAP
to be two so they would look something
like this parent div can be class name
space y to be four now below this div
right here we will have two tabs so you
may have already seen over here we have
two tabs one one for form one for
markdown so we have already installed
the tabs component from chat CN UI so it
looks something like this let me just
copy it up and paste it right here so
basically what's going on over here is
we have this tabs uh parent tag imported
from components and then we have tabs
list one for account one for password
for an example over here when we click
on account this value account matches
this value of tabs content and it
renders this component else it will
render the other component let me show
you let me just import all of these from
SL components folder tabs content and
yeah I think all of them are imported
now let's see y you can see account
password looks good instead of these two
I'm going to have form and markdown and
default value can be form but you know
what we don't want a default value
because that will differ right if we
already have a resume we will show the
markdown tab else we will show the form
tab let me get rid of this class name
over here as well and and let's just
create a separate State for the active
tab right so I'll just put this active
tab State over here let's import U State
and by default I'm going to put it to be
edit and right here I'm going to say the
value is going to be active tab on value
change we will use this set active tab
so this one can be edit and this one can
be preview let's check it out form and
markdown okay so now let's first go on
and build this form tab right so so for
forms obviously we are using react hook
form and zort so I'm going to import the
use form hook over here and this for
this we will need to create the schema
as well right so I'm going to go to this
lib schema. JS file and inser of this
let's see first what are all the
different things that our form will
contain so we will have this contact
information then professional summary
skills and for each and every one of
these this will be an array right we can
add a work experience then we can add it
then we can add another work experience
right something like that so this has to
be an array so I'll create a separate
schema for this separate schema for this
and then I'll combine them in one single
schema so first of all for contact
schema I'm going to say export cons
contact schema Z doob and we will keep
four things over here let's just keep
email as required only and other things
as optional so z. string email and if it
it's not provided I'm going to say
invalid email address and for mobile
LinkedIn and Twitter all of these three
will be optional now let's understand
the entry schema we can call it like
these these ones right let's build these
so export con entry schema do object and
inside this we'll take an object what
are the different things that we are
going to be having inside of it uh title
organization start date end date we can
have a checkbox for current experience
and we can add the you know check to see
if this is selected we won't be
requiring this or if if this is added no
need to enter this right so and yeah
also description right so we would need
all of these things so it would look
something like this title it can be a
string minimum one character else title
is required same for organization start
date is going to be minimum one
characters as the start date is required
end date can be optional description and
the current right which will be a
Boolean now let's add the check so I'm
going to say refine over here and refine
will help us to add some you know checks
over here if it's working properly or
not so inside of this call back I'm
going to take the data that we have
received and simply I'm going to say if
data do current is not present and data.
end dat is also not present then we will
return false that is we will throw the
error else if either of one is present
I'm going to say return true and how do
we throw the error after it so I'll say
comma and inste of an object I'm going
to say message and date is required
unless this is your current position
right and we will throw the err on the
end it like below end it if none of
these are
selected yeah and uh then in the end
let's combine this one and this both of
these schema into one single schema so
I'll say resume schema equals z doob
first I'll add the contact info contact
schema then summary
skills experience all of these will be
z. array and we'll provide this entry
schema that we have created just over
here right simple enough let's take this
rese schema go back to our rese Builder
inside of use form form I'm going to say
resolver equals to Zord resolver import
it from uh this hook for/ resolver Zod
and inste of this I'm going to give this
resume schema make sure to import this
as well and then we can provide some
default values to our form which I'm
going to say contact info to be empty
array sorry empty object summary to be
empty skill to be empty and all of these
to be an to be empty arrays yep and just
like before we will take out a few
things from inside of it like control
register so we did not uh you know took
out earlier so I'll explain you what
this is all about so register handle
submit to you know submit the form watch
to watch a particular field inside of
our form or multiple Fields as well and
from the form State we will take out the
errors to display the errors now this
control basically helps us to
essentially take the control of the
whole form and we can manipulate it
using this variable right here and I'll
show you how we are going to be using it
and now while we are at it let me just
take our a API over here as well so I'm
going to say use fetch import use fetch
I'll take the save resume as well which
will enable us to save the resume when
we submit the form and we will take out
loading function data and errors and
rename all of them one by one cool now
as we are going to be uh you know
writing inside of our form right we'll
be entering some data over here right
like something like this we will be
updating our markdown in real time right
so for that we need to watch all of of
the form Fields so what I'll say I'll
say const form values equals watch right
now we will make use of it very soon and
also if there has been the initial
content provided to us right if a resume
already exist then we can say we'll have
a use effect over here make sure to
import it from react I'm going to say if
initial content is there then the SE
active tab will be previewed right that
means we will go to markdown tab
directly without going to the form okay
and obviously I'll put it in the
dependenc area because because if it
changes then this changes as well all
right now let's go on and first start
building our form so I'll go down inside
of this tab content value edit and I'll
say form over here inside of this first
of all we will be taking the contact
information so let's create the dev for
that I'll say H3 to be contact
information then after this I'll have
our div which will contain four
different things like four different
inputs so the first one will be label
email and the input let's import input
from Shad CN UI we will use register
over here obviously to tie it up with
the contact info schema so contact
inside the contact infos schema I'm
going to go and tie it up with the email
right and I'm going to say type email
some placeholder over here and error
equals errors. Conta info. email if you
remember this errors object comes from
right here right and after this let's uh
display our errors as well just like
we've done earlier errors. Conta info.
email if it exists then we will show
inside of a P tag this this exact error
message with the text at 500 and text
small let's provide some Gap in between
of those I'll say space Y tob2 and since
there are going to be four such divs
over here so actually we should be
wrapping them in another div over here
so I'll have a div I'll wrap this whole
thing in another div and for this div
right here I'm going to say class name
to be display grid in the smaller
screens we show one single column in the
medium screens two column Gap to be four
between them some padding four around
them some border rounded Corners are
going to be large like back uh border
radius background is going to be muted
with 50 opacity right let's see how this
looks currently inside of our app server
only cannot be imported okay so I
believe we have not added yeah use
server directive over here so I'll say
use server and save it now we should not
see the error yep something like this
cool let's build the other contact
information so I'll actually duplicate
this one three times 1 2 3 okay let me
provide some gap between these and for
the second one this will be for mobile
number right so I'm going to Simply say
mobile number in the label and in the
input this will be contact info do
mobile with some with the type of
telephone and some placeholder over here
in the errors instead of email I'm going
to say mobile then next this one will be
for LinkedIn URL again same contact
info. LinkedIn and change this error
over here as well then the last one will
be for the Twitter account so I'll say
Twitter or X profile I'll say contact
info. Twitter and I'll provide some
placeholder and the errors as well cool
then after this Dev we will have the
professional summary and again it would
look exactly the same so I'll just
quickly import it it's just a Dev with
the ag3 of professional summary and now
over here this is something that you
need to understand so when it comes to
react hook forms or react hook form it
works fine with the native HTML tags
right but when it comes to such third
party components like text area which is
provided by you know shaten UI or
probably like SEL it component or
something like that we need to wrap it
inside of this controller component
which comes from react hook form right
so we will name it summary with respect
to what it was in our schema then we
need to provide the control like this
control if you remember we took it from
right here from our form right so this
will help it to get the control of the
form then in the render this is where we
can render the component in our case
this is text area for writing the
professional summary and this gives us
this field over here which we need to
spread over here so it this takes the
control of this text area then we can
simply give some class name placeholder
and error to be errors. summary and
simply render the errors in the end
let's see how it looks text area is not
defined obviously we need to import it
as well yep something like that let's
just give the parent if some class so
that we have some gap between them so
this one I'm going to say class name to
be space y to be four and our form to
have space y to be8 and also we will
have an on submit over here right which
will give which we will give it handle
submit Ander this we will provide the on
submit form which sorry on submit
function which does not exist so let me
just create a dummy function for now
just like this let's make it an
asynchronous function which will get all
of the form data okay so now after where
is it professional summary we need to
add the skills but let's see how it
looks currently yep something like that
this looks much better so that one will
also be a text area so what I'll do I'll
just copy it and paste it right here
instead of professional summary it will
be skills name is going to be skills
over here we can say list your key
skills and in the error as well instead
of summary I'm going to say errors.
skills now after this we will render The
Experience projects and education part
right where we we can add multiple
experiences or you know skills Etc sorry
not skills I mean projects Etc so simply
it will be uh just like what we have
done over here as well so I'm just going
to copy it and paste it at here and
notice what I'll do so we won't be
rendering text area of course we will be
rendering a custom component over here
so first of all this will be called as
work experience let's say experience and
then in the errors as well I'm going to
say experience after this we will be
doing the same thing for the education
and the projects as well so let me just
duplicate it multiple times so I'll
paste it right here and then Gap paste
it right here obviously this will give
us error since we don't have anything
over here so the second one can be
education education over here as well
and over here as well for the third one
instead of this I'll say projects and
over here as well okay so what do we put
inside this render we will be creating a
separate component a new component
called entry form where we'll be writing
the logic for this thing right so let's
just create a a component right here I'm
going to create a new file entry Das
form. jsx R A fce entry form and then
inside of this we will take three things
the type all of the entries that we have
and on change what happens right so we
will be controlling this component
obviously using uh use form as well and
also we will be using that improve with
AI logic inside of this entry form as
well now over here first of all let's
create our use form just like we did
earlier so I'll import use form from
react hook form Zord resolver and
obviously entry schema that we have
created separately right here right then
we will provide some default values to
title organization started ended etc etc
right and we will take out the register
handle submit form State reset watch set
value and all that over here as well and
I'll rename this handle submit to handle
validation because simply this will help
us in validation only right not
submitting the form that's why this
makes more sense and I'll also watch
this uh current field so that we can
disable the end date field let's import
this right here so first for the work
experience I'm going to say entry form
type experience entry field. value and
on change field.on change same for the
education just type education and for
the project as well with the type
project okay let's go back to the entry
form and let me just first see if
there's any error or not so yeah we can
see all these three right here so first
thing first we need to have a button
over here which will be responsible for
adding the you know whatever experience
and stuff and we need to have a state as
well which will tell us that if a new
experience or project or education is
being added currently so what I'll do
I'm going to say is adding and let's
take use State from here and let's make
it a client component so say use client
and now I'm going to say if is adding is
not true then only we will show this
button and on click we will say set is
adding to True right now above it we can
add the form when is adding is true we
can display that form right so now above
this button I'm going to say if is
adding is true then we will you know
let's add a card component over here so
let's just take the card from Shad CN UI
yep over here and paste it right here
let me import the card card header we
not going to be needing the card
description card content and also import
card footer as well okay so card title
can be add and the type like whatever it
fits Experience Project Etc in the card
content we can create our form so this
can have a class name of space y to be
four inste of it I'm going to have a div
which will contain it and then separate
divs for things like title organization
etc etc so inside of it I'm going to
have a div first of all for the input of
title /os we will register it with our
hook form with the title in the schema
right so error will be errors. title and
then error St title if it's there an
error then we're going to display the
error right here then below it I'm going
to have another d similarly for
organization as well and I'm going to
register it along with the errors and
these both will be inside of one single
div right here so I'm going to say class
name display grid grid columns to be two
and some gap of four between them so
let's see how it looks button is not
defined okay let's import this button
from over here plus circle is not
defined okay yeah I have not imported
this let's click on ADD experience now
and yep we can see title position
organization company ET Etc okay now
below this div we will have another
similar div but that one will be for you
know our start date and end date so
again similar class name so I'll just
take this grid class name and I'll put
it right here and I'll put it right here
then insert this div first of all I'm
going to have a div for type month input
type month and register with the start
date and similarly I'll have another div
I'll just duplicate it below it and I'm
going to have this for
and date but this one will be disabled
if the current is enabled so if you
remember this current is watching this
current checkbox right if this checkbox
is ticked then end date will be disabled
and we will be creating that checkbox
right below it so I'm going to have a
div and input inside of it of type
checkbox ID is going to be current
register current onchange we will set
the value like manually setting the
value inside the hook form for the
current field to be e. target. checked
and if it is checked then I'll say set
value of end it to be empty right then
we don't want to have an end it over
here and of course we're going to have a
label called current besides the
checkbox let's see how this looks so far
if I click on over here yep you can see
this is disabled now we cannot select it
now below it we need a description as
well again exactly the same just like
what we've done until now we're going to
have a d with text area placeholder
description of your whatever you know
experience or project Etc class name
height 32 register with the description
error errors. description and we will
render the errors over here as well
let's see text area is not defined if I
click on ADD experience yep we can see
if I click on ADD education we can see
all the things related to education
let's add a button for you know
improving with AI but actually before
that let's just uh import that API right
here that we created improve with AI API
or server action whatever you want to
call it so I'll take the use fetch hook
and improve with AI so this will have
loading function data and error let's
also have a function for handle delete
which we will come back to very soon
which will enable us to delete uh you
know the experiences or the projects or
education we will also have a function
for improving our description with a so
I'll say handle improv description and
another function for handle add as well
right to add the experience education of
projects and while we are at it let's
just write this handle improv Des uh
description function right away so it
will be very simple uh let's just it
will be an asynchronous function and
start of this I'm going to Simply say
take the description like I'm I'm going
to watch the descriptions value we will
have the descriptions value inside of it
and then I'm going to say if description
does not exist I'll say toast error
please enter a description first you
know before trying to improve it with AI
then after that I'm going to say await
improve with AI FN this thing right here
I'm going to call it with two things
first of all the current description
right in the current field and what the
type is right that is experience
education or project let's render the
button now so I'll go right over here
yep text area and right below the text
area Dev I'm going to have a button of
type button sorry we don't submit
anything like not submit the form over
here in the Resume Builder variant is
going to be ghost size is going to be
small on click we will call that handle
improve description that we just now
wrote it will be disabled when the
description is being improved so is
improving is like loading is going on or
we are or we don't have the description
over here and we will display the
loading indicator when it's loading
using the loader to so make sure to
import it from Lucid react and we will
also import the Sparkles icon to improve
with AI let's try it out if it's working
or not so let's just say same thing
Google sde even though these two things
does not matter say I worked on Chrome
browser let's click on improve with AI
okay improving and this should generate
a description for us which it did not
let's see let's open the network Tab and
try again improve with AI it did
something yeah you can see we are
getting the uh no when oh sorry it
should be in the preview developed
Implement oh yeah we are getting the
response but we are not able to see it
right here I think I have missed
something oh yeah I have to add a use
effect over here inside of this we will
take the data from the API right
improved content so inside of the use
effect I'm going to say if we get this
uh improved content right here first of
all over here in the dependency area we
need to add a few things like improved
content only then this will run or
improve error or is improving or set we
don't need actually this one so yeah
these three things if any of these three
things changes then we will say if
improved content has something and it is
not loading anymore then we will set the
value of the description and I'll show
the toast. success otherwise if there's
any error I'll show toast. error fail to
improve description let's check it out
now let's just say I worked on Chrome
browser if you want you can also you
know Supply the name of the company as
well I think that would be much helpful
okay toast is not defined import the
toast one more time let's click on
improve with AI and now it should work
yep just like like that awesome and
again I would recommend you guys to add
like send this company name to the API
as well you just have to you know send
it from over here and make changes in
this API inside of this right here you
have to take this spam over here and
provide it to the prompt very simple I
believe you guys can do it on your own
all right now how do we add this uh to
our form add this like particular item
for the experience and all let's add two
buttons inside of this card footer first
one will be for cancelling so when we
click on cancel we will reset the form
and set is adding to false this will be
of type like variant outline and type
button then another one we will had have
the add entry button which will trigger
this handle add function that we have
created but not added any logic inside
of it and we will say add entry let's
import this yeah plus circle is already
imported so let's check it out yep
something like this let's give this some
class name to be displayed at the very
end so I'll say display Flex justify end
and Space X to be two so this will yeah
appear right here now when we click on
ADD entry we want to add it right so
let's go to handle add function over
here and first of all we want to format
the data so I'm going to save and by the
way since we are submitting this right
so let's wrap this in handle validation
or this handle submit function that we
took right here right so that we get the
date like access to the data of the form
then I'm going to say const formatted
entry insert this we're going to have
the object which will take data and then
let's format the date right the start
date and the end date so for that I'm
going to create a function right here a
helper function so I'll say const
format display date this will be a
function obviously which will take a
date string andert this first of all
I'll check if date string does not exist
return empty but if it does I'm going to
take this pass from date- FNS and pass
this date string in this format right
here and then return I'll like import
this format from D- and return it in
this like month and whatever 2024 Etc
okay let's go back to this format entry
and here simply I'll take the start date
and end date and I'll use this format
display date and provide it with the
data. start date and obviously if the
current is true then we won't be needing
the end date right else we will provide
the end date as well after this we would
get this on on change function over here
right so if you remember if I go back we
get this onchange field. on change which
will enable us to add this to our main
form which is inside this Resume Builder
so after this I'm going to say on change
whatever the entries are already I'll
provide it and then after that formatted
entry and then reset the form set is
adding to be false after this after
handling the addition we need to render
all of these fields over here as well
right so above this I'm going to map
through all of the entries so let let's
just have a div over here and for this
parent div I'm going to say class name
space y24 and this div as well space yb4
and inside of it we will simply just you
know map through all of the entries that
we have inside of it so I'll say entries
do map and here inside of this call back
we will just have a card component so
again let's go back to shaten UI and
take this card component and I'll paste
it over here make sure to add return
over here I'll say key to be index
obviously we need to take the index from
here and also each and every entry so or
each and every item so item and index
for the card title I'm going to say this
title of the user at the organization
whatever companies or whatever the
project that you're building for right
let's remove this
description and the footer let's import
all of these things I think they are
already imported since we have used them
in other places below this I will have
the option to delete them right so I'll
have a button variant outline size icon
type button and on click we will trigger
this handle delete function which we
will come back to this very soon and
let's import this x icon from Lucid
react then inside the card content
simply we will just render item do
current if this is true that is if this
is a current organization then I will
render the date from start date to
present else we render from start date
to end date and obviously the
description that they've written for
that particular experience education or
project for this header let's give this
some class name of display Flex Flex to
be row so there are one single line
items to be Center justify between so
that they are on Far ends Spacey to be
zero and padding bottom to be two
between them so that we have some Gap
and I think this should render it let's
just quickly go over here and handle it
and write the logic so this will simply
take the index right so if you see we
are taking the index over here right and
it will take the index and it will say
con new entries and it will simply
filter out that particular Index right
if index is not equals to this index
only then return else remove it and on
change we will add these new entries
let's check it out I'm going to add
let's say XYZ XYZ
XYZ description XYZ org let's just say
I'm going to select any random month
current experience and click on ADD
entry okay there we go it has been added
if I click on X yep it is removed from
over here as well awesome so we have
successfully built this form right here
now let's render this form and this
markdown and add the logic to save our
resume inside of our database as well in
the markdown format so for doing that we
will be using this package right here
react MD editor and this will allow us
to you know render in markdown format
and also allow us different different
views to you know edit the markdown
directly as well if you you know if you
don't want to edit the form so cool all
we have to do is just copy this up npm
install and I'm going to go to my
terminal and paste it right here npm
install at uw/ react MD editor and press
enter we will also be using this another
Library called HTML to PDF to save our
markdown resume as a PDF file so copy
this up as well and paste it right here
and actually I don't think we need to
add uh the version so yeah this should
install it now so now let's go inside of
this other tab where is it sorry not
here in the Resume Builder component
over here in the tab content value value
to be preview so inside of it we want to
First display a button over here so that
we can add the ability to directly edit
the markdown so if you see in our
deployed application we have this button
called edit resume when we click on it
see we are able to directly edit the
markdown and we also show this warning
that if we you know edit the markdown
and then go on to edit the form again we
will lose all of the you know progress
over here after editing the markdown so
okay we only want to show this button
right here if we are on the preview
right so so I'm going to be adding this
button right here which will say variant
link type button and class name to be
margin bottom to be two and we will have
this edit icon over here let's import it
from Lucid react and let's see how this
looks inside of our application if you
go to markdown yep we can see this edit
resume so let's create a state right
here on the top which will check if the
resume mode is on preview or not if it's
on preview we will show the you know
preview else we will show the edit
markdown State and then right here if we
are on the resume mode preview we will
show edit rume else we will show Show
preview and on click whenever we click
on it I'm going to say set res mode if
it's on preview set it to edit else we
will say preview okay let's see oh
monitor icon is not being imported so
let's see if I go over here Yep this is
working fine now below it is where we
will be rendering markdown editor but
before that like if we are on the edit
mode mode we want to show that warning
as well right so I'm going to say here
if resme mode is not equals to preview
that is we are on the edit mode then
I'll have a d which will show this alert
triangle and a message called you will
lose edited markdown if you update a
form data and by the way I created this
custom over here but you can also take
this uh alert from Shad CN UI so alert
yeah you can see it provides you with
pre-built component similar to this but
this also works fine right so again
let's go on and check it out yeah
something like this now below it we can
uh render the mdor so how do we do it so
let's go and check in the docs we simply
have to take this component MD editor
and we need to provide the value of what
we want to render and on change whatever
the function is we want to trigger it
right so let's create a state for it I'm
going to say preview content right this
is where we will have our markdown
content we'll provide it the initial
content right here if there's already
some content inside of our database now
let's just create a function over here
to get the you know to basically convert
the form data ins to the markdown format
So Below this use effect right here I'm
going to create a new function called
get combined content and inide of this
is where we will be creating our
markdown content now our markdown
content contain multiple different
things right so let me just take out the
things from form values so it will be
summary skills experience C education
etc etc so I'm going to say return and
it's going to be an array and first of
all we need to combine uh the contact
information right and then we will add
summary skills and then the experience
education and project so first of all
for creating the contact I'm going to
have a function over here get contact
marked down and I will have one more
function so let me just uh you know
create a separate helper file for it so
helper. JS over here
inside this I'll be creating entries to
markdown function which will take the
array of you know experiences or
projects Etc and it will convert them
into the markdown format right so these
two functions are what we will be
building and then inside of this return
I'm going to use them something like
this so first of all get markdown
contact it should create the contact
markdown summary I'll render the summary
in this format so this hashes and two
hashes are what you know represent the
headings I would recommend you guys to
uh learn more more about markdown it's
basically a format on how you can create
a particular document or something right
just the way you create your readme
files on GitHub right so I'll say this
professional summary heading and then
I'll say slash n for changing the line
and then add this summary similarly for
skills and this entries to markdown will
take the experiences education and
projects and will structure them
accordingly let's first see this get
contact markdown also let me just import
it yeah so first of all for getet
contact markdown we will take the
contact info from the form values and
this has four parts right email mobile
LinkedIn or Twitter right so I'm going
to say const parts I'll take an empty
array I'm going to say if email is there
then parts. push with this emoji and
contact INF for email same with mobile
same with ledin and same with Twitter as
well and then in the end I'm going to
combine all those four parts by saying
Parts dot length if it's more than zero
if we have something inside of it then
we will take a div and we want to render
it in the very center right so I'm going
to say align Center some inline styling
user do full name on the top and then
after this the contact information that
is part parts. join with this thing
right here right simple enough and then
we will render it right here what about
entries to markdown first of all we will
check if entries has anything inside of
it so if it does not have anything
return empty but if it does then return
this so I'm going to say first of all
what is the type right so let's say if
it's experience so I'm going to
experience then change the line Plus for
each and every experience we will do the
following we'll take each and every
experience over here and we will render
the date from this like if the current
is present then from start date to
present else from start date to end date
and then after that we will render
things like what is the title the
organization how much time we have
worked there and the description let me
show you how this will look like so if I
go to my deployed app see something like
this title at theate organization and
then the date and then the description
right after obviously this heading of
work experience or education Etc all
right let's go back and now we have this
get combined content function and now I
want to render it in the markdown right
so let's just let's just before this I'm
going to create a use effect hook over
here inside of this whenever our form
values changes so inside of this array
I'm going to say form values if this
changes or the active tab changes then
we will update the markdown content with
whatever we have inside the form right
so I'm going to say if active tab is
equals to edit then I'm going to take
the content from this get combined
content over here and then I'm going to
set preview content if we have anything
inside this new content then we will set
it else we will take whatever the
initial content was being provided to us
so if you remember this state that we
just now created set preview content
okay now we will take that uh preview
content and then render our markdown
right over here inside of the other tab
content right in the preview So Below
this so just like we saw in the docs let
me just have a div over here with the
class name of border and rounded large
and instead of it I'm going to import MD
editor and provide it with the value
preview content and on change set
preview content and I'm going to say hi
to be 800 and in inside of the preview
we'll provide this resume mode so if you
remember we created this state right if
it's preview we will show the preview if
it's edit we will show the edit so let
me just check it out inside of our app
right here what is the error maximum
update depth increased okay let me just
refresh this I think something is wrong
over here it is updating this again and
again so it's right here let me check I
think I know what's causing that error
we have this return over here right
after this we need to say do filter
Boolean like if we don't have anything
in any of these we need to filter it out
and then join them with the sln so that
they are on the separate lines now let's
see let's see if I add anything over
here user is not defined okay yeah we
are taking user over here right in the
contact uh markdown so I need to take
the user from clerk so I'll say use user
and I'll take out the user info over
here so I'll say user now it should work
let's add something and go to markdown
yep something like that let's add some
gibberish over here some
skills right and let's see yep we are
starting to uh you know form it let's
add some chish in the
experience let's say it's a current
experience add entry it should have been
added now awesome you see how beautiful
this looks we are able to add our resume
right here how do we get the PDF
generated out of it let's see so what
I'll do right below this one this MD
editor I'm going to have another div and
and I'm going to be rendering just the
markdown we will give this the class
name of hidden so that we cannot see it
but since we will have the HTML inside
of our Dom we will just take it and
convert it into the PDF right so that's
why we given this ID resume PDF over
here because we will be using this thing
HTML to PDF library to convert it right
so how does it work let's go and see
first of all we need to add the button
for downloading PDF which I believe we
have already added right so it should be
right here yeah let's create a state for
it while the PDF is generating right so
I'm going to have U State over here is
generating and set is generating okay
then in this button download PDF I'm
going to say if is generating is true
then display the loader imported from
Lucid react and show generating PDF else
show the download button with download
PDF right now over here in the button
I'm going to call a function right here
called generate PDF and it will be
disabled when the PDF is being generated
so let's create this generate PDF
function uh let's create it right here
just generate PDF this will be an
asynchronous function and first of all
I'm going to say is generating to true
then I will have a try catch block over
here inside the tri block first of all
we will get that particular element from
which we will be generating the PDF so
I'll say const element equals document.
get element by ID resume D PDF okay
after it how does it uh work html2 PDF
PF first of all let me just import it so
HTML to PDF import it like this let's
see if it has been imported yep I think
this might cause some issues because you
know while I was developing this project
this is how it allowed me to use it
right so make sure that you also import
this HTML to PDF like this I'm not sure
if it has been fixed or not but let's
see we will try it out and check if it's
been fixed so this will be a wait call
html2pdf Dot and I will set some options
over here so let's see what options so
I'll say const options equals inside of
an object I'm going to provide it few
things like there will be margin 1515
file name is going to be resume.pdf
image type JPEG and we'll provide the
quality we will use HTML to Canvas so
basically these are some of the things
that I have taken from their
documentation and we will provide some
PDF configuration over here as well
right like in the format of A4 and
orientation be portrayed Etc then I'm
going to say these are the options and
from this particular element so I'm say
from element and then save it in the end
after this for the catch I'm going to
say console. error PDF generation error
if there's any error and finally I'm
going to say set is generating two fs
and this should give us our PDF let's
try it out so yep over here so let's
just write anything for let me just have
this contact information and try to
download this so if I click on download
PDF okay generated is okay let's see oh
yeah amazing it has generated this file
for us we can select all this text and
all as well we this is clickable you can
see at the bottom you can see mail to
this email address right so this is
fully interactive resume over here as
well if you want to add some you know
links and all to this experience
description over here they will continue
to work awesome now the only thing
remaining over here is to be able to
save this resume inside of our database
and by the way one more thing if we want
to edit this directly we can click on
edit resume and and we can edit it right
over here if I want to Let's remove agal
and click on Show preview yep you can
see this is editable in real time
awesome let's go back to the code and we
have already created this on submit
function let's see what do we get when
we submit it right so I'm just going to
console log the data for now and go back
open inspect console click on Save uh
doesn't do anything let's see on submit
and this form should have have that
button for submitting it so I don't
think we are calling that function so
let's see yeah we have this save oh yeah
we are not calling it right here so I'm
going to say on click all this handle
submit on submit and this won't be
useful over here so we can remove it
from here yeah so handle submit and
we'll call this on submit right here and
it will be disabled when this is being
saved and let's just use this loading
indicator over here as well so I'm going
to say if it's saving show the loader to
Icon and saving else show the save
button okay what else do we need to do
let's try it out first if I click on
Save still doesn't work what's wrong oh
I believe it because we have not filled
this professional summary over here so
let me just write anything right here
can I save it now oh yeah email address
let me just open the inspect click on
Save okay it's asking for the skills
again let me just add in gibberish over
here click on Save again yes now we are
getting something over over here okay
cool then but I don't think we would be
making use of this data anyways so it's
not useful for us so you know what uh
when I'm clicking on that button over
here we can choose to remove this handle
submit from over here right like this so
inside of this onsubmit I'm just simply
going to have a try catch block and I'll
say await save resme function and
provide the preview content that we
earlier had save resume function this
one and obviously we won't be making use
of this data now now and inside the
catch just simply say console error and
whatever the save error is if there's
any error right and yeah we need to have
a use effect as well which will trigger
our toast right so I'm going to say if
save result changes save error changes
or is saving changes then trigger this
and I'll go inside of it I'll check if
save result has something and it's not
saving right now then display this toast
make sure to import it from sonor resume
save successfully else if there's an
error show the error message right here
right that is all I think this should
work now now let's see let me just add a
bunch of jish again over here so this is
a random number don't try calling it
it's not my number so let's just say XY
Z XY Z let's add some work experience
XYZ
XYZ from this to this add entry and
let's go to markdown yep we can see some
rume over here let's click on Save and
it should save it yeah resume save
successfully if I refresh it now we
should still see that rume being fetched
yep we can see our resume being fetched
from the DB let's go and check it out
inside of our neon DB there we go this
is the content the markdown content that
you can see is being stored and which
user does it belong to and yeah that's
pretty much it for the resume right
awesome so you have just built a
production grid feature right this is a
I would say pretty engineering heavy
feature if you have not built something
like this up until now right we are
rendering stuff in markdown format we
are downloading this as a PDF this
feature alone in itself trust me is such
an impressive thing to be added inside
of your resume right building a rume
added to your resume so yeah definitely
I would recommend you guys to Showcase
this in your interviews whenever you're
applying to a company and all right now
the next feature is building the cover
letter so let me first go to my deployed
website and check how this feature works
so you can see we have all of our cover
letters over here if you click on create
new we would enter the company name the
job title and the description over here
and click on generate cover letter and
what this will essentially do and you
can see over here we have used Zord
validation here as well right and this
will basically provide it to the Gemini
API and generate a cover letter for us
very very simple very straightforward
feature and I want you guys to build
this feature because let's be honest if
you have followed this tutorial up until
this point and if you're not able to you
know build a feature like this because I
would say this is probably the simplest
of features and don't worry I'll tell
you as well how you can build this if
you're not able to still build a feature
like this then there's something wrong
with your learning trust me so I don't
want to spoon feed you guys by building
every single aspect so this is an
assignment for you guys if you want me
to make a separate video on this I will
make a separate video on this let me
know in the comments down below but let
me explain you how you're going to be
expl like you know building this and I
already have the code for the whole
thing inside of my GitHub repo so if you
get stuck you can go over here so first
of all you will have to build the server
actions right and again if you don't
want to see this part you can skip this
part and go directly to the deployment
right you can check from the chapters
below the video I have mentioned where
the deployment starts so if you want to
get an idea how you can build this
feature see we will create this function
very simple generate cover letter right
and we will send it the data of the
title company description and the like
the job title right and we will write
this prompt over here we'll provide the
user industry years of experience the
skills from what user has entered in the
top like at the beginning in the
onboarding screen right so that is the
information that we will be using to
craft this cover letter and we will
enter the requirements on how we want it
we would want it in the markdown format
as well keep that in mind so if you go
back and see the one that I've already
generated if I click on over here in the
I button see this is in the markdown
format so this is pretty straightforward
to store it and then you can create a
server action to get all of the cover
letters if you want to get one single
cover letter for one single page right
that is what you can do as well and if
you want to delete you can write dis
delete all of these things we have
already covered in this course and now
you need to take action from your side
and build this feature right and the UI
which is I would say in this case is
pretty simple is inside of over here in
the main folder AI cover letter right I
have a separate component for cover
letter generator list preview and
obviously the page over here as well the
dynamic ID page and obviously for
creating the new cover letter as well
right so definitely try it out yourself
if you're not able to do it let me know
in the comments down below for now if
you want to host this res uh project
let's go on and host it on verell right
and then maybe later on you can continue
building this cover letter feature
awesome so first thing first we need to
push our project to a GitHub repository
so let's open GitHub and click on over
here to create a new repository let's
name this Sensei let's keep it a public
repository create repository all right
now let's push all of our code to it so
I'm going to go to the terminal and I'll
say first of all get in it press enter
and also by the way we need to add
inside of package.json a script over
here called post install which will run
Prisma generate you know generate all of
our schemas after deployment all right
now I'm going to say get add dot to add
all the files and then I'll commit the
changes so I'll say get commit dasm oops
one one thing I don't don't want to add
the do EnV so let me just check if I
have not accidentally pushed it or let's
see I don't think we have make sure in
the dogit ignore we have EnV over here
yes we do so no issues so now I'm going
to say get commit DM and provide the
message let's say preparing for
deployment press enter there we go now
I'm going to say get push
origin master and this should push it uh
what's oh sorry we have not added the
origin so make sure to copy this line
and paste it over here first get remote
add origin and then get push origin
Master there we go it is posted so if I
refresh this page now we should see our
code right here awesome so now let's go
to verell ver.com and we want to deploy
our website right here so click on add
new project over here let's click on
import and this will import our get
repository and by the way we need to
configure inist as well so I'll go to
in.com slocs and inside of this we want
to find the deployment so over here in
the deployment overview deploy with
versell okay so we are using App router
yes installing injest official versell
Integrations click on over here yep
right here we need to connect our versel
account so click on connect account and
let's select the team connect account
okay and then after this oh yeah we
after this we need to select that uh
verel project right so right now let's
just first log into verel I'm going to
sorry not verel I mean inest we will log
into inest right here it's going to show
me all of these projects so this is not
the project this is actually my previous
deployment so never mind let me just
close it for now let's just first go to
versel and deploy it so let's add the
environment variables first of all over
here so I'll go to my code go to EnV
let's take these one by one and add it
right over here the key and the value
let me just add them quickly and come
back there we go I have added all of
these values over here and now I think
we are good to go let's click on deploy
and let's see so it has started
deploying right here and first of all
it's installing all the dependencies
okay creating an optimized production
build hopefully no error will occur okay
it is generating static Pages now and
this is a good sign I think our
deployment is about to be completed
amazing our website is successfully
deployed awesome let's click on over
here and see and yeah we need to do one
more thing we need to connect inist as
well right so click on connect account
now in the same page that we opened
earlier and now we need to choose over
here that Sensei website y this one
right and click on Save configuration
continue to injust worel dashboard let's
click on configure and yeah we should be
taken to right over here and we should
see yep our Sensei website over here so
if I click on configure our project
status enabled versal deployment
protection might be block syncing uh
might block syncing okay yeah we need to
change this actually so one thing that
you need to change uh let's click on
continue to dashboard invers Cel go to
settings and we need to disable the
deployment protection over here so turn
it off and save it and now if I refresh
this up yep now it's working fine so if
I go back if I should see over here I
don't think we have over let's click on
sync new app and we need to get the uh
URL for our app so our URL is Sensei D1
verel app so let me just copy it up
paste it over here and add/ API SL inest
after it and click on sync app and there
we go our app is synced successfully if
it does not work for the first time try
doing it again sometimes it fails right
so you can see we have our injust
functions over here as well and we can
see how many times they have been
replayed obviously right now we have
deployed so they have not been replayed
but we can manually trigger it from over
here invoke it from over here right so
this is awesome let's go on and test out
our app I'm going to click on sign in
and let's sign in with Google okay we
are signed in let's click on get started
and there we go we are inside of our
dashboard if I go to build resume okay
this was the resum that we built earlier
right great we have our form and
everything over here amazing so you have
successfully created this awesome
project to your resume now the next step
for you guys is to start applying for
interviews and if you want to prepare
for you already know you can check the
link in description down below for my
front inter preparation course and enter
the coupon career for the maximum
discount
UNLOCK MORE
Sign up free to access premium features
INTERACTIVE VIEWER
Watch the video with synced subtitles, adjustable overlay, and full playback control.
AI SUMMARY
Get an instant AI-generated summary of the video content, key points, and takeaways.
TRANSLATE
Translate the transcript to 100+ languages with one click. Download in any format.
MIND MAP
Visualize the transcript as an interactive mind map. Understand structure at a glance.
CHAT WITH TRANSCRIPT
Ask questions about the video content. Get answers powered by AI directly from the transcript.
GET MORE FROM YOUR TRANSCRIPTS
Sign up for free and unlock interactive viewer, AI summaries, translations, mind maps, and more. No credit card required.