This tutorial provides a comprehensive guide to building a Trello-like project management application called "Tasky" using Next.js 14, Tailwind CSS, and Shadcn UI. It covers front-end development, back-end logic with server actions, database integration with Prisma, authentication with Clerk, and deployment.
Mind Map
Click to expand
Click to explore the full interactive mind map • Zoom, pan, and navigate
hey there my name is Antonio and welcome
to the newest video on my channel in
this tutorial you're going to learn how
to build an amazing Trello clone which
we are going to call tasky and as you
can see I've already prepared some
boards for us so let's go ahead and
check out what's inside my board as you
can see I've prepared a list to do in
progress and done and we can easily move
them between each other just by dragging
and dropping
and as you can see we also have a
reflective notification in the lower
right corner right here now let's go
ahead and add a new card inside of the
to-do list all we have to do is click on
add a card or we can visit the options
and click add a card from here and now
let's add a new task optimization for
example and we can click enter or add a
card like this and we have a
notification for that as well perfect
now let's see how easy it is to drag and
drop that inside of another column just
like that we can do it as many times as
we want perfect now let's go ahead and
click on an individual card to see
what's inside as you can see it says the
name the list where it's in its
description as well as its activity for
now all activity that it says is that I
have created this card so let's go ahead
and rename this card
and once I save this you can see that I
have a new activity they have updated
the card and we have a new name right
here the same activity is triggered if I
add a description and click save perfect
and now let's see how easy it is to copy
a card as you can see I can select
landing page for example and let's add a
unique description here and let's click
save and now once I click on the copy
action right here inside of that same
list on the bottom we have a new task
landing page copy and as you can see the
description has been copied as well but
the only activity we have is that we
created that card perfect exactly what
we want and we can just as easily delete
a card if we no longer want it but
besides this we can also go ahead and
copy the entire list so let's do that
with the done column right here I'm
going to click copy list and look at
this it is right here and the tasks have
been copy as well beautiful and we can
just as easily delete an entire list or
we can manually create a completely new
list like for example design and let's
click add a list and let's go ahead and
move it to its appropriate Place perfect
so now we know everything that we can do
inside of this board
we can also go ahead and rename the
entire board as well just by pressing
the enter and we have a notification for
that as well now let's go back inside of
our dashboard to see what else we have
as you can see on my left side right
here you probably noticed that I have
some workspaces that is because this
tutorial is also going to teach you how
to create a businesso business software
as a service so I have created a couple
of workspaces and you're going to learn
how to do that as well just by pressing
on this plus button your users are going
to be able to create as many
organizations or workspaces as they want
so let's see what we can do in an
individual workspace first of all you
can select an individual workspace right
here in the sidebar for example I can
select the boards inside of this
organization and as you can see I have
none of them here or you can go inside
of the nov bar and from here you can
select the organiz ization that you want
perfect so let's go ahead now and let's
click on the activity tab right here as
you can see inside of here we have a
similar component that we saw inside of
my card model that is because inside of
this page we have a list of entire
activity inside of this organization so
you as an admin can keep track of
everything that's going on inside of
your project besides activity we also
have this settings tab inside of here
you can manage new invitations and you
can also assign roles like admins or
members you can also kick people and you
can also manage profile settings like
changing the name or uploading an image
perfect and last tab we have here is the
billing Tab and once I click here we
have a model which says to upgrade to
task ify Pro and you are going to learn
how to build this entire thing
but let's not click on this right away
first let's see how to create a new
board as you can see I have an option
here to create a new board and it says
one remaining and let's hover over this
question mark to see why as you can see
it says that free workspaces can have up
to five open boards for unlimited boards
upgrade this workspace so let's fill up
this boards right here last one is left
and as you can see we have a beautiful
set of random unlash images loaded every
time you click create a new board so you
surprise your users perfect let's select
this one and as you can see we also have
the name and a link to the author of
that image so we Oblige by unsplash API
guidelines perfect and let's create this
one to be last board and click create
and you can see how fast that was
created perfect now let's go back inside
of our dashboard and now it say says
zero remaining so let's try and do that
now I'm going to try and create a new
board and as you can see I have an error
that I have reached my limit of fre
boards please upgrade to create more so
let's go ahead and finally click on this
upgrade button right here and we are
redirected to the stripe checkout page
and as you can see we have a
subscription of $20 per month so I'm
going to go ahead and enter some fake
stripe information here I'm going to
enter a fake name and I'm going to click
pay And subscribe right here and after
this has been processed we're going to
get redirected back to our organization
page but you can see that now it says
that I have unlimited boards and you can
see that right here it says that I'm on
a Pro Plan so let's try if that is
working I'm going to go ahead and pick
another beautiful image from unsplash
here and call this board premium board
and let's go ahead and click click
create and you might be wondering what
does this big create button do in the
nav bar right here well that is useful
for when we want to create a new board
without leaving this screen and you
might be wondering well how do we know
inside of which organization is this
going to be created well we can easily
take a look at that inside of our navbar
right here so this is our selected
organization meaning that that's where
the board is going to be created so
let's test that out when I click create
there we go I am redirected to the new
page perfect and what I want to show you
next is that inside of this organization
I have unlimited boards but if I go
ahead and pick another organization you
can see that here I do not here it says
free and I have five boards remaining
that is because the subscription is per
organization only so so this is going to
be a true businesso business software as
a service and of course you're going to
be able to manage your subscription in
case you want to cancel it at any point
for your business just right here
perfect but that's not all the entire
application is going to be fully
responsive on all devices just like this
and lastly we're also going to have a
beautiful Landing p agage so you can
showcase to your users exactly what
they're going to get out of your
application and one last thing that I'm
very excited about and that is that this
entire application is going to be using
next 14 and the new stable server
actions we have a lot to do so without
further Ado let's get
started so let's go ahead and let's set
up our project here on the left side
I've prepared prepared my visual studio
code which is my editor of choice for
this tutorial and here on the right side
I've prepared my browser in my case it's
Google Chrome so let's go ahead and
let's open up our terminal you can of
course use any terminal you want but I'm
going to use the one inside of Visual
Studio code in order to access that
terminal all you have to do is go into
the lower left corner and click on these
two icons right here and then just go
ahead and select terminal like this now
we're going to run a command which is
going to initialize our next 14 project
create-- apppp at latest and now let's
go ahead and give our project a name in
my case it's going to be Trello D
tutorial like this and let me just
explain what this at latest means so at
latest this specifies that we're using
the highest or the most upto-date
version of this package right here so
once you've written this feel free to
just press enter and now let's answer
some questions about the setup of our
project for the typescript option we're
going to select yes because we're going
to be using typescript for the Sint
option we're going to select yes as well
we're also going to be working with
Tailwind so select yes for that now for
the source directory make sure you
select no mine is pre-selected and if
you're wondering how to uh change
between the options you can use the
arrow keys on your keyboard so just make
sure you select the no option for Source
directory now very important for the app
router make sure you select yes because
app router allows us to work with server
components and use the new stable server ACC
ACC
which is going to be something we will
be exploring in this tutorial so make
sure you select yes for this option now
whether you would like to customize the
import alas is completely up to you of
course but I would recommend that you
follow the exact options that I have so
you have the closest setup to mine so
make sure you select no for this option
and that's going to leave this add sign
as the default import alas if you don't
know what an import alas is don't worry
once we start coding it's going to be
pretty clear perfect and now just go
ahead relax and wait for all of this to
install after the installation has been
complete you're going to see this
success message right here what we have
to do next is open up that project
inside of our editor so just for now I'm
going to close my terminal and I'm going
to click on the open right here and then
I'm going to select Trello Das tutorial
because that is the name of my project
and just go ahead and click open and if
you get this big prompt feel free to
press yes great and now once you open
your project you should have uh this
kind of project structure you should
most certainly have the app folder and
inside you should have the globals
layout and page you should have the node
modules which just means that this has been
been
uh installed for you and you should have
the public folder and of course some
configuration files for Tailwind like
this for typescript for Sint and some G
ignore files right here before we start
this project I just want to install one
more package inside and let's go inside
of our terminal again and I'm going to
go ahead and please excuse me I have to
upgrade my terminal so I'm just going to
pause for a second you probably don't
have have this so ignore
that great so excuse me about that and
now let's go ahead and write npx shed
cn- UI at latest in it like this perfect
and now now let's see the options so
would you like to use typescript
recommended make sure you select yes
because we selected yes for the initial
command which we run to initialize the
project so make sure those two match by
just pressing yes and now it's asking us
which style we would like to use you can
of course pick your own style but again
I highly recommend that you follow
exactly what I do and select the default
style and now about the color I'm going
to go ahead and select neutral this is
the least important part you can select
any color you want here but again if you
want your project to look exactly like
mine just follow along perfect and now
it's asking us where where is our
global. CSS file as you can see here
it's placeholder to appg global. CSS so
we can quickly check that by opening up
our sidebar and let's confirm that we
have uh our app folder global. CSS looks
like that is completely okay perfect so
we can just press enter it's asking us
whether we want to use a CSS variables
select yes for that and now here's the
bit of a tricky part it's asking because
where is our Tailwind doc config in my
case JS located right and it it has this
option right here but let's take a look
at our setup here as you can see we
don't have Tailwind doc config.js we
config.txt
placeholder which this provides you with
but you are going to have two
unnecessary files so what you can do in
this option if you have Js here the same
way I do is you can press the tab on
your keyboard and then you can go ahead
and edit this to be TS like this and
just press enter again nothing dangerous
or scary is going to happen if you leave
it at JS your project is still going to
work but you're going to have one
unnecessary file great and now we have
the question for the import alas for our
components if you left the default alas
uh the uh default as I did in the
initial options you can just press enter
here if you did any modification there
you're going to have to modify this as
well so this is why I recommended that
you follow the exact options which I
selected so just feel free to press
enter here and the same thing is for the
utils you can just press enter and last
question is referring to whether we are
using using the app router as I've
mentioned app router allows us to use
react server components so yes we are
going to be using them so just go ahead
and press enter for the yes option and
now that's it all you have to do is
confirm this so you can press enter or
just type y on your keyboard and let's
go ahead now and let's see what we have
so I'm going to go ahead and close
everything just to see what new items we
have inside so as you can see we now
have the components folder which is
currently empty we also have the new lib
folder inside and inside of this lip
folder we have our utilus file and as
you can see here we have some new
packages clsx and Tailwind merge and
this function CN which if you watched my
previous tutorial you already know this
is going to come in handy and we're
going to use this function a lot now I'm
just going to briefly explain what it
does but it is going to make more sense
once we start coding so the CN function
is enabling us to safely and properly
combine Tailwind classes specifically
Dynamic Tailwind classes for example
we're going to have a specific class for
error State specific class for Success
State and the best way to do that is
using this two packages so the shetan
command nicely combined those two for us
in this very nice util right here
perfect you can go ahead and explore a
bit more about all the files you have
here you can see that our Tailwind
config has now been filled with a bunch
of variables here so all of that came
from the shat CN UI and if you're
wondering what shat CN UI is my
apologies for not explaining uh because
I use shaten in all of my tutorials so
sometimes I assume that people know but
still I think it's important if you this
is your first time watching shed cnii is
a component Library which is very
compatible with next 14 which we are
going to use in this tutorial great so
now we are finally ready to go inside of
our terminal and run the command mpm run
Dev like this and as you can see I have nextjs
nextjs
14.01 running on a local host 3000 so
let's go ahead and refresh my browser
here where I have prepared that URL and
in a couple of seconds you're going to
see a landing page similar to this
perfect so let's go ahead and quickly
clean up this uh page so we are ready to
do some more work let's go inside of the
app folder and let's go inside uh of
page. TSX right here so everything that
you see on this page right here is
actually written inside of app folder
page. SX so you can just go ahead and
remove everything inside of this return
function so everything inside of this
brackets right I'm going to go ahead and
start with main I'm going to go all the
way down and I'm holding the shift I
click here and I click remove perfect
and now I can just write a div saying
hello Trello like this and let's remove
this unused import and clean up this
white space at the top and now if you
take a look at my uh browser here you
can see that I have a text hello Trello
don't worry if yours is smaller I just
zoomed in so you can see so now let's go
ahead and test out whether we set up our
shat CN correctly which means that we
should have our Tailwind working so I'm
going to go ahead and write class
name I'm just going to zoom in even more
so you can see and inside of this class
name I'm going to write text- sky- 500
and that should change the color as it
did in my browser into a nice bluish
color perfect and if you're wondering
how do I have this little color box
right here well that is a very cool
extension called Tailwind CSS
intellisense so you can go inside of
extensions if you're using visual studio
code and write Tailwind inside and the
very first option is going to be
Tailwind CSS intellisense so just go
ahead and install that refresh your
Visual Studio code and then you should
be seeing this and you're also going to
get another cool feature which is if
you're not that familiar with Tailwind
very useful for example if you have no
idea what this class does and you have
that extension installed you can easily
hover over a class and you're going to
see everything it does let's take a look
at something different for example let's
use flex das1 and when I hover you see
exactly what see CS that is perfect so
now we have this working we are ready uh
to start figuring out how routing Works
inside of
nex3 great great
job so now let's learn how routing Works
inside of a next project so let's go
inside of the app folder right here and
what I want you to do is create a new
folder inside of this folder give it a
name examle example like this and inside
of example create a new file page. DSX
like this let's go ahead and let's write
const page let's return a simple div
which is just going to say example page
like this and don't forget to do export
default page at the end otherwise
routing is not going to work perfect so
let me just clear something things up
this constant page does not have to be
named page it can be named anything you
want just make sure that you export
default the same constant so it doesn't
matter but in the tutorial you're
probably going to see me combine the
folder name and the well the fact that
it is a page so in my tutorial you're
going to see stuff like this my
constants for routes are going to be
example page this is the convention that
I'm going to use which if I accidentally
open this file I immediately know okay
this is a page this is a route this is
not some random component I know exactly
what this component does so I'm just
quickly explaining the structure here
perfect so now you might be wondering
all right well how do I access this
route well let's just confirm our folder
structure here so inside of our app
folder we have the example folder and
then then we have page. DSX in the uh
first part of the video I explained that
everything you saw on the main page is
inside of this uh root page page. ESX
which is just inside of the app folder
so now that we moved I'm sorry created a
new page. DSX inside of this example
folder that means that we can visit it
by going on Local Host 3000 SL example
and there we go we have now have our
example page right here perfect and now
let's go ahead and let's create another
folder and let's name it users so let's
imagine this is some kind of dashboard
right somewhere where we are going to
display our users and more importantly
how do we create a dynamic route meaning
that so far you've learned how to create
routes which are hardcoded like slash
example but what if we want to Route by
a specific user ID for example your user
clicks on some other users's profile how
do we create that URL what is the
structure well there is a very
convenient feature inside of next which
allows you to do just that so inside of
your users folder create another folder
and inside with give it a name starting
inside of square brackets so next
recognizes that this is going to be a
dynamic part of the URL it's not going
to be hardcoded like slash example or
slash users it's going to be dynamic
right and inside give it a name of the
variable that you expect the name
doesn't actually matter but you are
going to have to be aware of what is the
name because that's how you're going to
access it inside of your code so let's
clear this up let's see exactly how to
do that inside of users I'm going to
write square brackets ID like this and
then I'm going to create a new file
page. TSX and let's go ahead and write
const ID page you already know that is
my convention and I'm going to write a
div ID
page and don't forget to of course do uh
the export default at the end of ID page
perfect so let's check what that route
is going to be so that's going to be
slash users and then slash whatever we
want right because IDs can be a lot of
things so let's try that out inside of
my Local Host I'm going to write slash
users and then I'm going to write slash1
2 3 and look at this it's working even
though we have not defined this one two
three anywhere inside of our code so
that's what this convention of using
square brackets in our folder name
allows us to do so now what's important
for you to understand is how to fetch
this ID inside of this page. vsx because
that can be very useful right we you
probably going to want to then uh go to
the database and request the user with
that ID which we have in our URL what's
important for you to understand is that
by default every every page inside of
the app folder is a server component
meaning that from here you have some
special conventions and well probably
some different stuff that you're not
used to if you're coming from the single
page application react World but don't
worry I'm going to try my best to
explain that here so how do we access
that ID first thing we have to do is
confirm what is the name of the variable
in this case it is ID we know that by
looking at this folder and then go
inside of page. DSX right here and go
inside of the props
here and let's go ahead and extract the
params sorry just the params like this
and let's give it a type so let's give a
type to this props to be params which is
an object which is going to hold an ID
which is a string like this like that
and now let's change this to be ID
ID params
params
doid and there we go now we have a text
which says ID 1 to 3 and if I change my
URL to 456 for example the ID changes as
well so if you're wondering where did
this params come from right we usually
if you're working in a single page
application you're used to passing props
to your components well we are going to
do a lot of that in this tutorial as
well but server components have some
special options like accessing the
perams and in the future is also going
to be search perams you're going to see
all of that here but I just think this
is an important concept for you to
understand perfect now that we have this
I want to cover another type of folders
here so you just learned that pretty
much every folder that you create inside
of next 13 app sorry next 14 app folder
is going to become a route but what if
you just want to create a folder an
organizational folder for example which
is excluded from the URL well you can do
that let's go ahead and let's create a
new folder and let's write normal
brackets and let's write for example I
don't know let's write test like this
and go ahead and try a new file page.
TSX inside of that so I'm going to go
ahead and write con test page I'm going
to go ahead and I'm going to return a
div which says test page and I'm going
to make sure that I do export default
test page so usually we should be able
to access slash test right let's see if
that is true so if I go to slash test I
have a 404 if I try and put parentheses
which you is not possible in the URL you
can see that they still get a404 so this
folder is completely excluded from the
URL and this is going to be very useful
for us but it's not that these folders
should only be used for excluding things
out of the routing system when creating
folders they are also very useful for
when you want to create reusable layouts
for many different routes for example
let's go ahead head and uh create a new
folder inside of here which is going to
be called I don't know let's say uh
something like that and let's go ahead
and create a new file inside page. DSX
like this and let's do
const something
page and let's return a div something
page and Export the default something
page we can now access this route but
what is the URL well very simply it is
just slash
something like this now we have the
something page as you can see our URL
does not have the test folder inside of
it so that's very useful for us also
let's remove this page. DSX which we
have inside of the test folder like this
let's just remove it perfect so make
sure you just have the parenthesis test
folder and then something inside and now
let's go ahead and create another folder
called uh other like this it doesn't
really matter we're just playing around
now and create a new file page. DSX
inside and let's do const other
page return a div other
page and let's export default other page
like this so now if you go to slash
other there we go we have the other page
perfect but why am I doing this inside
of this test folder well of course I
wanted to demonstrate how you can
organize things right because sometimes
if you have a lot of routes it's very
useful to organize it inside of a folder
but you don't want that to become part
of the routing system right well let me
show you one one cool thing that you can
also do inside there is a reserved file
inside of the next framework just like
page. DSX but it is called layout. DSX
and let's go ahead and create that file
inside of this test folder so create a
new file layout. DSX like this and as
you can see we have an error here which
says that the default expert is not a
react component don't get scared by this
error that is because just like page.
TSX layout. TSX requires an default
export but we have not written anything
yet so let's do that now I'm going to
write const uh test
layout so why am I calling it test
layout well because it's inside of the
test folder right so I'm using the same
Convention as with pages and let's go
ahead and let's return a div inside like
this and let's write layer layout and
make sure you do export default test
layout perfect and what happened now as
you can see in my URL I am still on
slash other but the only thing that's
rendering is this layout text which
we've written here and the same thing
happens if I try to go to the something
folder or route which we created here
right so I'm going to change this to
slash something and again and it only
says layout well that's because we have
not finished configuring our layout. vsx
go back inside and from the props in
every layout file you create you can
always destructure children so let's go
ahead and destructure the children and
let's immediately give it a type
children is a type of react. react node
also uh in next you don't have to import
react right uh except if you are
importing use effect use State use memo
and stuff like that or some very
specific types but for stuff like this
you don't have to import react it's
globally available here you can see the
name space perfect and instead of
writing layout we're going to render
children like this and let's take a look
at the browser now I'm on/ something and
it's rendering something page I go to
slash other and it's rendering the other
page correctly
great so what exactly did we achieve by
creating this layout file well let me
show you you can now go ahead and give
this div a class name of BG D
r-500 for example and as you can see the
other page now became red if I go to
slash something that page is red as well
but if I go ahead and try one of these
routes like example or users let's see
what happens with them so if we go to/
example this one is not red so that's
what layouts are so useful for because
they can create well layouts for
specific uh routes and when combined
with this organizational folders which
initially we just thought are being used
for you know uh uh ignoring the app
router are actually much more useful
than it seems and this is going to come
very handy because in our project we're
going to have a landing or a marketing
page where we want to use one specific
layout with a specific Navar with the
footer where we're going to have the
privacy policy and everything and then
when we get to the actual application
we're going to want to have a dashboard
layout with a sidebar with a different
night bar so that's why these
organizational routes are so important
to understand because we're going to be
working aot lot with those routes and in
here in these folders like other and
something you can continue doing this
like we did with users like Dynamic IDs
and more perfect so just one thing that
I've noticed right now which we have to
fix before we move on to other stuff uh
is that I want to give this uh div so
I'm inside of layout. TSX inside of the
test folder and I want to give it a
height of full but when I do that
nothing really changes right it's still
not obviously this has a name of h- full
which means 100% height but for some
reason it's not doing that so let's fix
that by visiting our global. CSS file so
go inside of the app folder global. CSS
and just below this Tailwind directives
also don't worry about this warnings so
they are not errors they are warnings
because this rule at Tailwind is simply
not recognized in my editor there are
ways to fix that but they're not going
to be a problem throughout this tutorial
don't worry so go ahead and add the
following HTML comma body comma colum
root and just add height 100% like this
and once you save that file you can see
how now our layout. TSX H full and BG
row 500 has taken on a completely
different effect perfect and here maybe
a more realistic example of what we
would do here so I'm going to remove
this right and instead I'm going to
write a div here this is a nav bar like
this and there we go this is what these
routes are so useful for I'm just going
to add an little HR element here so it
separates this two there we go as you
can see this is how we're going to use
organizational routes inside of our
layout file we're this is where we're
going to put our navbar if we have a
sidebar that's exactly where we're going
to put our sidebar right and then we're
going to have some other routes like
example which are not going to have that
because we don't need that so this way
we're going to be able to separate
organize and reuse our layout components
in a nice way and all of that inside of
this big uh folder structure here so
that's what I wanted you to understand
uh in this part of the tutorial some
basics of routing of course if this
still seems complicated or unclear don't
worry because we just did this but we're
going to delete all of these files and
we are slowly going to repeat exactly
what I just did but with a real world
example because we're going to be
building a real world project so it's I
hope going to be more clear then if it's
not already and you can of course always
visit the official next4 documentation
just make sure that you are reading the
app router version and there they're
going to even further explain how all of
these things work perfect so now we can
actually go ahead and remove the test
folder example and the users folder just
move them all to trash we are not going
to need them and we can go back to Local
Host 3000 where we have hello Trello in
a nice blue c and one more type of route
that I want to show you is the API route
so in the older versions of nextjs when
there was a Pages router uh we had to
write an API folder nowadays we don't
have to do that in my tutorial I'm still
going to use the API folder just because
I think it's a nice structure to hold my
API routes inside of the API folder
rather than them being cluttered with my
other client routes but just for this
very simple example let's not do all of
that let's just simply create a new
folder and let's also call it users for
example and inside of this file let's go
ahead and let's create
route. TS so this is the same type of
Convention as with page. TSX or layout.
TSX but since this is an API route no
need for the TSX it's just TS right so
let's save this right here and now let's
write a very simple route Handler inside
so I'm going to write export
asynchronous sorry it can be just export
function get like this and let's go
ahead and let's just write return uh
next response and make sure you import
next response from next SLS server. Json
and I'm going to write
hello Trello like this a very simple
object let me just remove this pop up so
that you can see there we go like this
perfect and if I try and go inside of my
browser to uh Slash users now you can
see that I have a little extension which
pries my Json output but you should see
the same thing basically an object uh
maybe not with this pretty colors right
again this is an extension I'm not sure
what's the name so that's how we create
API routes inside of nextjs framework I
just wanted to show you that one last
thing and this is called a route Handler
so inside of this single route. TS file
you're going to be able to create uh
functions like post functions like patch
right so that's why we don't do uh
default exports in this one because they
can have a lot of them in one if you try
and write export default uh it's not
going to work as you can see the page
isn't working but when I remove it's
working perfect so now we know how to
create API routes how to create
organizational routes how to create
layouts how to create Dynamic ID routes
how to access those in server components
and I think we have warmed up enough for
us to start implementing our
authentication great great job just make
sure you delete those files and folders
we created so you should just have a
clean slate favicon globals layout and page
page
Perfect all right so now that we've
practiced routing let's go ahead and
let's utilize that to create our
marketing page in order to do that first
thing I want to do is create this sorry
is delete this page. TSX which is
currently rendering a simple text hello
Trello which is visible right here on
the Local Host 3000 so make sure that
you're are on Local Host 3000 and not on
any other route and go ahead and remove
p page. vsx from your application and
now you're going to get a 404 page and
now we're going to bring it back but
inside of an organizational folder so
that we can create a custom layout for
our marketing page which is also going
to be our root page so go ahead and
create a new folder inside of the app
folder open up the parenthesis and write
marketing and inside create a new file
page. DSX and let's go ahead and write
const marketing page
let's return a div marketing page and
don't forget to export
default marketing page and when you save
there we go we are back at the beginning
this is acting as our root page. DSX but
this time it's in a nice folder this
means that we well first of all it's
better organized right now we know
exactly what this page. vsx is without
changing its name because well we cannot
change its name it needs to be page. DSX
to be accepted into the routing system
but besides that if you remember from
our practice we can now create a
reusable layout which is going to be
reflecting everything inside of this
marketing folder and we're also going to
be create uh be able to create a custom
components folder inside of this
marketing folder which is only going to
be available to use inside of this page
uh sorry it's it's going to be available
to use everywhere of course but we are
kind of you know with our structure
indicating that it should be used only
inside of the marketing page so just a
bit of a practice uh with structuring
your project great so let's go ahead and
create the layout page inside of this
marketing file and of course we have an
error so let's go ahead let me expand my
code here and let's write const
marketing layout let's immediately
destructure the children and let's give
it a
type uh of
children to be react. react
node and let's go ahead and simply
return a div and render the children
inside and Export default marketing
layout and there we go nothing much has
changed but now let's go ahead and give
this div a class name of H full so it
takes the full screen height and BG
slate 100 so it's a bit darker and now
let's create a main element and wrap our
children inside of that and let's give
the main element a class
name of PT 40 padding bottom of 20 and
BG slate 100 as well and if you're
interested in exact pixels you can hover
over these classes to see exactly what
they are doing to the code so why am I
moving this from the top and why am I
adding the space on the bottom well I'm
going to add a comment for now because
here we're going to have a enough bar
and at the bottom we're going to have a
footer you don't have to write this
we're going to come back and do it later
perfect so now I want to go back inside
of the marketing page and go ahead and
we style it to look like something let's
go ahead and give this div a class name
of flex items
Center whoops justify Center and flex
call if you're ever unsure about what
classes do you can always hover to see
exactly what CSS they are implying right
now what I'm doing is centering this
text into the middle like this and flex
call indicates that all the items that I
create are not going to be next to each
other but one below another if it was
just Flex then that would mean that it's
in a flex row position but Flex goal
indicates that I want it to go one below
another great and let's go ahead and
create another div
here with the class
name of flex items Center justify Center
again and flex call exactly the same
thing that we did above and in here I
want to open a new div which is going to
hold our metal from Lucid react if
you're wondering where do we have this
Lucid react installed well that came
installed when we run our shat CN UI and
when we chose the default style for our
app if you chose the New York style
you're not going to have lucid react
installed so if you're getting an error
here you can just go ahead and run npm
install Lucid react no biggy perfect and
now we can render this metal right here
and if you save you're going to get this
little medal here in the browser now
let's go ahead and give this uh a class
name of height six with six and margin
right of two perfect and below that
management like this perfect and now
let's go inside of this empty div and
let's give it some proper classes so
class name is going to be uh margin
bottom of four Flex items
Center border Shadow
small padding four so we add some space
to it BG amember 100 text amember
100 sorry text Ember 700 so it's a bit
darker like this rounded full and
uppercase everything like this and let
me just expand this and there we go now
you can see how we have a nice little
badge here in the middle of the screen
perfect now let's go outside of this div
which is our badge right and let's open
up an H1 element which is going to say
task ify helps team move like this and
let's style this so it looks like an
actual heading so we're going to give it
a text of 3 Exel but on medium devices
we're going to give it a text of 6 Exel
so it's bigger and now let's write text
Center like this so it aligns properly
let's write text neutral 800 so it's
just a tiny bit lighter like that and
margin bottom of six perfect below that
open up a div and write work
forward and a DOT at the end and in here
we're going to write a class name to be
text 3 Excel the same it was above and
on medium devices it's going to be text
6 Excel so same as above everything
we're also going to go ahead and give it
a BG
gradient to WR like that from and now
we're going to write a color I'm not
sure how to pronounce this so F I'm not
sure all right so fushia 600 I'm just
going to call it purple from purple 600
to Pink 600 like that so we have a nice
little gradient text is going to be
white PX is going to be four padding
overall is going to be two rounded is
going to be medium padding bottom is
going to be four and W is going to be
fit like this so you can go ahead and
expand this and there we go you can see
how this looks now and if you think that
the font looks weird don't worry we're
going to change the font later for now I
just want to create a little uh
structure here perfect so now we have
that now let's go outside of this div
which is wrapping our task management
our task if I help steam move and this
little gradient B box so not outside of
this div but outside of this one right
here and let's open a new div and inside
projects and reach new productivity
productivity
Peaks like that and now we can go ahead
and just write from higher Rises to the
home office the theme sorry the way your
team works is unique and let's go ahead
and add accomplish it all with task
device so basically it doesn't matter
just a little promotional text right on
our website and give this div a class
name of text small on medium text is
going to be extra large text is going to
be neutral 400 margin top is going to be
four Max width is going to be uh extra
small but on medium devices Max width is
going to be 2
Excel let's Center the text using text
Center and MX outo to push it from both
sides like this and if you expand you
can see how this looks on desktop mode
like that and you can see how it looks
on mobile mode like this perfect so now
that we have this uh all that I want to
do is import a component called button
from shaten so for that let's head
inside of our terminal I'm going to open
a new one here so here I have my project
running and I clicked a little plus icon
here so I have a new instance and inside
I'm going to write npx Shad cn- at
latest add button like
this and just wait for this to install
the button and there we go we can close
this terminal let me Zoom back in and
now we have a new component inside of
our components folder inside the UI
folder we have button. DSX so this is
what's different uh with chaten in
regards to other libraries other
libraries give you the same type of
components but usually you're not able
to see the exact code that is inside of
them but with shat CN UI you able to
visit the code and you can modify it how
ever you want we're not going to do too
much modifying I'm quite satisfied with
what chatan offers by default but if
you're interested you can always change
the the focus visible you can change the
opacity when it's disabled you can
change the variants of your button the
sizes of your button whatever you want
but for now I recommend that you don't
play around with it too much until it's
time to do so so now let's use that
button by going outside of this div and
opening up that button and import it
from at/ components UI button like this
let me show you how that looks so right
here at the top I've imported button
from s/ components UI button and this
little add sign is what's called an
alias so what does an alias do well it
saves us from doing this right usually
we had to go uh outside manually right
using this dot do slash notation but
with an alias we can easily do this and
it's the same thing so that's what I was
talking about when I was telling you
about the option to customize the
default import Alas and I told you to
leave it exactly as it is and by default
that's the at sign perfect so as you can
see I like to separate my imports so I'm
always going to keep my Global Imports
at the top by global I mean those things
that I installed using npm after that
I'm going to keep this alest Imports and
and then uh even lower I'm going to keep
my local relative Imports if I ever need
to use them for now we don't have any
perfect and inside of this button let's
go ahead and let's import a link
component from next slash link so make
sure you import that and since it is
from npm I'm going to add it here to the
top and let's go ahead and let's write
get tasky for free and it's missing an
hre so let's for now just write sign
Dash up like this perfect and now let's
style this button by giving a class name
of margin top six let's give it a size
of large and let's write as child so it
properly works with this link component
inside perfect so here it is our initial
landing page if you click here of course
you're going to get a 404 so now it's
time for us to add custom fonts so this
looks even better
so what I want you to do now is go
inside of my GitHub you can find the
link in the description and I want you
to go inside of the public folder and in
here you can find a file called font so
go ahead and click on this file and find
a way to download that file once you've
downloaded that file go ahead and open
up uh the public folder which you have
right here and drag and drop that
downloaded file inside if you're
wondering what font this is this is the
Cal Sans font or Cal open sense font I'm
not sure what's the name and it is from
a project called
call.com is a competitor to um calendly
if I'm correct and they've created this
amazing font for us to use you can
Google cal font GitHub to see more
information about that so make sure you
have this font inside of your public
folder if you don't it doesn't matter so
this is just a font right we're this is
just styling no important functionality
is being done here but I want to show
you how to uh import local fonts using
nextjs so in order to do that we first
have to import something called local
font from next SL font SL looc like this
and then go ahead and write const
heading font to be local font F like
that and go ahead and give it a source
of dot do SL do do SLU fonts font.
wf2 like that and now we have our font
perfect so now we have to add that font
uh inside of one of this divs right here
so let me just go back to my local host
here uh and let's see if I did any
mistake here and I did yes so inside of
my public folder I just dragged and
dropped the font but I actually want to
create a new folder inside called fonts
and then drop that inside so it's even
better organized and there we go now we
have no error because our uh relative
input here is correct perfect so now now
what I want to do is I want to import
our util called CN which we researched
at the beginning of this tutorial where
we installed shat CN so with this CN we
can now com combine the existing class
names which we have and then append this
initi additional class name which is
going to come from this constant right
here thanks to this local font instance
from next font perfect so let's go ahead
and let's go inside of this second div
right here and let's just add curly
brackets around this class name like
this so when we save nothing should
change and then what I want to do is at
the beginning of this curly class add
the CN library and wrap this entire
thing inside of parenthesis like this
and again nothing is going to change so
here is how CN works in the first
parameter of CN we're going to pass our
default classes and then I'm going to
add a comma and I'm going to write the
dynamic class like heading font. class
name and when I save you can see that we
have a beautiful font right here see how
nice this looks perfect so now we know
how to import
local fonts and now let me show you how
to import fonts from Google fonts
because you can do that as well so go
ahead and import the specific font you
want in my case that's going to be
Poppins so import popins from next SL
font SLG
gooogle like this perfect now we have
the popins font and let's initialize it
in a similar way so I'm going to go
ahead and write const text font to be popins
popins
we have to give it subsets which is just
going to be an option Latin and we also want to give it
want to give it weight uh and let's go ahead and open
weight uh and let's go ahead and open our array here and let's write 100 let's
our array here and let's write 100 let's write 200 so basically we're going to
write 200 so basically we're going to fill up all the options it give us
fill up all the options it give us 400 then it's
400 then it's 500
500 600
600 700 800 and lastly is going to be
900 perfect and now that we have this we're going to append this font to the
we're going to append this font to the rest of our text so let's go ahead all
rest of our text so let's go ahead all the way to the bottom here where we have
the way to the bottom here where we have this collaborate manage projects text
this collaborate manage projects text and do the same thing so first add the
and do the same thing so first add the curly brackets around this class name
curly brackets around this class name like this nothing changes then add CN
like this nothing changes then add CN and wrap the entire thing inside the
and wrap the entire thing inside the brackets great let's collapse this so
brackets great let's collapse this so the default thing is in the first line
the default thing is in the first line and then add a comma and write text
and then add a comma and write text font. class name like this and when you
font. class name like this and when you save you can see that we have a nicer
save you can see that we have a nicer font for our description as well perfect
font for our description as well perfect and we can leave the button as it is so
and we can leave the button as it is so now it's time for us to create our uh uh
now it's time for us to create our uh uh navbar component our footer component
navbar component our footer component and also one reusable component called l
and also one reusable component called l go because we're going to use it in the
go because we're going to use it in the navbar in the footer and we're also
navbar in the footer and we're also going to use it in the dashboard of our
going to use it in the dashboard of our tasky project
tasky project itself so let's go ahead and create our
itself so let's go ahead and create our first reusable component called logo so
first reusable component called logo so in order to do that first go back inside
in order to do that first go back inside of my public folder here and find
of my public folder here and find logo.svg right here this is a logo from
logo.svg right here this is a logo from the logo ion.com website where you can
the logo ion.com website where you can find a bunch shop free logos just make
find a bunch shop free logos just make sure not to actually use them for your
sure not to actually use them for your business you can use them for your demo
business you can use them for your demo project or some education purposes so go
project or some education purposes so go ahead and download this file and go
ahead and download this file and go ahead and drag and drop that straight
ahead and drag and drop that straight into the public folder similar to what
into the public folder similar to what we did with the
we did with the font just like this so paste it here and
font just like this so paste it here and make sure to rename it into
make sure to rename it into logo.svg in case you have this little
logo.svg in case you have this little number as I do so just make sure to uh
number as I do so just make sure to uh that it's called logo. SVG perfect so
that it's called logo. SVG perfect so I'm going to close everything and I'm
I'm going to close everything and I'm going to go uh back here and let's go
going to go uh back here and let's go inside of the components folder so I'm
inside of the components folder so I'm going to keep the UI folder for all the
going to keep the UI folder for all the shats and components which we're going
shats and components which we're going to import but for our own custom
to import but for our own custom reusable components I'm going to write
reusable components I'm going to write that inside of the components folder
that inside of the components folder directly so right click here and create
directly so right click here and create Lo logo. DSX make sure it's outside of
Lo logo. DSX make sure it's outside of the UI folder like that and in here
the UI folder like that and in here let's go ahead and write export con logo
let's go ahead and write export con logo like this and let's go ahead and return
like this and let's go ahead and return a link component from next link let's
a link component from next link let's give it an HRA of just an empty slash
give it an HRA of just an empty slash let's go ahead and give it a div and
let's go ahead and give it a div and let's add an image from next SL image
let's add an image from next SL image and in here the source is going to be/
and in here the source is going to be/ logo.svg which we just added into our
logo.svg which we just added into our public folder alt is going to be
public folder alt is going to be logo height is going to be 30 and width
logo height is going to be 30 and width is going to be 30 as well great and now
is going to be 30 as well great and now let's go ahead uh below that and let's
let's go ahead uh below that and let's add a paragraph here which is going to
add a paragraph here which is going to have uh which is just going to say tasky
have uh which is just going to say tasky like this perfect and let's go ahead and
like this perfect and let's go ahead and give this paragraph a class name of text
give this paragraph a class name of text large text neutral
large text neutral 700 and padding bottom of one great and
700 and padding bottom of one great and now let's give this div which is
now let's give this div which is wrapping our image a class name and that
wrapping our image a class name and that one is going to be hover opacity
one is going to be hover opacity 75
75 transition items Center Gap
transition items Center Gap X2 hidden by default but on medium
X2 hidden by default but on medium devices it's going to be visible so from
devices it's going to be visible so from medium and up meaning on desktop devices
medium and up meaning on desktop devices going to see this logo but on mobile
going to see this logo but on mobile devices it's going to be invisible great
devices it's going to be invisible great and now let's go ahead and let's import
and now let's go ahead and let's import CN from s/ lib utils and let's go ahead
CN from s/ lib utils and let's go ahead and let's also import local font from
and let's also import local font from next font local so we're going to reuse
next font local so we're going to reuse that font which we uh imported
that font which we uh imported previously perfect and let's go ahead
previously perfect and let's go ahead and do that adding con heading font to
and do that adding con heading font to be local font let's write a source to be
be local font let's write a source to be DOA
DOA /u/ font
V2 great and now let's go ahead and let's wrap this class name inside of
let's wrap this class name inside of curly brackets right let's add our CN
curly brackets right let's add our CN util and let's wrap this entire thing
util and let's wrap this entire thing inside of
inside of parenthesis let's add a comma and then
parenthesis let's add a comma and then we write heading font do class name and
we write heading font do class name and that's it our reusable logo component is
that's it our reusable logo component is finished perfect so now let's create our
finished perfect so now let's create our navigation bar so I'm going to close
navigation bar so I'm going to close everything and I'm going to go inside of
everything and I'm going to go inside of the app folder and inside of marketing
the app folder and inside of marketing and in here we're going to create our
and in here we're going to create our components folder but I'm going to show
components folder but I'm going to show you a little trick so there is a third
you a little trick so there is a third type of folders inside of nextjs uh
type of folders inside of nextjs uh which is a second type of folder which
which is a second type of folder which can be used to be outside of the routing
can be used to be outside of the routing system so we we learned that we can use
system so we we learned that we can use the parenthesis around the folder to
the parenthesis around the folder to ensure that it's not inside of the URL
ensure that it's not inside of the URL but as you can see even uh then we can
but as you can see even uh then we can still render some pages and routing
still render some pages and routing inside of it so there is an actual type
inside of it so there is an actual type of folder which can be used to
of folder which can be used to completely excluded from the router and
completely excluded from the router and that is called an underscore folder I
that is called an underscore folder I mean it probably has a different name
mean it probably has a different name I'm calling it an underscore folder
I'm calling it an underscore folder because it begins with an underscore so
because it begins with an underscore so let's add underscore components like
let's add underscore components like this so even if we added page. TSX
this so even if we added page. TSX inside it would not be visible anywhere
inside it would not be visible anywhere you will not be able to access it unless
you will not be able to access it unless you imported into a page so that's
you imported into a page so that's exactly what we're going to do inside of
exactly what we're going to do inside of the components folder create a new file
the components folder create a new file navb bar. DSX like this and let's go
navb bar. DSX like this and let's go ahead and let's write con
ahead and let's write con Novar and let's return a div saying
Novar and let's return a div saying Novar like this and make sure you add
Novar like this and make sure you add export const navbar so no export default
export const navbar so no export default when we write components we're going to
when we write components we're going to use export default only in layouts and
use export default only in layouts and pages but not in individual reusable
pages but not in individual reusable components or just components like this
components or just components like this that's when we're going to use just an
that's when we're going to use just an export const uh great and now let's
export const uh great and now let's immediately add this inside of our
immediately add this inside of our layout. DSX right here so instead of
layout. DSX right here so instead of this comment here we're going to add
this comment here we're going to add that navbar component from do slore
that navbar component from do slore components slnb bar and you can see how
components slnb bar and you can see how I imported this at the top perfect and
I imported this at the top perfect and now you can see a little text here which
now you can see a little text here which says Novar perfect let's go inside
says Novar perfect let's go inside inside of the Navar and let's finish it
inside of the Navar and let's finish it up so I'm going to give this div a class
up so I'm going to give this div a class name of fixed top zero so it's always
name of fixed top zero so it's always going to be at the top W is going to be
going to be at the top W is going to be full height is going to be a fixed 14
full height is going to be a fixed 14 like that PX is going to be four
like that PX is going to be four border bottom like this Shadow small BG
border bottom like this Shadow small BG white flex and items Center great so you
white flex and items Center great so you can now definitely see that here at the
can now definitely see that here at the top and we have a little text here we
top and we have a little text here we have our border and we have some slight
have our border and we have some slight Shadow perfect now let's go ahead and
Shadow perfect now let's go ahead and open a new div inside with a class
open a new div inside with a class name MD maxw screen to excel MX
name MD maxw screen to excel MX Auto Flex items Center W full and
Auto Flex items Center W full and justify between so we are doing another
justify between so we are doing another Flex box but this time we are doing it
Flex box but this time we are doing it in a row right and what does this MD Max
in a row right and what does this MD Max W screen to excel do well it ensures
W screen to excel do well it ensures that on desktop devices there is a limit
that on desktop devices there is a limit to how far we can expand the screen
to how far we can expand the screen right so for example let's go ahead and
right so for example let's go ahead and write something here I'm going to write
write something here I'm going to write test here and you can see how now uh
test here and you can see how now uh when I expand to a certain point it
when I expand to a certain point it stops resizing are you noticing that in
stops resizing are you noticing that in the Navar right so that's what this
the Navar right so that's what this class name uh Max W screen 2 Excel does
class name uh Max W screen 2 Excel does you can make it even shorter for example
you can make it even shorter for example Max screen large and let's see you can
Max screen large and let's see you can see that now it stops here it stops
see that now it stops here it stops resizing at a certain point but for my
resizing at a certain point but for my case I think 2XL is just fine so it's
case I think 2XL is just fine so it's enough room for a small for a big
enough room for a small for a big desktop monitor but if it gets larger
desktop monitor but if it gets larger than that it stops
than that it stops resizing great so we now have that
resizing great so we now have that perfect and now we can render our logo
perfect and now we can render our logo component and make sure you import that
component and make sure you import that from add/ components logo and
from add/ components logo and destructure it like this all right and
destructure it like this all right and it seems like we have a little uh error
it seems like we have a little uh error here so let's go inside uh of our logo
here so let's go inside uh of our logo component so that's located uh inside of
component so that's located uh inside of components UI sorry inside of components
components UI sorry inside of components folder logo here and we are missing the
folder logo here and we are missing the fonts folder here there we go and now if
fonts folder here there we go and now if you expand there we go we have a nice
you expand there we go we have a nice little logo here and we can click on it
little logo here and we can click on it but right now it's not doing anything
but right now it's not doing anything but it's actually redirect into the page
but it's actually redirect into the page that we are already on great let's head
that we are already on great let's head back inside of our uh navbar component
back inside of our uh navbar component Here and Now below this logo let's add a
Here and Now below this logo let's add a new div here with a last
new div here with a last name of space x 4 so the items between
name of space x 4 so the items between are spaced evenly on the X AIS let's add
are spaced evenly on the X AIS let's add MD block let's add MDW Auto let's add
MD block let's add MDW Auto let's add Flex items Center justify between and W
Flex items Center justify between and W pull and then let's add our buttons so
pull and then let's add our buttons so import button from at/ components UI
import button from at/ components UI button and write log in and below that
button and write log in and below that add a new button which is going to say
add a new button which is going to say get tasky for free like that perfect so
get tasky for free like that perfect so you can see on mobile devices they're
you can see on mobile devices they're going to be uh each on its own corner
going to be uh each on its own corner but on a large screen they're both going
but on a large screen they're both going to be in this corner right here now
to be in this corner right here now let's add some styles to this button so
let's add some styles to this button so the first one size is going to be small
the first one size is going to be small variant is going to be outline
variant is going to be outline and as child like that and if we are
and as child like that and if we are using as child we also have to add a
using as child we also have to add a link from next slash link so make sure
link from next slash link so make sure you add that
you add that import all
import all right and let's give this link an hre of
right and let's give this link an hre of slash sign Dash uh in like that perfect
slash sign Dash uh in like that perfect and then we're going to reuse that link
and then we're going to reuse that link already here uh in this other
already here uh in this other button like that and this one is going
button like that and this one is going to lead to sign Dash up like that and
to lead to sign Dash up like that and let's go ahead and give this button a
let's go ahead and give this button a size of small and also as child prop
size of small and also as child prop perfect so let's see how this looks now
perfect so let's see how this looks now there we go we have our logo here we
there we go we have our logo here we have our two uh buttons here when we
have our two uh buttons here when we click on them they lead to 404 because
click on them they lead to 404 because we don't have authentication set up yet
we don't have authentication set up yet but we're going to do that in just a
but we're going to do that in just a moment and now uh a last thing we have
moment and now uh a last thing we have to do inside of our marketing page is
to do inside of our marketing page is the footer so let's go ahead uh and do
the footer so let's go ahead uh and do that so we can actually copy the
that so we can actually copy the existing navbar component so I'm going
existing navbar component so I'm going to go ahead and do that so go ahead and
to go ahead and do that so go ahead and copy the Navar component and paste it
copy the Navar component and paste it inside of the underscore components
inside of the underscore components folder and rename it to footer like
folder and rename it to footer like this and let's go ahead uh and let's
this and let's go ahead uh and let's slightly modify it so instead of top
slightly modify it so instead of top zero first let's actually rename it so
zero first let's actually rename it so footer like this and then instead of top
footer like this and then instead of top zero it's going to be bottom zero like
zero it's going to be bottom zero like that uh we don't need to have the height
that uh we don't need to have the height of 14 like this we also don't need a
of 14 like this we also don't need a border bottom instead we need a border
border bottom instead we need a border top we don't need any Shadow so we can
top we don't need any Shadow so we can remove that we can also remove the BG
remove that we can also remove the BG white like that and flex item Center are
white like that and flex item Center are not needed either and instead let's
not needed either and instead let's write BG slate 100 like that perfect uh
write BG slate 100 like that perfect uh we can leave this exactly as it is the
we can leave this exactly as it is the only thing we're going to change here is
only thing we're going to change here is these two buttons so you can remove the
these two buttons so you can remove the as child uh and you can remove the
as child uh and you can remove the variant and you can remove the link as
variant and you can remove the link as well so do the same thing here remove
well so do the same thing here remove the as child and remove the link here
the as child and remove the link here and change this one to say privacy
and change this one to say privacy policy and change the other one to say
policy and change the other one to say terms of service like that and let's
terms of service like that and let's give both of them a variant of
give both of them a variant of ghost like that perfect we can remove
ghost like that perfect we can remove this import now and now that we have our
this import now and now that we have our footer ready remember if you get stuck
footer ready remember if you get stuck at any point in a specific component you
at any point in a specific component you can always head directly into my GitHub
can always head directly into my GitHub from where you downloaded my fonts and
from where you downloaded my fonts and uh my the images and you can just take a
uh my the images and you can just take a look at the component and see exactly
look at the component and see exactly what's going on you can copy it if
what's going on you can copy it if you're or you can use it as a guide and
you're or you can use it as a guide and now let's go back inside of our layout
now let's go back inside of our layout here and remove this commment which says
here and remove this commment which says footer and let's actually import the
footer and let's actually import the footer component from slore components
footer component from slore components uh footer great and let's go ahead and
uh footer great and let's go ahead and try this out
try this out now there we go you can see we have a
now there we go you can see we have a footer here but something uh seems a
footer here but something uh seems a little bit off uh I I have a feeling
little bit off uh I I have a feeling like our footer is too small right it
like our footer is too small right it seems too clutter
seems too clutter so let's actually go ahead and give it a
so let's actually go ahead and give it a fixed height you know I removed the
fixed height you know I removed the height because I thought we're not going
height because I thought we're not going to need it uh actually instead of height
to need it uh actually instead of height we can just do this instead of PX just
we can just do this instead of PX just give it a P4 like this inside of the
give it a P4 like this inside of the footer component so we had a px here
footer component so we had a px here just change it to be P4 and let's see
just change it to be P4 and let's see that now there we go that looks much
that now there we go that looks much cleaner now perfect so we have finished
cleaner now perfect so we have finished our Landing or marketing page so that is
our Landing or marketing page so that is now our root page perfect we are now
now our root page perfect we are now ready to implement our authentication
ready to implement our authentication and by doing that we're going to
and by doing that we're going to implement our middleware which is then
implement our middleware which is then going to take a look if we are logged in
going to take a look if we are logged in or not and if we try to visit this
or not and if we try to visit this marketing page while we are logged in
marketing page while we are logged in it's going to immediately redirect us to
it's going to immediately redirect us to the dashboard and pre-select our
the dashboard and pre-select our organization great amazing amazing
organization great amazing amazing job now that we have our landing page
job now that we have our landing page finished it is time for us to implement
finished it is time for us to implement authentication but just before we go on
authentication but just before we go on about doing that I want to go ahead and
about doing that I want to go ahead and edit uh my favicon and the title of my
edit uh my favicon and the title of my tab right I want this to look like a
tab right I want this to look like a proper application so first thing that
proper application so first thing that I'm going to do is I'm going to create a
I'm going to do is I'm going to create a new folder called config which is going
new folder called config which is going to be outside of all of these other
to be outside of all of these other folders so just click on a random file
folders so just click on a random file like get IGN and then click on a new
like get IGN and then click on a new folder like this and create the config
folder like this and create the config and inside create a file site. DS just
and inside create a file site. DS just like that and then export const site
like that and then export const site config let's give the name of our app to
config let's give the name of our app to be task ify so from here you can also
be task ify so from here you can also change this if you're planning on
change this if you're planning on running this as your own software as a
running this as your own software as a service later and let's give it a
service later and let's give it a description to be
description to be collaborate
collaborate manage
manage projects and reach new
projects and reach new productivity PS basically just some
productivity PS basically just some marketing stuff great and now that we
marketing stuff great and now that we have this let's go ahead and let's visit
have this let's go ahead and let's visit one file which we have not looked at yet
one file which we have not looked at yet which is This Global layout right so we
which is This Global layout right so we created our own layouts in the example
created our own layouts in the example and here but we haven't really explored
and here but we haven't really explored this root layout so this root layout
this root layout so this root layout needs to exist and as you can see it has
needs to exist and as you can see it has a name root layout and it renders the
a name root layout and it renders the HTML the body and our children so this
HTML the body and our children so this is important you cannot change uh you
is important you cannot change uh you cannot remove this file but what we can
cannot remove this file but what we can of course modify it so let's go ahead
of course modify it so let's go ahead and modify this metadata a bit I'm going
and modify this metadata a bit I'm going to change this from a string to an
to change this from a string to an object so it gives me more options
object so it gives me more options default this is going to use site
default this is going to use site config which you can import from at/
config which you can import from at/ config site as I did here so it's going
config site as I did here so it's going to be site config do name like that and
to be site config do name like that and let's also give it a template and
let's also give it a template and template is going to be uh well open a
template is going to be uh well open a pactic like this and write a percentage
pactic like this and write a percentage s then write a pipe and then render the
s then write a pipe and then render the site config do name so if you're
site config do name so if you're wondering what this is so as it says
wondering what this is so as it says this is a template so this is the title
this is a template so this is the title this is our default title if we are on
this is our default title if we are on the root page like on the marketing page
the root page like on the marketing page if you take a look at my browser uh tab
if you take a look at my browser uh tab you can see that it says tasky now right
you can see that it says tasky now right but later when we open up a specific
but later when we open up a specific board or a specific organization we're
board or a specific organization we're going to change it to be something like
going to change it to be something like my
my organization pipe task ify like that so
organization pipe task ify like that so that's how it's going to look like so
that's how it's going to look like so instead of ask manually doing that every
instead of ask manually doing that every time we can just use this template
time we can just use this template syntax right here so this is going to be
syntax right here so this is going to be replaced with whatever we uh let the
replaced with whatever we uh let the title be when we create some additional
title be when we create some additional layouts all right and now let's change
layouts all right and now let's change the
the description to be site config do
description to be site config do description like that and last thing we
description like that and last thing we have to do is add our logo because right
have to do is add our logo because right now by default you can see that it has
now by default you can see that it has this versel logo here so first thing
this versel logo here so first thing that we have to do is go ahead inside of
that we have to do is go ahead inside of our app folder and remove the
our app folder and remove the favicon.ico like that and then go back
favicon.ico like that and then go back inside of the layout file and add icons
inside of the layout file and add icons open up an array open an object and
open up an array open an object and write a URL to be/
write a URL to be/ logo.svg and hb/
logo.svg and hb/ logo.svg and save that and take a look
logo.svg and save that and take a look at the browser again and there we go we
at the browser again and there we go we have a matching logo in our tab perfect
have a matching logo in our tab perfect so now we are ready to head on creating
so now we are ready to head on creating our uh
our uh authentication so in order to set up our
authentication so in order to set up our authentication we're going to use clerk
authentication we're going to use clerk so head to clerk.com or use the link in
so head to clerk.com or use the link in the description and go ahead and find in
the description and go ahead and find in the sign up or sign in button and enter
the sign up or sign in button and enter the
the dashboard and as you can see I have a
dashboard and as you can see I have a bunch of projects here from my previous
bunch of projects here from my previous tutorials from my side projects and
tutorials from my side projects and stuff like that and just go ahead and
stuff like that and just go ahead and find a button which says add an
find a button which says add an application or perhaps that's the
application or perhaps that's the default screen for you so let me just
default screen for you so let me just zoom out so you can see this is
zoom out so you can see this is something uh what it's going to look
something uh what it's going to look like so let me zoom in even further
like so let me zoom in even further there we go so let's give our
there we go so let's give our application a name so this is going to
application a name so this is going to be Trello Das tutorial uh you can of
be Trello Das tutorial uh you can of course change this to task ify or
course change this to task ify or whatever is the name of your product and
whatever is the name of your product and you can see exactly how that's going to
you can see exactly how that's going to look in the components so here it says
look in the components so here it says Trello tutorial uh and then you can go
Trello tutorial uh and then you can go ahead and play around with whatever you
ahead and play around with whatever you want to add to for your users to sign in
want to add to for your users to sign in so there's a bunch of options as you can
so there's a bunch of options as you can see all the way to metamask so web 3
see all the way to metamask so web 3 login if that's what you want so I'm
login if that's what you want so I'm going to turn off the email address and
going to turn off the email address and instead I'm just going to use Google uh
instead I'm just going to use Google uh like that and you can of course enable
like that and you can of course enable something else now let's go ahead and
something else now let's go ahead and click create application like that and
click create application like that and now let's go ahead and set up our
now let's go ahead and set up our project so as you can see first thing we
project so as you can see first thing we have to do is copy and paste this
have to do is copy and paste this environment Keys you should have these
environment Keys you should have these two keys right here so go ahead and just
two keys right here so go ahead and just click copy and let's go ahead and do
click copy and let's go ahead and do something before we add this to our
something before we add this to our environment file and that is go inside
environment file and that is go inside of dogit ignore so this is very
of dogit ignore so this is very important make sure you go inside of dog
important make sure you go inside of dog ignore and just below this environment.
ignore and just below this environment. loal add environment itself like that so
loal add environment itself like that so this environment. local is of course
this environment. local is of course well for that file but I also want to do
well for that file but I also want to do it for this file now you might be
it for this file now you might be wondering well why am I not using
wondering well why am I not using environment. loal well that's because
environment. loal well that's because we're later going to add Prisma and
we're later going to add Prisma and Prisma uses the environment file now yes
Prisma uses the environment file now yes you can change the location of the
you can change the location of the environment keys where Prisma looks for
environment keys where Prisma looks for them but honestly it's just a teeny bit
them but honestly it's just a teeny bit unnecessarily complicated for this
unnecessarily complicated for this tutorial we can just use the environment
tutorial we can just use the environment file and teach you how to add that to
file and teach you how to add that to your G ignore now why is it important
your G ignore now why is it important that this is inside of your git ignore
that this is inside of your git ignore well that way it will not be committed
well that way it will not be committed to GitHub meaning that it will not exist
to GitHub meaning that it will not exist anywhere except locally on your computer
anywhere except locally on your computer making it much more secure uh and it is
making it much more secure uh and it is even more dangerous if you uh make your
even more dangerous if you uh make your GitHub repository public that way
GitHub repository public that way there's a lot of bots and rpers which
there's a lot of bots and rpers which can look for public environment keys and
can look for public environment keys and steal those keys and that itself might
steal those keys and that itself might not sound dangerous because in this
not sound dangerous because in this tutorial we're all going to use free
tutorial we're all going to use free tiers right we're not going to pay for
tiers right we're not going to pay for anything but imagine if you actually use
anything but imagine if you actually use this for your business and have some
this for your business and have some environment Keys which are on paid
environment Keys which are on paid Services well they can uh create a lot
Services well they can uh create a lot of damage then so that's why it's
of damage then so that's why it's important that you add do environment
important that you add do environment inside your G ignore file and make sure
inside your G ignore file and make sure you save that file and then go ahead and
you save that file and then go ahead and create the environment file and inside
create the environment file and inside paste those two keys which we just
paste those two keys which we just copied so you can get them from here
copied so you can get them from here perfect and make sure you have selected
perfect and make sure you have selected the nextjs option in the quick start and
the nextjs option in the quick start and click on this purple button continue in
click on this purple button continue in Doc perfect and if you watched my
Doc perfect and if you watched my previous tutorials you're probably going
previous tutorials you're probably going to notice that clerk has updated their
to notice that clerk has updated their documentation it looks pretty nice so uh
documentation it looks pretty nice so uh in here make sure that you select the
in here make sure that you select the app router option like this uh actually
app router option like this uh actually don't have to I thought that this was a
don't have to I thought that this was a switch between the two sorry this is the
switch between the two sorry this is the example quick start repos if you want to
example quick start repos if you want to look at that uh looks like we don't have
look at that uh looks like we don't have to click on that okay so first thing we
to click on that okay so first thing we have to do is npm install this package
have to do is npm install this package so let's go ahead inside of our
so let's go ahead inside of our terminal and you can actually shut down
terminal and you can actually shut down the app for now it doesn't matter if
the app for now it doesn't matter if you're running it here so npm install
you're running it here so npm install clerk nextjs perfect and while that's
clerk nextjs perfect and while that's installing let's see what the next steps
installing let's see what the next steps are so we have to set set the
are so we have to set set the environment keys and we already did that
environment keys and we already did that and next thing we have to do is add the
and next thing we have to do is add the clerk provider inside of our layout so
clerk provider inside of our layout so let's go ahead and do
let's go ahead and do that but I'm not going to do do this
that but I'm not going to do do this inside of my main layout I'm actually
inside of my main layout I'm actually going to do this only in the layout that
going to do this only in the layout that I expect to be protected without so let
I expect to be protected without so let me show you what what I'm thinking of so
me show you what what I'm thinking of so this has installed great let's leave
this has installed great let's leave this as it is make sure it's installed
this as it is make sure it's installed it's important let me close everything
it's important let me close everything here instead of the app folder I'm going
here instead of the app folder I'm going to create a new folder called platform
to create a new folder called platform also instead of brackets inside of
also instead of brackets inside of brackets here and then inside of here
brackets here and then inside of here I'm going to create a new layout. TSX
I'm going to create a new layout. TSX and this one is going to be uh a
and this one is going to be uh a platform layout remember we can extract
platform layout remember we can extract the children immediately and let's also
the children immediately and let's also give it a type react react
give it a type react react node and let's return Clerk Provider
node and let's return Clerk Provider from at clerk nextjs so this package
from at clerk nextjs so this package which we just installed so just make
which we just installed so just make sure that is installed perfect and
sure that is installed perfect and inside we can just render the children
inside we can just render the children so in my previous tutorials I always
so in my previous tutorials I always added the clerk provider inside of my uh
added the clerk provider inside of my uh root layout right here but that way uh
root layout right here but that way uh you are going to make your website um
you are going to make your website um I'm not sure what's the proper way
I'm not sure what's the proper way you're either going to lose on on on
you're either going to lose on on on static r on static rendering or
static r on static rendering or something like that I'm not sure but
something like that I'm not sure but they're working on improving that
they're working on improving that perhaps they've even fixed that I'm not
perhaps they've even fixed that I'm not sure but you know let's try out this new
sure but you know let's try out this new method of doing it it's no biggie uh and
method of doing it it's no biggie uh and you know it's a normal practice so we
you know it's a normal practice so we are wrapping the layout where our actual
are wrapping the layout where our actual platform where the user needs to be
platform where the user needs to be logged in is going to be because in
logged in is going to be because in marketing we're not going to be
marketing we're not going to be accessing any logged in states or
accessing any logged in states or something like that we're going to let
something like that we're going to let the middleware take care of that so
the middleware take care of that so create a platform folder and create
create a platform folder and create layout. DSX and make sure you add
layout. DSX and make sure you add export default platform
export default platform layout perfect so now that we have that
layout perfect so now that we have that let's see uh what are our next steps
let's see uh what are our next steps here so we have to create a middleware
here so we have to create a middleware so let's go ahead and copy this so this
so let's go ahead and copy this so this is step four require authentication to
is step four require authentication to access your app and let's go ahead uh
access your app and let's go ahead uh and you you have to cre it in the root
and you you have to cre it in the root of your application so create a new file
of your application so create a new file middleware do DS like that and just
middleware do DS like that and just paste this code inside great uh let's
paste this code inside great uh let's see what is next let me Zoom this in for
see what is next let me Zoom this in for you so you can see all right so we just
you so you can see all right so we just added the
added the middleware and before we add this uh
middleware and before we add this uh button what I want to do is I want to go
button what I want to do is I want to go ahead and create custom signin and sign
ahead and create custom signin and sign up Pages usually this was all in this
up Pages usually this was all in this page but they decided to move it perhaps
page but they decided to move it perhaps they feel that's a better way to do it
they feel that's a better way to do it so if you're confused you know if you're
so if you're confused you know if you're watching my previous videos you probably
watching my previous videos you probably expect this to be here but it's not now
expect this to be here but it's not now it's in this next steps right here so
it's in this next steps right here so create custom sign up and sign in pages
create custom sign up and sign in pages so click right here and let's see what
so click right here and let's see what we have to create so we have to create a
we have to create so we have to create a signup page and then we have to create
signup page and then we have to create this catch all folder with also the sign
this catch all folder with also the sign up inside and then a page. CSX and then
up inside and then a page. CSX and then this code all right not too complicated
this code all right not too complicated let's copy this right here and I'm going
let's copy this right here and I'm going to do that in a very specific way so
to do that in a very specific way so usually again I did this in the root of
usually again I did this in the root of my application or the out folder
my application or the out folder something like that but this time I'm
something like that but this time I'm going to do it a bit differently so make
going to do it a bit differently so make sure you save the middleware file go
sure you save the middleware file go inside of the app folder go inside of
inside of the app folder go inside of the platform and in here create another
the platform and in here create another organizational folder called clerk so
organizational folder called clerk so this is where we're going to hold
this is where we're going to hold everything regarding clerk that's going
everything regarding clerk that's going to be our authentication and our
to be our authentication and our organization settings our organization
organization settings our organization creating uh selection of organization
creating uh selection of organization and stuff like that so go ahead and
and stuff like that so go ahead and create the sign in uh folder like this
create the sign in uh folder like this and inside of it go ahead and create a
and inside of it go ahead and create a new folder which is going to have this
new folder which is going to have this syntax so double square brackets dot dot
syntax so double square brackets dot dot dot assign Dash in and inside create a
dot assign Dash in and inside create a new file page. DSX and paste this inside
new file page. DSX and paste this inside and as you can see this uses the sign up
and as you can see this uses the sign up so I made a mistake first I should have
so I made a mistake first I should have created sign up but very easy to fix I
created sign up but very easy to fix I mean it doesn't really matter but just
mean it doesn't really matter but just import sign in and render sign in just
import sign in and render sign in just like that and the very same way we're
like that and the very same way we're going to do the other page so go ahead
going to do the other page so go ahead and create a new folder sign Dash up and
and create a new folder sign Dash up and go ahead and create a new folder again
go ahead and create a new folder again double square brackets sign Dash up just
double square brackets sign Dash up just make sure this is exactly like this and
make sure this is exactly like this and page. DSX perfect and then you can just
page. DSX perfect and then you can just paste this up and replace this two with
paste this up and replace this two with sign
sign up perfect now that we have that we have
up perfect now that we have that we have to add some other environment variables
to add some other environment variables so let me just uh go back here uh
so let me just uh go back here uh okay uh we did this we did this and now
okay uh we did this we did this and now we have to update our environment
we have to update our environment variables so the middleware knows where
variables so the middleware knows where to redirect us so let's go ahead and
to redirect us so let's go ahead and copy these files or you don't have to
copy these files or you don't have to copy them you can just look exactly what
copy them you can just look exactly what I'm going to paste now so go back inside
I'm going to paste now so go back inside of your environment uh file and just
of your environment uh file and just after the clerk secret key add the
after the clerk secret key add the public clerk sign in sign up after sign
public clerk sign in sign up after sign in and after sign up like this perfect
in and after sign up like this perfect so as you can see we have defined that
so as you can see we have defined that my signin URL is/ signin my sign up URL
my signin URL is/ signin my sign up URL is/ signup how do we know that these are
is/ signup how do we know that these are the values well that is because that's
the values well that is because that's how we named our folders as you can see
how we named our folders as you can see I have the platform folder I have the
I have the platform folder I have the clerk folder and then I have sign in and
clerk folder and then I have sign in and sign up so if I want it to be register
sign up so if I want it to be register and login I have to change this folder
and login I have to change this folder to login I have to change this one to be
to login I have to change this one to be dot dot do login and then I have to
dot dot do login and then I have to change it right here to be login as well
change it right here to be login as well and it's still going to work if you need
and it's still going to work if you need that for any reason uh and remember this
that for any reason uh and remember this is not part of the URL this is not part
is not part of the URL this is not part of the URL as well meaning that this
of the URL as well meaning that this route is just Local Host 3000 SL signin
route is just Local Host 3000 SL signin so this is why I mentioned that it's
so this is why I mentioned that it's important to understand the concept of
important to understand the concept of organizational routes perfect so now
organizational routes perfect so now that we have that I believe that we are
that we have that I believe that we are ready to try this out let's go ahead uh
ready to try this out let's go ahead uh and do that so I'm going to go ahead
and do that so I'm going to go ahead inside of my terminal
inside of my terminal and I'm going to run mpm runev make sure
and I'm going to run mpm runev make sure you have the environment files make sure
you have the environment files make sure you have the middleware and all of those
you have the middleware and all of those pages and let's see if we did this
pages and let's see if we did this correctly so I'm going to refresh this
correctly so I'm going to refresh this page and nothing should change on this
page and nothing should change on this page and as you can see all right so
page and as you can see all right so something changed I'm going to explain
something changed I'm going to explain in a second all right so let's
in a second all right so let's immediately go back inside of our
immediately go back inside of our middleware and allow the landing page to
middleware and allow the landing page to be visited by non logged in users
be visited by non logged in users because right now the middle bar
because right now the middle bar detected that we are not logged in and
detected that we are not logged in and it redirected me to Local Host 3000 SL
it redirected me to Local Host 3000 SL sign in and you can see this little
sign in and you can see this little component which we are going to uh Style
component which we are going to uh Style just a bit so let's go ahead inside of
just a bit so let's go ahead inside of the
the middleware let's find that there we go
middleware let's find that there we go and inside of this middleware go inside
and inside of this middleware go inside of this Al middleware inside of the
of this Al middleware inside of the object add public routes and just add
object add public routes and just add slash to be the public route and that's
slash to be the public route and that's it just save that file and let's go
it just save that file and let's go ahead back inside of Local Host 3000 and
ahead back inside of Local Host 3000 and there we go as you can see now we are
there we go as you can see now we are logged out uh and we can still visit our
logged out uh and we can still visit our landing page exactly what we wanted but
landing page exactly what we wanted but now when we click on get Tas ify free it
now when we click on get Tas ify free it leads us to slash sign up so when I
leads us to slash sign up so when I click here there we go I can now log in
click here there we go I can now log in when I click on login same thing but
when I click on login same thing but this one leads to slash sign in and this
this one leads to slash sign in and this one leads to sign up perfect so now we
one leads to sign up perfect so now we have to just style this screen a little
have to just style this screen a little bit so it's not like this in the corner
bit so it's not like this in the corner but instead I want it centered
but instead I want it centered so it's very easy to style last thanks
so it's very easy to style last thanks to the organizational folders and the
to the organizational folders and the fact that we know how to use a layouts
fact that we know how to use a layouts so all we have to do is find the app
so all we have to do is find the app folder we have to find the platform and
folder we have to find the platform and then we have to find Clerk and if you
then we have to find Clerk and if you have any idea you can already start
have any idea you can already start doing this on your own so how would you
doing this on your own so how would you Center every route inside of clerk what
Center every route inside of clerk what did we say about layout and
did we say about layout and organizational components uh sorry yes
organizational components uh sorry yes organizational folders so if you have
organizational folders so if you have any idea how to do that I advise you to
any idea how to do that I advise you to pause the video and try and do it
pause the video and try and do it yourself if not you can just follow
yourself if not you can just follow along no problem so what we're going to
along no problem so what we're going to do is we're going to create a layout
do is we're going to create a layout inside of the clerk folder like this and
inside of the clerk folder like this and then we're going to write const clerk
then we're going to write const clerk layout we're immediately going to
layout we're immediately going to destructure the
children and we're going to return a div which renders those children and now
which renders those children and now let's just go ahead and give those
let's just go ahead and give those children a type so children are a type
children a type so children are a type of react react
of react react node all right and let's give this div a
node all right and let's give this div a class name of H full Flex items Center
class name of H full Flex items Center and justify Center perfect so if you got
and justify Center perfect so if you got anywhere near uh to you you probably
anywhere near uh to you you probably didn't guess the exact class names right
didn't guess the exact class names right if this is your first time guessing this
if this is your first time guessing this but if you manag to figure out that you
but if you manag to figure out that you need to create a layout and render the
need to create a layout and render the children you were on the right path good
children you were on the right path good job perfect if not now big is you're
job perfect if not now big is you're going to get it uh and now let's just
going to get it uh and now let's just export default clerk
export default clerk layout great and as you can see now we
layout great and as you can see now we have a much nicer centered screen so
have a much nicer centered screen so whenever I click it's like this perfect
whenever I click it's like this perfect so one more thing that I want to show
so one more thing that I want to show you is another component called user
you is another component called user button so I'm going to go ahead and
button so I'm going to go ahead and create a new page inside of my platform
create a new page inside of my platform right here which is just going to be
right here which is just going to be called protected like that and inside
called protected like that and inside creating new file page. DSX so we're
creating new file page. DSX so we're going to delete this later I just want
going to delete this later I just want to demonstrate how it looks when we are
to demonstrate how it looks when we are logged in versus when we are not so
logged in versus when we are not so let's go ahead and write const protected
let's go ahead and write const protected page return a div protected
page return a div protected page uh perfect and let's export default
page uh perfect and let's export default protected page like that great so I'm
protected page like that great so I'm going to go ahead and log in with
going to go ahead and log in with Google and after you log in you
Google and after you log in you redirected back to this page later in
redirected back to this page later in the tutorial this is not going to work
the tutorial this is not going to work exactly like that if we are logged in we
exactly like that if we are logged in we are only going to be able to access the
are only going to be able to access the dashboard right we're not going to be
dashboard right we're not going to be seeing this landing page all the time
seeing this landing page all the time what I want you to try now make sure
what I want you to try now make sure you're logged in is go to localhost 3000
you're logged in is go to localhost 3000 SL protected so this route which we just
SL protected so this route which we just created and as you can see right here it
created and as you can see right here it says protected page like this perfect
says protected page like this perfect and now I want to show you a a couple of
and now I want to show you a a couple of cool stuff that you can do so by default
cool stuff that you can do so by default every page is a server component that
every page is a server component that means that we can try and access some of
means that we can try and access some of the information about our logged in user
the information about our logged in user using the clerk server utils so let's
using the clerk server utils so let's write const user to be current user from
write const user to be current user from clerk nextjs like this import current
clerk nextjs like this import current user from Clerk
user from Clerk nextjs and that returns a promise as you
nextjs and that returns a promise as you can see so it means that we need to wrap
can see so it means that we need to wrap this inside of an asynchronous function
this inside of an asynchronous function and avate this result and then if I
and avate this result and then if I hover over the user it says that it is a
hover over the user it says that it is a type of user so let's write user and
type of user so let's write user and let's then do user Dot and you can try
let's then do user Dot and you can try any of these email addresses first name
any of these email addresses first name I'm going to check the first name so
I'm going to check the first name so question mark. first name and there we
question mark. first name and there we go you can see that it says user is
go you can see that it says user is Antonio because I am logged in perfect
Antonio because I am logged in perfect you can also try another hook called uh
you can also try another hook called uh out so it's just out from Clerk nextjs
out so it's just out from Clerk nextjs and from here you can extract user ID so
and from here you can extract user ID so this is very useful you're going to see
this is very useful you're going to see it later so user ID is just user ID like
it later so user ID is just user ID like this and there we go you can see the ID
this and there we go you can see the ID rendered out right here perfect this is
rendered out right here perfect this is going to be very useful for us when we
going to be very useful for us when we want to protect our uh API calls and our
want to protect our uh API calls and our server actions to ensure that the user
server actions to ensure that the user is logged in we're not going to check
is logged in we're not going to check for this one but we're going to use this
for this one but we're going to use this to quickly check okay we're logged in
to quickly check okay we're logged in perfect so now you know how to do this
perfect so now you know how to do this in server components but what if this
in server components but what if this was a client component let's try it out
was a client component let's try it out so I'm going to remove all of these
so I'm going to remove all of these things I'm going to remove this I'm
things I'm going to remove this I'm going to remove this Hooks and let's
going to remove this Hooks and let's just leave this like this for now and
just leave this like this for now and let's mark this as use client so now
let's mark this as use client so now this is a client components basically
this is a client components basically it's the component you're used to when
it's the component you're used to when working with react and now we can no
working with react and now we can no longer use those uh those utils which I
longer use those uh those utils which I just tried instead we have as you can
just tried instead we have as you can see I have this autoc completion from
see I have this autoc completion from GitHub co-pilot to give me use out so
GitHub co-pilot to give me use out so let's try that so const use out from
let's try that so const use out from clerk nextjs you can see how it has a
clerk nextjs you can see how it has a prefix use meaning that it is a hook
prefix use meaning that it is a hook when something is a hook you almost
when something is a hook you almost immediately know that you need to use
immediately know that you need to use that inside of use client component you
that inside of use client component you cannot use that inside of a server
cannot use that inside of a server component and let's go ahead and D
component and let's go ahead and D structure user ID from use out and now
structure user ID from use out and now let's get our user from use user like
let's get our user from use user like this
this and let's get our user and let's just
and let's get our user and let's just hover over to see there we go use user
hover over to see there we go use user return and let's just check maybe we
return and let's just check maybe we have to destructure the user yes we have
have to destructure the user yes we have to destructure the user great and as you
to destructure the user great and as you can see this can stay exactly the same
can see this can stay exactly the same and as you can see it is still working
and as you can see it is still working exactly what we expect perfect and now
exactly what we expect perfect and now that we have that we can go ahead uh and
that we have that we can go ahead uh and remove this we can remove these two
remove this we can remove these two hooks and we can remove everything
hooks and we can remove everything inside and instead instead let's render
inside and instead instead let's render clerk uh sorry user button from clerk
clerk uh sorry user button from clerk nextjs from the same package right and
nextjs from the same package right and uh let's go ahead and just close this
uh let's go ahead and just close this all right and now as you can see we have
all right and now as you can see we have a user button here and from here we can
a user button here and from here we can manage our account or we can log out we
manage our account or we can log out we can do a lot of things so let's click
can do a lot of things so let's click manage account and from here you can see
manage account and from here you can see uh you can change your password you can
uh you can change your password you can change connected accounts you can go
change connected accounts you can go ahead inside of your profile and change
ahead inside of your profile and change your image if you want to basically a
your image if you want to basically a bunch of different things and you can of
bunch of different things and you can of course also sign out so let's click sign
course also sign out so let's click sign out here and right now you can see that
out here and right now you can see that I'm redirected to this weird page check
I'm redirected to this weird page check out the URL right it's not Local Host so
out the URL right it's not Local Host so just go back to Local Host and what I
just go back to Local Host and what I want you to do is go ahead uh oops let's
want you to do is go ahead uh oops let's go back inside of that protected page I
go back inside of that protected page I want you to add a little prop here to
want you to add a little prop here to the user button after sign out URL lead
the user button after sign out URL lead us back to the landing page so after we
us back to the landing page so after we sign out we want to go back to the
sign out we want to go back to the landing page so let's try this again
landing page so let's try this again make sure you are logged out landing
make sure you are logged out landing page is accessible if I try to go on SL
page is accessible if I try to go on SL protected I'm redirected to login
protected I'm redirected to login exactly what we wanted now I'm going to
exactly what we wanted now I'm going to go ahead and log
go ahead and log in and now I am a allowed to go to slash
in and now I am a allowed to go to slash protected as you can see I'm right here
protected as you can see I'm right here perfect and let's try one more time when
perfect and let's try one more time when I click sign out there we go I am back
I click sign out there we go I am back on the landing page so I just wanted to
on the landing page so I just wanted to show you uh how that works perfect and
show you uh how that works perfect and now let's go ahead and remove this
now let's go ahead and remove this protected folder we are not going to
protected folder we are not going to need it anymore I just wanted to
need it anymore I just wanted to demonstrate how to uh that our
demonstrate how to uh that our authentication is actually working
authentication is actually working perfect great great job so far and now
perfect great great job so far and now we're going to work on uh creating our
we're going to work on uh creating our organizations inviting users uh admins
organizations inviting users uh admins all of that stuff great great
all of that stuff great great job great so now that we have
job great so now that we have authentication set up we are ready to
authentication set up we are ready to start exploring how organizations work
start exploring how organizations work and thankfully clerk does all of that
and thankfully clerk does all of that for us so let's go ahead head back
for us so let's go ahead head back inside of clerk so right now you should
inside of clerk so right now you should have this kind of screen you can see I
have this kind of screen you can see I have my user here and let's go ahead and
have my user here and let's go ahead and click on organizations right here let me
click on organizations right here let me just zoom in so you can see so right
just zoom in so you can see so right here on the organizations and inside you
here on the organizations and inside you can see that organizations have not been
can see that organizations have not been enabled for this application so go ahead
enabled for this application so go ahead and click enable organizations and
and click enable organizations and that's going to redirect you to
that's going to redirect you to organization settings here and just
organization settings here and just enable them like this great perfect so
enable them like this great perfect so make sure uh organizations are enabled
make sure uh organizations are enabled and there we go now we have
and there we go now we have organizations
organizations here great so you can now close clerk no
here great so you can now close clerk no need to do anything further and what I
need to do anything further and what I want to create now is a protected route
want to create now is a protected route which we're going to use to select an
which we're going to use to select an organization or to create a new one so
organization or to create a new one so let's go inside of the app folder let's
let's go inside of the app folder let's go inside of the platform inside of
go inside of the platform inside of clerk right here and create a new folder
clerk right here and create a new folder called select dorg like that and inside
called select dorg like that and inside create another folder with double uh
create another folder with double uh square brackets and catch all route
square brackets and catch all route inside select dorg again and then add a
inside select dorg again and then add a page. DSX so make sure it's inside of
page. DSX so make sure it's inside of this clerk folder so this select or
this clerk folder so this select or route is also going to share the same
route is also going to share the same layout where we centered our children so
layout where we centered our children so we don't have to worry about that
we don't have to worry about that perfect and inside of here all I want to
perfect and inside of here all I want to do is import organization
do is import organization list from Clerk nextjs and Export
list from Clerk nextjs and Export default function create organization
default function create organization page like that and just return
page like that and just return organization
organization list perfect and now let's go ahead and
list perfect and now let's go ahead and log in I'm just going to go ahead uh and
log in I'm just going to go ahead uh and do
do that and now that I'm logged in I'm
that and now that I'm logged in I'm going to go and manually go to/ select
going to go and manually go to/ select dorg like this and there we go as you
dorg like this and there we go as you can see from here I can either select my
can see from here I can either select my personal account or I can click create
personal account or I can click create an organization from here I can upload a
an organization from here I can upload a specific image but I don't have to and I
specific image but I don't have to and I can go ahead and give this uh you know a
can go ahead and give this uh you know a name name for example F Incorporated and
name name for example F Incorporated and I can click create organization and from
I can click create organization and from here I can add email addresses to invite
here I can add email addresses to invite some other members and I can immediately
some other members and I can immediately assign their roles to be members or
assign their roles to be members or admins for now I'm going to skip that
admins for now I'm going to skip that perfect and there we go now we have my
perfect and there we go now we have my organization right here and I can click
organization right here and I can click here and now I have selected my
here and now I have selected my organization but nothing seems to be
organization but nothing seems to be happening here and that's because we
happening here and that's because we need to add some additional options to
need to add some additional options to this component right here so the first
this component right here so the first option is that we we remove this
option is that we we remove this personal account so this is going to be
personal account so this is going to be primarily a businessto business software
primarily a businessto business software so let's go ahead and write hide
so let's go ahead and write hide personal inside of this component and
personal inside of this component and now when you refresh select org as you
now when you refresh select org as you can see we no longer have uh that
can see we no longer have uh that personal option great and now what I
personal option great and now what I want to do is add after select
want to do is add after select organization URL let's go ahead and
organization URL let's go ahead and redirect to let me just extract this
redirect to let me just extract this sorry expand this so we want to redirect
sorry expand this so we want to redirect to
to organization
organization slash ID like this so this is uh this is
slash ID like this so this is uh this is going to be recognized inside of this
going to be recognized inside of this prop that this is a slug right this is a
prop that this is a slug right this is a dynamic part of a URL and below that
dynamic part of a URL and below that we're going to copy and paste this and
we're going to copy and paste this and change this to after create organization
change this to after create organization URL so this one is after select and this
URL so this one is after select and this one is after uh create perfect so let's
one is after uh create perfect so let's go ahead and try that out I'm just going
go ahead and try that out I'm just going to hit refresh here make sure you do
to hit refresh here make sure you do that as well let's click here and we
that as well let's click here and we should be redirected to a 404 page but
should be redirected to a 404 page but check a look uh at the URL it says
check a look uh at the URL it says organization slash and the organization
organization slash and the organization ID in the URL perfect so that is exactly
ID in the URL perfect so that is exactly what we needed and now we're just going
what we needed and now we're just going to go ahead and quickly create uh those
to go ahead and quickly create uh those pages so we have to go back inside of
pages so we have to go back inside of our platform organizational route so let
our platform organizational route so let me close everything here go inside of
me close everything here go inside of the app folder platform right here and
the app folder platform right here and let's go ahead and create uh another
let's go ahead and create uh another organizational folder besides clerk
organizational folder besides clerk which is going to be our dashboard like
which is going to be our dashboard like this and inside go ahead and create uh a
this and inside go ahead and create uh a new folder called organization like that
new folder called organization like that and then another folder which is going
and then another folder which is going to be organization
to be organization ID great and then we can create a page.
ID great and then we can create a page. DSX inside and let's write con
DSX inside and let's write con organization ID
organization ID page return a div saying organization
page return a div saying organization page like this and Export default
page like this and Export default organization ID page perfect and as you
organization ID page perfect and as you can see I know longer have that error
can see I know longer have that error now because my URL matches SL
now because my URL matches SL organization and then the organization
organization and then the organization ID so if you're having any problems go
ID so if you're having any problems go ahead and confirm that you have a folder
ahead and confirm that you have a folder called organization and the folder old
called organization and the folder old organization ID it doesn't matter if you
organization ID it doesn't matter if you put it inside of this organizational
put it inside of this organizational route because remember this is omitted
route because remember this is omitted from the URL so this dashboard is not
from the URL so this dashboard is not inside of the URL platform is not inside
inside of the URL platform is not inside of the URL as well right so none of this
of the URL as well right so none of this make the URL but only here we actually
make the URL but only here we actually start with the URL because this has no
start with the URL because this has no parenthesis around it and this if you
parenthesis around it and this if you remember from the first part of this
remember from the first part of this tutorial is a dynamic part of the URL
tutorial is a dynamic part of the URL and if it's still not working and this
and if it's still not working and this is correct correct then go ahead again
is correct correct then go ahead again into the platform but this time into
into the platform but this time into Clerk and then go inside of Select org
Clerk and then go inside of Select org right here in this page and confirm that
right here in this page and confirm that you didn't misspell something here
you didn't misspell something here perhaps there's an S here instead of Z
perhaps there's an S here instead of Z or something like that great and now
or something like that great and now you're actually going to see how useful
you're actually going to see how useful these organizational routes are once
these organizational routes are once again because as you can see here in my
again because as you can see here in my clerk folder I share a specific layout
clerk folder I share a specific layout where I just Center all of these items
where I just Center all of these items but here in dashboard that's not happen
but here in dashboard that's not happen happening so that's why we created a
happening so that's why we created a specific uh organizational route but at
specific uh organizational route but at this time just for the dashboard so for
this time just for the dashboard so for now what I want to do is I actually want
now what I want to do is I actually want to show you how to fetch uh some uh um
to show you how to fetch uh some uh um organization information so you can use
organization information so you can use the out which we already played around
the out which we already played around with from Clerk nextjs and from here
with from Clerk nextjs and from here besides the user ID you can also extract
besides the user ID you can also extract the or ID like this so let's go ahead
the or ID like this so let's go ahead and write organization ation and org ID
and write organization ation and org ID like this perfect as you can see now we
like this perfect as you can see now we have the organization and the org ID is
have the organization and the org ID is right here and you can see that it
right here and you can see that it matches what is in the URL perfect and
matches what is in the URL perfect and now let's go ahead and let me show you
now let's go ahead and let me show you another component which we have so I'm
another component which we have so I'm going to remove this for now and let's
going to remove this for now and let's add the organization switcher component
add the organization switcher component import that from Clerk nextjs and let me
import that from Clerk nextjs and let me just close it so it's a self closing tag
just close it so it's a self closing tag and you can see how that looks so
and you can see how that looks so similar to like that uh user button
similar to like that uh user button component as you can see right here I
component as you can see right here I can switch I can manage my organization
can switch I can manage my organization from here I can check out my users a
from here I can check out my users a bunch of stuff like that perfect and in
bunch of stuff like that perfect and in here I can still switch to personal
here I can still switch to personal account but also from here you can add
account but also from here you can add that prop hide personal and after you
that prop hide personal and after you refresh it's not going to be here and
refresh it's not going to be here and from here you can also trigger a
from here you can also trigger a creation of organization if it is faster
creation of organization if it is faster for your users so very very convenient
for your users so very very convenient component perfect so what I want to do
component perfect so what I want to do now is I actually just want to bring
now is I actually just want to bring this back to say organization page we
this back to say organization page we don't need this hooks we don't need this
don't need this hooks we don't need this like that uh and just save it like this
like that uh and just save it like this because now we're going to create a
because now we're going to create a specific layout only for this dashboard
specific layout only for this dashboard component sorry for this dashboard
component sorry for this dashboard organizational route and we're going to
organizational route and we're going to start and create a little nov bar and
start and create a little nov bar and add all of those compon components
add all of those compon components there so let's go ahead inside of the
there so let's go ahead inside of the dashboard organizational folder and
dashboard organizational folder and create a new file layout. TSX so let me
create a new file layout. TSX so let me just collapse this organization folder
just collapse this organization folder just to show you that this layout file
just to show you that this layout file is inside of dashboard not inside of
is inside of dashboard not inside of organization that's important because
organization that's important because we're going to have some more routes
we're going to have some more routes here uh which are going to be inside of
here uh which are going to be inside of the dashboard uh organizational folder
the dashboard uh organizational folder so go inside of this layout and add cons
so go inside of this layout and add cons dashboard layout and you already know
dashboard layout and you already know that we can immediately extract children
that we can immediately extract children from
from this uh sorry let's give them a type so
this uh sorry let's give them a type so children are a type of react. react
children are a type of react. react node and let's go ahead and return a div
node and let's go ahead and return a div and let's render the
and let's render the children and we also have to export
children and we also have to export default dashboard
default dashboard layout and let's just not misspell that
layout and let's just not misspell that all right and there we go no no more uh
all right and there we go no no more uh errors perfect and now what I want to do
errors perfect and now what I want to do is I want to give this div a class name
is I want to give this div a class name of height
of height full and then I want to render a nbar
full and then I want to render a nbar component from here but once I save this
component from here but once I save this I'm going to get an error because the
I'm going to get an error because the enough bar component does not exist so
enough bar component does not exist so inside of this dashboard right here go
inside of this dashboard right here go ahead and create a new
ahead and create a new folder underscore components and inside
folder underscore components and inside of here create a new file navb bar. DSX
of here create a new file navb bar. DSX like that perfect let's export cons
like that perfect let's export cons navbar and let's return a div saying
navbar and let's return a div saying Novar like this and then we can
Novar like this and then we can immediately go back inside of our layout
immediately go back inside of our layout and we can fix this undefined error so
and we can fix this undefined error so make sure you don't import the N bar
make sure you don't import the N bar from the marketing page right that's the
from the marketing page right that's the different one we're going to import this
different one we're going to import this closest to us _ components Novar and
closest to us _ components Novar and once you save we have a nice little text
once you save we have a nice little text nbar here Perfect all right and now
nbar here Perfect all right and now let's go ahead uh and let's go inside of
let's go ahead uh and let's go inside of this knv bar and let's go ahead and
this knv bar and let's go ahead and slightly uh style it right so I'm going
slightly uh style it right so I'm going to give this D A Class name of fixed
to give this D A Class name of fixed Z50 top Z w- full height of 14 like that
Z50 top Z w- full height of 14 like that border bottom Shadow small
border bottom Shadow small BG is going to be white and flex and
BG is going to be white and flex and items Center like that perfect and now
items Center like that perfect and now let's go ahead and open a new div here
let's go ahead and open a new div here with a class name of flex items Das
with a class name of flex items Das Center and GAP X4 and just above this
Center and GAP X4 and just above this div I'm going to add a little comment
div I'm going to add a little comment mobile sidebar so this is just for me
mobile sidebar so this is just for me you didn't have to write this but just
you didn't have to write this but just for me to remember that we need to
for me to remember that we need to handle responsivity at some point but
handle responsivity at some point but we're going to do that later first we
we're going to do that later first we have have to implement the desktop
have have to implement the desktop sidebar right and inside of here go
sidebar right and inside of here go ahead and open a div uh with a class
ahead and open a div uh with a class name hidden MD Flex so this is only
name hidden MD Flex so this is only going to be visible on desktop not on
going to be visible on desktop not on mobile and that's going to be our logo
mobile and that's going to be our logo component which we created and store in
component which we created and store in at/ components logo like this perfect so
at/ components logo like this perfect so now if I expand from here there we go
now if I expand from here there we go you can see our nice little logo here
you can see our nice little logo here perfect and let's also go ahead and give
perfect and let's also go ahead and give this div which holds our nav bar a PX4
this div which holds our nav bar a PX4 and let's also maybe change this from
and let's also maybe change this from div to nav right to be more semantically
div to nav right to be more semantically correct there we go all
correct there we go all right and now uh outside of this div
right and now uh outside of this div right here which holds our logo just go
right here which holds our logo just go ahead and import a button from
ahead and import a button from components UI Button as I did uh right
components UI Button as I did uh right here and let's just go ahead and write
here and let's just go ahead and write create like this perfect and let's give
create like this perfect and let's give this one a size of small and let's give
this one a size of small and let's give it a class name of rounded Small hidden
it a class name of rounded Small hidden MD Block H AO py
MD Block H AO py 1.5 and PX 2 so let me just go ahead and
1.5 and PX 2 so let me just go ahead and add a little space here so you can see
add a little space here so you can see exactly py is 1.5 and PX is 2 like this
exactly py is 1.5 and PX is 2 like this and there we go you can see our little
and there we go you can see our little button here now perfect and I just want
button here now perfect and I just want to go ahead and add another button
to go ahead and add another button here which is going to have a different
here which is going to have a different uh size sorry a different look so class
uh size sorry a different look so class name is going to be rounded small block
name is going to be rounded small block and MD
hidden and inside of here render a plus from Lucid react like that so this
plus from Lucid react like that so this is only going to be visible uh on mobile
is only going to be visible uh on mobile devices and give this plus a class
devices and give this plus a class name of h-4 and
name of h-4 and W-4 like that and since this is from npm
W-4 like that and since this is from npm I'm going to move that import to the top
I'm going to move that import to the top perfect so this is how it looks on
perfect so this is how it looks on mobile we just have a little plus button
mobile we just have a little plus button but on desktop we have a big create
but on desktop we have a big create button and now that I'm looking at it it
button and now that I'm looking at it it kind of feels like we could have done
kind of feels like we could have done this in a better way instead of making
this in a better way instead of making this hidden and MD block on the button
this hidden and MD block on the button we can actually
we can actually do it oh yeah but then we have to wrap
do it oh yeah but then we have to wrap this into a span H I don't know let's
this into a span H I don't know let's leave it like this for now I think it's
leave it like this for now I think it's fine yeah perhaps we could have improved
fine yeah perhaps we could have improved reusing the same component but you know
reusing the same component but you know I don't think it's a big deal you can
I don't think it's a big deal you can play around with it uh if you want uh
play around with it uh if you want uh great all right so now that we have that
great all right so now that we have that what I want to add outside of this div
what I want to add outside of this div so just just here in this empty space
so just just here in this empty space before we close the nov bar is a new div
before we close the nov bar is a new div with a class name of ml D aouto Flex
with a class name of ml D aouto Flex items D Center and GAP X2 and inside of
items D Center and GAP X2 and inside of here we're going to render our
here we're going to render our organizational switcher sorry
organizational switcher sorry organization switcher from clerk nextjs
organization switcher from clerk nextjs so make sure you import organization
so make sure you import organization switcher from clerk
switcher from clerk nextjs like that and let's go ahead and
nextjs like that and let's go ahead and give it some props so first hide
give it some props so first hide personal we already know that after
personal we already know that after create organization URL we're going to
create organization URL we're going to go to/ organization ID
go to/ organization ID after leave organization URL we're going
after leave organization URL we're going to go to/ select dorg like that after uh
to go to/ select dorg like that after uh what's left after select organization
what's left after select organization URL that's also going to go to SL
URL that's also going to go to SL organizationid like that and now I'm
organizationid like that and now I'm just going to modify the appearance a
just going to modify the appearance a bit so I'm going to write
bit so I'm going to write appearance
appearance elements I'm going to go inside of the
elements I'm going to go inside of the root
root box and I'm going to add the display to
box and I'm going to add the display to be Flex
oops so display is going to be Flex justify content is going to be Center
justify content is going to be Center and align items is going to be Center as
and align items is going to be Center as well
well perfect great so we just improved uh
perfect great so we just improved uh this little image here usually without
this little image here usually without this styling it just looks a tiny bit
this styling it just looks a tiny bit off in comparison to all other all the
off in comparison to all other all the other elements which we are going to
other elements which we are going to have great and just below this
have great and just below this organization switcher we're also going
organization switcher we're also going to add the user button from clerk SL
to add the user button from clerk SL nextjs so import this user button from
nextjs so import this user button from clerk nextjs right here and go ahead and
clerk nextjs right here and go ahead and give it an after sign out URL to be a
give it an after sign out URL to be a slash and appearance is going to be
slash and appearance is going to be elements Avatar box and inside of here
elements Avatar box and inside of here give it a height of 30 and a width of 30
give it a height of 30 and a width of 30 as well perfect so now we have all of
as well perfect so now we have all of those elements inside of our Navar here
those elements inside of our Navar here perfect but we're not exactly finished
perfect but we're not exactly finished now because I can still go to local
now because I can still go to local close 3000 and as you can see I can
close 3000 and as you can see I can visit my landing page but I don't want
visit my landing page but I don't want that to happen right uh in fact I always
that to happen right uh in fact I always want to be on one specific organization
want to be on one specific organization right even if I haven't selected any
right even if I haven't selected any organization I want in in that case I
organization I want in in that case I want to get redirected to that SL select
want to get redirected to that SL select org or route which we created right so
org or route which we created right so let's go ahead and modify our middleware
let's go ahead and modify our middleware to do exactly that so we're going to
to do exactly that so we're going to improve our middleware a bit usually we
improve our middleware a bit usually we pretty much never attach the middleware
pretty much never attach the middleware in my tutorials but you know this is a
in my tutorials but you know this is a really cool feature that clerk has this
really cool feature that clerk has this organizations so we get to play around
organizations so we get to play around with it a bit so let's go ahead and
with it a bit so let's go ahead and write after
write after out this is a fun which will give us an
out this is a fun which will give us an out and a request and let's go ahead and
out and a request and let's go ahead and open it and first thing that I want to
open it and first thing that I want to check if we are authenticated and we're
check if we are authenticated and we're going to do that using out. user ID so
going to do that using out. user ID so if we have the user ID it means we are
if we have the user ID it means we are authenticated and then we're going to
authenticated and then we're going to check if we are on the public route so
check if we are on the public route so if out is public route so this means if
if out is public route so this means if we are logged in and if we are uh
we are logged in and if we are uh visiting the landing page in that case I
visiting the landing page in that case I I want to redirect to either my
I want to redirect to either my organization ID or to select org so
organization ID or to select org so let's write the initial path which we
let's write the initial path which we want to redirect the user is going to be
want to redirect the user is going to be select or right if they are logged in
select or right if they are logged in and they attempt to visit the landing
and they attempt to visit the landing page we're just going to push them back
page we're just going to push them back to select an organization but if we have
to select an organization but if we have the user organization ID in that case
the user organization ID in that case the pet is going to become make sure you
the pet is going to become make sure you write this inside of back text SL
write this inside of back text SL organization SL
organization SL individual out. organization ID right so
individual out. organization ID right so we we are going to move them to there
we we are going to move them to there and now we just have to create a
and now we just have to create a function to do that so const org
function to do that so const org selection is going to be new
selection is going to be new URL we get the current path which we
URL we get the current path which we have and we combine it with request.url
have and we combine it with request.url and then we do return next response
and then we do return next response which we can import from next SLS server
which we can import from next SLS server so just make sure sure you add this
so just make sure sure you add this little import so return next response uh
little import so return next response uh do
do redirect or
redirect or selection like that
selection like that perfect and now outside of this big if
perfect and now outside of this big if clause which we've just written we have
clause which we've just written we have to handle some other uh regular cases
to handle some other uh regular cases which we might have so if we are not
which we might have so if we are not authenticated and if we are not on our
authenticated and if we are not on our on a public route we have to explicitly
on a public route we have to explicitly tell what to do and that's going to be
tell what to do and that's going to be very simple we're just going to return
very simple we're just going to return redirect to sign in from clerk sln
redirect to sign in from clerk sln nextjs so just make sure you import
nextjs so just make sure you import redirect to sign in from Clerk
redirect to sign in from Clerk nextjs and we're simply going to pass in
nextjs and we're simply going to pass in the URL from where they came from so
the URL from where they came from so once they log in like obviously this
once they log in like obviously this user which is not logged in try to
user which is not logged in try to access a private route perhaps they it
access a private route perhaps they it was bookmarked or perhaps they had a
was bookmarked or perhaps they had a link right so if they attempt to do that
link right so if they attempt to do that and we tell them that they need to log
and we tell them that they need to log in first it would be nice of us to give
in first it would be nice of us to give them a return back URL so once they log
them a return back URL so once they log in we're just going to give them the
in we're just going to give them the current URL they just tried to
current URL they just tried to access and last one we have to check is
access and last one we have to check is if we are logged in so if Al is has the
if we are logged in so if Al is has the user ID and if we don't have out org
user ID and if we don't have out org idid and if request. next url. paath
idid and if request. next url. paath name is not/ Select
name is not/ Select dorg let me see if I can expand my
dorg let me see if I can expand my screen just a bit more so you can see it
screen just a bit more so you can see it in one line basically let me show you so
in one line basically let me show you so this is the if Clause if we have the
this is the if Clause if we have the user ID and we don't have a specified
user ID and we don't have a specified organization meaning we never selected
organization meaning we never selected any organization and if we are not on
any organization and if we are not on slash select organization basically if
slash select organization basically if the user is trying to do anything even
the user is trying to do anything even if they're logged in but they don't have
if they're logged in but they don't have an organization we have to force them to
an organization we have to force them to create an organization or select one
create an organization or select one first except if they are on that page
first except if they are on that page where we do the organization selection
where we do the organization selection so just make sure you don't have any
so just make sure you don't have any typos here otherwise you might create
typos here otherwise you might create some infinite Loops here so const or
some infinite Loops here so const or selection is new URL / select dorg and
selection is new URL / select dorg and request.url and return next response.
request.url and return next response. redirect org
redirect org selection like that and that is it that
selection like that and that is it that is our middleware so make sure you save
is our middleware so make sure you save this if you're having any issues you can
this if you're having any issues you can always copy it directly from my GitHub
always copy it directly from my GitHub and now let's try this out so I'm going
and now let's try this out so I'm going to go ahead and attempt to go to Local
to go ahead and attempt to go to Local Host 3,000 and as you can see I'm
Host 3,000 and as you can see I'm immediately redirected back to my
immediately redirected back to my organization perfect but what about if I
organization perfect but what about if I create a completely new account what
create a completely new account what happens then so let me just go ahead and
happens then so let me just go ahead and log out all right so I just logged out
log out all right so I just logged out and let's just confirm that I cannot go
and let's just confirm that I cannot go anywhere so I'm going to try the
anywhere so I'm going to try the organization there we go that is not
organization there we go that is not working and now let me just uh go back
working and now let me just uh go back to Local Host 3000 so I'm allowed to go
to Local Host 3000 so I'm allowed to go on the landing page and now I'm going to
on the landing page and now I'm going to log in but a completely different
log in but a completely different account that has never selected any
account that has never selected any organization let's see what happened
organization let's see what happened then there we go as you can see I'm
then there we go as you can see I'm redirected directly to/ select dorg
redirected directly to/ select dorg right and let's try and clean let's
right and let's try and clean let's attempt to go to landing page now as you
attempt to go to landing page now as you can see we cannot so if the user is
can see we cannot so if the user is logged in but we have not created an
logged in but we have not created an organization we need to make the user
organization we need to make the user create their own right so create the
create their own right so create the organization Skip and there we go we are
organization Skip and there we go we are back inside of here perfect amazing
back inside of here perfect amazing amazing job just one tiny bit thing that
amazing job just one tiny bit thing that I want to create is I want to create a
I want to create is I want to create a special variant for our button here so
special variant for our button here so it's kind of a bluish color rather than
it's kind of a bluish color rather than this one for that we have to visit our
this one for that we have to visit our button component in shat CN sorry in the
button component in shat CN sorry in the UI folder so components UI this is the
UI folder so components UI this is the shat CN button right so make sure you
shat CN button right so make sure you are here and then inside of this
are here and then inside of this variance we have default destructive
variance we have default destructive outline secondary ghost and Link and
outline secondary ghost and Link and we're going to add our own so we're
we're going to add our own so we're going to add primary and that's going to
going to add primary and that's going to be B- sky- 700 like this text- primary D
be B- sky- 700 like this text- primary D foreground hover BG sky-
foreground hover BG sky- 70090 like this so we just added this
70090 like this so we just added this one line and then we can go inside of
one line and then we can go inside of our app folder inside of the platform
our app folder inside of the platform dashboard components navbar right here
dashboard components navbar right here and we can give both both of these
and we can give both both of these buttons a new variant which is going to
buttons a new variant which is going to be as you can see now we even have the
be as you can see now we even have the autocomplete for it primary and if I
autocomplete for it primary and if I save there we go you can see how now it
save there we go you can see how now it has a nice bluish color and currently it
has a nice bluish color and currently it doesn't do anything but later it's going
doesn't do anything but later it's going to open a little form in a for in a type
to open a little form in a for in a type of popover perfect so you have uh
of popover perfect so you have uh started working on organizations your
started working on organizations your middleware is working and you have
middleware is working and you have completely completed the Al and now it's
completely completed the Al and now it's time for
time for uh to start working on the sidebar here
uh to start working on the sidebar here and actually rendering some information
and actually rendering some information about the users's current uh
about the users's current uh organization great great
organization great great job all right so now that we have our
job all right so now that we have our nav bar for this organization page right
nav bar for this organization page right here it's time for us to create a layout
here it's time for us to create a layout for our organization route which is
for our organization route which is going to push the content below the nav
going to push the content below the nav bar and also render a little sidebar
bar and also render a little sidebar here so we can choose between each
here so we can choose between each organization that we have so first thing
organization that we have so first thing that I want to do is go back inside of
that I want to do is go back inside of the app folder platform inside of our
the app folder platform inside of our dashboard and here in the organization
dashboard and here in the organization folder create a new file layout. DSX so
folder create a new file layout. DSX so just make sure that it's not inside of
just make sure that it's not inside of organization ID we're going to have a
organization ID we're going to have a separate layout for that as well uh and
separate layout for that as well uh and as you can see we have this error
as you can see we have this error because we're not exporting anything
because we're not exporting anything from layout so let's write const
from layout so let's write const organization layout from here we already
organization layout from here we already know that we can extract the children so
know that we can extract the children so let's do that and let's assign a proper
let's do that and let's assign a proper type to them and then we can just go
type to them and then we can just go ahead and render main render children
ahead and render main render children inside and Export default organization
inside and Export default organization layout great as you can see now nothing
layout great as you can see now nothing should change but the error should go
should change but the error should go away now let's give this main element a
away now let's give this main element a class name of padding top 20 and as you
class name of padding top 20 and as you can see now this content has been pushed
can see now this content has been pushed below our nav bar here you probably
below our nav bar here you probably forgot that we even had that it is
forgot that we even had that it is inside of this organization ID page
inside of this organization ID page right here we have a text organization
right here we have a text organization page but inside of this layout where we
page but inside of this layout where we render the nov bar that has been hidden
render the nov bar that has been hidden so now when we created this layout
so now when we created this layout inside of the organization folder we
inside of the organization folder we brought it back here besides this let's
brought it back here besides this let's also give it a medium device where it's
also give it a medium device where it's going to be pt24 so even further down
going to be pt24 so even further down and PX is going to be four and Max width
and PX is going to be four and Max width is going to be 6 Excel so if you forgot
is going to be 6 Excel so if you forgot what that is well that's the limit to
what that is well that's the limit to how far something will go in resizing
how far something will go in resizing right so let's go ahead and give it a
right so let's go ahead and give it a two Excel so if we are on a large
two Excel so if we are on a large monitor then Max resizing is going to be
monitor then Max resizing is going to be uh W screen XL and MX AO to make sure
uh W screen XL and MX AO to make sure that uh it doesn't overflow there we go
that uh it doesn't overflow there we go you can see how now I cannot resize past
you can see how now I cannot resize past a certain point great and now let's go
a certain point great and now let's go ahead and wrap this children inside of a
ahead and wrap this children inside of a div and let's give this div a class name
div and let's give this div a class name of
of Plex and GAP
Plex and GAP X7 and then in here we're going to open
X7 and then in here we're going to open a new div with a class name of w- 64
a new div with a class name of w- 64 shrink
shrink d0 hidden and MD block so this is going
d0 hidden and MD block so this is going to be a wrapper where we are going to
to be a wrapper where we are going to render the sidebar so for now I'm going
render the sidebar so for now I'm going to leave a comment for that so this is
to leave a comment for that so this is defining how wide that sidebar is going
defining how wide that sidebar is going to be and Shrink zero is saying that
to be and Shrink zero is saying that when we uh collapse our screen so not on
when we uh collapse our screen so not on mobile right on mobile as you can see
mobile right on mobile as you can see it's hidden it becomes visible on medium
it's hidden it becomes visible on medium devices so this shrink zero is only
devices so this shrink zero is only actually going to relate to this screens
actually going to relate to this screens right so you can see how this
right so you can see how this organization page content is shrinking
organization page content is shrinking right when I Collapse my screen what we
right when I Collapse my screen what we want to ensure with shrink zero is that
want to ensure with shrink zero is that this sidebar content never changes its
this sidebar content never changes its fixed width which we decided right here
fixed width which we decided right here great and now before we start and build
great and now before we start and build this entire sidebar thing uh I want you
this entire sidebar thing uh I want you to do the following so I want you to go
to do the following so I want you to go ahead and create a new organization
ahead and create a new organization let's call this I don't know another
let's call this I don't know another organization let's click create and
organization let's click create and we're not going to invite anyone and I
we're not going to invite anyone and I want you to take a look at something so
want you to take a look at something so right now if I go ahead and switch from
right now if I go ahead and switch from the organization here you can see that
the organization here you can see that my URL changes as well right so if I
my URL changes as well right so if I choose another you can see that I have
choose another you can see that I have the new URL right here the organization
the new URL right here the organization has changed but what what if I copy the
has changed but what what if I copy the URL then change the organization and
URL then change the organization and then paste it here what happens I should
then paste it here what happens I should technically be on this organization
technically be on this organization because that's the URL right but I am
because that's the URL right but I am not so we're going to create a little
not so we're going to create a little component which is going to act actively
component which is going to act actively take a look at our uh URL and then uh
take a look at our uh URL and then uh programmatically change the active
programmatically change the active organization so let's go ahead and
organization so let's go ahead and create that and we're going to do that
create that and we're going to do that by creating a new layout inside of the
by creating a new layout inside of the organization ID folder so leave this
organization ID folder so leave this like this for now and then inside of
like this for now and then inside of organization ID create a layout. TSX and
organization ID create a layout. TSX and let's go ahead and name this Con
let's go ahead and name this Con organization ID layout you already know
organization ID layout you already know that we can extract the
that we can extract the children let's give them a type of react
children let's give them a type of react react
react node and in here let's just uh we can
node and in here let's just uh we can actually return a fragment which is
actually return a fragment which is going to render the children and Export
going to render the children and Export default organization ID layout just save
default organization ID layout just save that and you should have no errors and
that and you should have no errors and nothing should change and now what we're
nothing should change and now what we're going to do is we're going to create a
going to do is we're going to create a component called organization control
component called organization control but we're going to spell it as or
but we're going to spell it as or control so it's shorter great and now
control so it's shorter great and now let's go ahead and create this so inside
let's go ahead and create this so inside of organization ID here create a new
of organization ID here create a new folder underscore
folder underscore components and inside create a new file
components and inside create a new file or- control. DSX let's mark this as use
or- control. DSX let's mark this as use client let's go ahead and import use
client let's go ahead and import use effect from react let's go ahead and
effect from react let's go ahead and import use prams from next SL navigation
import use prams from next SL navigation and let's go ahead and import use
and let's go ahead and import use organization list from from Clerk
organization list from from Clerk nextjs and then export con or
nextjs and then export con or control it's going to have nothing in
control it's going to have nothing in its props and in here we're going to
its props and in here we're going to return null because this is purely going
return null because this is purely going to be used to do some things in the use
to be used to do some things in the use effect so first let's get the params
effect so first let's get the params from use params then let's go ahead and
from use params then let's go ahead and let's D structure set active from use
let's D structure set active from use organization
organization list and you already guessed it we can
list and you already guessed it we can use this to change the organization
use this to change the organization programmatically so what we're going to
programmatically so what we're going to do is we're going to open up the use
do is we're going to open up the use effect
effect call whoops
call whoops in the dependency array we're going to
in the dependency array we're going to pass in the set active and pam.
pass in the set active and pam. organization ID so it's only going to
organization ID so it's only going to change it's only going to be called once
change it's only going to be called once this changes and let's go ahead and
this changes and let's go ahead and write if we don't have set active we can
write if we don't have set active we can just break the function and then set
just break the function and then set active and let's say organization is
active and let's say organization is going to be pam.
going to be pam. organization organization ID as string
there we go and now let's go back inside of this layout where we attempted to use
of this layout where we attempted to use that but we don't have the import and
that but we don't have the import and let's just add this import so a few
let's just add this import so a few things that I want you to confirm make
things that I want you to confirm make sure inside the org control component
sure inside the org control component you did not misspell pam. organization
you did not misspell pam. organization ID as you can see we don't have any
ID as you can see we don't have any types here so we just spelled it out
types here so we just spelled it out right we hope that we spelled it out
right we hope that we spelled it out correctly we we never defined which are
correctly we we never defined which are the types uh of this params so make sure
the types uh of this params so make sure you didn't misspell this and make sure
you didn't misspell this and make sure that it matches this folder variable
that it matches this folder variable right here otherwise you're going to
right here otherwise you're going to have some issues so now let's test out
have some issues so now let's test out that thing that I was talking about so
that thing that I was talking about so I'm going to choose this test
I'm going to choose this test organization I'm going to copy the URL
organization I'm going to copy the URL I'm going to switch to another and as
I'm going to switch to another and as you can see now another is selected but
you can see now another is selected but if I change my URL manually and press
if I change my URL manually and press enter there we go it switched to test
enter there we go it switched to test you can see for a second and it was
you can see for a second and it was another and then our org control
another and then our org control component activated and it switched to
component activated and it switched to test perfect so that's what I wanted us
test perfect so that's what I wanted us to do before we go on to creating the
to do before we go on to creating the sidebar because the sidebar is also
sidebar because the sidebar is also going to just push to the URL so instead
going to just push to the URL so instead of us calling the set active every
of us calling the set active every single time we do route push we can
single time we do route push we can create a component like this which is
create a component like this which is actively taking a look at the parameters
actively taking a look at the parameters inside of our URL and if it changes it
inside of our URL and if it changes it will change the organization for us
will change the organization for us great
great job so first I want us to head into the
job so first I want us to head into the terminal and install a couple of
terminal and install a couple of packages which we're going to need to
packages which we're going to need to create this sidebar component let's go
create this sidebar component let's go ahead inside of the terminal I'm just
ahead inside of the terminal I'm just going to shut down the app and I'm going
going to shut down the app and I'm going to write mpm install use hooks DTS this
to write mpm install use hooks DTS this is going to be a compilation of useful
is going to be a compilation of useful hooks for us to use which are type safe
hooks for us to use which are type safe and then we're going to go ahead and
and then we're going to go ahead and call npx shat cn- at latest at skeleton
call npx shat cn- at latest at skeleton so we're going to make sure that we have
so we're going to make sure that we have the skeleton component and we're also
the skeleton component and we're also going to add in the accordion so
going to add in the accordion so accordion like this make sure you have
accordion like this make sure you have this two installed perfect and then you
this two installed perfect and then you can go ahead and run mpm run Dev like
can go ahead and run mpm run Dev like that perfect now let's head back inside
that perfect now let's head back inside uh of our organization layout so right
uh of our organization layout so right here where we where I wrote the comment
here where we where I wrote the comment sidebar and in here now I'm going to
sidebar and in here now I'm going to render the actual sidebar so if I save
render the actual sidebar so if I save I'm going to get uh an error here just
I'm going to get uh an error here just make sure you refresh your Local Host
make sure you refresh your Local Host every time you shut down the app and
every time you shut down the app and rerun it again uh so there we go now you
rerun it again uh so there we go now you can see that I have an error so let's go
can see that I have an error so let's go ahead inside of the dashboard components
ahead inside of the dashboard components right here where we have the nov bar so
right here where we have the nov bar so why here why not create uh components
why here why not create uh components inside of this folder if that's where we
inside of this folder if that's where we use it well because we're going to reuse
use it well because we're going to reuse it in the nov bar as well in a different
it in the nov bar as well in a different component called mobile uh sidebar you
component called mobile uh sidebar you can see that I have a comment here for
can see that I have a comment here for mobile sidebar but we're going to get
mobile sidebar but we're going to get onto that later so for now find the
onto that later so for now find the dashboard folder find the underscore
dashboard folder find the underscore components where you have the navbar and
components where you have the navbar and in here create sidebar TSX let's go
in here create sidebar TSX let's go ahead and Mark this as use client and
ahead and Mark this as use client and let's export con
let's export con sidebar and just return a div say
sidebar and just return a div say sidebar great and now we can head back
sidebar great and now we can head back into this organization layout where we
into this organization layout where we have an error for importing sidebar and
have an error for importing sidebar and we can safely now add it not from Lucid
we can safely now add it not from Lucid react but from doore components sidebar
react but from doore components sidebar great if you save you should now no
great if you save you should now no longer have an error and when you expand
longer have an error and when you expand you should have a little text which says
you should have a little text which says sidebar here which is only visible on
sidebar here which is only visible on desktop devices so if you collapse like
desktop devices so if you collapse like this it's not going to be visible all
this it's not going to be visible all right so now let's go ahead uh and let's
right so now let's go ahead uh and let's uh start developing this so first I want
uh start developing this so first I want to create an interface sidebar props
to create an interface sidebar props which is going to hold the storage key
which is going to hold the storage key and here I'm going to explain why we're
and here I'm going to explain why we're going to need the storage key once we
going to need the storage key once we actually use the accordion component so
actually use the accordion component so let's go ahead and extract that storage
let's go ahead and extract that storage key so just assign the props sidebar
key so just assign the props sidebar props here and let's extract the storage
props here and let's extract the storage key all right so storage key is going to
key all right so storage key is going to be used inside of our accordion that's
be used inside of our accordion that's because the accordion can be expanded or
because the accordion can be expanded or collapsed and it can get pretty annoying
collapsed and it can get pretty annoying the fact that accordion uh collapses
the fact that accordion uh collapses itself after a rerender right so if we
itself after a rerender right so if we change a specific um Oran active
change a specific um Oran active organization all the sidebar items are
organization all the sidebar items are going to get collapsed so that's why
going to get collapsed so that's why we're going to have this storage key and
we're going to have this storage key and we're going to use local storage to keep
we're going to use local storage to keep track of what was opened and what wasn't
track of what was opened and what wasn't so let's go ahead and add all the
so let's go ahead and add all the necessary Imports we're going to need to
necessary Imports we're going to need to create this component so we're going to
create this component so we're going to need some link component from next SL
need some link component from next SL link we're going to need plus icon from
link we're going to need plus icon from Lucid react we're going to need use
Lucid react we're going to need use local storage from use hooks TS which we
local storage from use hooks TS which we just installed and we're going to need
just installed and we're going to need use organization and use organization
use organization and use organization list from Clerk
list from Clerk nextjs and then let's go ahead and let's
nextjs and then let's go ahead and let's import the button from add/ components
import the button from add/ components UI button let's go ahead and let's
UI button let's go ahead and let's import the separator if we have it we
import the separator if we have it we don't have separator so I forgot about
don't have separator so I forgot about that let's head back inside of our
that let's head back inside of our terminal I'm just going to open a new
terminal I'm just going to open a new one here and write npx shat cn- UI at
one here and write npx shat cn- UI at latest add separator whoops let me just
latest add separator whoops let me just delete all of this and write it myself
delete all of this and write it myself separator like that so just make sure
separator like that so just make sure you have this component installed great
you have this component installed great make sure you have your project running
make sure you have your project running and now let's try again import separator
and now let's try again import separator make sure you don't import it from from
make sure you don't import it from from radx make sure you always import from
radx make sure you always import from at/ components whenever radx is offered
at/ components whenever radx is offered in your Imports make you you did
in your Imports make you you did something wrong right so make sure you
something wrong right so make sure you never do that let's go ahead and let's
never do that let's go ahead and let's import skeleton from components UI
import skeleton from components UI skeleton and let's import our
skeleton and let's import our accordion again you can see I have
accordion again you can see I have imports from radics and I have imports
imports from radics and I have imports from at/ components so always make sure
from at/ components so always make sure you're using the components not radics
you're using the components not radics because you're not going to easily know
because you're not going to easily know notice what's wrong right there not
notice what's wrong right there not going to be any errors in your code
going to be any errors in your code you're just going to notice that your
you're just going to notice that your component is unstyled and it doesn't
component is unstyled and it doesn't work as it should and 99% of the case is
work as it should and 99% of the case is because you imported something from
because you imported something from radics great perfect so now we have this
radics great perfect so now we have this and let's go ahead now uh and let's add
and let's go ahead now uh and let's add all the hooks we're going to need and
all the hooks we're going to need and all the other stuff so first let's uh
all the other stuff so first let's uh create a state which is going to be
create a state which is going to be connected to our local storage which is
connected to our local storage which is going to keep track of which accordion
going to keep track of which accordion is is expanded and which one is not so
is is expanded and which one is not so const expanded set expanded and this
const expanded set expanded and this used local storage works very similar to
used local storage works very similar to use state so you can see that we have
use state so you can see that we have the expanded set expanded and now we're
the expanded set expanded and now we're going to add use local storage here like
going to add use local storage here like that and let's pass in the storage key
that and let's pass in the storage key and let's pass in the default object
and let's pass in the default object which is an empty object and now let's
which is an empty object and now let's go ahead and just give a default value
go ahead and just give a default value to this storage key in case we don't
to this storage key in case we don't pass it to the sidebar so in this case
pass it to the sidebar so in this case I'm going to use the letter T to
I'm going to use the letter T to indicate that this is our you know
indicate that this is our you know Trello project T sidebar state so why am
Trello project T sidebar state so why am I even using this as a prop right why
I even using this as a prop right why not just hard code that here well as I
not just hard code that here well as I said we're going to reuse the sidebar in
said we're going to reuse the sidebar in our uh drawer for mobile for mobile
our uh drawer for mobile for mobile devices and it can get pretty
devices and it can get pretty inconsistent if you use the same storage
inconsistent if you use the same storage key for both so we're going to going to
key for both so we're going to going to have something like T Mobile sidebar
have something like T Mobile sidebar State once we create our mobile sidebar
State once we create our mobile sidebar so for now just pass in this and now
so for now just pass in this and now what I want to do is give this uh local
what I want to do is give this uh local storage a defined type of what to expect
storage a defined type of what to expect inside so that's going to be an object
inside so that's going to be an object so record with string and any like that
so record with string and any like that perfect so now that we have that let's
perfect so now that we have that let's go ahead and let's get the current
go ahead and let's get the current active organization so const
active organization so const organization is going to be an alias
organization is going to be an alias we're going to Alias it to active
we're going to Alias it to active organization so if you don't know when
organization so if you don't know when working with objects when you the
working with objects when you the structure things uh by default if I just
structure things uh by default if I just did whoops so if I just did this then we
did whoops so if I just did this then we access that organization right if I want
access that organization right if I want to I can render the organization like
to I can render the organization like this right but you can also do this like
this right but you can also do this like active organization or whatever you want
active organization or whatever you want you can rename it to whatever you want
you can rename it to whatever you want and then you would use active
and then you would use active organization in your component so that's
organization in your component so that's what the ls does and let's get the
what the ls does and let's get the active organization from use
active organization from use organization and while we uh are here
organization and while we uh are here let's also extract the is loaded prop so
let's also extract the is loaded prop so is loaded and we're going to remap that
is loaded and we're going to remap that to is loaded organization but org for
to is loaded organization but org for short and now below this
short and now below this constant let's also add another Hook
constant let's also add another Hook from the organization list so const and
from the organization list so const and we're going to go ahead and extract user
we're going to go ahead and extract user memberships is loaded which we are going
memberships is loaded which we are going to remap to is loaded organization list
to remap to is loaded organization list and we're going to get that from use
and we're going to get that from use organization
organization list perfect and let me just
list perfect and let me just uh de indent this all right and now
uh de indent this all right and now inside of this organization list I'm
inside of this organization list I'm going to write user
going to write user memberships infinite true so if you're
memberships infinite true so if you're wondering how do I know that we have to
wondering how do I know that we have to write this infinite true very simply
write this infinite true very simply this is from the clerk nextjs
this is from the clerk nextjs documentation right so this isn't
documentation right so this isn't something anyone would expect you to
something anyone would expect you to know right you have simply have to take
know right you have simply have to take a look at their organization hooks uh
a look at their organization hooks uh documentation and from there you can see
documentation and from there you can see exactly uh why we need it and what it
exactly uh why we need it and what it does basically it's kind of a
does basically it's kind of a imagination thing all right so now that
imagination thing all right so now that we have this let's create a constant
we have this let's create a constant called default accordion value so const
called default accordion value so const default accordion value is going to
default accordion value is going to return an array of string and we're
return an array of string and we're going to call object. Keys over the
going to call object. Keys over the current
current expanded uh constant so this one right
expanded uh constant so this one right here so object Keys expanded and we're
here so object Keys expanded and we're going to call reduce we're going to get
going to call reduce we're going to get the accumulator which is going to be a
the accumulator which is going to be a type of array of strings and we're also
type of array of strings and we're also going to have a key which is going to be
going to have a key which is going to be a string itself and then we're going to
a string itself and then we're going to open an object and if
open an object and if expanded uh if the current key is
expanded uh if the current key is expanded Ed in that case we're going to
expanded Ed in that case we're going to push that to the array so key like that
push that to the array so key like that and let's go ahead and return the
and let's go ahead and return the accumulator and let's give it a default
accumulator and let's give it a default value of an empty array like that
value of an empty array like that perfect uh now that we have the default
perfect uh now that we have the default accordion value let's go ahead and let's
accordion value let's go ahead and let's add a function which is going to
add a function which is going to actually add something to the expanded
actually add something to the expanded list when we click on it so const on
list when we click on it so const on expand is going to take in the ID of the
expand is going to take in the ID of the organization which is a string and all
organization which is a string and all all all we're going to do is set
all all we're going to do is set expanded get the current
expanded get the current value open up an immediate object spread
value open up an immediate object spread the current value and then just add that
the current value and then just add that ID and we're going to make it the
ID and we're going to make it the opposite of what it currently is so
opposite of what it currently is so expanded ID so if it was true it's going
expanded ID so if it was true it's going to be false if it was false it's going
to be false if it was false it's going to be true great and now let's go ahead
to be true great and now let's go ahead head and let's create our loading state
head and let's create our loading state so if we have not loaded organization or
so if we have not loaded organization or if we have not loaded organization list
if we have not loaded organization list or if user
or if user memberships that is
memberships that is loading uh sorry this is not uh this one
loading uh sorry this is not uh this one we write without this uh exclamation
we write without this uh exclamation point like that so if either of those is
point like that so if either of those is loading in that case let's go ahead and
loading in that case let's go ahead and let's return for now we can just write a
let's return for now we can just write a skeleton it doesn't matter we imported
skeleton it doesn't matter we imported it uh later we're going to actually
it uh later we're going to actually style it to look something to look like
style it to look something to look like something uh great so let's just go
something uh great so let's just go ahead and refresh to see if uh we are
ahead and refresh to see if uh we are seeing anything great you can see how
seeing anything great you can see how for a second nothing is rendered because
for a second nothing is rendered because we have a little skeleton but I mean
we have a little skeleton but I mean it's not really visible yet but don't
it's not really visible yet but don't worry we're going to go ahead uh and
worry we're going to go ahead uh and style it later all I want to ensure is
style it later all I want to ensure is that for a second our sidebar is loading
that for a second our sidebar is loading great and now let's go ahead and let's
great and now let's go ahead and let's actually style uh this div here
actually style uh this div here so first off I want to wrap this entire
so first off I want to wrap this entire thing inside of a
thing inside of a fragment and then I want to go inside of
fragment and then I want to go inside of the div and give it a class name of font
the div and give it a class name of font medium text extra small Flex items
medium text extra small Flex items Center and margin bottom of one then I'm
Center and margin bottom of one then I'm going to write a span here which is
going to write a span here which is going to say
going to say workspaces and the class name
workspaces and the class name PL whoops
PL whoops pl-4 and then let's add our button
pl-4 and then let's add our button component which we imported and inside
component which we imported and inside add a link which we imported as well
add a link which we imported as well let's give this link an hre of/ select
let's give this link an hre of/ select dorg and inside we're going to render
dorg and inside we're going to render our plus icon from Lucid react all of
our plus icon from Lucid react all of the things basically which we imported
the things basically which we imported uh here great all right and let's give
uh here great all right and let's give this plus icon a class name of
this plus icon a class name of h-4 and
h-4 and W-4 and then let's give this button an S
W-4 and then let's give this button an S ch child prop a type of button a size of
ch child prop a type of button a size of Icon a variant of ghost and a class name
Icon a variant of ghost and a class name of ml AO so is push so it is pushed to
of ml AO so is push so it is pushed to the right uh great and now let's go
the right uh great and now let's go ahead uh and see if we can already uh
ahead uh and see if we can already uh take a look at that we can there we go
take a look at that we can there we go so now we have our workspaces text and
so now we have our workspaces text and when I click here I'm redirected to
when I click here I'm redirected to create a new organization or select one
create a new organization or select one perfect so now let's go ahead and render
perfect so now let's go ahead and render our organizations here so go outside of
our organizations here so go outside of this div which renders this workspace
this div which renders this workspace and a button so right at the end of our
and a button so right at the end of our fragment and in here go ahead and add
fragment and in here go ahead and add the accordion component which we already
the accordion component which we already imported right here again make sure you
imported right here again make sure you don't actually accidentally use the
don't actually accidentally use the radic one and let's go ahead and give it
radic one and let's go ahead and give it a type of multiple so multiple accordion
a type of multiple so multiple accordion can be active at the same time let's
can be active at the same time let's give it a default value of default
give it a default value of default accordian value so yes I kind of forgot
accordian value so yes I kind of forgot to explain what we're actually doing
to explain what we're actually doing here so the values the way we keep
here so the values the way we keep values in here in this expanded is not
values in here in this expanded is not exactly compatible to the way default
exactly compatible to the way default values are expected inside of the
values are expected inside of the accordian component so that's why we
accordian component so that's why we create this little uh constant here
create this little uh constant here default accordion value to reduce over
default accordion value to reduce over that expanded object which we have you
that expanded object which we have you can see it's an object right and this
can see it's an object right and this object is going to look like
object is going to look like uh something like let me just write here
uh something like let me just write here something like I don't know basically my
something like I don't know basically my organization ID true or false so this is
organization ID true or false so this is what that object looks like right so
what that object looks like right so basically what we're doing here is we're
basically what we're doing here is we're iterating over that entire object and
iterating over that entire object and we're creating an array of active IDs so
we're creating an array of active IDs so we are turning them from that object let
we are turning them from that object let me write a comment here so we are
me write a comment here so we are turning it from this object which was 1
turning it from this object which was 1 2 3 true
2 3 true into an array which just going going to
into an array which just going going to hold 1 through three so that's what this
hold 1 through three so that's what this default accordian value does and the
default accordian value does and the reason again why we need that is because
reason again why we need that is because that is what this uh accordian component
that is what this uh accordian component expects in the default value you can see
expects in the default value you can see when I hover it expects a string of an
when I hover it expects a string of an array of strings or undefined completely
array of strings or undefined completely great and let's give it a class name of
great and let's give it a class name of space Y 2 perfect and then inside let's
space Y 2 perfect and then inside let's go ahead and let's do user memberships
go ahead and let's do user memberships do dat. map and let's immediately
do dat. map and let's immediately destructure the individual organization
destructure the individual organization from here and let's go ahead and for now
from here and let's go ahead and for now let's add a paragraph
let's add a paragraph organization do uh ID like that and give
organization do uh ID like that and give it a key of
it a key of organization. ID and let's see if we can
organization. ID and let's see if we can now take a look there we go you can see
now take a look there we go you can see because we have two organizations we are
because we have two organizations we are rendering two or organization ID here
rendering two or organization ID here perfect so now we're going to create a
perfect so now we're going to create a component called nav item which is
component called nav item which is actually going to render that uh
actually going to render that uh organization in a nicer way with a
organization in a nicer way with a little image and some additional
little image and some additional actions so that component is going to be
actions so that component is going to be called nav item so let's go ahead and
called nav item so let's go ahead and return this paragraph So we remove that
return this paragraph So we remove that paragraph and instead render the nav
paragraph and instead render the nav item component which we don't have yet
item component which we don't have yet but we're going to have in a moment
but we're going to have in a moment let's give it a key of
let's give it a key of organization. ID let's give it an
organization. ID let's give it an isactive prop to be if active
isactive prop to be if active organization. ID is equal to current
organization. ID is equal to current organization in this array ID let's go
organization in this array ID let's go ahead and give it is expanded prop to be
ahead and give it is expanded prop to be if we have this organization ID inside
if we have this organization ID inside of the expanded uh object so
of the expanded uh object so organization.
organization. ID and let's also pass in the entire
ID and let's also pass in the entire organization
organization organization and let's add on expand to
organization and let's add on expand to be on expand so we should have all of
be on expand so we should have all of this props here great and now let's go
this props here great and now let's go inside of this underscore components
inside of this underscore components folder where we have navbar and the
folder where we have navbar and the sidebar itself and create a nav D item.
sidebar itself and create a nav D item. TSX perfect so let's mark this as use
TSX perfect so let's mark this as use client as well and let's go ahead and
client as well and let's go ahead and Export con naap
Export con naap item and return a div nav
item and return a div nav item and then we can go back inside of
item and then we can go back inside of the sidebar and we can import the nav
the sidebar and we can import the nav item from slnv item and I'm going to uh
item from slnv item and I'm going to uh separate this like that perfect let's
separate this like that perfect let's head back inside of the nav item and
head back inside of the nav item and let's go ahead and give it some props so
let's go ahead and give it some props so interface nav item props is going to
interface nav item props is going to have an is expanded which is a Boolean
have an is expanded which is a Boolean is
is active which is a Boolean as well it's
active which is a Boolean as well it's going to have
going to have organization which for now let's say any
organization which for now let's say any we're going to create a type for it in a
we're going to create a type for it in a moment let's also add on expand which is
moment let's also add on expand which is a function which accepts an ID which is
a function which accepts an ID which is a string and returns a void and now
a string and returns a void and now let's go ahead and create a type
let's go ahead and create a type organization what's important for us is
organization what's important for us is that it has a ID a slug an image URL and
that it has a ID a slug an image URL and a
a name which is a string
name which is a string and now let's use this organization
and now let's use this organization instead of this any type and let's also
instead of this any type and let's also export type organization so you can we
export type organization so you can we can use it
can use it elsewhere great so now let's go ahead
elsewhere great so now let's go ahead and assign those props so nav item
and assign those props so nav item props and let's extract is expanded is
props and let's extract is expanded is active and organization and on expand
active and organization and on expand great and now head back inside of the
great and now head back inside of the sidebar where we render the nav item and
sidebar where we render the nav item and as you can see we have a little error
as you can see we have a little error here here so let's just fix that by
here here so let's just fix that by adding as organization and import
adding as organization and import organization from /nv item let me show
organization from /nv item let me show you that right here so from nav item I'm
you that right here so from nav item I'm importing the component itself and the
importing the component itself and the organization type which we just uh made
organization type which we just uh made exportable like this there we go and now
exportable like this there we go and now you should no longer have any errors
you should no longer have any errors here perfect so let's go ahead uh and
here perfect so let's go ahead uh and let's style this so instead of a div
let's style this so instead of a div this is actually going to be an
this is actually going to be an accordion item from at/ components UI
accordion item from at/ components UI accordion so let me show you make sure
accordion so let me show you make sure you have this imported again not from
you have this imported again not from radics give it a value of organization.
radics give it a value of organization. ID give it a class name of
ID give it a class name of Border
Border none and then let's go ahead and add
none and then let's go ahead and add accordion
accordion trigger from components uh accordion let
trigger from components uh accordion let me just collapse this ones great so
me just collapse this ones great so accordion trigger
accordion trigger and
and inside let's go ahead and give this un
inside let's go ahead and give this un onclick which is an arrow function which
onclick which is an arrow function which calls on expand and uh passes in the
calls on expand and uh passes in the organization ID let's also go ahead and
organization ID let's also go ahead and give it a class name and this is going
give it a class name and this is going to be dynamic so make sure you open
to be dynamic so make sure you open curly brackets and import the CN Library
curly brackets and import the CN Library so I imported CN library right here I'm
so I imported CN library right here I'm just going to move it to the top all
just going to move it to the top all right so this CN library is going to
right so this CN library is going to have some default classes which are
have some default classes which are going to be Flex items Center Gap
going to be Flex items Center Gap X2 padding of
X2 padding of 1.5 text- neutral
1.5 text- neutral d700 rounded
d700 rounded MD hover BG D neutral Dash uh 500
MD hover BG D neutral Dash uh 500 sl10 transition text- start no dash
sl10 transition text- start no dash underline so those classes and we're
underline so those classes and we're also going to add an additional hover no
also going to add an additional hover no underline as well great and then go
underline as well great and then go ahead and add a comma and now we're
ahead and add a comma and now we're going to write some Dynamic classes so
going to write some Dynamic classes so if this current accordion which
if this current accordion which represents an organization is active and
represents an organization is active and if it's not expanded in that case we're
if it's not expanded in that case we're going to render an indicator for the
going to render an indicator for the user to know that it is active so BG sky
user to know that it is active so BG sky 500/10 and text Sky 700 100 great and
500/10 and text Sky 700 100 great and then inside of this accordion trigger
then inside of this accordion trigger let's go ahead and let's create a div
let's go ahead and let's create a div let's give it a class
let's give it a class name of flex items Center and GAP
name of flex items Center and GAP X2 let's add a new div with a class name
X2 let's add a new div with a class name of W7 and H7 and relative and inside
of W7 and H7 and relative and inside we're going to render an image component
we're going to render an image component from next SL image so let me show you
from next SL image so let me show you where I imported next SL image and I'm
where I imported next SL image and I'm just moving it to the top here so let's
just moving it to the top here so let's give this image a fill property let's
give this image a fill property let's give it a source of organization. image
give it a source of organization. image URL let's give it an ALT of organization
URL let's give it an ALT of organization and let's give it a class name of
and let's give it a class name of rounded small and object cover and I
rounded small and object cover and I think that
think that already we should be seeing something
already we should be seeing something and as you can see I have a little error
and as you can see I have a little error here so this happens when you add an
here so this happens when you add an image
image uh to the uh next image component but
uh to the uh next image component but you don't modify the uh config as you
you don't modify the uh config as you can see in the error right here so what
can see in the error right here so what you have to do is find what it says for
you have to do is find what it says for the host name it's probably going to be
the host name it's probably going to be exactly the same uh for you as it is for
exactly the same uh for you as it is for me but you know just in case something
me but you know just in case something changes in the future find this image
changes in the future find this image clerk.com and now let's head inside of
clerk.com and now let's head inside of next config to allow nextjs to use
next config to allow nextjs to use images from that source so let's go
images from that source so let's go ahead inside of next. config.js here and
ahead inside of next. config.js here and let's write images remote
let's write images remote patterns and let's open an object so
patterns and let's open an object so protocol is going to be
protocol is going to be https and host
https and host name is going to be image. clerk.com
name is going to be image. clerk.com like that great and now what I recommend
like that great and now what I recommend you do is just restart your application
you do is just restart your application so I'm going to do that mpm runev and
so I'm going to do that mpm runev and then I'm just going to refresh my local
then I'm just going to refresh my local host and while that is refreshing I'm
host and while that is refreshing I'm going to go back inside of my nav item
going to go back inside of my nav item component where I just rendered uh the
component where I just rendered uh the image and let's see there we go you can
image and let's see there we go you can see how nice this looks now we have a
see how nice this looks now we have a little image and you can see how because
little image and you can see how because this organization is our selected
this organization is our selected organization it has a bluish color right
organization it has a bluish color right until we expand it then it doesn't have
until we expand it then it doesn't have a bluish color so that's defined right
a bluish color so that's defined right here in this Dynamic is active and it's
here in this Dynamic is active and it's not expanded and if you're wondering why
not expanded and if you're wondering why does it lose a color why did I make it
does it lose a color why did I make it like that well it's going to make sense
like that well it's going to make sense in a second when you see the actual
in a second when you see the actual content which is going to be inside of
content which is going to be inside of what's expanded perfect so let's just
what's expanded perfect so let's just try and switch to another organization
try and switch to another organization and there we go you can see now the top
and there we go you can see now the top one is bluish perfect exactly uh what we
one is bluish perfect exactly uh what we want but besides rendering the image
want but besides rendering the image we're also going to render the actual
we're also going to render the actual organization name so outside of the div
organization name so outside of the div which is wrapping an image add a span
which is wrapping an image add a span which is to render the organization name
which is to render the organization name and give this a class
and give this a class name uh which is going to be font D
name uh which is going to be font D medium and text small great let's see
medium and text small great let's see that there we go you can see how nice
that there we go you can see how nice this looks now perfect so what I want to
this looks now perfect so what I want to do now is create the actual uh uh
do now is create the actual uh uh accordion content but in order to do
accordion content but in order to do that I first want to create uh an actual
that I first want to create uh an actual routes array for that so let's go ahead
routes array for that so let's go ahead and first let's import all the icons
and first let's import all the icons which we're going to need from Lucid
which we're going to need from Lucid react for that so go ahead and import
react for that so go ahead and import from Lucid react the following icons
from Lucid react the following icons activity we're going to need the credit
activity we're going to need the credit card we're going to need layout and
card we're going to need layout and settings so those are the icons which we
settings so those are the icons which we are going to use for our content and now
are going to use for our content and now let's go inside of the nav item before
let's go inside of the nav item before we return this and in here let's go
we return this and in here let's go ahead and let's write const routes to be
ahead and let's write const routes to be an array of objects and each object is
an array of objects and each object is going to have a label first one is going
going to have a label first one is going to be boards with an icon of layout and
to be boards with an icon of layout and it's going to have a class name of
it's going to have a class name of H4 h-4 W-4 and Mr of two and it's going
H4 h-4 W-4 and Mr of two and it's going to have an HRA and open btic for this
to have an HRA and open btic for this one and also let's add a comma here so
one and also let's add a comma here so we don't have that error and the hre is
we don't have that error and the hre is is going to go to slash organization
is going to go to slash organization slash specific organization
slash specific organization whoops ID so basically the default page
whoops ID so basically the default page and then let's go ahead and just copy
and then let's go ahead and just copy this three items here the second objects
this three items here the second objects label is going to be activity and the
label is going to be activity and the icon we're going to use is
icon we're going to use is activity and it's going to go to
activity and it's going to go to organization organization id/
organization organization id/ activity great and let's go ahead and
activity great and let's go ahead and copy and paste
copy and paste this the third one is going to be
this the third one is going to be settings it's going to use the icon
settings it's going to use the icon settings and it's going to go to slash
settings and it's going to go to slash settings so this part pretty much always
settings so this part pretty much always stays the same uh let's copy this
stays the same uh let's copy this again and this one is going to say
again and this one is going to say billing the icon we're going to use is
billing the icon we're going to use is credit card and it's going to go to
credit card and it's going to go to slash billing great and while we are
slash billing great and while we are here let's also go ahead and import our
here let's also go ahead and import our router and our path name here so const
router and our path name here so const router from use router from next
router from use router from next navigation so let me just show you that
navigation so let me just show you that quickly there we go use router uh from
quickly there we go use router uh from next SL
next SL navigation make sure you add that also
navigation make sure you add that also just a quick tip sometimes if you use
just a quick tip sometimes if you use like this Auto Imports it can happen
like this Auto Imports it can happen that it Imports router from next SL
that it Imports router from next SL router and you can see that there's no
router and you can see that there's no error here right but you can no longer
error here right but you can no longer use this uh if you're using the app
use this uh if you're using the app router you have to use next SL
router you have to use next SL navigation so whenever you're using use
navigation so whenever you're using use router if you have some weird errors and
router if you have some weird errors and nothing is read in the code uh just make
nothing is read in the code uh just make sure that your use router is coming from
sure that your use router is coming from next SL navigation and besides use
next SL navigation and besides use router let's immediately import use path
router let's immediately import use path name and now let's go ahead and add path
name and now let's go ahead and add path name use path name great and now let's
name use path name great and now let's go ahead and let's add our const on
go ahead and let's add our const on click to accept an H rev which is a
click to accept an H rev which is a string and router. push that very same
string and router. push that very same hre great and now we can finally go
hre great and now we can finally go inside of uh outside of accordion
inside of uh outside of accordion trigger and render the accordion content
trigger and render the accordion content again from add/ components UI accordion
again from add/ components UI accordion so I added it right here make sure you
so I added it right here make sure you have it as well and let's go ahead and
have it as well and let's go ahead and give this accordion content a class name
give this accordion content a class name of padding top one text- neutral
of padding top one text- neutral d700 like that and then let's go ahead
d700 like that and then let's go ahead and render rout uh routes. map get the
and render rout uh routes. map get the individual route and go ahead and render
individual route and go ahead and render a button component from add/ components
a button component from add/ components UI button let me just show you how I
UI button let me just show you how I imported that right here all right and
imported that right here all right and let's go ahead and give this button a
let's go ahead and give this button a key of route. label or maybe hre would
key of route. label or maybe hre would be better a size is going to be smaller
be better a size is going to be smaller on click is going to be an arrow
on click is going to be an arrow function which calls the on click and
function which calls the on click and passes in the route
passes in the route hre and class name is going to be
hre and class name is going to be dynamic so the default ones are going to
dynamic so the default ones are going to be W full F normal justify start pl1 and
be W full F normal justify start pl1 and margin bottom of one and then we're
margin bottom of one and then we're going to have if path name is equal to
going to have if path name is equal to route.
route. hre then we're going to make it
hre then we're going to make it bluish so BG sky 500/10 and text Sky 700
bluish so BG sky 500/10 and text Sky 700 great and let's give it a variant of
great and let's give it a variant of ghost and now finally inside of this
ghost and now finally inside of this button we can just render route. icon so
button we can just render route. icon so route. icon don't accidentally do router
route. icon don't accidentally do router right I I wrote that a couple of times
right I I wrote that a couple of times so route.
so route. label great and let's test this out now
label great and let's test this out now there we go and as you can see when I
there we go and as you can see when I expand now it makes sense right this is
expand now it makes sense right this is why when I expand this loses the color
why when I expand this loses the color because this one is active right trust
because this one is active right trust me if both of them are blue it just
me if both of them are blue it just looks weird so this looks much better
looks weird so this looks much better perfect and here's the cool thing when I
perfect and here's the cool thing when I refresh you can see that because we use
refresh you can see that because we use the use local storage uh this accordion
the use local storage uh this accordion stays open so the user doesn't have to
stays open so the user doesn't have to always manually expand that especially
always manually expand that especially if they're going to have a bunch of
if they're going to have a bunch of workspaces and perhaps organized exactly
workspaces and perhaps organized exactly you know the ones they want to see open
you know the ones they want to see open and if you try and click on this you're
and if you try and click on this you're going to get a 404 which is good because
going to get a 404 which is good because it's working so just check in the URL
it's working so just check in the URL and you can see we go to slash settings
and you can see we go to slash settings here you know check the billing you can
here you know check the billing you can see we go to slash billing and if you
see we go to slash billing and if you click on the boards itself nothing is
click on the boards itself nothing is going to change but if you click on the
going to change but if you click on the boards inside of another organization
boards inside of another organization you can see that it's switches to that
you can see that it's switches to that organization so take a look at my navb
organization so take a look at my navb bar now it says test here and this is
bar now it says test here and this is blue color but when I click on boards
blue color but when I click on boards here now this is blue color and this has
here now this is blue color and this has changed to that workspace great so you
changed to that workspace great so you finished this little sidebar here so now
finished this little sidebar here so now what I want to create is a way to open
what I want to create is a way to open that sidebar if we are on mobile because
that sidebar if we are on mobile because right now there's no way well first of
right now there's no way well first of all there's no space to render that
all there's no space to render that sidebar technically we could like render
sidebar technically we could like render it on the top and then display the
it on the top and then display the content on the bottom but I really don't
content on the bottom but I really don't like that solution so what we're going
like that solution so what we're going to do is we're going to head back inside
to do is we're going to head back inside of our app folder platform dashboard
of our app folder platform dashboard components here in the nov bar you can
components here in the nov bar you can see I left a little to do here to create
see I left a little to do here to create a mobile sidebar so if you don't have
a mobile sidebar so if you don't have this commment to help you find that
this commment to help you find that first of all go inside of this Novar
first of all go inside of this Novar component and find this logo component
component and find this logo component and outside of these two divs so just
and outside of these two divs so just just the first line inside of your nav
just the first line inside of your nav element that's where we're going to
element that's where we're going to render the mobile sidebar which we're
render the mobile sidebar which we're going to create now first thing I want
going to create now first thing I want to create is a hook which is going to
to create is a hook which is going to control the state of that mobile sidebar
control the state of that mobile sidebar so whether it's open whether it's closed
so whether it's open whether it's closed and also we're going to use it so that
and also we're going to use it so that when we click on a specific board in the
when we click on a specific board in the sidebar the whole drawer or the sidebar
sidebar the whole drawer or the sidebar whatever you want to call it closes
whatever you want to call it closes because in in my previous tutorials some
because in in my previous tutorials some people told me that when I create these
people told me that when I create these mobile sidebars there it's not exactly
mobile sidebars there it's not exactly the best user experience because it kind
the best user experience because it kind of stays open after you change route so
of stays open after you change route so that's why this time uh I made an
that's why this time uh I made an attempt to improve that of course there
attempt to improve that of course there are many ways you can do that but you
are many ways you can do that but you know in the specific way we're doing it
know in the specific way we're doing it I kind of feel like we have the most
I kind of feel like we have the most options by using a state to control that
options by using a state to control that there are of course like um what's the
there are of course like um what's the name proper components to do that from
name proper components to do that from shaten but it simply kind of feels like
shaten but it simply kind of feels like too much work in spaghetti code to do it
too much work in spaghetti code to do it I don't know uh you can of course try it
I don't know uh you can of course try it yourself so this is what we're going to
yourself so this is what we're going to do we're going to go inside of the
do we're going to go inside of the terminal I'm just going to close it and
terminal I'm just going to close it and I'm going to write uh mpm
I'm going to write uh mpm install to St that so make sure you have
install to St that so make sure you have that installed and we're also going to
that installed and we're also going to need a sheet component so let's go ahead
need a sheet component so let's go ahead uh and let's add npx shat CN UI latest
uh and let's add npx shat CN UI latest ad sheet like this make sure you have uh
ad sheet like this make sure you have uh that package and this and then you can
that package and this and then you can mpm run Dev again great so let's go
mpm run Dev again great so let's go ahead and let's create a new folder
ahead and let's create a new folder called Hooks and inside of this folder
called Hooks and inside of this folder create a new file use- mobile Das
create a new file use- mobile Das sidebar. TS like that and let's import
sidebar. TS like that and let's import create from two so the package which we
create from two so the package which we just installed let's create a type for
just installed let's create a type for this so mobile sidebar store is going to
this so mobile sidebar store is going to have a property is open which is a
have a property is open which is a Boolean it's going to have a function on
Boolean it's going to have a function on open which is a very simple function and
open which is a very simple function and the same goes for on close and then
the same goes for on close and then export const use mobile sidebar is going
export const use mobile sidebar is going to be create and let's give it a type of
to be create and let's give it a type of mobile sidebar store let's extract the
mobile sidebar store let's extract the set and then immediately return an
set and then immediately return an object like that so make sure you wrap
object like that so make sure you wrap it inside of additional parentheses
it inside of additional parentheses right if you've never seen this syntax
right if you've never seen this syntax let me just briefly explain if you do
let me just briefly explain if you do this this opens uh a function so you
this this opens uh a function so you would have to do return and then an
would have to do return and then an object right but if you want to skip
object right but if you want to skip that you can just open parenthesis and
that you can just open parenthesis and then an object and this tells uh
then an object and this tells uh JavaScript you know okay so this is
JavaScript you know okay so this is immediately returning an object and
immediately returning an object and let's add is open to be false let's add
let's add is open to be false let's add unopen to be a
unopen to be a function which calls the set which we
function which calls the set which we extracted from the props here and it's
extracted from the props here and it's going to set is open to be true and we
going to set is open to be true and we need on close and then our errors are
need on close and then our errors are going to go away and we need is open to
going to go away and we need is open to be false here great so we have that and
be false here great so we have that and now let's go back inside of our navbar
now let's go back inside of our navbar components so app platform dashboard
components so app platform dashboard components navbar and in here I'm
components navbar and in here I'm removing this comment again so uh the
removing this comment again so uh the first item inside of your nav element
first item inside of your nav element that's where it should be let's add the
that's where it should be let's add the mobile
mobile sidebar and if we save of course we're
sidebar and if we save of course we're going to get a little error as soon as
going to get a little error as soon as my Local Host refreshes so let's go
my Local Host refreshes so let's go inside of the underscore components so
inside of the underscore components so where all of our new items are and let's
where all of our new items are and let's add mobile sidebar
add mobile sidebar DSX let's mark it as use client and
DSX let's mark it as use client and Export con
Export con mobile
mobile sidebar and let's return a div saying
sidebar and let's return a div saying mobile
mobile sidebar and now we can go back inside of
sidebar and now we can go back inside of the Novar and we can now safely import
the Novar and we can now safely import mobile sidebar
mobile sidebar great so let's go back inside of the
great so let's go back inside of the mobile sidebar now and first thing I
mobile sidebar now and first thing I want to do is I want to add uh that uh
want to do is I want to add uh that uh hook so first we're going to get extract
hook so first we're going to get extract uh on open which is going to be use a
uh on open which is going to be use a mobile sidebar we're going to get the
mobile sidebar we're going to get the state and we're directly going to add
state and we're directly going to add state is open sorry on open we're going
state is open sorry on open we're going to copy this two more times the second
to copy this two more times the second one is going to be on close and the last
one is going to be on close and the last one is going to be is open there we go
one is going to be is open there we go so we have the three states here perfect
so we have the three states here perfect and now let's go ahead and let's add the
and now let's go ahead and let's add the path name use path name from next SL
path name use path name from next SL navigation and let's also add is mounted
navigation and let's also add is mounted and set is mounted to be controlled from
and set is mounted to be controlled from use state so this is going to help us uh
use state so this is going to help us uh by to prevent hydration errors
by to prevent hydration errors especially when working with suant
especially when working with suant States and uh components like models or
States and uh components like models or sheet
sheet components uh so let let me just align
components uh so let let me just align my imports a bit so these are the global
my imports a bit so these are the global ones great all right so now let's finish
ones great all right so now let's finish doing this is mounted trick here so I'm
doing this is mounted trick here so I'm going to call use
going to call use effect and I'm going to set is
effect and I'm going to set is mounted to be
mounted to be true and then I'm going to write if it's
true and then I'm going to write if it's not mounted just return null so why am I
not mounted just return null so why am I doing this so in next uh JS even if you
doing this so in next uh JS even if you mark something as used client that
mark something as used client that doesn't exactly mean that it's not going
doesn't exactly mean that it's not going to be server side rendered right there
to be server side rendered right there is one I mean you can correct me if I
is one I mean you can correct me if I wrong but this is how I understood it
wrong but this is how I understood it and why these hydration errors even
and why these hydration errors even happen when using used client components
happen when using used client components so as far as I understand each component
so as far as I understand each component even if it is in Ed client is going to
even if it is in Ed client is going to be server side rendered at least the
be server side rendered at least the first iteration of it right so what
first iteration of it right so what happens when you're using this models
happens when you're using this models and sheets and dis type type of
and sheets and dis type type of components is that on the server side it
components is that on the server side it has a specific State like closed right
has a specific State like closed right and then on the client side it suddenly
and then on the client side it suddenly opened and that creates a hydration
opened and that creates a hydration error right and it just doesn't look
error right and it just doesn't look nice and it's very confusing to you why
nice and it's very confusing to you why is it happening and you know how to fix
is it happening and you know how to fix it so how does using this use state is
it so how does using this use state is mounted and this use effect actually
mounted and this use effect actually help well one thing that you can do to
help well one thing that you can do to guarantee that a specific component is
guarantee that a specific component is only going to be rendered on the client
only going to be rendered on the client so never on the server not even serers
so never on the server not even serers side rendered is by using use effect
side rendered is by using use effect because use effect will not run in that
because use effect will not run in that server side rendering iteration so
server side rendering iteration so basically what we are telling this
basically what we are telling this component is if this is mounted has not
component is if this is mounted has not changed to true meaning that if we have
changed to true meaning that if we have never reached this initial use effect we
never reached this initial use effect we don't render anything because obviously
don't render anything because obviously this is still uh running on the server
this is still uh running on the server right but then when it reaches this use
right but then when it reaches this use effect it changes it to true and then we
effect it changes it to true and then we skip this and just render our component
skip this and just render our component so I hope this kind of cleared it up you
so I hope this kind of cleared it up you can also Google a very good blog post uh
can also Google a very good blog post uh called The Perils of hydration and
called The Perils of hydration and inside of there you can see like an
inside of there you can see like an in-depth explanation of what I just said
in-depth explanation of what I just said with probably you know much better way
with probably you know much better way of explaining than I I just did great so
of explaining than I I just did great so I hope that kind of
I hope that kind of cleared it up all right and now let's go
cleared it up all right and now let's go ahead and let's add um a new use effect
ahead and let's add um a new use effect here which is going to be called every
here which is going to be called every time a path name
time a path name changes and let's also add on close here
changes and let's also add on close here so we can call it properly and very
so we can call it properly and very simply we're just going to call on close
simply we're just going to call on close basically what this means is that
basically what this means is that whenever our URL changes mobile sidebar
whenever our URL changes mobile sidebar will close so we're going to use that
will close so we're going to use that that when we click click on a specific
that when we click click on a specific item in a sidebar the router is going to
item in a sidebar the router is going to push the URL is going to change and our
push the URL is going to change and our sidebar is going to be closed so this
sidebar is going to be closed so this with this way we don't have to manually
with this way we don't have to manually call on close every single time great so
call on close every single time great so now that we have all of those things
now that we have all of those things let's go ahead and change this div into
let's go ahead and change this div into a fragment let's remove this text and
a fragment let's remove this text and let's render a button component uh from
let's render a button component uh from components UI button and inside I want
components UI button and inside I want to render the menu icon from Lucid react
to render the menu icon from Lucid react so make sure you have uh that imported
so make sure you have uh that imported let's give this menu a class name of H4
let's give this menu a class name of H4 nw4 give this button an on click to be
nw4 give this button an on click to be on open let's give this class name a
on open let's give this class name a block and MD hidden so this is only
block and MD hidden so this is only visible on mobile devices right when it
visible on mobile devices right when it reaches a medium screen it's immediately
reaches a medium screen it's immediately hidden because on medium screens we have
hidden because on medium screens we have that big sidebar here on the side so no
that big sidebar here on the side so no need for this additional one uh and
need for this additional one uh and let's go ahead and give it a variant of
let's go ahead and give it a variant of of Ghost and the size small great and
of Ghost and the size small great and then below this button let's open up a
then below this button let's open up a sheet component from component UI right
sheet component from component UI right here so again make sure you don't
here so again make sure you don't accidentally import it from radic and
accidentally import it from radic and while we are here let's also add the
while we are here let's also add the content great so let's go ahead now and
content great so let's go ahead now and let's render the
content and inside all I want to render is the sidebar from Dot slidebar so
is the sidebar from Dot slidebar so we're going to reuse that entire
we're going to reuse that entire component and let's go ahead and pass
component and let's go ahead and pass some props and some Styles before we try
some props and some Styles before we try it out so the sidebar needs to have a
it out so the sidebar needs to have a different storage key so T sidebar
different storage key so T sidebar mobile State like that just so it
mobile State like that just so it differs from the desktop State and let's
differs from the desktop State and let's give this sheet content a side of left
give this sheet content a side of left and a class name of padding two and
and a class name of padding two and padding top of 10 and this sheet is
padding top of 10 and this sheet is going to have an open of open sorry is
going to have an open of open sorry is open and on open change is going to be
open and on open change is going to be on close just like that so let's try it
on close just like that so let's try it out so make sure you are on mobile mode
out so make sure you are on mobile mode click here and there we go you can see
click here and there we go you can see how when I click here it changes the
how when I click here it changes the organization and it closes beautiful
organization and it closes beautiful beautiful uh job great so just one quick
beautiful uh job great so just one quick thing I want to do here these two
thing I want to do here these two buttons seem just a bit too close to one
buttons seem just a bit too close to one another so so I'm going to go ahead and
another so so I'm going to go ahead and give this to this one margin right two
give this to this one margin right two there we go that that looks just a tiny
there we go that that looks just a tiny bit better perfect so you've finished
bit better perfect so you've finished the entire thing you can see you can do
the entire thing you can see you can do anything on mobile uh as you can do it
anything on mobile uh as you can do it on desktop as well great great job so
on desktop as well great great job so next thing we're going to do is we're
next thing we're going to do is we're going to create the settings page for
going to create the settings page for each of our
each of our organization so before we go ahead and
organization so before we go ahead and create the settings page P there's one
create the settings page P there's one thing that we haven't quite finished and
thing that we haven't quite finished and that's the loading of this sidebar you
that's the loading of this sidebar you can see how now we just have a blank
can see how now we just have a blank space I completely forgot that we didn't
space I completely forgot that we didn't finish creating our skeleton here so I
finish creating our skeleton here so I want you to go inside of the app folder
want you to go inside of the app folder platform dashboard components and in
platform dashboard components and in here we have our sidebar component and
here we have our sidebar component and if I remember correctly here we have
if I remember correctly here we have this loading state where we just added a
this loading state where we just added a lonely skeleton here so when I'm making
lonely skeleton here so when I'm making skeleton what I try to do is I try to
skeleton what I try to do is I try to replicate the actual content which is
replicate the actual content which is appearing here so what I'm going to do
appearing here so what I'm going to do actually is I'm going to attempt to
actually is I'm going to attempt to recreate this entire structure but
recreate this entire structure but inside of the skeleton so I'm going to
inside of the skeleton so I'm going to go ahead and create this uh first part
go ahead and create this uh first part which is actually just rendering the
which is actually just rendering the workspace and a plus icon right so this
workspace and a plus icon right so this workspaces and a button we know how that
workspaces and a button we know how that looks right so it has this space here
looks right so it has this space here and then this thing on the side so let's
and then this thing on the side so let's go ahead and do that I'm going to go
go ahead and do that I'm going to go ahead here and I'm going to give this
ahead here and I'm going to give this div a class name of flex item Center Gap
div a class name of flex item Center Gap X4 actually we don't need gap X4 in fact
X4 actually we don't need gap X4 in fact we can just uh move them away from each
we can just uh move them away from each other as far as possible using justifi
other as far as possible using justifi between and let's add a margin bottom of
between and let's add a margin bottom of two and then let's give this skeleton a
two and then let's give this skeleton a class name of height 10 and width of 50%
class name of height 10 and width of 50% so that's representing our text saying
so that's representing our text saying workspaces and then below that I'm going
workspaces and then below that I'm going to add another skeleton with the class
to add another skeleton with the class name of h10 and with a width of 10
name of h10 and with a width of 10 representing the button so let's see if
representing the button so let's see if that improved this when I refresh there
that improved this when I refresh there we go I think this looks nice you can
we go I think this looks nice you can see how we have a little loading box for
see how we have a little loading box for the text and a little loading box in the
the text and a little loading box in the same size of this button perfect so now
same size of this button perfect so now I want to create the same skeletons for
I want to create the same skeletons for this one and I want to show you a a knit
this one and I want to show you a a knit trick that you can do by going inside of
trick that you can do by going inside of the component which you need and then
the component which you need and then using it to render a skeleton so I want
using it to render a skeleton so I want you to go inside of the nav item
you to go inside of the nav item component like this and then let's go
component like this and then let's go all the way to the bottom and let's add
all the way to the bottom and let's add a skeleton for it so nav item.
skeleton is a function skeleton nav
skeleton nav item and inside let's go ahead and let's
item and inside let's go ahead and let's return a similar structure so a div with
return a similar structure so a div with a class name of flex item Center and GAP
a class name of flex item Center and GAP X2 and inside let's open up a another
X2 and inside let's open up a another div let me just close this one all right
div let me just close this one all right and give it a class name of W10 h10
and give it a class name of W10 h10 relative and Shrink Z so this is
relative and Shrink Z so this is representing our image component and
representing our image component and let's add a scan inside with a class
let's add a scan inside with a class name of H full which is going to be a
name of H full which is going to be a maximum of 10 right and let's go ahead
maximum of 10 right and let's go ahead and add W full and absolute and let's
and add W full and absolute and let's also import the skeleton because it
also import the skeleton because it seems like we don't have it in this
seems like we don't have it in this component so I just added a skeleton
component so I just added a skeleton right here above the expert organization
right here above the expert organization perfect so we have the skeleton now and
perfect so we have the skeleton now and just below outside of this div which is
just below outside of this div which is representing our image let's add a pure
representing our image let's add a pure skeleton here with the class name of h10
skeleton here with the class name of h10 and W pull like this and the reason
and W pull like this and the reason we're doing this inside of the component
we're doing this inside of the component is because we can now go inside of the
is because we can now go inside of the sidebar and now what we can do is just
sidebar and now what we can do is just open up a new div which is going to
open up a new div which is going to render an array uh well not an array but
render an array uh well not an array but a couple of those skeletons from nav
a couple of those skeletons from nav item so this way we don't have to style
item so this way we don't have to style it inside of here instead let me show
it inside of here instead let me show you what we can do so let's do space Y 2
you what we can do so let's do space Y 2 so they're going to have space below one
so they're going to have space below one another and then we're going to use nav
another and then we're going to use nav item which we already have imported but
item which we already have imported but we're going to write do skeleton like
we're going to write do skeleton like this and let's copy and paste this three
this and let's copy and paste this three times and let's take a look at that now
times and let's take a look at that now when I refresh there we go you can see
when I refresh there we go you can see how that looks so it's representing this
how that looks so it's representing this state right we are putting three of them
state right we are putting three of them because I don't know I imagine users are
because I don't know I imagine users are going to have more organizations so I'm
going to have more organizations so I'm kind of trying to get closer to what it
kind of trying to get closer to what it would actually look like perfect I think
would actually look like perfect I think this looks much better now great and now
this looks much better now great and now we are ready to create this settings
we are ready to create this settings page so creating the settings page is
page so creating the settings page is actually going to be quite easy because
actually going to be quite easy because we have finished component for that
we have finished component for that we're actually going to use this
we're actually going to use this component which opens up when we click
component which opens up when we click on manage organizations right here so
on manage organizations right here so we're going to reuse this component but
we're going to reuse this component but clerk offers us either to use that
clerk offers us either to use that inside of uh a model or inside of a
inside of uh a model or inside of a inline component so what we have to do
inline component so what we have to do is fix this 404 first as we can see in
is fix this 404 first as we can see in the URL that goes to/ settings so let's
the URL that goes to/ settings so let's go inside of the uh organization
go inside of the uh organization organization ID and in here create a new
organization ID and in here create a new folder settings which is going to
folder settings which is going to represent that route and then let's
represent that route and then let's create the page. DSX inside const organ
create the page. DSX inside const organ I actually call it settings
I actually call it settings page and return a div saying set
page and return a div saying set settings like this and Export default
settings like this and Export default settings page and when I save we should
settings page and when I save we should no longer be having a 404 error instead
no longer be having a 404 error instead you can see that this is selected now
you can see that this is selected now because the path name matches so let me
because the path name matches so let me just show you where that works just in
just show you where that works just in case you're having any errors it's
case you're having any errors it's inside of the nav item here you can see
inside of the nav item here you can see that we check if the path name is equal
that we check if the path name is equal to route. hre and as you can see the hre
to route. hre and as you can see the hre let's find my uh routes right here the
let's find my uh routes right here the href is/ organization organization ID
href is/ organization organization ID and then activity right here so make
and then activity right here so make sure you don't have any typos here like
sure you don't have any typos here like organization with an S or something like
organization with an S or something like that make sure it has no typos and then
that make sure it has no typos and then it should be working perfect so let's
it should be working perfect so let's head back inside of this settings page
head back inside of this settings page and all we have to do is we have to
and all we have to do is we have to import organization profile from Clerk
import organization profile from Clerk nextjs and then let's render the
nextjs and then let's render the organization profile like this let's
organization profile like this let's give the wrapper div a width of full and
give the wrapper div a width of full and you can already see that rendering here
you can already see that rendering here and let's go ahead and add an appearance
and let's go ahead and add an appearance to it so elements root box is going to
to it so elements root box is going to have a box shadow of none and a width of
have a box shadow of none and a width of 100 and still inside of the elements my
100 and still inside of the elements my apologies we're going to have a card and
apologies we're going to have a card and in here we're we going to add a border
in here we're we going to add a border to be 1px solid E5 E5 E5 and we're going
to be 1px solid E5 E5 E5 and we're going to have a box Shadow again of none and a
to have a box Shadow again of none and a width of
width of 100% great and let's take a look at that
100% great and let's take a look at that now there we go you can see how we now
now there we go you can see how we now have settings and when I click here you
have settings and when I click here you can see that it works on a different
can see that it works on a different organization so if I go back to this one
organization so if I go back to this one it's another if I go to test its test
it's another if I go to test its test perfect so now that is working as well
perfect so now that is working as well great great job and if you're interested
great great job and if you're interested in what this appearance does you can go
in what this appearance does you can go ahead and comment it out like this and
ahead and comment it out like this and then you're going to see that it looks
then you're going to see that it looks just a little bit different it has this
just a little bit different it has this Shadow which kind of looks like it
Shadow which kind of looks like it should have been a model but thankfully
should have been a model but thankfully we have access to the appearance prop so
we have access to the appearance prop so we can add it necessary attributes to
we can add it necessary attributes to make it look more in line and more
make it look more in line and more according to our shaten component
according to our shaten component great great
great great job great so now that we have our
job great so now that we have our settings page finished what I want to do
settings page finished what I want to do is I want to set up our database and the
is I want to set up our database and the reason I want to do that already is
reason I want to do that already is because the next thing I want to teach
because the next thing I want to teach you is how to use the new server actions
you is how to use the new server actions we're going to start from the most
we're going to start from the most primitive way of using server actions
primitive way of using server actions and then we are slowly going to abstract
and then we are slowly going to abstract them into our own custom hook and our
them into our own custom hook and our own custom uh util which is going to
own custom uh util which is going to make those server actions completely
make those server actions completely type safe and also validated using Zod
type safe and also validated using Zod so the first thing I want you do is head
so the first thing I want you do is head inside of your terminal so let's go
inside of your terminal so let's go ahead and open that and you can feel
ahead and open that and you can feel free to shut down the app if you are
free to shut down the app if you are running it in the same terminal and
running it in the same terminal and let's go ahead and npm install D Prisma
let's go ahead and npm install D Prisma like that and let's wait a second for
like that and let's wait a second for this to
this to finish and after it's finished let's go
finish and after it's finished let's go ahead and run npx Prisma in it and
ahead and run npx Prisma in it and that's going to generate a couple of
that's going to generate a couple of files inside of our project so let's
files inside of our project so let's take a look at what those files are as
take a look at what those files are as you can see now we have a Prisma folder
you can see now we have a Prisma folder and we have a schema. Prisma file
and we have a schema. Prisma file inside and inside of our environment
inside and inside of our environment file you can see that after our
file you can see that after our environment keys for clerk we now have
environment keys for clerk we now have this big comment that this was inserted
this big comment that this was inserted by Prisma in it and we have a mock
by Prisma in it and we have a mock database UR so this is not going to work
database UR so this is not going to work this is just a dummy placeholder but
this is just a dummy placeholder but we're going to replace that with our
we're going to replace that with our actual database URL so let's go ahead
actual database URL so let's go ahead and do that now so for my solution I'm
and do that now so for my solution I'm going to use Planet scale Planet scale
going to use Planet scale Planet scale offers a completely uh free unlimited uh
offers a completely uh free unlimited uh database but it does require a credit
database but it does require a credit card to prevent abuse of that free tier
card to prevent abuse of that free tier uh by creating more accounts so if you
uh by creating more accounts so if you don't have a credit card for any reason
don't have a credit card for any reason or just don't want to use it you don't
or just don't want to use it you don't have to use Planet scale so the only
have to use Planet scale so the only important thing is that you use uh some
important thing is that you use uh some kind of SQL if you want to you can set
kind of SQL if you want to you can set up your own local Docker MySQL instance
up your own local Docker MySQL instance or you can Google Planet scale free
or you can Google Planet scale free alternative the reason I'm using Planet
alternative the reason I'm using Planet scale is because it's by far the fastest
scale is because it's by far the fastest way to create a production ready mySQL
way to create a production ready mySQL database with load balancers and of
database with load balancers and of course amazing Prisma support for
course amazing Prisma support for migrations so I'm going to show you how
migrations so I'm going to show you how to do it with Planet scale again if you
to do it with Planet scale again if you don't have a credit card or don't want
don't have a credit card or don't want to use it you don't have to you can just
to use it you don't have to you can just Google uh how to set up MySQL locally
Google uh how to set up MySQL locally because the only thing we need from
because the only thing we need from planet scale is this database URL that's
planet scale is this database URL that's the only thing we need okay so head to
the only thing we need okay so head to planet scale.com or whatever your
planet scale.com or whatever your solution is and just go ahead and sign
solution is and just go ahead and sign in after you sign in go ahead and find
in after you sign in go ahead and find the button which says new database make
the button which says new database make sure you don't have any other databases
sure you don't have any other databases active so this is my paid database
active so this is my paid database that's why I have it but you always have
that's why I have it but you always have one free database so go ahead and create
one free database so go ahead and create a new database and give it a name in my
a new database and give it a name in my case it's going to be Trello tutorial
case it's going to be Trello tutorial and as you can see you have the option
and as you can see you have the option hobby which is free forever but as I
hobby which is free forever but as I said it does require a credit card again
said it does require a credit card again if you don't want to do that you can
if you don't want to do that you can just find a local solution for MySQL
just find a local solution for MySQL those are always free so make sure you
those are always free so make sure you select the free option and just go ahead
select the free option and just go ahead and click create database and confirm
and click create database and confirm one more time here that total monthly
one more time here that total monthly cost is free and click create a database
cost is free and click create a database and while that is creating let's go
and while that is creating let's go ahead and let's select our uh
ahead and let's select our uh development instance so as you can see
development instance so as you can see we have a lot of options here but the
we have a lot of options here but the one we are using is Prisma so go ahead
one we are using is Prisma so go ahead and select Prisma like
and select Prisma like this and here we have to create a
this and here we have to create a password for our database which is in
password for our database which is in same way going to create our database
same way going to create our database URL so go ahead and click create
URL so go ahead and click create password the name does not matter uh if
password the name does not matter uh if you want to you can copy the username
you want to you can copy the username and password and store it somewhere safe
and password and store it somewhere safe uh if you accidentally lose it you can
uh if you accidentally lose it you can always create a new password but you
always create a new password but you cannot see it after it's been generated
cannot see it after it's been generated right so make sure you copy it um again
right so make sure you copy it um again if you forget it it's it's not a big
if you forget it it's it's not a big problem you can always generate a new
problem you can always generate a new one for the same database um but you
one for the same database um but you cannot see it immediately after you copy
cannot see it immediately after you copy it great so now it says that we have to
it great so now it says that we have to configure our Prisma application we
configure our Prisma application we already did this and we run this command
already did this and we run this command line and go ahead and make sure this
line and go ahead and make sure this optimized is selected for the lowest
optimized is selected for the lowest latency between our application and the
latency between our application and the actual database and there we go here we
actual database and there we go here we have the database URL so this is what we
have the database URL so this is what we need so let's go ahead and copy that and
need so let's go ahead and copy that and let's replace this database URL with
let's replace this database URL with that solution and there we go I have my
that solution and there we go I have my database URL right here uh again if
database URL right here uh again if you're doing this in a different
you're doing this in a different solution all that matters is that you
solution all that matters is that you find the equivalent mySQL database URL
find the equivalent mySQL database URL and the other thing that matters that
and the other thing that matters that you do is update the Prisma uh client so
you do is update the Prisma uh client so let's go ahead uh we can skip over this
let's go ahead uh we can skip over this we don't need that what we actually need
we don't need that what we actually need uh is this sorry I I scrolled too far we
uh is this sorry I I scrolled too far we have to modify our schema Prisma so
have to modify our schema Prisma so let's take a look at our schema Prisma
let's take a look at our schema Prisma to see the differences so I'm going to
to see the differences so I'm going to go inside of Prisma folder schema.
go inside of Prisma folder schema. Prisma and as you can see we have to
Prisma and as you can see we have to change the provider to be MySQL we have
change the provider to be MySQL we have to change the url to be uh the database
to change the url to be uh the database URL actually that is the same and we
URL actually that is the same and we have to change the relation mode to
have to change the relation mode to Prisma so what I'm going to do is just
Prisma so what I'm going to do is just copy this entire thing and replace it
copy this entire thing and replace it with this so again if you're not using
with this so again if you're not using Planet scale you can do this manually
Planet scale you can do this manually just by looking at what I did Prov
just by looking at what I did Prov provider is my SQL URL is the
provider is my SQL URL is the environment variable variable database
environment variable variable database URL relation mode is Prisma and provider
URL relation mode is Prisma and provider is Prisma client JS perfect and that's
is Prisma client JS perfect and that's it you don't have to look at Planet
it you don't have to look at Planet scale again you can close it
scale again you can close it completely so again just ensure that in
completely so again just ensure that in your dot environment where you have all
your dot environment where you have all the clerk environment Keys you now have
the clerk environment Keys you now have your database URL and in Scream up
your database URL and in Scream up Prisma you modified it to look like
Prisma you modified it to look like this and now what I want to do is I want
this and now what I want to do is I want to create our first model here so go
to create our first model here so go ahead and write model board let's go
ahead and write model board let's go ahead and give it an ID which is a
ahead and give it an ID which is a string a type of ID and the default
string a type of ID and the default value of uu ID and now let's go ahead
value of uu ID and now let's go ahead and simply give it a title which is a
and simply give it a title which is a string and once you add a new model
string and once you add a new model inside of your schema Prisma you have to
inside of your schema Prisma you have to head inside of your terminal here and
head inside of your terminal here and you have to run the following command
you have to run the following command MPX Prisma generate so this is the first
MPX Prisma generate so this is the first command which will locally create types
command which will locally create types and well functions for this new model
and well functions for this new model which we added what does it mean locally
which we added what does it mean locally it it means that it's added inside of
it it means that it's added inside of your nod modules so with the next
your nod modules so with the next package we're going to install which is
package we're going to install which is this Prisma client and then when we use
this Prisma client and then when we use our Prisma client like this const Prisma
our Prisma client like this const Prisma new Prisma client and then when we write
new Prisma client and then when we write Prisma do board you're going to see no
Prisma do board you're going to see no errors because it's added low Al so
errors because it's added low Al so that's the first thing you have to do
that's the first thing you have to do whenever you add a new model or modify a
whenever you add a new model or modify a model inside of your database and the
model inside of your database and the second thing you have to run is npx
second thing you have to run is npx Prisma DB push what this is going to do
Prisma DB push what this is going to do is push it in my case to Planet scale so
is push it in my case to Planet scale so the planet scale is in sync with what we
the planet scale is in sync with what we have and whenever you do these changes
have and whenever you do these changes npx Prisma DB push and npx Prisma
npx Prisma DB push and npx Prisma generate make sure that you also restart
generate make sure that you also restart your application so if you haven't
your application so if you haven't already shut down your application
already shut down your application and then just mpm run Dev again but
and then just mpm run Dev again but we're not going to do that just yet
we're not going to do that just yet because the first thing I want to do is
because the first thing I want to do is do mpm install at Prisma client so this
do mpm install at Prisma client so this is going to allow us to use Prisma in
is going to allow us to use Prisma in our application and now we can mpm run
our application and now we can mpm run Dev great so now that we have this saved
Dev great so now that we have this saved let's go ahead and create a little
let's go ahead and create a little Library which we are going to use to
Library which we are going to use to access our database so inside of the lid
access our database so inside of the lid folder create a new file db. TS go ahead
folder create a new file db. TS go ahead and import the Prisma client from at
and import the Prisma client from at Prisma client and write export con DB to
Prisma client and write export con DB to be Global this. Prisma or new Prisma
be Global this. Prisma or new Prisma client and you can ignore these errors
client and you can ignore these errors for now we're going to resolve them in a
for now we're going to resolve them in a moment and now let's write an if Clause
moment and now let's write an if Clause if process. environment. node
if process. environment. node environment is not production so if we
environment is not production so if we are either in development or local or
are either in development or local or something like that in that case Global
something like that in that case Global this. Prisma will be our database
this. Prisma will be our database constant and now let's go ahead and fix
constant and now let's go ahead and fix these errors or you can see our Global
these errors or you can see our Global this module doesn't have Prisma
this module doesn't have Prisma registered we can do that quite easily
registered we can do that quite easily by adding a
by adding a declare Global VAR Prisma to be a type
declare Global VAR Prisma to be a type of Prisma client or
of Prisma client or undefined like that and now let's
undefined like that and now let's briefly explain why we are doing this so
briefly explain why we are doing this so in production this is what's going to
in production this is what's going to happen you can imagine that we didn't
happen you can imagine that we didn't write this so this is what would happen
write this so this is what would happen we wouldn't even technically need this
we wouldn't even technically need this this is the you know the most primitive
this is the you know the most primitive way to export our database which uses
way to export our database which uses the new Prisma client so why do we do
the new Prisma client so why do we do all of this uh for our development mode
all of this uh for our development mode or anything that's not production well
or anything that's not production well that's because nextjs uses hot reload
that's because nextjs uses hot reload and during that hot reload uh new Prisma
and during that hot reload uh new Prisma client will be initialized multiple
client will be initialized multiple times and that's going to create a
times and that's going to create a warning inside of your project so what
warning inside of your project so what we do is we we do the following so when
we do is we we do the following so when we Define the database variable we check
we Define the database variable we check if we have it stored in global this.
if we have it stored in global this. Prisma if we don't meaning this is the
Prisma if we don't meaning this is the first time we just started the app in
first time we just started the app in that case we just initialize new Prisma
that case we just initialize new Prisma client and then since we are in
client and then since we are in development because of the check in this
development because of the check in this if Clause we assign that database
if Clause we assign that database constant to Global this. Prisma and then
constant to Global this. Prisma and then then when hot reload activates because
then when hot reload activates because we changed some other file all it does
we changed some other file all it does it looks like at this Global this.
it looks like at this Global this. Prisma and since we already have it here
Prisma and since we already have it here it knows that it doesn't have to
it knows that it doesn't have to initialize it again and if you're
initialize it again and if you're wondering why doesn't uh hot reload
wondering why doesn't uh hot reload affect global this. Prisma well that is
affect global this. Prisma well that is because Global is excluded from hot
because Global is excluded from hot reload so that's a brief explanation of
reload so that's a brief explanation of why we are doing
why we are doing that so what I'm want to do now is
that so what I'm want to do now is create the most primitive server action
create the most primitive server action and I'm going to do that inside of app
and I'm going to do that inside of app folder platform dashboard organization
folder platform dashboard organization organization ID page. DSX so right here
organization ID page. DSX so right here where we have the text organization page
where we have the text organization page which is currently rendered right here
which is currently rendered right here and we can access that component by
and we can access that component by clicking on the boards in an individual
clicking on the boards in an individual workspace so uh just make sure you
workspace so uh just make sure you refresh your application so everything
refresh your application so everything is up to date because we just did a lot
is up to date because we just did a lot of turning on and turning off make sure
of turning on and turning off make sure you see the organization page text and
you see the organization page text and we're going to do the following we're
we're going to do the following we're going to replace this text right here
going to replace this text right here with a form so I'm going to write the
with a form so I'm going to write the native HTML form element I'm going to
native HTML form element I'm going to add a native HTML input here I'm going
add a native HTML input here I'm going to give it an ID of title I'm going to
to give it an ID of title I'm going to give it a name of title and I'm also
give it a name of title and I'm also going to make sure it's required like
going to make sure it's required like this and let's go ahead and see uh if we
this and let's go ahead and see uh if we can see that right here okay it's right
can see that right here okay it's right here here but let me just go ahead and
here here but let me just go ahead and give it a little placeholder so we can
give it a little placeholder so we can see what we are typing so
see what we are typing so enter a board
enter a board title and let me just give it a class
title and let me just give it a class name border
name border input or maybe just border black it
input or maybe just border black it doesn't really matter and
doesn't really matter and Border there we go and a little padding
Border there we go and a little padding one great so just something that we can
one great so just something that we can see it uh clearer perfect so we have
see it uh clearer perfect so we have this little input here and one thing
this little input here and one thing that I want to just ensure here is that
that I want to just ensure here is that organization ID page is a server
organization ID page is a server component so you can either simply know
component so you can either simply know that by having you know experience in uh
that by having you know experience in uh the app router as I said a couple of
the app router as I said a couple of times every page that you create is
times every page that you create is automatically a server component except
automatically a server component except if it is rendered inside of a client
if it is rendered inside of a client component which you already learned we
component which you already learned we Define by adding use client at the top
Define by adding use client at the top so now this becomes a client component
so now this becomes a client component and let's see the difference now so I'm
and let's see the difference now so I'm going to add a console log I am rendered
going to add a console log I am rendered in the
in the browser sorry I am logged in the browser
browser sorry I am logged in the browser that would be a better idea so make sure
that would be a better idea so make sure you have used client and add this
you have used client and add this console log right here refresh your page
console log right here refresh your page and open up your uh console uh just a
and open up your uh console uh just a second let me assign my console right
second let me assign my console right here there we go and you can see my logs
here there we go and you can see my logs here I'm logged in the browser you can
here I'm logged in the browser you can see how it's visible right here but what
see how it's visible right here but what happens if I remove use client let's try
happens if I remove use client let's try that so I'm going to go ahead and uh
that so I'm going to go ahead and uh expand my screen and refresh the entire
expand my screen and refresh the entire thing and now as you can see I no longer
thing and now as you can see I no longer have those logs so where is that log
have those logs so where is that log well that log is visible inside of our
well that log is visible inside of our terminal right here as you can see there
terminal right here as you can see there it is I'm logged in the browser well the
it is I'm logged in the browser well the message stay the same but you see uh
message stay the same but you see uh what's the difference this is obviously
what's the difference this is obviously rendered on the server meaning that the
rendered on the server meaning that the logs from this component are only
logs from this component are only visible in our terminal because our
visible in our terminal because our terminal is rendering is running the
terminal is rendering is running the server great so we ensured that this is
server great so we ensured that this is a server component make sure you don't
a server component make sure you don't have used client at the top uh so if you
have used client at the top uh so if you watched any of my previous tutorials you
watched any of my previous tutorials you know that with server components we can
know that with server components we can do cool stuff but like directly fetching
do cool stuff but like directly fetching fetching the database right so but one
fetching the database right so but one solution that we didn't have until now
solution that we didn't have until now at least not in the stable version is
at least not in the stable version is how to mutate data from server
how to mutate data from server components that was one mystery that we
components that was one mystery that we had so what I want to do now is I want
had so what I want to do now is I want to add an asynchronous function create
to add an asynchronous function create which accepts form data which is a type
which accepts form data which is a type of form data which you don't have to
of form data which you don't have to import from anywhere we just have it and
import from anywhere we just have it and inside we're going to add use server
inside we're going to add use server like this and then let's conso log uh
like this and then let's conso log uh for for now I am
for for now I am triggered and let's add this Asing
triggered and let's add this Asing function create inside of form
function create inside of form action like this and let's go ahead and
action like this and let's go ahead and open our terminal so we can see the logs
open our terminal so we can see the logs here so I'm going to refresh my page now
here so I'm going to refresh my page now and I'm going to write test and I'm
and I'm going to write test and I'm going to press enter and once I pressed
going to press enter and once I pressed enter you can see that I have a message
enter you can see that I have a message here I am triggered meaning that this
here I am triggered meaning that this function which we created is
function which we created is successfully triggered inside of a
successfully triggered inside of a server component if this doesn't look uh
server component if this doesn't look uh if this looks like completely ordinary
if this looks like completely ordinary to you you have to understand that in
to you you have to understand that in server components usually you were not
server components usually you were not able to pass any functions or any hooks
able to pass any functions or any hooks anything like that so that's why if you
anything like that so that's why if you uh if you come from my previous
uh if you come from my previous tutorials this is a new thing for you
tutorials this is a new thing for you but if you came from purely a uh single
but if you came from purely a uh single page application world this probably
page application world this probably looks like a normal function to you but
looks like a normal function to you but there is a difference because this is
there is a difference because this is entirely run on the server this is a
entirely run on the server this is a server component and because of this use
server component and because of this use server declarative we can now access our
server declarative we can now access our database and use this form data to well
database and use this form data to well create a record so let me show you
create a record so let me show you exactly how we can do that right here
exactly how we can do that right here I'm going to write const title to be
I'm going to write const title to be form data. getet
form data. getet title and then what I'm going to do is
title and then what I'm going to do is I'm going to call our database and I'm
I'm going to call our database and I'm going to import it from s/ Le DB so it's
going to import it from s/ Le DB so it's that function which we created recently
that function which we created recently and then I'm going to write db. board.
and then I'm going to write db. board. create and I'm going to pass in the data
create and I'm going to pass in the data and I'm going to pass in the title and
and I'm going to pass in the title and as you can see now we have a little type
as you can see now we have a little type error here so let's just mark this form
error here so let's just mark this form data as
data as string there we go and before we run
string there we go and before we run this let's go ahead inside of our
this let's go ahead inside of our terminal here I'm going to open a new
terminal here I'm going to open a new one and I'm going to write npx Prisma
one and I'm going to write npx Prisma studio so let me just expand my screen
studio so let me just expand my screen so you can see it in one line npx Prisma
so you can see it in one line npx Prisma studio and press enter and that's going
studio and press enter and that's going to be running the Prisma Studio which is
to be running the Prisma Studio which is basically a UI for our database as you
basically a UI for our database as you can see it notice that we have defined
can see it notice that we have defined the model board in our Prisma schema but
the model board in our Prisma schema but we have zero records of that board so
we have zero records of that board so let's see if this function now it's
let's see if this function now it's working so I'm going to try and add a
working so I'm going to try and add a test here and press enter let's refresh
test here and press enter let's refresh here and it looks like it's not here so
here and it looks like it's not here so let's go ahead uh and check out what's
let's go ahead uh and check out what's going
going on all right so my apologies I forgot
on all right so my apologies I forgot that we have to put a weight here so
that we have to put a weight here so make sure you put a weight inside of
make sure you put a weight inside of your uh before your DB board create like
your uh before your DB board create like that and let's try this again so I'm
that and let's try this again so I'm going to refresh this I'm going to
going to refresh this I'm going to refresh my Prisma Studio no records here
refresh my Prisma Studio no records here I'm going to write test I'm going to
I'm going to write test I'm going to press enter And now when I refresh here
press enter And now when I refresh here there we go you can see I have a new
there we go you can see I have a new record inside of my database so as you
record inside of my database so as you can see we just used a react component
can see we just used a react component to do a create method or what would
to do a create method or what would usually be a post API request that's
usually be a post API request that's really cool of course there are
really cool of course there are discussions going around whether this is
discussions going around whether this is actually good or not some people with
actually good or not some people with Val very valid reasons may still prefer
Val very valid reasons may still prefer API routes and that's completely fine
API routes and that's completely fine you know depending on the type of
you know depending on the type of application you have if it's an
application you have if it's an Enterprise application this is
Enterprise application this is definitely something to discuss first if
definitely something to discuss first if you're an indie hacker this is just a
you're an indie hacker this is just a very fast solution for you so you know
very fast solution for you so you know it comes down to your preference I
it comes down to your preference I personally think it's great that we have
personally think it's great that we have this many options in this tutorial I'm
this many options in this tutorial I'm not really going to discuss whether this
not really going to discuss whether this is always the best solution instead I'm
is always the best solution instead I'm going to focus on mastering This Server
going to focus on mastering This Server actions as much as we can so you at the
actions as much as we can so you at the end of the tutorial can decide yourself
end of the tutorial can decide yourself whether this is something you like or
whether this is something you like or not great so the next thing that I want
not great so the next thing that I want to do is I want to show you how to
to do is I want to show you how to separate this inside of a different file
separate this inside of a different file because sure this is great but you know
because sure this is great but you know we kind of have the problem of
we kind of have the problem of separating concerns I would kind of like
separating concerns I would kind of like that this organization ID page is only
that this organization ID page is only responsible for ID and passing the
responsible for ID and passing the actions but not EX exactly for defining
actions but not EX exactly for defining actions because as you know in the API
actions because as you know in the API so this is representing our API we have
so this is representing our API we have to check for authentication we have to
to check for authentication we have to check for errors you know a bunch of
check for errors you know a bunch of different stuff here so that's what I
different stuff here so that's what I want to do next is separate this into
want to do next is separate this into its own
its own file so let's go ahead and do the
file so let's go ahead and do the following first thing I want to do is go
following first thing I want to do is go ahead inside of my terminal here and I'm
ahead inside of my terminal here and I'm going to shut it down for a second and
going to shut it down for a second and I'm just going to run npm install Zod
I'm just going to run npm install Zod like this and I'm just run my app again
like this and I'm just run my app again make sure you have Zod installed and
make sure you have Zod installed and refresh your Local Host if you shut down
refresh your Local Host if you shut down the app then I'm going to go ahead and
the app then I'm going to go ahead and copy this function from here and remove
copy this function from here and remove it at the same time and I'm going to go
it at the same time and I'm going to go and I'm going to create a new
and I'm going to create a new folder called actions and inside I'm
folder called actions and inside I'm going to create a new file create board.
going to create a new file create board. THS actually I'm going to use create
THS actually I'm going to use create dashboard. vs like this and let's go
dashboard. vs like this and let's go ahead and paste that here and let's
ahead and paste that here and let's remove the this use server from here and
remove the this use server from here and instead let's just go ahead and put it
instead let's just go ahead and put it at the top like that and then let's just
at the top like that and then let's just import the
import the database from addlib DB like that and
database from addlib DB like that and let's also do an export for this
let's also do an export for this asynchronous function right here great
asynchronous function right here great and now let's go ahead and remove
and now let's go ahead and remove this and let's now import create from
this and let's now import create from actions create board and let's confir
actions create board and let's confir that the same thing is working so I have
that the same thing is working so I have one record in my database here I'm going
one record in my database here I'm going to try and create a new one I make sure
to try and create a new one I make sure you just press enter here let's refresh
you just press enter here let's refresh and there we go it's still working uh I
and there we go it's still working uh I have two items in my database now so one
have two items in my database now so one thing I want to do before we move on is
thing I want to do before we move on is just add a little button here which says
just add a little button here which says submit and give it a type of submit just
submit and give it a type of submit just so we don't have to press enter all the
so we don't have to press enter all the time uh and in fact we can use the
time uh and in fact we can use the button component from components UI but
button component from components UI but button like
button like that there we go this looks a bit better
that there we go this looks a bit better so let's go ahead and refresh type 1 to
so let's go ahead and refresh type 1 to three click on submit let's refresh here
three click on submit let's refresh here and there we go we have our third record
and there we go we have our third record here and you probably already noticed
here and you probably already noticed that one thing that's missing are the
that one thing that's missing are the loading States and also the errors right
loading States and also the errors right the only kind of validation that we have
the only kind of validation that we have right now is this native HTML required
right now is this native HTML required field because we put the required prop
field because we put the required prop inside of the input so now let's go
inside of the input so now let's go ahead inside of create board right here
ahead inside of create board right here and let's make use of that Zod Library
and let's make use of that Zod Library so go ahead and import Z from Zod and
so go ahead and import Z from Zod and let's create our schema so const create
let's create our schema so const create board is going to be z.
board is going to be z. object which accepts the title which is
object which accepts the title which is a type of Z do string like
this great now let's go ahead and use use
great now let's go ahead and use use this create board to pass our form data
this create board to pass our form data to confirm that everything is working as
to confirm that everything is working as expected everything has the exact types
expected everything has the exact types that we want so I'm going to go ahead
that we want so I'm going to go ahead and write const and from here I'm going
and write const and from here I'm going to extract the title and I'm going to
to extract the title and I'm going to write create board. pars and then I'm
write create board. pars and then I'm going to write the title to be form
going to write the title to be form data. getet title like
data. getet title like that and as you can see now we no longer
that and as you can see now we no longer have to write that as string because
have to write that as string because right here you can see that Zod is going
right here you can see that Zod is going to ensure that title is a string for us
to ensure that title is a string for us so let's go ahead and save that and
so let's go ahead and save that and let's see if it's still working I'm
let's see if it's still working I'm going to write in Zod and click submit
going to write in Zod and click submit here let's refresh and there we go it is
here let's refresh and there we go it is still working because uh our input is a
still working because uh our input is a string and that validates this pars
string and that validates this pars right
right here before we move on into extracting
here before we move on into extracting the errors from this right so we can
the errors from this right so we can display it back to our users what I want
display it back to our users what I want to do is I want to show you how to
to do is I want to show you how to actually see those results and how to
actually see those results and how to fetch them so since this is a server
fetch them so since this is a server component we can do the following we can
component we can do the following we can write const boards to be await DB from
write const boards to be await DB from SLI db. board. find
SLI db. board. find many and that's actually the only thing
many and that's actually the only thing we have to do and since we're using a
we have to do and since we're using a weight we have to turn this into an
weight we have to turn this into an asynchronous function and then let's go
asynchronous function and then let's go ahead and give this a class name of flex
ahead and give this a class name of flex Flex call and space
Flex call and space y4 and below this form let's go ahead
y4 and below this form let's go ahead and open a div with the class name space
and open a div with the class name space Y2 and let's iterate over the board so
Y2 and let's iterate over the board so boards. map get the individual board
boards. map get the individual board like that open up a div with a key to be
like that open up a div with a key to be board. ID and let's write board name to
board. ID and let's write board name to be board. title so actually it's going
be board. title so actually it's going to be board do Title Here There we go
to be board do Title Here There we go you can see that now I have my four
you can see that now I have my four records here as well as in the database
records here as well as in the database but if I try to create a new one the new
but if I try to create a new one the new one was just created so let's check in
one was just created so let's check in my database there we go you can see this
my database there we go you can see this is the newest one here but it's not
is the newest one here but it's not visible here until I refresh so let me
visible here until I refresh so let me show you how you can revalidate your
show you how you can revalidate your path inside uh of well in inside of your
path inside uh of well in inside of your uh create action so what I'm going to do
uh create action so what I'm going to do for now is I'm just going to copy uh my
for now is I'm just going to copy uh my URL so make sure you do the same thing
URL so make sure you do the same thing copy the entire
copy the entire URL and go back inside of your create
URL and go back inside of your create function here and we're going to go
function here and we're going to go ahead and we're going to add a
ahead and we're going to add a revalidate path function from next
revalidate path function from next slash like this and I'm going to pass in
slash like this and I'm going to pass in my URL but I'm going to go ahead and
my URL but I'm going to go ahead and remove the Local Host part of it so just
remove the Local Host part of it so just SL organization and then my organization
SL organization and then my organization ID so the reason I copied the URL is
ID so the reason I copied the URL is because we are working with Dynamic urls
because we are working with Dynamic urls right so it's just simply easier to copy
right so it's just simply easier to copy it now but don't worry I'm going to
it now but don't worry I'm going to teach you how to of course pass the
teach you how to of course pass the actual ID so you can do this uh
actual ID so you can do this uh programmatically so make sure you have
programmatically so make sure you have that let's refresh and let's write real
that let's refresh and let's write real time click submit and there we go you
time click submit and there we go you can see how now it was uh created in
can see how now it was uh created in real time here and
real time here and updated so now I want to show you how to
updated so now I want to show you how to we learn how to create but how about the
we learn how to create but how about the deleting or updating a specific board
deleting or updating a specific board well obviously we can kind of guess how
well obviously we can kind of guess how we would do that right but what we don't
we would do that right but what we don't know is how do we pass an ID inside of
know is how do we pass an ID inside of This Server action well in order to do
This Server action well in order to do that let's go back inside of this page.
that let's go back inside of this page. DSX and let's go ahead and copy this div
DSX and let's go ahead and copy this div which represents our board and inside of
which represents our board and inside of this organization ID where we have the
this organization ID where we have the page. DSX uh let's go ahead and just
page. DSX uh let's go ahead and just create a new file called board. DSX so
create a new file called board. DSX so we're going to delete this later I just
we're going to delete this later I just want to create a quick component here so
want to create a quick component here so const delete
const delete board and let's go ahead and return well
board and let's go ahead and return well the same thing we just rendered right
the same thing we just rendered right but this time we don't need this key and
but this time we don't need this key and let's go ahead and create a quick
let's go ahead and create a quick interface for this so interface
interface for this so interface delete board
delete board props actually let's call it I don't
props actually let's call it I don't know why I called it delete board it's
know why I called it delete board it's going to be board so board props it's
going to be board so board props it's going to have a title which is a string
going to have a title which is a string and an ID which is a string so let's
and an ID which is a string so let's assign those props here board props and
assign those props here board props and then let's extract the title and the ID
then let's extract the title and the ID and then we can render the title here go
and then we can render the title here go back inside of page. DSX and instead of
back inside of page. DSX and instead of this div we can now render that board
this div we can now render that board component and let's just make sure that
component and let's just make sure that we uh do an export here so export con
we uh do an export here so export con board and then
board and then we're going to be able to import that
we're going to be able to import that from do/ board like I did right here and
from do/ board like I did right here and we just have to pass in the key to be
we just have to pass in the key to be board. ID here now and then let's go
board. ID here now and then let's go ahead and give it a title which is
ahead and give it a title which is board. tile and ID which is
board. tile and ID which is board. so make sure you have these three
board. so make sure you have these three props here and as you can see nothing
props here and as you can see nothing much has changed but what I want to do
much has changed but what I want to do now is I want to go ahead and turn this
now is I want to go ahead and turn this into a form as well and let's go ahead
into a form as well and let's go ahead and do the following let's give it a
and do the following let's give it a class name of flex item Center and GAP X
class name of flex item Center and GAP X of two and let's wrap this into a
of two and let's wrap this into a paragraph like this and then let's add a
paragraph like this and then let's add a little button component from UI button
little button component from UI button which is going to render delete and give
which is going to render delete and give it a variant of
destructive oops and let's give give it a size of
oops and let's give give it a size of small like
small like this great so now we have a little
this great so now we have a little delete button here right now it's not
delete button here right now it's not doing anything as you can see it's just
doing anything as you can see it's just refreshing the page uh because we are we
refreshing the page uh because we are we wrapped it inside of a form so let's
wrapped it inside of a form so let's just give it a type of submit Here and
just give it a type of submit Here and Now what I want to do is I want to
Now what I want to do is I want to create a new server action which is
create a new server action which is going to be used for deleting uh our
going to be used for deleting uh our sorry for deleting our um boards so
sorry for deleting our um boards so let's go ahead inside of actions and
let's go ahead inside of actions and create a new file delete dasboard
create a new file delete dasboard dots like that let's go ahead and
dots like that let's go ahead and Export asynchronous function delete
Export asynchronous function delete board which passes in the ID which is a
board which passes in the ID which is a type of
type of string and what I want to do in here
string and what I want to do in here actually it's not an arrow function
actually it's not an arrow function sorry what I want to do in here is
sorry what I want to do in here is simply await db. board do delete where
simply await db. board do delete where and passing the ID like this and then
and passing the ID like this and then I'm just going to go back inside of my
I'm just going to go back inside of my create board and I'm going to copy this
create board and I'm going to copy this revalidate path just make sure that you
revalidate path just make sure that you are in the same organization as you were
are in the same organization as you were when you copied this and make sure you
when you copied this and make sure you import revalidate path from next cache
import revalidate path from next cache great so now we have the delete board
great so now we have the delete board right here and now we have to find a way
right here and now we have to find a way to pass in this ID so let's go ahead
to pass in this ID so let's go ahead inside of board do uh. DSX here
inside of board do uh. DSX here and let's go ahead and let's write const
and let's go ahead and let's write const delete board with ID to be delete board
delete board with ID to be delete board which we just created so import it from
which we just created so import it from add actions delete board and then do
add actions delete board and then do bind null and then pass in the ID like
bind null and then pass in the ID like that and give this form an action of
that and give this form an action of delete board with ID like
delete board with ID like that and as you can see we have a little
that and as you can see we have a little error here that's because inside of my
error here that's because inside of my delete dashboard I forgot to write use
delete dashboard I forgot to write use server at the top and when I save as you
server at the top and when I save as you can see we no longer have any errors so
can see we no longer have any errors so let's try this out I'm going to click
let's try this out I'm going to click delete here and there we go you can see
delete here and there we go you can see because uh it revalidates the path it's
because uh it revalidates the path it's deleted in real time so that's how you
deleted in real time so that's how you can pass in a specific ID uh to the
can pass in a specific ID uh to the server action so what is the main
server action so what is the main purpose of server actions well obviously
purpose of server actions well obviously allowing us to do mutations from server
allowing us to do mutations from server components but one thing that is heavily
components but one thing that is heavily mentioned inside of nextjs documentation
mentioned inside of nextjs documentation is Progressive enhancement which as I
is Progressive enhancement which as I understand it allows us to do this
understand it allows us to do this mutations without JavaScript being
mutations without JavaScript being active making it thus much faster than
active making it thus much faster than usual API
usual API calls great and what I want to show you
calls great and what I want to show you now is how to create loading States as
now is how to create loading States as well as displaying errors in our
well as displaying errors in our fields so the only way that I know this
fields so the only way that I know this can be done is by mix and match of
can be done is by mix and match of server components and client components
server components and client components so I'm going to close everything just so
so I'm going to close everything just so I clear my view I'm going to go ahead
I clear my view I'm going to go ahead and find this page. DSX where we have uh
and find this page. DSX where we have uh the form and what I'm going to do is I'm
the form and what I'm going to do is I'm going to go ahead and change this form
going to go ahead and change this form right here to be a client component so
right here to be a client component so I'm going to go ahead and just as I
I'm going to go ahead and just as I created this board. TSX I'm going to
created this board. TSX I'm going to create a new
create a new form. DSX I'm going to mark it as use
form. DSX I'm going to mark it as use client and I'm going to export con
form and I'm simply going to render uh those actions
those actions here and let's import the button from
here and let's import the button from here from UI button and now we have a
here from UI button and now we have a little problem with this create function
little problem with this create function here so for now let's just go ahead and
here so for now let's just go ahead and import it like this and then go back to
import it like this and then go back to page and import that form from SL form
page and import that form from SL form the same way you did with/ board and we
the same way you did with/ board and we don't need these two anymore great so
don't need these two anymore great so now we have our form component and the
now we have our form component and the first thing I want to add is a hook
first thing I want to add is a hook called use form state so const let's
called use form state so const let's just add an empty array for now to be
just add an empty array for now to be use form state from react domum and in
use form state from react domum and in the first argument we're going to pass
the first argument we're going to pass in the create method and then I'm going
in the create method and then I'm going to create a constant above called
to create a constant above called initial St
initial St to have a message of null and errors
to have a message of null and errors which is an empty object and the second
which is an empty object and the second argument is going to be initial state so
argument is going to be initial state so this kind of works like a reducer and
this kind of works like a reducer and then I'm going to go ahead and extract
then I'm going to go ahead and extract the state and dispatch from this use
the state and dispatch from this use form State
form State here and now let's go ahead back inside
here and now let's go ahead back inside of this create function and modify it
of this create function and modify it slightly because when used in use form
slightly because when used in use form state which is going to give us like
state which is going to give us like some p ending State and some errors we
some p ending State and some errors we have to also modify this but just before
have to also modify this but just before we do that we are no longer going to be
we do that we are no longer going to be using the create itself here explicitly
using the create itself here explicitly instead we're going to be using the
instead we're going to be using the dispatch so make sure you change your
dispatch so make sure you change your form action to use the dispatch like
form action to use the dispatch like that now let's go ahead and go back
that now let's go ahead and go back inside of our actions create board and
inside of our actions create board and inside of my Z object I'm going to add a
inside of my Z object I'm going to add a requirement that a minimum length is
requirement that a minimum length is three like that and we can also add a uh
three like that and we can also add a uh let's see is it a message or invalid I
let's see is it a message or invalid I think it's a message I'm just going to
think it's a message I'm just going to say uh
say uh minimum length of three letters is
minimum length of three letters is required so a type of error right so now
required so a type of error right so now our Zod has an error here and now let's
our Zod has an error here and now let's go ahead and let's create a type here
go ahead and let's create a type here sorry
export type state which is going to have an optional errors object which is going
an optional errors object which is going to have an optional errors uh array of
to have an optional errors uh array of Errors for the title and we're going to
Errors for the title and we're going to have a message which is a string or null
have a message which is a string or null so as you can see it's going to match
so as you can see it's going to match our initial State here errors which is
our initial State here errors which is an object and message which is a null
an object and message which is a null and then I'm going to tweak this
and then I'm going to tweak this function create that before it accepts
function create that before it accepts the form data is going to accept the
the form data is going to accept the previous state which is going to be a
previous state which is going to be a type of state which we just created
type of state which we just created above great so now we have
above great so now we have that so now let's go ahead and let's
that so now let's go ahead and let's modify um this create board pars to use
modify um this create board pars to use safe pars like that so pars will throw
safe pars like that so pars will throw an error and break the application
an error and break the application whereas save pars will not do that so
whereas save pars will not do that so let's go ahead and change this from the
let's go ahead and change this from the structured title to be validated fields
structured title to be validated fields and then we can use that to check if we
and then we can use that to check if we have any form errors like a too short of
have any form errors like a too short of a title so let's write if uh not
a title so let's write if uh not validated fields. success so make sure
validated fields. success so make sure you put an exclamation point before that
you put an exclamation point before that in that case we are going to return an
in that case we are going to return an errors object which is going to be
errors object which is going to be validated fields. error.
validated fields. error. flatten field errors and a message is
flatten field errors and a message is going to be missing
going to be missing fields like
fields like that and then instead of using title
that and then instead of using title like this what we're going to do is
like this what we're going to do is extract the title from validated fields.
extract the title from validated fields. data just like
data just like that and in order to handle this type of
that and in order to handle this type of message error let's go ahead and wrap
message error let's go ahead and wrap this into a
this into a try and catch
method so I'm going to go ahead and add a catch here
a catch here which is going to have our error and
which is going to have our error and then we're going to return a
message database error like that and lastly alongside
error like that and lastly alongside revalidate path let's also add
revalidate path let's also add redirect from next SL navigation so make
redirect from next SL navigation so make sure you import redirect and we're going
sure you import redirect and we're going to redirect to that very same
to redirect to that very same route like that
route like that perfect and you can see that now when I
perfect and you can see that now when I added this redirect if you paid
added this redirect if you paid attention all the errors inside of my
attention all the errors inside of my form. CSX have disappeared so what
form. CSX have disappeared so what exactly did we achieve with this how can
exactly did we achieve with this how can we actually look at our errors now well
we actually look at our errors now well we can do that by accessing this state
we can do that by accessing this state right here which is going to have this
right here which is going to have this kind of object right so you can take a
kind of object right so you can take a look that if our Zod validation fails
look that if our Zod validation fails that means that we're going to uh have
that means that we're going to uh have an array of Errors for a specific field
an array of Errors for a specific field which matches whatever this schema is
which matches whatever this schema is trying to validate So Below this input
trying to validate So Below this input right here I'm going to go ahead uh and
right here I'm going to go ahead uh and let's let's just do it like this I'm
let's let's just do it like this I'm going to wrap this
going to wrap this div I'm going to wrap this input inside
div I'm going to wrap this input inside of a div I'm going to write a class name
of a div I'm going to write a class name of flex flex-all and space Y 2 like that
of flex flex-all and space Y 2 like that and then in here I'm going to go ahead
and then in here I'm going to go ahead and write if we have
and write if we have state errors and let's just not misspell
state errors and let's just not misspell state so if we have state errors title
state so if we have state errors title in that case let's go ahead and let's
in that case let's go ahead and let's add a div here and let's add an else to
add a div here and let's add an else to be now and inside of this div let's go
be now and inside of this div let's go ahead and iterate our state. errors.
ahead and iterate our state. errors. tile. map get individual error which is
tile. map get individual error which is a type of string and let's go ahead and
a type of string and let's go ahead and add a paragraph here which is going to
add a paragraph here which is going to render that error give it a key of error
render that error give it a key of error and a class
and a class name text rows 500 like that so now
name text rows 500 like that so now let's try this out I'm going to refresh
let's try this out I'm going to refresh here and I'm going to enter a title
here and I'm going to enter a title which is only one letter and let's see
which is only one letter and let's see there we go we have an error now which
there we go we have an error now which says that minimum length of three
says that minimum length of three letters is required so again if this is
letters is required so again if this is not impressive to you you're probably
not impressive to you you're probably coming from a background of single page
coming from a background of single page applications right but this is really
applications right but this is really cool because we're technically doing
cool because we're technically doing this client level stuff purely inside of
this client level stuff purely inside of a server component which has Progressive
a server component which has Progressive enhancement and not a single line of
enhancement and not a single line of JavaScript is needed uh sorry let me
JavaScript is needed uh sorry let me rephrase this you don't have to have
rephrase this you don't have to have JavaScript enabled in your browser for
JavaScript enabled in your browser for you to do these actions right so that's
you to do these actions right so that's why that's one of the main things of
why that's one of the main things of server actions which they allow us great
server actions which they allow us great so now you know how to create um how to
so now you know how to create um how to create uh uh uh errors right right but
create uh uh uh errors right right but one thing that you still don't know how
one thing that you still don't know how to do is how to handle loading States
to do is how to handle loading States right when I go ahead and create
right when I go ahead and create something let's say it's successful I
something let's say it's successful I wanted this little submit button to be
wanted this little submit button to be disabled or maybe even the input itself
disabled or maybe even the input itself I want it to be disabled so how do we do
I want it to be disabled so how do we do that so what I want to do now is I want
that so what I want to do now is I want to separate my input component into a
to separate my input component into a separate component as well as my button
separate component as well as my button component so I'm going to do that right
component so I'm going to do that right here I'm going to copy this input
here I'm going to copy this input component here and I'm just going to
component here and I'm just going to create it here like it doesn't really
create it here like it doesn't really matter because we're going to remove
matter because we're going to remove these files later so go ahead and create
these files later so go ahead and create input uh. CSX or let's call it form
input uh. CSX or let's call it form input. CSX like that and let's go ahead
input. CSX like that and let's go ahead and do export con form
and do export con form input and let's just return this uh
input and let's just return this uh input like
input like that and what I want to do next is I
that and what I want to do next is I also want to pass in this errors right
also want to pass in this errors right so I want our component to be able to
so I want our component to be able to render both of those so for that I'm
render both of those so for that I'm going to wrap the entire thing uh inside
going to wrap the entire thing uh inside of a
of a div like
div like this great so now we have to create uh a
this great so now we have to create uh a little prop which is going to accept
little prop which is going to accept those errors so I'm going to create an
those errors so I'm going to create an interface form input props which is
interface form input props which is going to accept an errors which is going
going to accept an errors which is going to be uh an
to be uh an optional well let's just do it a record
optional well let's just do it a record string
string any like that and then let's go ahead
any like that and then let's go ahead and assign that so form input props
and assign that so form input props let's extract the errors and then we can
let's extract the errors and then we can just use the errors directly and the
just use the errors directly and the errors directly like this or perhaps we
errors directly like this or perhaps we can do it in a better way we can just
can do it in a better way we can just pass in the title which is optional and
pass in the title which is optional and the string of arrays because we know
the string of arrays because we know that's what is going to look like of
that's what is going to look like of course this is you can see that this
course this is you can see that this form input component would not exactly
form input component would not exactly be reusable because we are fixating on
be reusable because we are fixating on the title prop but this is just for
the title prop but this is just for example right so make sure you do this
example right so make sure you do this and then we can remove this entire input
and then we can remove this entire input and we can use the form input from dot
and we can use the form input from dot whoops from slash uh form input so make
whoops from slash uh form input so make sure you add that import here great and
sure you add that import here great and let's pass in the errors which are going
let's pass in the errors which are going to be state. errors like that and I'm
to be state. errors like that and I'm just going to pass in uh the question
just going to pass in uh the question mark here just in case state is
mark here just in case state is undefined all right and right now I
undefined all right and right now I think everything should still be working
think everything should still be working exactly the same so I'm going to write a
exactly the same so I'm going to write a short title and there we go I still have
short title and there we go I still have my errors so how do I disable this while
my errors so how do I disable this while it is submitting well for that we can
it is submitting well for that we can use a hook called use form status so
use a hook called use form status so let's go ahead and first let's mark this
let's go ahead and first let's mark this as use client that's important so use
as use client that's important so use client and then write a constant here
client and then write a constant here use form uh status from react Dum so you
use form uh status from react Dum so you can see no external libraries here we're
can see no external libraries here we're purely using what react offers us and
purely using what react offers us and from here let's extract the pending
from here let's extract the pending State and now let's go ahead and let's
State and now let's go ahead and let's add a disabled let's go at the end here
add a disabled let's go at the end here and add the disabled prop uh to be when
and add the disabled prop uh to be when it's pending and let's actually replace
it's pending and let's actually replace this input to use the input from shaten
this input to use the input from shaten UI we don't have it so head into the
UI we don't have it so head into the terminal here let me just shut down my
terminal here let me just shut down my Prisma studio and run npx shed TN UI lat
Prisma studio and run npx shed TN UI lat add input like that so wait a second for
add input like that so wait a second for that to install all right and let's
that to install all right and let's replace this native HTML input with the
replace this native HTML input with the input from the components UI input uh
input from the components UI input uh great and we can remove this class name
great and we can remove this class name now so I had this class name for Border
now so I had this class name for Border black but we don't need it if we're
black but we don't need it if we're using the input from
using the input from shaten uh great and now let's take a
shaten uh great and now let's take a look if anything changes so I'm going to
look if anything changes so I'm going to write test here I will click submit and
write test here I will click submit and you can see how it was disabled for a
you can see how it was disabled for a second you can see even when I have an
second you can see even when I have an error but especially now you saw for a
error but especially now you saw for a second how I have uh a little cursor my
second how I have uh a little cursor my cursor changed that I cannot edit this
cursor changed that I cannot edit this so we successfully used this use form
so we successfully used this use form status when does this use form status
status when does this use form status work where does this pending come from
work where does this pending come from well it works because our form input
well it works because our form input component is inside of a form component
component is inside of a form component so that's why it is working and we can
so that's why it is working and we can do the exact same thing for this button
do the exact same thing for this button so let's copy this button component and
so let's copy this button component and let's create a new one called form
let's create a new one called form button. CSX so I'm just creating a
button. CSX so I'm just creating a random components here and let's do
random components here and let's do export cons form
button and let's just return this items here let's import the button from shat
here let's import the button from shat CN and let's go ahead and let's add
CN and let's go ahead and let's add const use form status from react Dom and
const use form status from react Dom and let's extract the
let's extract the pending and let's go
pending and let's go ahead and give it a disabled prop of
ahead and give it a disabled prop of pending like
pending like this and now go back inside of form
this and now go back inside of form right here and replace this button with
right here and replace this button with form button
form button component like that so now our submit
component like that so now our submit should also be disabled when it's
should also be disabled when it's creating there we go you can see how my
creating there we go you can see how my button is disabled perfect and using the
button is disabled perfect and using the exact same method we can do it for the
exact same method we can do it for the delete button right here so let's just
delete button right here so let's just quickly do that do that so we wrap up
quickly do that do that so we wrap up this thing so we have this board
this thing so we have this board component where we have the delete
component where we have the delete button and I'm going to create a new one
button and I'm going to create a new one called form delete.
TSX so let me just show you there we go form delete. DSX export
form delete. DSX export const form
delete and let's just import this button from components UI
from components UI button and let's go ahead and let's add
button and let's go ahead and let's add con pending from use form status from
con pending from use form status from react Dum and give it a disabled prop
react Dum and give it a disabled prop when it's
when it's pending and then let's go back inside of
pending and then let's go back inside of our board and replace this with form
our board and replace this with form delete from this component which we just
delete from this component which we just created right here uh and let's go ahead
created right here uh and let's go ahead and make sure that form delete is a
and make sure that form delete is a client component so use
client component so use client so we get rid of this error and
client so we get rid of this error and let's see if that is enough for this to
let's see if that is enough for this to work when I click delete there we go you
work when I click delete there we go you can see how it's disabled until it is
can see how it's disabled until it is finished perfect so you just learned a
finished perfect so you just learned a bunch of new things about server
bunch of new things about server components uh you see everything that is
components uh you see everything that is available now and what we're going to do
available now and what we're going to do in the next part is we're going to start
in the next part is we're going to start creating some abstractions around this
creating some abstractions around this basically I want to create a reusable
basically I want to create a reusable way to use this server actions because
way to use this server actions because sure this is great but it seems like a
sure this is great but it seems like a lot of work right and that's not at
lot of work right and that's not at fault for the server actions you know
fault for the server actions you know this we have to create a structure for
this we have to create a structure for it keep in mind that you we are all
it keep in mind that you we are all probably uh a little spoiled by this
probably uh a little spoiled by this amazing libraries like react query uh
amazing libraries like react query uh which offers an easy use mutation and
which offers an easy use mutation and immediately gives us the loading status
immediately gives us the loading status and the success message the error
and the success message the error message all of those things so we're
message all of those things so we're going to attempt to recreate uh us
going to attempt to recreate uh us mutation kind of uh to create our
mutation kind of uh to create our reusable server components and basically
reusable server components and basically there is not going to be a single API
there is not going to be a single API endpoint uh which does post patch or
endpoint uh which does post patch or delete in this tutorial we are entirely
delete in this tutorial we are entirely going to use server actions even for
going to use server actions even for generating the stripe checkout page the
generating the stripe checkout page the the only place where we're going to use
the only place where we're going to use API calls is to fetch some information
API calls is to fetch some information inside of those drag and drop components
inside of those drag and drop components which simply have to be client
which simply have to be client components no matter how much I tried to
components no matter how much I tried to make them server components uh I
make them server components uh I couldn't do it because the package
couldn't do it because the package itself requires work with client
itself requires work with client components but that's not a big deal you
components but that's not a big deal you know the the existence of server
know the the existence of server components doesn't make it so that using
components doesn't make it so that using client components is something bad if
client components is something bad if you want to you can use client
you want to you can use client components absolutely everywhere you
components absolutely everywhere you just always have an option to use server
just always have an option to use server components which definitely have its
components which definitely have its perks and right now now they might seem
perks and right now now they might seem complicated it might seem a bit weird
complicated it might seem a bit weird but keep in mind that they literally
but keep in mind that they literally just came out so we still have months
just came out so we still have months and years until people create amazing
and years until people create amazing packages around them and we're going to
packages around them and we're going to have something like react query but for
have something like react query but for Server components very soon I'm sure of
Server components very soon I'm sure of it great great job so far and let's go
it great great job so far and let's go ahead and start creating those
ahead and start creating those abstractions
abstractions now all right so now that we know the
now all right so now that we know the basics of server actions and we've cover
basics of server actions and we've cover cover the most primitive way of using it
cover the most primitive way of using it we're going to create the following
we're going to create the following abstraction so let me go ahead and
abstraction so let me go ahead and expand my screen and here I've prepared
expand my screen and here I've prepared something so we're going to uh extract
something so we're going to uh extract This Server action which we have right
This Server action which we have right now as you can see in this actions
now as you can see in this actions folder let me show you so we have an
folder let me show you so we have an individual action like create board so
individual action like create board so that represents This Server action we're
that represents This Server action we're going to create that into a folder which
going to create that into a folder which is going to have types which are where
is going to have types which are where we going to Define what kind of stuff we
we going to Define what kind of stuff we expect the user to input and we're also
expect the user to input and we're also going to Define what exactly we expect
going to Define what exactly we expect to Output from this server action which
to Output from this server action which can either be uh an error or for example
can either be uh an error or for example it can be a specific type from Prisma
it can be a specific type from Prisma database we're also going to create a
database we're also going to create a file called schema. CS where we are
file called schema. CS where we are going to keep our Zod validation and
going to keep our Zod validation and that schema is actually going to be our
that schema is actually going to be our input type and lastly we're going to
input type and lastly we're going to have the index TS which is going to be
have the index TS which is going to be the server action itself and all of that
the server action itself and all of that is going to be combined inside of create
is going to be combined inside of create safe action as you see right here so
safe action as you see right here so that's going to be a wrapper which is
that's going to be a wrapper which is going to combine all of them and that is
going to combine all of them and that is going to leave us with the following
going to leave us with the following thing so we're going to create a hook
thing so we're going to create a hook called use action as you can see right
called use action as you can see right here and inside we're going to pass this
here and inside we're going to pass this safe action which is going to be this
safe action which is going to be this wrapper which is going to combine all of
wrapper which is going to combine all of those and from this use action we be
those and from this use action we be able to uh extract the execute function
able to uh extract the execute function itself which is going to be used to call
itself which is going to be used to call the server action we're going to be able
the server action we're going to be able to extract the data the error if it's a
to extract the data the error if it's a server error like something went wrong
server error like something went wrong in database or if it is a field error
in database or if it is a field error like um I don't know the title is too
like um I don't know the title is too short and besides that we're also going
short and besides that we're also going to have some callbacks so we don't have
to have some callbacks so we don't have to rely on putting this stuff inside the
to rely on putting this stuff inside the fuse effect to I don't know just render
fuse effect to I don't know just render a success message or something so we're
a success message or something so we're going to have an unsuccess callback
going to have an unsuccess callback which is going to give us the data uh
which is going to give us the data uh inside of its params here and that's
inside of its params here and that's going to be a type of output which we
going to be a type of output which we defined all the way here so it's going
defined all the way here so it's going to be completely type safe from start to
to be completely type safe from start to finish on error is going to produce an
finish on error is going to produce an error from the server and on complete is
error from the server and on complete is simply going to be something like
simply going to be something like finally right so regardless if we
finally right so regardless if we succeeded or not uh that's what use
succeeded or not uh that's what use sorry that's what on complete is going
sorry that's what on complete is going to
to be so let's let's go ahead and let's now
be so let's let's go ahead and let's now create this create safe safe action. TS
create this create safe safe action. TS so I'm going to go ahead inside of I'm
so I'm going to go ahead inside of I'm first I'm going to close everything and
first I'm going to close everything and then I'm going to go inside of my lib
then I'm going to go inside of my lib folder and create a new file called
folder and create a new file called create save
create save action.
action. TS and first thing I want to do is I
TS and first thing I want to do is I want to import Zod like this and then
want to import Zod like this and then I'm going to write export type field
I'm going to write export type field errors so these are going to be generic
errors so these are going to be generic errors which we are able to get from Zod
errors which we are able to get from Zod validation which are going to be
validation which are going to be individual field errors so that's going
individual field errors so that's going to be a K in key of T because the t is
to be a K in key of T because the t is going to be an object with fields which
going to be an object with fields which have our errors and they are going to
have our errors and they are going to produce an array of strings where each
produce an array of strings where each string is going to be an individual
string is going to be an individual error and then let's write export type
error and then let's write export type action
state which will accept a generic T input
input and the
output that's going to be an object which is going to have optional field
which is going to have optional field errors which is a type of field
errors which is a type of field errors which is going to validate the
errors which is going to validate the input which we send uh to that uh well
input which we send uh to that uh well function and then we're going to have an
function and then we're going to have an optional error which is going to be a
optional error which is going to be a server error like something went wrong
server error like something went wrong in the database right so that's either a
in the database right so that's either a string or null and lastly we're going to
string or null and lastly we're going to have an optional result which we
have an optional result which we represent as data which is going to be
represent as data which is going to be our T output so with these two actions
our T output so with these two actions we've created generics uh which will
we've created generics uh which will work with any type of action we have
work with any type of action we have right all we have to do is send in the
right all we have to do is send in the input which we expect the user to pass
input which we expect the user to pass and the output which we expect uh the
and the output which we expect uh the user to receive which can either be you
user to receive which can either be you know a success like data which is going
know a success like data which is going to be some Prisma type like a board or
to be some Prisma type like a board or an error which is a string or field
an error which is a string or field errors which is going to be as you can
errors which is going to be as you can see an object which has a specific key
see an object which has a specific key inside and then an array of Errors
inside and then an array of Errors inside like that and then let's actually
inside like that and then let's actually create the safe action so export con
create the safe action so export con create safe action is going to be a t
create safe action is going to be a t input and T
output and that's going to have a pram of schema which is z.
of schema which is z. schema pass inside is going to be T
schema pass inside is going to be T input
input besides the schema we're also going to
besides the schema we're also going to have uh the Handler itself which will
have uh the Handler itself which will have the validated data which is T
have the validated data which is T input and we're going to return a
input and we're going to return a promise which is going to return the
promise which is going to return the action State and one more time inside
action State and one more time inside we're going to pass in the T
we're going to pass in the T input and T
input and T output like that and let's go ahead and
output like that and let's go ahead and return this and now let's write return
return this and now let's write return asynchronous function which accepts the
asynchronous function which accepts the data which is T input and returns a
data which is T input and returns a promise which is action State the input
promise which is action State the input and the
output let's go ahead and open this Arrow function and first let's validate
Arrow function and first let's validate our schema so const validation result is
our schema so const validation result is going to be schema. safe pars
going to be schema. safe pars data and if we haven't received a
data and if we haven't received a success message so if exclamation point
success message so if exclamation point validation result.
validation result. success then let's go ahead and let's
success then let's go ahead and let's return one of the possible types for
return one of the possible types for this action state in this case it's just
this action state in this case it's just going to return field errors so there's
going to return field errors so there's no error in the database and there's no
no error in the database and there's no data to return we just have filled
data to return we just have filled errors to work with if this scheme of
errors to work with if this scheme of validation fails using Zod so let's
validation fails using Zod so let's return the field errors you can see how
return the field errors you can see how we already have typescript here and
we already have typescript here and we're going to return validation result.
we're going to return validation result. error. flatten do field errors and we're
error. flatten do field errors and we're going to manually write as field errors
going to manually write as field errors which holds the T input inside so let me
which holds the T input inside so let me just zoom out quickly so you can see it
just zoom out quickly so you can see it in one line like
in one line like that and then outside of this if
that and then outside of this if function we're going to go ahead and
function we're going to go ahead and return the Handler which gets the
return the Handler which gets the validated data so validation result do
validated data so validation result do data like that perfect so we finished
data like that perfect so we finished our save action Handler and now we're
our save action Handler and now we're going to go ahead and modify the
going to go ahead and modify the function to actually uh be able to uh
function to actually uh be able to uh well be used by this create save
well be used by this create save action so let's go inside of our actions
action so let's go inside of our actions folder and let's go ahead and create a
folder and let's go ahead and create a new folder called create
new folder called create dashboard and inside first let's create
dashboard and inside first let's create schema. TS
schema. TS and in inside let's import Zod and let's
and in inside let's import Zod and let's write const create board to be Z
write const create board to be Z doob and let's render uh well let's pass
doob and let's render uh well let's pass in what we expect from the form to come
in what we expect from the form to come here in our case for now that's just
here in our case for now that's just going to be a string which is a title of
going to be a string which is a title of course and we're going to pass in the
course and we're going to pass in the required error to be title is required
required error to be title is required we're also going to pass in the invalid
we're also going to pass in the invalid type error to be uh typ is required as
type error to be uh typ is required as well and then let's add a minimum value
well and then let's add a minimum value of three and let's pass in the invalid
of three and let's pass in the invalid type sorry the uh message
type sorry the uh message inside to be title is too
inside to be title is too short like that perfect so we now
short like that perfect so we now created our uh create board schema and
created our uh create board schema and let's also immediately export
let's also immediately export that and now let's create the file which
that and now let's create the file which is going to hold our types so input and
is going to hold our types so input and output typ type inside of this folder
output typ type inside of this folder create a new file types. DS on the same
create a new file types. DS on the same level as schema and in here let's go
level as schema and in here let's go ahead and let's import Zod again let's
ahead and let's import Zod again let's go ahead and let's import board from
go ahead and let's import board from Prisma client so that's going to be our
Prisma client so that's going to be our expected output let's go ahead and
expected output let's go ahead and import action state from lib create save
import action state from lib create save action and finally let's import the
action and finally let's import the actual schema which is create board from
actual schema which is create board from do/
do/ schema and then simply export type input
schema and then simply export type input type to be z.
type to be z. info type of create
info type of create board and let's export type return type
board and let's export type return type to be action state which accepts the the
to be action state which accepts the the input type and the return type which is
input type and the return type which is board like
board like that and now we can finally create our
that and now we can finally create our Handler function so that's actually
Handler function so that's actually going to be this create board so let's
going to be this create board so let's just copy it and paste it inside of this
just copy it and paste it inside of this folder like that and just ignore all the
folder like that and just ignore all the errors for now and let's rename it to be
errors for now and let's rename it to be index.ts like that and now let's go
index.ts like that and now let's go ahead and modify slightly so I think
ahead and modify slightly so I think it's actually easier for us to remove
it's actually easier for us to remove everything from here and let's start by
everything from here and let's start by marking this as use server and then
marking this as use server and then let's write const Handler to be an
let's write const Handler to be an asynchronous function which accepts the
asynchronous function which accepts the data which is going to be the input type
data which is going to be the input type which we can now get from do/ types
which we can now get from do/ types because we are in the same folder right
because we are in the same folder right here and the return type is going to be
here and the return type is going to be a promise which accepts the return type
a promise which accepts the return type again from do/ uh
again from do/ uh types and let's go ahead and open this
types and let's go ahead and open this Arrow function and first thing that I
Arrow function and first thing that I want to check if I if we have user ID or
want to check if I if we have user ID or not from out clerk nextjs so we're all
not from out clerk nextjs so we're all already going to start implementing the
already going to start implementing the authentication inside of this Handler
authentication inside of this Handler which you can IM imagine as an API route
which you can IM imagine as an API route so if we don't have user ID in that case
so if we don't have user ID in that case let's just go ahead and return an error
let's just go ahead and return an error and you can see how we have type safe
and you can see how we have type safe type safety here right because we return
type safety here right because we return uh this return type which can have field
uh this return type which can have field errors error or data which is everything
errors error or data which is everything we defined inside of this types right
we defined inside of this types right here and because we wrapped it inside of
here and because we wrapped it inside of action state from create safe action you
action state from create safe action you can see that that's exactly what we
can see that that's exactly what we expect so from our server actions now we
expect so from our server actions now we can only return uh specific items so
can only return uh specific items so let's go ahead and write this error to
let's go ahead and write this error to be
be unauthorized great and now outside of
unauthorized great and now outside of here let's go ahead and let's extract
here let's go ahead and let's extract the title from this data which is
the title from this data which is already going to be validated at this
already going to be validated at this point because we're going to wrap it in
point because we're going to wrap it in safe action and if you take a look at
safe action and if you take a look at our safe action once we pass in the
our safe action once we pass in the schema and once we pass in the actual
schema and once we pass in the actual data you can see that it is validated
data you can see that it is validated right here so we are already going to
right here so we are already going to have have some fielded errors and we
have have some fielded errors and we don't have to do that every single time
don't have to do that every single time we do it here perfect so we can safely
we do it here perfect so we can safely extract the title from the data now and
extract the title from the data now and you can see how when I hover it expects
you can see how when I hover it expects the title in here and now let's go ahead
the title in here and now let's go ahead uh and let's simply write let board like
uh and let's simply write let board like that and let's open a try and catch
that and let's open a try and catch block inside of the try block let's
block inside of the try block let's simply write board to be equal to await
simply write board to be equal to await db. board. create and let's also import
db. board. create and let's also import the DB from at SL lib DB I'm just going
the DB from at SL lib DB I'm just going to move it
to move it here so let's go ahead and give it data
here so let's go ahead and give it data to just be
to just be title great uh and now let's go ahead
title great uh and now let's go ahead inside of the catch function and let's
inside of the catch function and let's get our error and if that happens we're
get our error and if that happens we're going to Simply return the error and
going to Simply return the error and that's going to be failed to create or
that's going to be failed to create or you can write database error or or
you can write database error or or internal error something like that
internal error something like that perfect and now let's go ahead uh and go
perfect and now let's go ahead uh and go further so if this catch did not happen
further so if this catch did not happen in that case let's revalidate the path
in that case let's revalidate the path using next cache so I'm just going to
using next cache so I'm just going to move it
move it here let's revalidate the following path
here let's revalidate the following path so open Matic SL board SL board ID which
so open Matic SL board SL board ID which we just created so we don't have this
we just created so we don't have this route yet but we are revalidating it for
route yet but we are revalidating it for future cases because that's where we are
future cases because that's where we are going to redirect and then let's simply
going to redirect and then let's simply return data board like this perfect and
return data board like this perfect and then last thing we have to do is export
then last thing we have to do is export cons create board to be create safe
cons create board to be create safe action which I just imported from at/ Li
action which I just imported from at/ Li create safe
create safe action and in the first argument let's
action and in the first argument let's pass in the create board schema so make
pass in the create board schema so make sure you import that from do/ schema as
sure you import that from do/ schema as I did right here and the second argument
I did right here and the second argument is going to be the Handler like that and
is going to be the Handler like that and there we go no errors so now this create
there we go no errors so now this create board in uh is going to be a promise
board in uh is going to be a promise which is going to return our title uh
which is going to return our title uh well is going to return our board right
well is going to return our board right but right now that's only the title
but right now that's only the title perfect what we have to do now is create
perfect what we have to do now is create a hook which is going to be able to
a hook which is going to be able to accept this safe action and give us all
accept this safe action and give us all those useful callbacks like on success
those useful callbacks like on success on complete Etc so let's go ahead uh and
on complete Etc so let's go ahead uh and let's just save this form it doesn't
let's just save this form it doesn't matter if you have an unsaved form just
matter if you have an unsaved form just save it it it's going to have an error
save it it it's going to have an error but it doesn't matter because we're
but it doesn't matter because we're going to go back and fix it now let's go
going to go back and fix it now let's go inside of hooks and create a new file
inside of hooks and create a new file use action. TS and first thing I want to
use action. TS and first thing I want to do is I want to import use State and use
do is I want to import use State and use call back
call back from react itself and then let's go
from react itself and then let's go ahead and let's import action state and
ahead and let's import action state and field
field errors from at/ lib SLC create safe
errors from at/ lib SLC create safe action and then let's create a type
action and then let's create a type action to accept the T input T
output and a data which is T input and it returns a promise which is an action
it returns a promise which is an action state which we just imported and that
state which we just imported and that uses the T input and T
uses the T input and T output and let me just zoom out for a
output and let me just zoom out for a second so you can see it in one line
second so you can see it in one line like that make sure you have that great
like that make sure you have that great and then let's create the interface to
and then let's create the interface to what we expect from this hook so
what we expect from this hook so interface use action options is going to
interface use action options is going to work with the
work with the output it's going to have an optional on
output it's going to have an optional on success Handler which is going to give
success Handler which is going to give us the data which is a type of the
us the data which is a type of the output and let's just not misspell
output and let's just not misspell output
output and returns a void then we're going to
and returns a void then we're going to have on error which is also an optional
have on error which is also an optional call back which gives us the server
call back which gives us the server error and we can then work with it if we
error and we can then work with it if we want to do that and on complete which is
want to do that and on complete which is simply going to be uh a finally function
simply going to be uh a finally function so nothing in the params here and now
so nothing in the params here and now let's finally write export const use
let's finally write export const use action which is going to accept the T
action which is going to accept the T input T
input T output
the first prop is going to be action which is a type of action which we
which is a type of action which we defined right here whoops and inside of
defined right here whoops and inside of that action we have to pass in the T
that action we have to pass in the T input and T output so let's give it that
input and T output so let's give it that t input and T
t input and T output besides that we're going to have
output besides that we're going to have options which are going to be use action
options which are going to be use action options and works with the
options and works with the output and by default that's going to be
output and by default that's going to be an empty object so let's open this Arrow
an empty object so let's open this Arrow function like this and let's just not
function like this and let's just not misspell the output like that and also I
misspell the output like that and also I forgot uh the equal sign here all right
forgot uh the equal sign here all right and first let's go ahead and let's
and first let's go ahead and let's create a state for field errors and
create a state for field errors and let's create a set field errors that's
let's create a set field errors that's going to be working with used
going to be working with used State and that use state is going to
State and that use state is going to have a type of field errors and the
have a type of field errors and the generic inside the input
generic inside the input great uh and by default let's give it
great uh and by default let's give it undefined and besides T input it's also
undefined and besides T input it's also sorry besides filled errors give it a
sorry besides filled errors give it a pipe of undefined so that's also a type
pipe of undefined so that's also a type of uh a type of well prop it can work
of uh a type of well prop it can work with great so just make sure you have uh
with great so just make sure you have uh it written like
it written like that besides filled errors we're also
that besides filled errors we're also going to have the actual error state
going to have the actual error state from the database so set
from the database so set error and inside use state which is a
error and inside use state which is a type of string or undefined and let's
type of string or undefined and let's give it an undefined by default and cons
give it an undefined by default and cons data set data which is again use state
data set data which is again use state which works with the
which works with the output or
output or undefined and by default it is
undefined and by default it is undefined and lastly we're going to have
undefined and lastly we're going to have is loading and set is
is loading and set is loading which is use State and it's
loading which is use State and it's going to be a Boolean which by default
going to be a Boolean which by default is
is false great so now we have these fills
false great so now we have these fills here and we can work with them so let's
here and we can work with them so let's write const execute to be use
write const execute to be use callback which is going to be an
callback which is going to be an asynchronous function which accepts the
asynchronous function which accepts the input which is T
input which is T input and right on the start we're going
input and right on the start we're going to set his loading to be true and then
to set his loading to be true and then we're going to open a try and catch
we're going to open a try and catch block so let's try and get the result
block so let's try and get the result from the Handler so const result is
from the Handler so const result is await action and pass in the
await action and pass in the input which is a type of T input as you
input which is a type of T input as you can see here if we don't have any
can see here if we don't have any result just break the
result just break the function and then if we have result
function and then if we have result field errors in that case let's set
field errors in that case let's set field errors to be result field
field errors to be result field errors if we have result. error like a
errors if we have result. error like a server error in that that case set error
server error in that that case set error to be result.
to be result. error and let's go ahead and write the
error and let's go ahead and write the last one so if result. data in that case
last one so if result. data in that case set data to be result result. data and
set data to be result result. data and while we are here let's also assign some
while we are here let's also assign some callbacks so we can do that for the
callbacks so we can do that for the error right here let's write options
error right here let's write options Doon error question
Doon error question mark. result. error and we're going to
mark. result. error and we're going to do this same thing but for the data here
do this same thing but for the data here so options on success result. data great
so options on success result. data great and now outside of this try and catch
and now outside of this try and catch block so let's add the catch block here
block so let's add the catch block here and let's go ahead sorry not the catch
and let's go ahead sorry not the catch block the finally block right so just
block the finally block right so just like that we're going to set is loading
like that we're going to set is loading to be false because it finished and
to be false because it finished and options on
options on complete and just execute it like that
complete and just execute it like that perfect so now let's go ahead and let's
perfect so now let's go ahead and let's pass in the action and the
pass in the action and the options there we go perfect so let's
options there we go perfect so let's take a look at it again so we have this
take a look at it again so we have this execute function which we are going to
execute function which we are going to call inside of our components which is
call inside of our components which is an asynchronous function which accepts
an asynchronous function which accepts the input which we have and we send that
the input which we have and we send that input inside of our action and then that
input inside of our action and then that action is going to run it uh through our
action is going to run it uh through our action State and make sure it's
action State and make sure it's validated if for any reason we don't
validated if for any reason we don't have any result there's nothing left to
have any result there's nothing left to do with this action we're not going to
do with this action we're not going to have any callbacks because obviously
have any callbacks because obviously something went wrong out of our reach if
something went wrong out of our reach if we have field errors it means that uh
we have field errors it means that uh something went wrong with the validation
something went wrong with the validation and we don't have a call back for that
and we don't have a call back for that because well we don't really need one
because well we don't really need one we're simply going to extract the field
we're simply going to extract the field errors and then we're going to pass them
errors and then we're going to pass them to the components which need them if we
to the components which need them if we have a server error we're going to set
have a server error we're going to set the error and we're also going to add a
the error and we're also going to add a options call back on error because
options call back on error because that's not the type of error which we're
that's not the type of error which we're going to render under the input field
going to render under the input field that would be an error which we would
that would be an error which we would render inside of a toast right like
render inside of a toast right like something went wrong or internal error
something went wrong or internal error and finally if we have result data it
and finally if we have result data it means that it successfully created the
means that it successfully created the record and then we're going to add a
record and then we're going to add a call back on success so we can work with
call back on success so we can work with that record like redirecting to a
that record like redirecting to a specific ID of that record or simply
specific ID of that record or simply showing showing a success message as
showing showing a success message as well and finally we're going to have on
well and finally we're going to have on complete
complete which well serves as the finally prop
which well serves as the finally prop nothing more nothing less and last thing
nothing more nothing less and last thing we have to do is actually uh return all
we have to do is actually uh return all of those so return execute field errors
of those so return execute field errors error and data and is
error and data and is loading great so we just finished our
loading great so we just finished our use action Handler so just a quick
use action Handler so just a quick reminder if any of this feels too much
reminder if any of this feels too much for you or you f feel like you've made
for you or you f feel like you've made some mistakes you can always visit my
some mistakes you can always visit my GitHub and take a look at these uh big
GitHub and take a look at these uh big files which we just created which are
files which we just created which are this use action and this lib create safe
this use action and this lib create safe action right here as well as the actions
action right here as well as the actions folder where I have my index schema and
folder where I have my index schema and type so you can always go ahead and
type so you can always go ahead and check them uh in my original source code
check them uh in my original source code perfect so what we have to do now is we
perfect so what we have to do now is we actually have to use that uh action
actually have to use that uh action inside of our
inside of our form so let's go back inside of our app
form so let's go back inside of our app folder platform dashboard organization
folder platform dashboard organization organization ID and in here we should
organization ID and in here we should have the form where we are using this
have the form where we are using this use form state to get the create
use form state to get the create function so we can now remove these two
function so we can now remove these two items from here and we can remove this
items from here and we can remove this uh create instead what we're going to do
uh create instead what we're going to do is we're going to uh import create board
is we're going to uh import create board like that and we don't need the button
like that and we don't need the button or use form State and let's go ahead and
or use form State and let's go ahead and let's do the following so we're going to
let's do the following so we're going to write
write const use action from hooks use action
const use action from hooks use action which we just created and inside we're
which we just created and inside we're going to go ahead and pass the create
going to go ahead and pass the create board action like that and then from
board action like that and then from here we're going to be able to extract
here we're going to be able to extract the execute function as well as the
the execute function as well as the field errors so let's replace this state
field errors so let's replace this state errors with those field errors and let's
errors with those field errors and let's go ahead and write const on
go ahead and write const on submit to accept form data which is a
submit to accept form data which is a type of form data
and we're going to pass that inside of this action here and then let's extract
this action here and then let's extract the title to be form data. getet title
the title to be form data. getet title as
as string and well that's it for now that's
string and well that's it for now that's the only field we have in the database
the only field we have in the database and all we have to do is call the
and all we have to do is call the execute function and pass in the
title just like that and the cool thing we can do now is call this callbacks so
we can do now is call this callbacks so just add a a comma and open up an object
just add a a comma and open up an object and in here as you can see we have the
and in here as you can see we have the on complete on error on success so let's
on complete on error on success so let's call the on success let's get the data
call the on success let's get the data and let's conso log the data
and let's conso log the data success like that let's add on error
success like that let's add on error here which will get our error and in
here which will get our error and in here console error
here console error error perfect so let's try that out now
error perfect so let's try that out now so I'm going to go ahead and I'm going
so I'm going to go ahead and I'm going to refresh my app
to refresh my app here and I'm going to add test click
here and I'm going to add test click submit and there we go you can see that
submit and there we go you can see that my call back is working and I have the
my call back is working and I have the immediate access to this record which
immediate access to this record which was just created in my callback perfect
was just created in my callback perfect so let's try it out and let's throw uh
so let's try it out and let's throw uh an error for example well actually let's
an error for example well actually let's do the quicker thing which is just
do the quicker thing which is just entering a very short name like the
entering a very short name like the there we go you can see that it works as
there we go you can see that it works as well perfect so this field errors is
well perfect so this field errors is working and now let's go back inside of
working and now let's go back inside of our actions create board index right
our actions create board index right here and inside of here let's throw new
error it doesn't really matter let's just throw an error like that and let's
just throw an error like that and let's see if that's going to uh give an error
see if that's going to uh give an error inside of our console which is later
inside of our console which is later going to be our well post notification
going to be our well post notification that something is wrong so once I click
that something is wrong so once I click submit there we go I have an error
submit there we go I have an error failed to create which is the exact
failed to create which is the exact error which we sent from the Handler
error which we sent from the Handler right here as you can see in the catch
right here as you can see in the catch function we send an error failed to
function we send an error failed to create and then we have access to it
create and then we have access to it inside of this use action hook on error
inside of this use action hook on error callback perfect so you just flawlessly
callback perfect so you just flawlessly executed our vision which we had right
executed our vision which we had right here so we combined our server action
here so we combined our server action with three individual files all that so
with three individual files all that so we can use it in a reusable way using
we can use it in a reusable way using use action so this was my idea of
use action so this was my idea of creating a kind of a clone of use
creating a kind of a clone of use mutation which react query offers us
mutation which react query offers us which we are used to right uh of of
which we are used to right uh of of course this is my first time working
course this is my first time working with server actions and I just kind of
with server actions and I just kind of experimented with this if you have any
experimented with this if you have any uh criticism I will be very happy to
uh criticism I will be very happy to hear it especially if it's optimization
hear it especially if it's optimization wise or security wise that would be
wise or security wise that would be great uh and you know your general
great uh and you know your general opinions about this hook feel free to
opinions about this hook feel free to leave them in the comments below uh
leave them in the comments below uh great great
great great job now that we have our abstraction
job now that we have our abstraction around server actions let's go ahead and
around server actions let's go ahead and let's create some custom comp components
let's create some custom comp components similar to these ones which we just
similar to these ones which we just worked with which are the custom input
worked with which are the custom input component which we named form input
component which we named form input custom form button and similar to that
custom form button and similar to that so I'm going to go ahead and close
so I'm going to go ahead and close everything here and I'm going to go
everything here and I'm going to go inside of my components folder and in
inside of my components folder and in here I'm going to go ahead and create a
here I'm going to go ahead and create a new folder called form like that so
new folder called form like that so inside of here I'm going to keep
inside of here I'm going to keep everything regarding my reusable forms
everything regarding my reusable forms so first thing I want to create is the
so first thing I want to create is the actual form input which has the same
actual form input which has the same name as the one we created in the
name as the one we created in the organization ID folder we're going to
organization ID folder we're going to delete it later but for now I just want
delete it later but for now I just want to create a reusable component uh that
to create a reusable component uh that is going to behave similarly to that one
is going to behave similarly to that one so first things first we're going to
so first things first we're going to mark it as used client because I already
mark it as used client because I already know that we're going to use that uh use
know that we're going to use that uh use form status to extract the pending state
form status to extract the pending state from it so this can easily be used
from it so this can easily be used inside of any form and it will
inside of any form and it will automatically be uh pending once the
automatically be uh pending once the form is submitting as well as the fact
form is submitting as well as the fact that we're going to have a lot of
that we're going to have a lot of handlers like on blur on change and
handlers like on blur on change and similar to those so let's create the
similar to those so let's create the interface form input props and first
interface form input props and first things first we're going to need an ID
things first we're going to need an ID we're going to have an optional label
we're going to have an optional label we're going to have an optional string
we're going to have an optional string sorry an optional type uh an optional
sorry an optional type uh an optional placeholder so all of these are type of
placeholder so all of these are type of string then we're going to have a
string then we're going to have a required field which is an optional
required field which is an optional Boolean a disabled field if we want to
Boolean a disabled field if we want to manually disable it and we're going to
manually disable it and we're going to have some errors which are going to be a
have some errors which are going to be a record string and string array or
record string and string array or undefined my
undefined my apologies undefined goes here so like
apologies undefined goes here so like this all right and then we're going to
this all right and then we're going to have a optional class name if we want to
have a optional class name if we want to modify the style of this input we're
modify the style of this input we're going to have default value which is
going to have default value which is going to be an optional string and and
going to be an optional string and and let's just not misspell this so default
let's just not misspell this so default value and we're going to have on blur
value and we're going to have on blur which is going to be an optional void so
which is going to be an optional void so that's all we're going to need for now
that's all we're going to need for now because uh let's go ahead now and let's
because uh let's go ahead now and let's do export const form input and first
do export const form input and first things first that I want to do is I want
things first that I want to do is I want to enable uh a ref forwarding inside of
to enable uh a ref forwarding inside of this component so we're going to write
this component so we're going to write forward ref and we're going to import
forward ref and we're going to import that from react so let's go ahead and
that from react so let's go ahead and let's give it the types of HTML input
let's give it the types of HTML input element and form input props like that
element and form input props like that great go ahead and open parenthesis and
great go ahead and open parenthesis and go ahead and open parenthesis again and
go ahead and open parenthesis again and this represents the props so inside
this represents the props so inside let's extract the ID
let's extract the ID label type not the prop types my
label type not the prop types my apologies just the type
apologies just the type placeholder required disabled
placeholder required disabled errors class name
errors class name default
default value and we're also going to have on
value and we're also going to have on blur and let's go ahead and give the
blur and let's go ahead and give the default value a type of empty string
default value a type of empty string like that and then in here add a comma
like that and then in here add a comma after this the structured props and
after this the structured props and extract the ref and then before this
extract the ref and then before this parenthesis end go ahead and open up
parenthesis end go ahead and open up this
this function and now let's go ahead and
function and now let's go ahead and before we do anything so we get rid of
before we do anything so we get rid of this error whenever you use forward ref
this error whenever you use forward ref you need to add a display name and we
you need to add a display name and we can do that quite easily by just adding
can do that quite easily by just adding form input. display name to be form
form input. display name to be form input and there we go uh great and now
input and there we go uh great and now let's go ahead and let's actually uh
let's go ahead and let's actually uh extract pending from use form
extract pending from use form status which we can import right here
status which we can import right here from react
from react Dom and then let's go ahead and let's
Dom and then let's go ahead and let's return a div with a class name of space
return a div with a class name of space Y2 then inside let's go ahead and add
Y2 then inside let's go ahead and add another one with a class name of space
another one with a class name of space y1 and then we're going to conditionally
y1 and then we're going to conditionally render the label so for now I'm just
render the label so for now I'm just going to write label here and add an
going to write label here and add an else here to be null and let's now let's
else here to be null and let's now let's actually create this label so for that
actually create this label so for that I'm going to use shatan label component
I'm going to use shatan label component so go ahead and add npx shaten UI at
so go ahead and add npx shaten UI at latest AD Label
latest AD Label wait for a second for this to uh be
wait for a second for this to uh be added to your project and there we go go
added to your project and there we go go back inside and now we can replace this
back inside and now we can replace this D with a label which we can import again
D with a label which we can import again not from radic but from do/ UI label as
not from radic but from do/ UI label as you see right here but I don't like uh
you see right here but I don't like uh using it like that instead I'm going to
using it like that instead I'm going to write components you can of course
write components you can of course choose the one you like most great so
choose the one you like most great so now that we have this label let's add an
now that we have this label let's add an equivalent close tag here and let's give
equivalent close tag here and let's give it a HTML 4 to be ID so these are going
it a HTML 4 to be ID so these are going to be some uh attributes which are going
to be some uh attributes which are going to improve the accessibility and it's
to improve the accessibility and it's easy to do that here and it's worthwhile
easy to do that here and it's worthwhile because this is a reusable component so
because this is a reusable component so we only have to do it once and our forms
we only have to do it once and our forms are going to be accessible throughout
are going to be accessible throughout the entire project and let's give it a
the entire project and let's give it a class name of text extra small font semi
class name of text extra small font semi bold text neutral
bold text neutral 700 great and then let's go ahead and
700 great and then let's go ahead and let's let's actually uh render the input
let's let's actually uh render the input component which I just imported from do
component which I just imported from do /ui input but I'm going to replace it
/ui input but I'm going to replace it with SL
with SL components input is going to be a
components input is going to be a self-closing tag so go ahead and pass in
self-closing tag so go ahead and pass in the on blur to be on blur default value
the on blur to be on blur default value to be default value we're also going to
to be default value we're also going to have the ref which is going to be the
have the ref which is going to be the ref required required name which I'm
ref required required name which I'm just going to map to ID D and it's also
just going to map to ID D and it's also going to have the ID which is
going to have the ID which is ID then we're going to give it a
ID then we're going to give it a placeholder to be placeholder we're
placeholder to be placeholder we're going to give it a type to B type and
going to give it a type to B type and let's go ahead and give it a disabled
let's go ahead and give it a disabled prop of either pending or manually
prop of either pending or manually disabled and let's give it a class name
disabled and let's give it a class name which is going to be dynamic so let's go
which is going to be dynamic so let's go ahead and let's import
CN from at/ Li user let's add the CN here let's give it a
let's add the CN here let's give it a default classes which is text small px2
default classes which is text small px2 py1 and
py1 and H7 and let's add the conditional class
H7 and let's add the conditional class name which the user can pass in case
name which the user can pass in case they want to extend this and then let's
they want to extend this and then let's add area- described by to
be id-h error like that great and now what I want to do next is I want to
what I want to do next is I want to create a reusable components for
create a reusable components for rendering our errors so let's go ahead
rendering our errors so let's go ahead and do that we're going to do it uh
and do that we're going to do it uh first we're going to assign it just
first we're going to assign it just inside of this component here so outside
inside of this component here so outside of this div which is wrapping our label
of this div which is wrapping our label and our input go ahead and render the
and our input go ahead and render the form errors like this don't worry the
form errors like this don't worry the fact that uh we have this error that
fact that uh we have this error that it's not defined because we're going to
it's not defined because we're going to create it now and go ahead and pass in
create it now and go ahead and pass in the ID which is the ID and errors which
the ID which is the ID and errors which is the errors
is the errors great and now let's go ahead and let's
great and now let's go ahead and let's create a new file form D errors. DSX
create a new file form D errors. DSX inside and let's go ahead and let's
inside and let's go ahead and let's import X circle from Lucid react so
import X circle from Lucid react so whenever you're working with Lucid react
whenever you're working with Lucid react I've notied that you can do both X
I've notied that you can do both X Circle and you can always do ex Circle
Circle and you can always do ex Circle icon right so if I try to import let's
icon right so if I try to import let's try something M Square you can also do M
try something M Square you can also do M Square icon so that's a cool little tip
Square icon so that's a cool little tip for you if you're ever wondering why you
for you if you're ever wondering why you have different Imports so either X
have different Imports so either X Circle or X Circle icon let's create the
Circle or X Circle icon let's create the interface form errors props let's give
interface form errors props let's give it an ID to be a string and errors is
it an ID to be a string and errors is going to be an optional record which
going to be an optional record which accepts the string as the first
accepts the string as the first parameter and the array of strings or
parameter and the array of strings or undefined as the second parameter and
undefined as the second parameter and then export con form
then export con form errors we get the ID and the
errors we get the ID and the errors let's give it a d form
errors let's give it a d form errors props type
errors props type here and let's go ahead and check if we
here and let's go ahead and check if we don't have any errors there is no need
don't have any errors there is no need to render this component otherwise let's
to render this component otherwise let's go ahead and let's return a div which is
go ahead and let's return a div which is going to have an ID of id-
going to have an ID of id- error it's going to have
error it's going to have area- live to be
area- live to be polite and we're going to have a class
polite and we're going to have a class name of of mt2 text extra small text
name of of mt2 text extra small text rows
rows 500 great and now inside let's go ahead
500 great and now inside let's go ahead and write errors question mark open
and write errors question mark open parenthesis ID map again with a question
parenthesis ID map again with a question mark here and let's go ahead and let's
mark here and let's go ahead and let's extract the individual error which is
extract the individual error which is going to be a
going to be a string and let's go ahead and render a
string and let's go ahead and render a div inside and let's just render that
div inside and let's just render that error let's give it a key of
error let's give it a key of error
error and now let's just style it a bit so I'm
and now let's just style it a bit so I'm going to give it a class name of flex
going to give it a class name of flex items Center font medium padding to
items Center font medium padding to border border rows 500 BG rows
border border rows 500 BG rows 500/10 and rounded small and then next
500/10 and rounded small and then next to the actual error we're going to
to the actual error we're going to render the X circle from Lucid react
render the X circle from Lucid react sorry we already have it imported right
okay so just one is enough we don't need both variations so X circle make sure
both variations so X circle make sure you have that and let's just quickly
you have that and let's just quickly give this some Styles so h-4 W-4 and
give this some Styles so h-4 W-4 and margin right two perfect and now we can
margin right two perfect and now we can go back inside of the form input and we
go back inside of the form input and we can import the form errors component
can import the form errors component from do/ form errors and since they are
from do/ form errors and since they are in the same folder I'm not going to
in the same folder I'm not going to replace that with the alas because I
replace that with the alas because I think this is good enough
think this is good enough uh great and now let's try this out so
uh great and now let's try this out so now what I want to do is head back
now what I want to do is head back inside of our app folder platform
inside of our app folder platform dashboard organization organization ID
dashboard organization organization ID and in here we have the form component
and in here we have the form component which is currently using the form input
which is currently using the form input from do/ form input so let's remove this
from do/ form input so let's remove this import and let's remove this component
import and let's remove this component form input inside of the organization ID
form input inside of the organization ID page right you can enter it just confirm
page right you can enter it just confirm that it's not the component which you
that it's not the component which you just created right so let's go ahead and
just created right so let's go ahead and remove that
remove that let's go inside of this form now and
let's go inside of this form now and let's go ahead and add our form input
let's go ahead and add our form input from components form form input there we
from components form form input there we go so we have our new component here let
go so we have our new component here let me just align this stuff here great so
me just align this stuff here great so make sure you have form input from s/
make sure you have form input from s/ components form form input which is the
components form form input which is the component we just worked on and besides
component we just worked on and besides the
the errors this one as you can see also
errors this one as you can see also needs to have an
needs to have an ID so ID is title like that and let's go
ID so ID is title like that and let's go ahead and let's see how this component
ahead and let's see how this component looks like so make sure you refresh your
looks like so make sure you refresh your Local Host there we go and let's go
Local Host there we go and let's go ahead and write a text and there we go
ahead and write a text and there we go you can see how now we have custom
you can see how now we have custom errors here and when we submitting you
errors here and when we submitting you can see that it still has a little uh
can see that it still has a little uh pending
pending state but let's also test out the label
state but let's also test out the label right because that's another thing we
right because that's another thing we added so let's give it a label to be
added so let's give it a label to be board title for
board title for example uh oh and it looks like we are
example uh oh and it looks like we are still rendering the hardcoded label so
still rendering the hardcoded label so let's go ahead and fix that so inside of
let's go ahead and fix that so inside of the components folder inside of form
the components folder inside of form form input we have the label prop but
form input we have the label prop but seems like we're not using it at all so
seems like we're not using it at all so label instead of the hardcoded and there
label instead of the hardcoded and there we go now we have board title here great
we go now we have board title here great and now what I want to test out is
and now what I want to test out is whether this is actually working because
whether this is actually working because I I don't think this should yield that
I I don't think this should yield that the title is too short so let's go ahead
the title is too short so let's go ahead and try that out I'm going to debug with
and try that out I'm going to debug with you here so let's go inside of the form
you here so let's go inside of the form right here and what first thing I'm
right here and what first thing I'm going to do is I'm going to conso log
going to do is I'm going to conso log the title here and I like to do it
the title here and I like to do it inside of an object because it's just
inside of an object because it's just easier to find in the inspect element so
easier to find in the inspect element so let me refresh this page let me open my
let me refresh this page let me open my inspect element and let's write test and
inspect element and let's write test and click submit and it looks like it is
click submit and it looks like it is right here oh looks like it's working
right here oh looks like it's working great great great so maybe that was just
great great great so maybe that was just some hot reload thing when I add a short
some hot reload thing when I add a short one we have an
one we have an error oh it's the error is not clearing
error oh it's the error is not clearing because we uh we have that throw error
because we uh we have that throw error inside of our action I think that's why
inside of our action I think that's why let me just go inside of our actions
let me just go inside of our actions create board yeah we added an artificial
create board yeah we added an artificial error here so let's remove the
error here so let's remove the error all right and let's try it out now
error all right and let's try it out now when I click
when I click submit there we go it looks like uh
submit there we go it looks like uh there is just this uh errors that that's
there is just this uh errors that that's still showing up but we're going to work
still showing up but we're going to work with that later uh we're going to see if
with that later uh we're going to see if it actually creates any problems uh or
it actually creates any problems uh or not for us uh great and now I want to do
not for us uh great and now I want to do uh the similar thing but for the submit
uh the similar thing but for the submit button so I want to create a reusable
button so I want to create a reusable submit
submit button so it's going to be again similar
button so it's going to be again similar to like this uh form button component
to like this uh form button component which we created here right
which we created here right there we go this form button so let's go
there we go this form button so let's go ahead and let me just close this stuff
ahead and let me just close this stuff let's go inside of components form and
let's go inside of components form and let's create form button.
let's create form button. PSX and first things first let's mark
PSX and first things first let's mark this as use client and actually it's not
this as use client and actually it's not going to be called form button I want to
going to be called form button I want to call it form submit so that's what its
call it form submit so that's what its purpose is going to be otherwise we
purpose is going to be otherwise we don't even need to use it right and
don't even need to use it right and let's go ahead and let's import use form
let's go ahead and let's import use form status from react Dum let's go ahead and
status from react Dum let's go ahead and and let's import CN from Li utils and
and let's import CN from Li utils and let's import the actual button component
let's import the actual button component from do/ UI button or slash components
from do/ UI button or slash components UI button let's create the interface uh
UI button let's create the interface uh form submit props it's going to have
form submit props it's going to have children which are type of react react
children which are type of react react node disabled which is an optional
node disabled which is an optional Boolean class name which is a string and
Boolean class name which is a string and variant which can be either default
destructive outline
outline secondary
secondary ghost link or
ghost link or primary and let's export con form submit
here and let me just assign this props so form submit props and let's extract
so form submit props and let's extract the
the children let's extract track disabled
children let's extract track disabled class name and
class name and variant and let's open this Arrow
variant and let's open this Arrow function here and first thing I want to
function here and first thing I want to do is extract the pending state from use
do is extract the pending state from use form
form status and then I'm just going to return
status and then I'm just going to return a button component which we imported and
a button component which we imported and render the children
render the children inside and then let's go ahead and
inside and then let's go ahead and assign all the necessary props so
assign all the necessary props so disabled is going to be pending or is
disabled is going to be pending or is manually disabled type is always going
manually disabled type is always going to be submit variant is going to be
to be submit variant is going to be variant size is going to be small class
variant size is going to be small class name is going to be CN class name so in
name is going to be CN class name so in case you have an error with this variant
case you have an error with this variant prop it probably means that you have a
prop it probably means that you have a mismatch between uh what I what you
mismatch between uh what I what you wrote here and what the button actually
wrote here and what the button actually accepts so just make sure that you don't
accepts so just make sure that you don't have any typers here you can always
have any typers here you can always visit my GitHub or or even better we can
visit my GitHub or or even better we can go directly inside of the button
go directly inside of the button component because it's right here in the
component because it's right here in the UI folder and in here you can see
UI folder and in here you can see exactly uh what variants you
exactly uh what variants you have uh great now let's go ahead and use
have uh great now let's go ahead and use this button so make sure you save that
this button so make sure you save that and let's go inside of the app folder
and let's go inside of the app folder platform dashboard organization
platform dashboard organization organization ID here uh open up the form
organization ID here uh open up the form and let's already delete form button
and let's already delete form button right we're not going to need it here
right we're not going to need it here great go back inside of the form
great go back inside of the form remove this and let's go ahead and let's
remove this and let's go ahead and let's add form
add form button uh maybe I forgot to export it h
button uh maybe I forgot to export it h components
components form oh I name it form submit of course
form oh I name it form submit of course yeah form submit there we go so form
yeah form submit there we go so form submit from components form form
submit from components form form submit and let's just replace this form
submit and let's just replace this form submit and let's give it a save text
submit and let's give it a save text inside
inside and I don't think we need to pass
and I don't think we need to pass anything else and this should work
anything else and this should work exactly as it was before right but it
exactly as it was before right but it has built in pending status perfect uh
has built in pending status perfect uh great great job so you just finished um
great great job so you just finished um creating this reusable form components
creating this reusable form components uh what we're going to do next is we're
uh what we're going to do next is we're going to create a popover which is going
going to create a popover which is going to be triggered when we click on this
to be triggered when we click on this plus icon here and then we're actually
plus icon here and then we're actually going to be able to create our first
going to be able to create our first board and we're also going to learn how
board and we're also going to learn how to connect to unsplash API so you can
to connect to unsplash API so you can load uh those random
load uh those random images all right so regarding this
images all right so regarding this little bug we have that when we type
little bug we have that when we type something short and then we go ahead and
something short and then we go ahead and type something again and it works but
type something again and it works but the error doesn't reset I think I found
the error doesn't reset I think I found the culprit to that so it's actually
the culprit to that so it's actually inside of our use action hook right here
inside of our use action hook right here this is something that I did not notice
this is something that I did not notice during my initial development so this is
during my initial development so this is what happens we only update the field
what happens we only update the field errors which is what is showing right
errors which is what is showing right here if we have result. field errors but
here if we have result. field errors but let's take a look at our lib create save
let's take a look at our lib create save action here as you can see here we only
action here as you can see here we only return field errors if this was not
return field errors if this was not successful meaning that this set field
successful meaning that this set field errors will not update if we fix our
errors will not update if we fix our error and we don't return anything in
error and we don't return anything in here so we can do that quite easily by
here so we can do that quite easily by just removing the if Clause so this is
just removing the if Clause so this is inside of the use action so we are
inside of the use action so we are always going to update the field errors
always going to update the field errors regardless if they exist or not so let's
regardless if they exist or not so let's try that out now when I write something
try that out now when I write something that too short I get an error but when I
that too short I get an error but when I write something that has the proper
write something that has the proper length I create a new record and the
length I create a new record and the error is gone great so I think that
error is gone great so I think that should work we are of course going to
should work we are of course going to see later in the development if this is
see later in the development if this is going to create any problems but I think
going to create any problems but I think think it should be just
think it should be just fine so now it's time for us to actually
fine so now it's time for us to actually clear this screen up so we can actually
clear this screen up so we can actually create you know the some information
create you know the some information about the current organization here at
about the current organization here at the top and a list of our boards here as
the top and a list of our boards here as well as the button to create new boards
well as the button to create new boards so let's go ahead inside of our
so let's go ahead inside of our organization ID page so dashboard
organization ID page so dashboard organization organization ID and in here
organization organization ID and in here we have page. TSX
we have page. TSX and we can now go ahead and we can
and we can now go ahead and we can remove this entire form we can remove
remove this entire form we can remove this entire fetching of the database and
this entire fetching of the database and we can remove all of these Imports and
we can remove all of these Imports and let's also remove everything we don't
let's also remove everything we don't need so we don't need this form anymore
need so we don't need this form anymore that was just for us to test things out
that was just for us to test things out we don't need form delete anymore and we
we don't need form delete anymore and we also don't need the individual board.
also don't need the individual board. CSX so inside of my organization ID you
CSX so inside of my organization ID you should have the underscore components
should have the underscore components page uh sorry folder the settings page
page uh sorry folder the settings page the layout out file and page. TSX so
the layout out file and page. TSX so let's go ahead and modify this by giving
let's go ahead and modify this by giving it a w full and margin bottom of 20 and
it a w full and margin bottom of 20 and then inside we're going to render the
then inside we're going to render the info component which we don't yet have
info component which we don't yet have and let's go ahead and create it now so
and let's go ahead and create it now so inside of underscore components here
inside of underscore components here create a new file info.
create a new file info. DSX and let's go ahead and Mark this as
DSX and let's go ahead and Mark this as use client and let's create an interface
use client and let's create an interface info
info props and actually we're going to do
props and actually we're going to do that later when we Implement
that later when we Implement subscription sorry for that so for now
subscription sorry for that so for now all I want you to do is export const
all I want you to do is export const info
info props and go ahead and simply return
props and go ahead and simply return ative info actually export con info
ative info actually export con info sorry all right go back to page. vsx and
sorry all right go back to page. vsx and then you can import the info component
then you can import the info component from _ components info and once you save
from _ components info and once you save you should see the text which says info
you should see the text which says info inside now let's go ahead and let's
inside now let's go ahead and let's import use
import use organization from Clerk
organization from Clerk nextjs and inside of here let's use that
nextjs and inside of here let's use that hook so use
hook so use organization and let's go ahead and
organization and let's go ahead and extract the current organization and is
extract the current organization and is loaded and now let's go ahead and let's
loaded and now let's go ahead and let's write if is not
loaded we're just going to go ahead and return a paragraph saying loading for
return a paragraph saying loading for now so there we go now when you refresh
now so there we go now when you refresh you have loading for a couple of seconds
you have loading for a couple of seconds and then the text info and now let's go
and then the text info and now let's go ahead inside of this return function and
ahead inside of this return function and give this div a class name of flex items
give this div a class name of flex items Center and GAP
Center and GAP X4 go ahead and open up a new div with a
X4 go ahead and open up a new div with a class name of w 60 pixels height of 60
class name of w 60 pixels height of 60 pixels as well and a relative and inside
pixels as well and a relative and inside we're going to render an image component
we're going to render an image component from next SL image so make sure you have
from next SL image so make sure you have that imported here it is a self-closing
that imported here it is a self-closing tag so let's go ahead and give it a
tag so let's go ahead and give it a Field property let's give it a source to
Field property let's give it a source to be whoops a source to be organization
be whoops a source to be organization question mark image URL let's give it an
question mark image URL let's give it an ALT to be
ALT to be organization and let's go ahead and give
organization and let's go ahead and give it a class name of rounded MD and object
it a class name of rounded MD and object D cover and to fix this error we can
D cover and to fix this error we can just put an exclamation point at the end
just put an exclamation point at the end and there we go now our info component
and there we go now our info component shows a big image of our current
shows a big image of our current organization you can see how it changes
organization you can see how it changes slightly when I change my
slightly when I change my organizations besides the image we also
organizations besides the image we also have to render the name of the actual
have to render the name of the actual organization so outside of this div
organization so outside of this div which wraps the image component open up
which wraps the image component open up a new div and open up a paragraph and
a new div and open up a paragraph and render organization question mark name
render organization question mark name let's give this paragraph a class name
let's give this paragraph a class name of font semi bold and text
of font semi bold and text XL great and now go ahead and give this
XL great and now go ahead and give this upper CL upper div a class name of space
upper CL upper div a class name of space y1 and below the paragraph open up a div
y1 and below the paragraph open up a div which is going to render the credit card
which is going to render the credit card icon from Lucid react so just make sure
icon from Lucid react so just make sure you add uh this credit card
you add uh this credit card icon let's give this credit card icon a
icon let's give this credit card icon a class name of h-3 w-3 and margin right
class name of h-3 w-3 and margin right of one and just write a small text which
of one and just write a small text which says free so this is later going to be
says free so this is later going to be dynamic when we Implement subscriptions
dynamic when we Implement subscriptions and give this div a class name of flex
and give this div a class name of flex items Center text extra small and text
items Center text extra small and text muted
muted foreground there we go so now we have a
foreground there we go so now we have a nice little info component here at the
nice little info component here at the top and now let's go ahead and let's
top and now let's go ahead and let's create a board
create a board list actually just before we do that I
list actually just before we do that I forgot that we have this loading state
forgot that we have this loading state which we have to improve so let's go to
which we have to improve so let's go to the bottom of this info component and
the bottom of this info component and let's add info.
let's add info. skeleton B function skeleton
skeleton B function skeleton info and let's return a div with a class
info and let's return a div with a class name of flex items Center and GAP X4
name of flex items Center and GAP X4 then open up a new div with the class
then open up a new div with the class name of w60 pixels height of 60 pixels
name of w60 pixels height of 60 pixels and relative inside add a scale
and relative inside add a scale component from add/ components UI
component from add/ components UI skeleton so just make sure you add that
skeleton so just make sure you add that and let's go ahead and give this
and let's go ahead and give this skeleton a class name of w
skeleton a class name of w full and H full and absolute property
full and H full and absolute property outside of this div open up a new one
outside of this div open up a new one with a class name of space Y
with a class name of space Y 2 and inside add a new skeleton
2 and inside add a new skeleton component with a class name of H h-10
component with a class name of H h-10 and W2 200
and W2 200 pixels open up a new div with a class
pixels open up a new div with a class name of flex items
name of flex items Center and inside we're going to render
Center and inside we're going to render two skeletons the first one is going to
two skeletons the first one is going to have a class name of the height H4 W H4
have a class name of the height H4 W H4 sorry H4 and W4 and margin right of two
sorry H4 and W4 and margin right of two and the lower one is going to have a
and the lower one is going to have a class name of H4 and a w of 800
class name of H4 and a w of 800 pixels
pixels and now let's go ahead back inside of
and now let's go ahead back inside of this is loaded and instead of rendering
this is loaded and instead of rendering this we're going to render info.
this we're going to render info. skeleton so save that let's refresh our
skeleton so save that let's refresh our local host and there we go you can see
local host and there we go you can see how now now we have a nice skeleton here
how now now we have a nice skeleton here representing the image the title and
representing the image the title and these two little icons here
these two little icons here great so what I want to do now is go
great so what I want to do now is go back to our page where we render this
back to our page where we render this info component component and just below
info component component and just below the info component go ahead and add a
the info component go ahead and add a little
separator we already have that and you can import it from at/ components UI
can import it from at/ components UI separator again just make sure you don't
separator again just make sure you don't accidentally import it from radic and
accidentally import it from radic and just go ahead and give it a class name
just go ahead and give it a class name of
of my4 great so now you should see a little
my4 great so now you should see a little line here and below that for now let's
line here and below that for now let's just go ahead and add a little div with
just go ahead and add a little div with a class name of bx2 M DPX 4 and then
a class name of bx2 M DPX 4 and then render the board list component which we
render the board list component which we are going to create just now so go ahead
are going to create just now so go ahead inside of your underscore components
inside of your underscore components here and create a new file board
here and create a new file board list.
list. DSX and let's go ahead and add uh expert
DSX and let's go ahead and add uh expert const board list here so this is also
const board list here so this is also going to be a server component we're not
going to be a server component we're not going to turn it into a client one and
going to turn it into a client one and just render Bard list for
just render Bard list for now go back to page and then you can
now go back to page and then you can import board list from at/ components
import board list from at/ components board Dash list and there we go we have
board Dash list and there we go we have a little board list here so now let's go
a little board list here so now let's go ahead and let's create uh the UI for
ahead and let's create uh the UI for that so class name for this first VI is
that so class name for this first VI is going to be space y4 then we're going to
going to be space y4 then we're going to go inside of here and we're going to
go inside of here and we're going to open up a new div with a class name of
open up a new div with a class name of flex items Center font semi bold text
flex items Center font semi bold text large and font sorry text neutral
large and font sorry text neutral 700 let's go ahead and render the user
700 let's go ahead and render the user two icon from Lucid react so make sure
two icon from Lucid react so make sure you add that import it's a self closing
you add that import it's a self closing tag and give it a class name of h-6 w-6
tag and give it a class name of h-6 w-6 and margin right of two and write your
and margin right of two and write your boards there we go so now we have this
boards there we go so now we have this little text here and then we're going to
little text here and then we're going to render the boards we're going to render
render the boards we're going to render the boards inside of a grid so open up a
the boards inside of a grid so open up a div and let's turn it into a grid by
div and let's turn it into a grid by adding it the class name grid and then
adding it the class name grid and then we're going to define the amount of
we're going to define the amount of columns it can have on each viewport so
columns it can have on each viewport so on mobile devices it's going to be grid
on mobile devices it's going to be grid calls 2 on small devices grid calls
calls 2 on small devices grid calls three on large devices grid calls four
three on large devices grid calls four and GAP is going to be
and GAP is going to be four and I forgot to added a little
four and I forgot to added a little space here so this should be joined LG
space here so this should be joined LG grid calls 4 let me just zoom out for a
grid calls 4 let me just zoom out for a second so you can see all of this in one
second so you can see all of this in one line so pause the video If you haven't
line so pause the video If you haven't great and now inside of here usually we
great and now inside of here usually we would go ahead and fetch the boards here
would go ahead and fetch the boards here and then iterate over them but since we
and then iterate over them but since we don't really have all the necessary
don't really have all the necessary information to render the boards the way
information to render the boards the way I want to render them we're going to go
I want to render them we're going to go ahead and just create uh the first item
ahead and just create uh the first item inside which is going to be the button
inside which is going to be the button to create a new board so go ahead and
to create a new board so go ahead and create a div give it a roll of button
create a div give it a roll of button and go ahead and create a class name
and go ahead and create a class name with aspect Das video relative h- full
with aspect Das video relative h- full w- full BG muted rounded small Flex
w- full BG muted rounded small Flex flex-all Gap
flex-all Gap dy-1 items Center justify Das Center so
dy-1 items Center justify Das Center so everything inside is going to be in the
everything inside is going to be in the middle hover opacity d75 and
middle hover opacity d75 and transition great you can see our little
transition great you can see our little box right here and inside of this box go
box right here and inside of this box go ahead and add a paragraph which is going
ahead and add a paragraph which is going to say create new board and let's give
to say create new board and let's give this paragraph a class
this paragraph a class name which is going to say text
name which is going to say text small below that let's go ahead and add
small below that let's go ahead and add a little span element which is going to
a little span element which is going to say for now uh five
say for now uh five remaining and let's give this span a
remaining and let's give this span a class name of text extra small so this
class name of text extra small so this is what it's going to look like great
is what it's going to look like great and now what I want to create is a
and now what I want to create is a component which is going to be used as a
component which is going to be used as a hint right so when we we're going to
hint right so when we we're going to have a little like question mark here so
have a little like question mark here so when we hover it's going to open a
when we hover it's going to open a little popover and it's going to explain
little popover and it's going to explain to the user it's going to open a tool
to the user it's going to open a tool tip not a popover which is going to
tip not a popover which is going to explain to the user what does it mean
explain to the user what does it mean that it says five remaining if you saw
that it says five remaining if you saw the demo you already know what that
the demo you already know what that means uh but let's go ahead and create
means uh but let's go ahead and create that component
that component now in order to do that first we have to
now in order to do that first we have to install the tool tip component from
install the tool tip component from shaten so let's go inside of our
shaten so let's go inside of our terminal
terminal here and let's Rite npx shaten UI at
here and let's Rite npx shaten UI at latest at tool
latest at tool tip wait a second for this to
tip wait a second for this to install and after it's done we're going
install and after it's done we're going to create a component uh called
to create a component uh called hint great so it's right here let me
hint great so it's right here let me just zoom back in and let's go ahead and
just zoom back in and let's go ahead and let's go inside of our components folder
let's go inside of our components folder and create a new new file hint.
and create a new new file hint. TSX let's go ahead uh and let's import
TSX let's go ahead uh and let's import everything we need from at/ components
everything we need from at/ components UI tool tip so we're going to need the
UI tool tip so we're going to need the tool tip itself we're going to need the
tool tip itself we're going to need the tool tip content the tool tip provider
tool tip content the tool tip provider and the trigger so tool tip content tool
and the trigger so tool tip content tool tip provider and Tool tip trigger now
tip provider and Tool tip trigger now let's create an
interface in props children we are going to be react react
children we are going to be react react node
node description is going to be a string side
description is going to be a string side is going to be optional and the type of
is going to be optional and the type of either left or right or
top or bottom and we're going to have side
bottom and we're going to have side offset which is an optional number great
offset which is an optional number great and now let's export con hint
and now let's export con hint let's assign those props so hint
let's assign those props so hint props and let's go ahead and extract the
props and let's go ahead and extract the children the description side and side
children the description side and side offset and let's go ahead and give the
offset and let's go ahead and give the default value of side to be bottom and
default value of side to be bottom and the default side offset to be
the default side offset to be zero and now inside all we have to do is
zero and now inside all we have to do is return a tool tip
return a tool tip provider inside add a tool tip itself
provider inside add a tool tip itself let's give the tool tip a prop called
let's give the tool tip a prop called delay duration zero so I don't want any
delay duration zero so I don't want any delay with opening the tool tip right I
delay with opening the tool tip right I want it to instantly open when the user
want it to instantly open when the user hovers on it let's add the tool tip
hovers on it let's add the tool tip trigger to be our
trigger to be our children and outside of the trigger go
children and outside of the trigger go ahead and write tool tip
ahead and write tool tip content and render the description
content and render the description inside and now let's go ahead and fill
inside and now let's go ahead and fill this content with some props so side
this content with some props so side offset is going to be side
offset is going to be side offset side is going to be side and
offset side is going to be side and class name is going to be text extra
class name is going to be text extra small Max W 220 pixels and break
small Max W 220 pixels and break words great so we have our hint
words great so we have our hint component finished and now we can head
component finished and now we can head back inside of our board list component
back inside of our board list component inside of our organization ID components
inside of our organization ID components right here so go inside the board list
right here so go inside the board list and let's go just below this fan element
and let's go just below this fan element where we wrote five remaining and add a
where we wrote five remaining and add a hint component from add/ components hint
hint component from add/ components hint so let me just show you where I imported
so let me just show you where I imported that here at the top so hint from at/
that here at the top so hint from at/ components hint and let's go ahead
components hint and let's go ahead inside and let's render the help Circle
inside and let's render the help Circle component from Lucid react so make sure
component from Lucid react so make sure you add that icon it is a self closing
you add that icon it is a self closing tag and go ahead and just give it a
tag and go ahead and just give it a class name of absolute bottom Dash to
class name of absolute bottom Dash to right -2 H 14 pixels and W 14 pixels and
right -2 H 14 pixels and W 14 pixels and then let's go ahead and give this hint a
then let's go ahead and give this hint a side offset of 40 and let's write a
side offset of 40 and let's write a description so you can open backck like
description so you can open backck like this and inside uh I want to write three
workspaces can have up to five open boards for unlimited boards upgrade this
boards for unlimited boards upgrade this workspace like that and now as you can
workspace like that and now as you can see we have a little help icon here and
see we have a little help icon here and when I hover on it we have a useful
when I hover on it we have a useful information for the user so they know
information for the user so they know what this means perfect so now let's
what this means perfect so now let's actually turn this into a popover and
actually turn this into a popover and then we're going to go ahead and create
then we're going to go ahead and create uh well the connection with the unsplash
uh well the connection with the unsplash and all the other
and all the other things so I'm going to create that
things so I'm going to create that inside of our components form folder and
inside of our components form folder and before we can do that we have to install
before we can do that we have to install the popover component uh I don't think
the popover component uh I don't think we have it we're just going to check now
we have it we're just going to check now so npx Shad
so npx Shad cn- at latest at
cn- at latest at popover looks like we don't have it so
popover looks like we don't have it so it's installing popover great once that
it's installing popover great once that is done go back inside uh of your
is done go back inside uh of your components folder here inside of the
components folder here inside of the form folder and create a new file form
form folder and create a new file form Das popover do TSX so the reason I'm
Das popover do TSX so the reason I'm putting this form inside of the uh
putting this form inside of the uh reusable components folder is because
reusable components folder is because we're going to reuse it both in the nav
we're going to reuse it both in the nav bar as you can see with this little plus
bar as you can see with this little plus icon here and also in this board list
icon here and also in this board list component so that's what why I decided
component so that's what why I decided to put it here so let's go ahead and
to put it here so let's go ahead and let's mark it as Ed client and now let's
let's mark it as Ed client and now let's go ahead and let's import everything in
go ahead and let's import everything in we need from at slash components UI
we need from at slash components UI popover so we're going to need the
popover so we're going to need the popover itself the popover content and
popover itself the popover content and the popover trigger great and now let's
the popover trigger great and now let's go ahead and let's import use action
go ahead and let's import use action from hooks use action and let's import
from hooks use action and let's import create board from actions create board
create board from actions create board so those server actions which we
so those server actions which we recently created right uh and let's go
recently created right uh and let's go ahead and let's import form input which
ahead and let's import form input which we have from do/ form input and let's
we have from do/ form input and let's import form submit from SL form submit
import form submit from SL form submit great uh and now what I want to do is I
great uh and now what I want to do is I want to go ahead and just uh well render
want to go ahead and just uh well render the most primitive version of this so
the most primitive version of this so export
export const form
const form popover and it's going to be an arrow
popover and it's going to be an arrow function so like
function so like this and let's go ahead and create an
this and let's go ahead and create an interface form popover props it's going
interface form popover props it's going to accept children which is react. react
to accept children which is react. react node is going to accept a side which is
node is going to accept a side which is is either going to be left right top or
is either going to be left right top or bottom besides the side we're also going
bottom besides the side we're also going to have the Align optional prop which
to have the Align optional prop which can be Start Center or
can be Start Center or end and we're going to have an optional
end and we're going to have an optional side offset as well as we did in our
side offset as well as we did in our hint component great so let's go ahead
hint component great so let's go ahead and assign those props to this form
and assign those props to this form popover so form popover props get the
popover so form popover props get the children get this side and let's give
children get this side and let's give the default value of bottom the Align
the default value of bottom the Align can stay empty and side offset let's
can stay empty and side offset let's give it a default value of zero great so
give it a default value of zero great so now let's go
now let's go ahead and let's return a popover
ahead and let's return a popover component which is going to have the
component which is going to have the popover
popover trigger we're going to give it a prop as
trigger we're going to give it a prop as child and inside we're going to render
child and inside we're going to render our
our children and then let's add the popover
children and then let's add the popover content
content let's go ahead and let's give it an
let's go ahead and let's give it an align prop of align let's give it a
align prop of align let's give it a class name of
class name of w80 and padding top of three side of
w80 and padding top of three side of side and side offset of side
side and side offset of side offset now let's go ahead and create a
offset now let's go ahead and create a div with a class name text small font
div with a class name text small font medium text Center and text neutral
medium text Center and text neutral 600 and padding bottom of
600 and padding bottom of four and inside let's just render create
four and inside let's just render create board perfect and now what I want to do
board perfect and now what I want to do is I want to use this uh form popover
is I want to use this uh form popover already before we start adding this
already before we start adding this other stuff so for that we have to head
other stuff so for that we have to head back inside of our board list which is
back inside of our board list which is in the platform dashboard organization
in the platform dashboard organization organization ID components board list
organization ID components board list and I want to wrap this entire div which
and I want to wrap this entire div which is our button right it's right this
is our button right it's right this which says create new board I'm going to
which says create new board I'm going to wrap that entire thing inside of the
wrap that entire thing inside of the form
form popover which I just imported from s SL
popover which I just imported from s SL components forms uh sorry form form
components forms uh sorry form form popover great so go ahead and wrap the
popover great so go ahead and wrap the entire thing inside of that so that's
entire thing inside of that so that's going to serve as the trigger to open up
going to serve as the trigger to open up the popover that's going to be our
the popover that's going to be our children right we accept the children
children right we accept the children and we pass it here to be a trigger
and we pass it here to be a trigger which is then going to open this cont
which is then going to open this cont here great and let's go ahead and just
here great and let's go ahead and just give it a side offset of 10 and a side
give it a side offset of 10 and a side of
of right so let's try it out now if I go
right so let's try it out now if I go ahead I'm going to refresh and I click
ahead I'm going to refresh and I click here there we go you can see a little
here there we go you can see a little pop over here which says create
pop over here which says create board now I want to add a little close
board now I want to add a little close icon to it but we have a little issue
icon to it but we have a little issue here so let's take a look back inside of
here so let's take a look back inside of our form popover it looks like I've
our form popover it looks like I've exhausted All Imports inside of my
exhausted All Imports inside of my popover right there is nothing else
popover right there is nothing else inside of here that's being exported we
inside of here that's being exported we have the popover the popover trigger and
have the popover the popover trigger and the popover content that seems to be it
the popover content that seems to be it well thankfully because shaten offers us
well thankfully because shaten offers us to enter direct components in here we
to enter direct components in here we can go ahead and add it ourselves so
can go ahead and add it ourselves so let's do that find the UI folder and go
let's do that find the UI folder and go inside of your newly installed popover
inside of your newly installed popover do TSX so you can either find it in the
do TSX so you can either find it in the folder or you can just use this form
folder or you can just use this form popover component which we are working
popover component which we are working in hold command or control and click on
in hold command or control and click on it and then just click inside like this
it and then just click inside like this so just make sure you're seeing this and
so just make sure you're seeing this and we can do it quite easily so let's write
we can do it quite easily so let's write con
con popover close to be popover
popover close to be popover primitive.
primitive. close and then we have to export it at
close and then we have to export it at the end so after popover content add pop
the end so after popover content add pop over close and that is it we just
over close and that is it we just modified this component so it serves our
modified this component so it serves our needs so outside of this div which says
needs so outside of this div which says create board go ahead and open the
create board go ahead and open the popover close
popover close component give it a prop as
component give it a prop as child and of course we have to import
child and of course we have to import that so let's just import it here there
that so let's just import it here there we go pop over
we go pop over close and inside of here add a button
close and inside of here add a button component so I just imported that from
component so I just imported that from data/ UI button but I'm going to change
data/ UI button but I'm going to change that to SL
components all right and inside of the button component I'm going to render an
button component I'm going to render an X icon from Lucid react so just make
X icon from Lucid react so just make sure you do this there we go import X
sure you do this there we go import X from Lucid
from Lucid react and now let's render that here
react and now let's render that here let's give this x a class name of h-4
let's give this x a class name of h-4 and W-4 and let's go ahead and give this
and W-4 and let's go ahead and give this button a class name of h-o w-o padding
button a class name of h-o w-o padding to absolute top-2 wr-2 and text- neutral
to absolute top-2 wr-2 and text- neutral 600 and let's also give it a variant of
600 and let's also give it a variant of ghost and let's take a look at our
ghost and let's take a look at our popover now so when I click here there
popover now so when I click here there we go I have a little close button here
we go I have a little close button here and it is working
and it is working great now let's go ahead and add some
great now let's go ahead and add some input elements inside of this popover
input elements inside of this popover right here so I'm going to go back
right here so I'm going to go back inside and inside of this popover
inside and inside of this popover content after the popover Clos let's go
content after the popover Clos let's go ahead and let's render the native HTML
ahead and let's render the native HTML form element and inside create a div
form element and inside create a div with a class name of space
with a class name of space y4 and let's render our form input
y4 and let's render our form input component which we imported at the
component which we imported at the beginning let's give it an ID of title
beginning let's give it an ID of title let's give it a label of word title and
let's give it a label of word title and let's give it a type of
let's give it a type of text so now when you click on your
text so now when you click on your component
component right here you should have the board
right here you should have the board title visible perfect now besides the
title visible perfect now besides the form input we're also going to need the
form input we're also going to need the form submit so let's go ahead and give
form submit so let's go ahead and give this form a class name of space y4 as
this form a class name of space y4 as well and then outside of this div which
well and then outside of this div which is wrapping this form input which is
is wrapping this form input which is going to have some more elements inside
going to have some more elements inside so just so you don't get confused why
so just so you don't get confused why we're even doing this and then render
we're even doing this and then render the form
the form submit which I think we also have
submit which I think we also have imported so form submit and form input
imported so form submit and form input and go ahead and say create inside and
and go ahead and say create inside and then create a class name with w- full
then create a class name with w- full here and now when you take a look there
here and now when you take a look there we go we have the board title and our
we go we have the board title and our create button here so what I want to do
create button here so what I want to do now is I want to connect that to our
now is I want to connect that to our actions so let's go ahead here at the
actions so let's go ahead here at the top and let's
top and let's include execute and field error
include execute and field error from our use action hook which we have
from our use action hook which we have imported pass in the create board and
imported pass in the create board and let's also add some callbacks so on
let's also add some callbacks so on success I want to receive the data and I
success I want to receive the data and I want to have the data logged in my
want to have the data logged in my console inspect element and same thing
console inspect element and same thing on error I want to get the error and I
on error I want to get the error and I want to conso
want to conso log the error if it
log the error if it happens perfect so now let's go ahead
happens perfect so now let's go ahead and let's create a little on submit
and let's create a little on submit function here so const onsubmit is going
function here so const onsubmit is going to work with form data which is a type
to work with form data which is a type of form
of form data go ahead and get the title which is
data go ahead and get the title which is form data. getet title as string and
form data. getet title as string and let's call the execute
let's call the execute function and pass in the
function and pass in the title and now let's assign this
title and now let's assign this onsubmit to be this forms
onsubmit to be this forms action great let's go ahead and refresh
action great let's go ahead and refresh this let me open up my uh inspect
this let me open up my uh inspect element here I'm going to create a test
element here I'm going to create a test board and you can see it's loading and
board and you can see it's loading and there we go we have the new test right
there we go we have the new test right here let's try the field errors now oh
here let's try the field errors now oh that's not working because we forgot to
that's not working because we forgot to pass in the field errors so let's go
pass in the field errors so let's go ahead and let's actually use those field
ahead and let's actually use those field errors let's see yes we extracted them
errors let's see yes we extracted them from use action but we're not using it
from use action but we're not using it anywhere so let's go inside of the form
anywhere so let's go inside of the form input here and get the errors and just
input here and get the errors and just pass them here there we go they
pass them here there we go they immediately showed up so let's refresh
immediately showed up so let's refresh and try it again there we go we have a
and try it again there we go we have a short title here let's go ahead and try
short title here let's go ahead and try and fill it up and it goes away perfect
and fill it up and it goes away perfect so now what I want to do is I want to
so now what I want to do is I want to add uh a toast component which is going
add uh a toast component which is going to show up when we successfully create
to show up when we successfully create something for that I'm going to be using
something for that I'm going to be using sonor because I think it's just a nice
sonor because I think it's just a nice and slick component so let's go ahead
and slick component so let's go ahead inside of our terminal and let's run mpm
inside of our terminal and let's run mpm inst sty sonor very simple just like
inst sty sonor very simple just like that and then first things first let's
that and then first things first let's go uh inside of our layout
go uh inside of our layout component uh right here in the app uh
component uh right here in the app uh layout right here and just inside of
layout right here and just inside of here actually we don't need to do it in
here actually we don't need to do it in the root layout we actually only need it
the root layout we actually only need it in the platform layout so this is where
in the platform layout so this is where I actually want to do it inside of this
I actually want to do it inside of this layout which has the clerk provider
layout which has the clerk provider let's go ahead and let's render our
let's go ahead and let's render our toaster
toaster component
component uh I think it's from sonor so let's go
uh I think it's from sonor so let's go import toaster from soner yes looks like
import toaster from soner yes looks like that is it and now I want to go ahead
that is it and now I want to go ahead and well I don't want to do anything
and well I don't want to do anything else actually I think this is just
else actually I think this is just enough so let's go back inside of our
enough so let's go back inside of our form popover so that is located in
form popover so that is located in components form form popover here and
components form form popover here and let's go ahead and in here let's add
let's go ahead and in here let's add toast from soner so make sure you add
toast from soner so make sure you add this toast import we just learned how to
this toast import we just learned how to import toaster but this time we're
import toaster but this time we're importing the toast from sonor and let's
importing the toast from sonor and let's run toast. success and let's write uh
run toast. success and let's write uh board
board created and for the error let's write
created and for the error let's write those do error and we can actually log
those do error and we can actually log the exact error which is going to come
the exact error which is going to come from our database because we take make
from our database because we take make sure that that is just a string like not
sure that that is just a string like not an object or something weird so let's
an object or something weird so let's try that out now I'm going to refresh
try that out now I'm going to refresh here
here test there we go you can see at the
test there we go you can see at the bottom I have board created here and now
bottom I have board created here and now let's try and simulate an error here so
let's try and simulate an error here so I'm going to go inside of actions create
I'm going to go inside of actions create board index and inside of this try I'm
board index and inside of this try I'm going to
going to throw new
throw new error let's try that out now so again
error let's try that out now so again I'm going to refresh I'm going to write
I'm going to refresh I'm going to write test and there we go it says failed to
test and there we go it says failed to create great so just make sure you
create great so just make sure you remove this artificial error here and
remove this artificial error here and save all of your files perfect so you're
save all of your files perfect so you're on a good track on creating this form
on a good track on creating this form popover what we are going to do next is
popover what we are going to do next is Implement another form component which
Implement another form component which is going to be called form picker and
is going to be called form picker and that's going to say
that's going to say above this board title and it's going to
above this board title and it's going to load nine random images from the
load nine random images from the unsplash API so you're going to learn
unsplash API so you're going to learn how to do that next great great
how to do that next great great job so now I want to go ahead and I want
job so now I want to go ahead and I want to connect to the unsplash API before we
to connect to the unsplash API before we wrap up this entire create board
wrap up this entire create board component so go ahead and Google
component so go ahead and Google unsplash developers or just go to
unsplash developers or just go to unsplash.com
unsplash.com developers and go ahead and register as
developers and go ahead and register as a developer
a developer after you do that go ahead and go back
after you do that go ahead and go back to unsplash.com developers because there
to unsplash.com developers because there is a chance that it redirects you and
is a chance that it redirects you and just go ahead and click on your apps so
just go ahead and click on your apps so you're going to see I have a different
you're going to see I have a different button now that I'm logged in and as you
button now that I'm logged in and as you can see I already have a Trello
can see I already have a Trello application here but I'm going to go
application here but I'm going to go ahead and guide you to the process of
ahead and guide you to the process of creating a new one so find a button
creating a new one so find a button which says new application and you have
which says new application and you have to agree to all of this uh well
to agree to all of this uh well guidelines right here and we are of
guidelines right here and we are of course going to oblige by these
course going to oblige by these guidelines for example they require you
guidelines for example they require you to show the user which created the
to show the user which created the picture and a link to redirect to that
picture and a link to redirect to that user's original picture so we're going
user's original picture so we're going to go ahead and Oblige by that and just
to go ahead and Oblige by that and just click accept terms let's give this
click accept terms let's give this application name a tr- tututorial and
application name a tr- tututorial and I'm going to go ahead and uh do the same
I'm going to go ahead and uh do the same thing for the description and click
thing for the description and click create an application and as you can see
create an application and as you can see now it redirect Ed us to this uh page
now it redirect Ed us to this uh page where it says that we can apply for
where it says that we can apply for production so you can do that of course
production so you can do that of course um if you want to if if this is just for
um if you want to if if this is just for your demo you don't have to do that
your demo you don't have to do that production is free as I understand and
production is free as I understand and they allow you up to 5,000 requests per
they allow you up to 5,000 requests per hour if you need more than that uh you
hour if you need more than that uh you can uh well get in touch with the theme
can uh well get in touch with the theme of course uh so let's go ahead and
of course uh so let's go ahead and scroll down here and as you can see for
scroll down here and as you can see for the demo we have 50 requests in an hour
the demo we have 50 requests in an hour of course this is completely free so
of course this is completely free so let's go ahead and let's use this access
let's go ahead and let's use this access key to create our library called
key to create our library called unsplash so I'm going to go ahead and
unsplash so I'm going to go ahead and close everything here and I'm going to
close everything here and I'm going to go inside of my lip folder and create a
go inside of my lip folder and create a new file unsplash
new file unsplash dots and inside of here I'm going to go
dots and inside of here I'm going to go ahead inside of my terminal first and
ahead inside of my terminal first and I'm going to run npm spash npm install
I'm going to run npm spash npm install unsplash DJs like this
unsplash DJs like this so a package from the official unsplash
so a package from the official unsplash documentation and go ahead and import
documentation and go ahead and import create
create API from
API from unsplash DJs and then export con
unsplash DJs and then export con unsplash is going to be create uh
unsplash is going to be create uh API and in here we're going to pass in
API and in here we're going to pass in the access key to be process.
the access key to be process. environment. nextore Public
environment. nextore Public unsplash Access underscore key and put
unsplash Access underscore key and put an exclamation point at the end and
an exclamation point at the end and fetch is just going to be our regular
fetch is just going to be our regular Fetch and now let's copy this
Fetch and now let's copy this environment key and let's add it to our
environment key and let's add it to our environment after the database URL and
environment after the database URL and then go back to your application and
then go back to your application and copy the access key from here so just
copy the access key from here so just click
click copy and go ahead and put it here so
copy and go ahead and put it here so that's it that's all you need to do uh
that's it that's all you need to do uh and I actually want you to to keep this
and I actually want you to to keep this open for now just because I want to show
open for now just because I want to show you how this is measured right here uh
you how this is measured right here uh great so I'm just going to switch back
great so I'm just going to switch back to this screen now and now that we have
to this screen now and now that we have this I want to go ahead and I want to
this I want to go ahead and I want to create a component called form picker so
create a component called form picker so let's go ahead inside of our components
let's go ahead inside of our components form and create a new file form picker.
form and create a new file form picker. vsx and let's go ahead and Mark this as
vsx and let's go ahead and Mark this as used client and then let's create an
used client and then let's create an interface form picker props that have an
interface form picker props that have an ID which is a string and errors which
ID which is a string and errors which are going to be a record string and
are going to be a record string and string array or
string array or undefined and Export const form picker
undefined and Export const form picker here and just return a div form
picker and now I just want to assign the props inside so form picker props and
props inside so form picker props and extract the ID and the
extract the ID and the errors great so now I want to go ahead
errors great so now I want to go ahead and add this component uh inside of our
and add this component uh inside of our form popover right here which we are
form popover right here which we are working in so above the form input where
working in so above the form input where we have the label board title let's add
we have the label board title let's add form picker component from do/ form
form picker component from do/ form picker and let's go ahead and give it an
picker and let's go ahead and give it an ID of image and errors of field
ID of image and errors of field errors great so now when you click here
errors great so now when you click here you should have a text which says uh
you should have a text which says uh form picker just above the board title
form picker just above the board title so we're now slowly going to style it uh
so we're now slowly going to style it uh so it actually renders the images so
so it actually renders the images so we'll go back inside of uh form picker
we'll go back inside of uh form picker here and let's go ahead and let's import
here and let's go ahead and let's import our unsplash library here so import
our unsplash library here so import unsplash from at SLB unsplash here let's
unsplash from at SLB unsplash here let's also go ahead and let's import use
also go ahead and let's import use effect and use state from
effect and use state from react now let's go ahead and let's
react now let's go ahead and let's assign assign the initial images here so
assign assign the initial images here so const images and set
const images and set images are used State and by default
images are used State and by default it's an empty array and let's go ahead
it's an empty array and let's go ahead and Define it as array and record of
and Define it as array and record of string
string any like that and then let's go ahead uh
any like that and then let's go ahead uh and let's create a use effect which is
and let's create a use effect which is going to fetch the images using the
going to fetch the images using the unsplash AP so use
effect let's go ahead and pass in the empty array and let's write const fetch
empty array and let's write const fetch images to be an asynchronous
images to be an asynchronous function and go ahead and open a try and
function and go ahead and open a try and catch
catch block let's resolve the error very
block let's resolve the error very simply by adding console log error so we
simply by adding console log error so we know something went wrong and let's add
know something went wrong and let's add set images and let's just put an empty
set images and let's just put an empty array in here and now let's let's go
array in here and now let's let's go ahead and let's write con result to be
ahead and let's write con result to be await on slash. photos. getet
await on slash. photos. getet random and now let's give it a property
random and now let's give it a property to only read from specific collection
to only read from specific collection IDs and that's going to be
IDs and that's going to be 31799 so where did I get this number
31799 so where did I get this number from well this is just an ID of an album
from well this is just an ID of an album from unsplash this specific one is the
from unsplash this specific one is the same that the original Trello uses so I
same that the original Trello uses so I went and looked at their Network
went and looked at their Network requests and I saw that they're making a
requests and I saw that they're making a request for this collection ID and the
request for this collection ID and the reason I think it's really good well
reason I think it's really good well first of all this uh collection is the
first of all this uh collection is the official collection from the unsplash
official collection from the unsplash editorial team and the second thing
editorial team and the second thing that's really good about it is that most
that's really good about it is that most of the images inside are compatible to
of the images inside are compatible to be wallpapers because as you know on
be wallpapers because as you know on unsplash we might have some images which
unsplash we might have some images which are not in the resolution or aspect
are not in the resolution or aspect ratio which fit the wallpaper so that's
ratio which fit the wallpaper so that's why this collection ID is perfect for
why this collection ID is perfect for what we need you can of course create
what we need you can of course create your own collection on unlash of free
your own collection on unlash of free images or you can just find any other
images or you can just find any other collection that you like great and
collection that you like great and besides that we're going to pick a count
besides that we're going to pick a count of nine so no more than that and then
of nine so no more than that and then let's go ahead and run
let's go ahead and run if we have result and if the result has
if we have result and if the result has a response in that case h
a response in that case h images let's simply Define them as
images let's simply Define them as result. response as array
record string and any and then let's write set images to
any and then let's write set images to be images and I just misspelled images
be images and I just misspelled images like this um great actually it looks
like this um great actually it looks like this constant images and this
like this constant images and this constant images is the same so let's
constant images is the same so let's call this result and then actually not
call this result and then actually not result let's call it okay new images so
result let's call it okay new images so just something different and then set
just something different and then set images new images because you can see
images new images because you can see it's confusing when I write images here
it's confusing when I write images here there's no error here because we defined
there's no error here because we defined images in here but this code will not
images in here but this code will not work so let's be explicit and let's
work so let's be explicit and let's rename this constant to new images and
rename this constant to new images and then assign the new images inside great
then assign the new images inside great and then let's write an else function to
and then let's write an else function to be console log uh sorry console. error
be console log uh sorry console. error fail to get images from
fail to get images from unsplash great and now let's go ahead
unsplash great and now let's go ahead and let's simp add uh a loading State
and let's simp add uh a loading State here so we indicated the user that we
here so we indicated the user that we loading the images so is loading and set
loading the images so is loading and set is loading it's going to be used State
is loading it's going to be used State and by default it's going to be true
and by default it's going to be true because we immediately start loading and
because we immediately start loading and then just in the finally
then just in the finally Block Set is loading to be
Block Set is loading to be false great and then outside of uh this
false great and then outside of uh this constant fetch images we have to
constant fetch images we have to actually call it so at the end of use
actually call it so at the end of use effect just fetch
effect just fetch images like that perfect and now let's
images like that perfect and now let's go ahead and write if is loading in that
go ahead and write if is loading in that case let's return a div with a loader 2
case let's return a div with a loader 2 element from Lucid react so make sure
element from Lucid react so make sure you import Loader
you import Loader 2 and let's give it a class name of h-6
2 and let's give it a class name of h-6 w-6 text- sky- 700 and animate Dash spin
w-6 text- sky- 700 and animate Dash spin and let's go ahead and give it a class
and let's go ahead and give it a class name of padding six Flex items Das
name of padding six Flex items Das Center and justify Das
Center and justify Das Center uh great and now inside of here I
Center uh great and now inside of here I want to go ahead and actually render the
want to go ahead and actually render the images but just before we do that let's
images but just before we do that let's go ahead and let's add one more State
go ahead and let's add one more State here so con selected image ID and set
here so con selected image ID and set selected image ID is going to be used
selected image ID is going to be used State
State null and let's also extract the pending
null and let's also extract the pending state from us use form status because
state from us use form status because this component is also going to be used
this component is also going to be used exclusively inside of forms so we can do
exclusively inside of forms so we can do this and then we can uh we don't have to
this and then we can uh we don't have to pass in any outside loading State
pass in any outside loading State instead we can use this pending one
instead we can use this pending one regardless if this is going to be an
regardless if this is going to be an input or not right uh great so now let's
input or not right uh great so now let's go inside of the uh form picker itself
go inside of the uh form picker itself and let's give this div a class name of
and let's give this div a class name of relative let's go ahead and open up a
relative let's go ahead and open up a div with a class name of grid grid Das
div with a class name of grid grid Das call -3 and GAP -2 and margin bottom of
call -3 and GAP -2 and margin bottom of two let's go ahead and let's do images.
two let's go ahead and let's do images. map let's get the individual image here
map let's get the individual image here and let's create a div with a class
and let's create a div with a class name and let's actually collapse it like
name and let's actually collapse it like this oops so this is going to be dynamic
this oops so this is going to be dynamic add CN from add/ li utils like
add CN from add/ li utils like this and let's go ahead and add the
this and let's go ahead and add the cursor Das pointer relative aspect Das
cursor Das pointer relative aspect Das video group hover opacity
video group hover opacity -75 transition and BG muted and then
-75 transition and BG muted and then let's add the dynamic if it's pending in
let's add the dynamic if it's pending in that case opacity is 50 hover opacity 50
that case opacity is 50 hover opacity 50 as well and cursor is
as well and cursor is Auto and let's give this a key of image.
Auto and let's give this a key of image. ID and then let's go ahead and give it
ID and then let's go ahead and give it an on click
an on click option if it's pending break the
option if it's pending break the function otherwise set selected image ID
function otherwise set selected image ID to be image. ID so we show the user
to be image. ID so we show the user which image they selected um perfect so
which image they selected um perfect so now let's go ahead and let's uh try this
now let's go ahead and let's uh try this out so inside of here we're going to
out so inside of here we're going to render an image component from next
render an image component from next slash image so just make sure you add
slash image so just make sure you add next slash image import here
next slash image import here it is a self- closing tag and let's go
it is a self- closing tag and let's go ahead and give it a property fill let's
ahead and give it a property fill let's go ahead and give an ALT of unsplash
go ahead and give an ALT of unsplash image let's give a class name of object
image let's give a class name of object D cover and rounded small and let's give
D cover and rounded small and let's give it a source of image. url. Thum like
it a source of image. url. Thum like this and if you save you should get a
this and if you save you should get a little error so let's go ahead and try
little error so let's go ahead and try this out now so I'm going to refresh I'm
this out now so I'm going to refresh I'm going to click here and there we go we
going to click here and there we go we have an error because it's trying to
have an error because it's trying to load an unsplash image but we don't have
load an unsplash image but we don't have the host name images. unsplash
the host name images. unsplash configured so the same thing that
configured so the same thing that happened when we try to render uh
happened when we try to render uh organization image from clerk so go back
organization image from clerk so go back to next. config.js and go ahead and
to next. config.js and go ahead and create a new remote pattern so the
create a new remote pattern so the protocol is https and host name is
protocol is https and host name is images. unsplash.com and what I
images. unsplash.com and what I recommend you do is actually uh restart
recommend you do is actually uh restart your entire application so let me just
your entire application so let me just do that I'm going to close this and
do that I'm going to close this and let's do npm run Dev
let's do npm run Dev again let's refresh the entire
again let's refresh the entire application
application here let's just wait a second for this
here let's just wait a second for this to initialize all right and when I click
to initialize all right and when I click here there we go look at all of these
here there we go look at all of these beautiful images and we have a nice
beautiful images and we have a nice little indicator uh that we are hovering
little indicator uh that we are hovering over them and that we can click on them
over them and that we can click on them but let's take a look at our um requests
but let's take a look at our um requests here so I'm going to refresh and there
here so I'm going to refresh and there we go you can see that I already used uh
we go you can see that I already used uh six requests from this hour right so
six requests from this hour right so while you're developing I think like 50
while you're developing I think like 50 is probably something you're going to
is probably something you're going to surpass so this is what I'm going to
surpass so this is what I'm going to show you to do
show you to do next so I want to create a fallback
next so I want to create a fallback right in case something goes wrong with
right in case something goes wrong with the unsplash API I want to have like a
the unsplash API I want to have like a constant of nine images which can always
constant of nine images which can always be used even if we run out of our
be used even if we run out of our requests in an hour so two options for
requests in an hour so two options for that you can go inside of my GitHub you
that you can go inside of my GitHub you can go inside of constants here and
can go inside of constants here and select images and you can just copy this
select images and you can just copy this entire images here you can see there's
entire images here you can see there's nine of them and it's a bunch of objects
nine of them and it's a bunch of objects and all kinds of information that we
and all kinds of information that we need to render them or you can create
need to render them or you can create your own version of that so for that
your own version of that so for that you're going to go ahead uh well let's
you're going to go ahead uh well let's let's just prepare that first so let's
let's just prepare that first so let's go ahead and create I think we have the
go ahead and create I think we have the constants folder do we we have the
constants folder do we we have the config folder we don't have the
config folder we don't have the constants okay so let's go ahead and
constants okay so let's go ahead and create the constants
folder and inside let's create images. THS like that and let's write
images. THS like that and let's write export default images uh sorry export
export default images uh sorry export cons default images like
cons default images like that uh and let's just make it an empty
that uh and let's just make it an empty the array for now and now what I want to
the array for now and now what I want to do is I want to open my inspect element
do is I want to open my inspect element and I want to open my network Tab and in
and I want to open my network Tab and in here when I
here when I click uh we we should be seeing uh wait
click uh we we should be seeing uh wait let me just try and click all here there
let me just try and click all here there we go okay uh so you should be seeing uh
we go okay uh so you should be seeing uh well an API request which starts with
well an API request which starts with random so you can go ahead and search
random so you can go ahead and search for random inside of here and there we
for random inside of here and there we go you should see the images here so go
go you should see the images here so go ahead and click on
ahead and click on copy and I'm not sure which one it is I
copy and I'm not sure which one it is I think it's copy response let's try that
think it's copy response let's try that so I'm going to go ahead and try and
so I'm going to go ahead and try and copy the response from that and paste it
copy the response from that and paste it here I don't think it's that one it
here I don't think it's that one it doesn't seem
doesn't seem formatted uh perhaps we can just copy it
formatted uh perhaps we can just copy it like directly from here just copy
like directly from here just copy everything let's try that so this is how
everything let's try that so this is how I did it I just want to show you how you
I did it I just want to show you how you can do it yourself uh there we go that's
can do it yourself uh there we go that's it now it works so again you can either
it now it works so again you can either if you don't want to do this like
if you don't want to do this like there's no need this is you know just
there's no need this is you know just Cosmetics you can just go inside of my
Cosmetics you can just go inside of my uh GitHub go into constant images. THS
uh GitHub go into constant images. THS and just copy them so this is also going
and just copy them so this is also going to save you if for any reason you didn't
to save you if for any reason you didn't manage to set up the unlash API and this
manage to set up the unlash API and this is what we're going to do next so we're
is what we're going to do next so we're going to make sure that we use this uh
going to make sure that we use this uh default images so let's go back inside
default images so let's go back inside of our form picker
of our form picker component and now uh let's go ahead and
component and now uh let's go ahead and let's import uh those default images
let's import uh those default images right here at the bottom so let's write
right here at the bottom so let's write import default images from constant
import default images from constant images like that and what I want to do
images like that and what I want to do is inside of this error if uh something
is inside of this error if uh something went wrong let's just set the images to
went wrong let's just set the images to be those default images or you can also
be those default images or you can also add them to be inside of the well the
add them to be inside of the well the the initial like array of images right
the initial like array of images right so let's try if that's working so when I
so let's try if that's working so when I click here
click here well it seems to still be working let's
well it seems to still be working let's try and kind of break this function uh I
try and kind of break this function uh I don't know I'm going to go ahead and I'm
don't know I'm going to go ahead and I'm just going to throw an error
already so something like that let's say something went wrong in our API when I
something went wrong in our API when I click here there we go something went
click here there we go something went wrong but you can see that every time I
wrong but you can see that every time I open I have the same set of images and
open I have the same set of images and that's enough for us to work with
that's enough for us to work with perfect so what we have to do next is
perfect so what we have to do next is oblig by API uh unsplash guidelines and
oblig by API uh unsplash guidelines and they say that we need to hot link to the
they say that we need to hot link to the original Creator so we're going to do it
original Creator so we're going to do it the same way Trella does and that's by
the same way Trella does and that's by creating a little black bar at the
creating a little black bar at the bottom uh of each image uh so we can see
bottom uh of each image uh so we can see exactly who created it so let's go ahead
exactly who created it so let's go ahead uh and let's find where our image
uh and let's find where our image component is it's right here and let's
component is it's right here and let's go to the bottom and let's add a link
go to the bottom and let's add a link component from next SL link so just make
component from next SL link so just make sure you add uh this import
and let's go ahead and let's give it an hre to be image. links. HTML like that
hre to be image. links. HTML like that and I'm just actually going to collapse
and I'm just actually going to collapse all the props we're going to have inside
all the props we're going to have inside so besides the HRA we're also going to
so besides the HRA we're also going to have uh a Target which is just going to
have uh a Target which is just going to be underscore blank and then we're going
be underscore blank and then we're going to have a class name of opacity zero
to have a class name of opacity zero group- hover opacity 100 absolute bottom
group- hover opacity 100 absolute bottom Das 0 w- full then we're going to have
Das 0 w- full then we're going to have some very small text of 10 pixels
some very small text of 10 pixels truncate so if name is too long it
truncate so if name is too long it doesn't overflow text- white hover
doesn't overflow text- white hover underline and padding one MBG black
underline and padding one MBG black sl10 like that perfect so uh let's just
sl10 like that perfect so uh let's just go ahead and confirm that for this group
go ahead and confirm that for this group hover we actually added a group element
hover we actually added a group element with it great so now whenever we hover
with it great so now whenever we hover on this top div this thing is going to
on this top div this thing is going to become visible perfect and inside of
become visible perfect and inside of this link let's go ahead and run their
this link let's go ahead and run their image. user.name so let's try that out
image. user.name so let's try that out now and we can actually remove this
now and we can actually remove this error so make sure you remove this error
error so make sure you remove this error from Fetch images great and let's try it
from Fetch images great and let's try it out now when I click here there we go
out now when I click here there we go you can see how we have a nice little uh
you can see how we have a nice little uh hot link to the actual user I just feel
hot link to the actual user I just feel like this can be a bit darker this
like this can be a bit darker this background right because it's barely
background right because it's barely visible here for example so let's just
visible here for example so let's just see if we did that uh correctly so I'm
see if we did that uh correctly so I'm going to go all the way here so BG black
going to go all the way here so BG black 10 how about we do BG black 50 for
10 how about we do BG black 50 for example is that better there we go yeah
example is that better there we go yeah that looks better so it has kind of a
that looks better so it has kind of a darker background and if you click on
darker background and if you click on here uh it's going to hotlink you to the
here uh it's going to hotlink you to the original creator of that image so this
original creator of that image so this is what we need to do uh to oblige by
is what we need to do uh to oblige by unsplash API guidelines if you ever want
unsplash API guidelines if you ever want to you know click on this uh apply for
to you know click on this uh apply for production thing you of course have to
production thing you of course have to read the entire guidelines right so you
read the entire guidelines right so you can see everything you need to do here
can see everything you need to do here uh perfect so now that we have that
uh perfect so now that we have that let's actually create the functionality
let's actually create the functionality that when we click on an image it shows
that when we click on an image it shows that it is uh selected so I think we
that it is uh selected so I think we already have that yeah we have the
already have that yeah we have the onclick and we set the selected image ID
onclick and we set the selected image ID but we don't have any indicator that
but we don't have any indicator that that is true so above this image let's
that is true so above this image let's go ahead and write if selected image ID
go ahead and write if selected image ID is equal to the current image ID which
is equal to the current image ID which is in the array in that
is in the array in that case let's go ahead and let's render a
case let's go ahead and let's render a div with a class name of absolute in Set
div with a class name of absolute in Set uh y z h full W full BG black 30 FX
uh y z h full W full BG black 30 FX items Center and justify Center and
items Center and justify Center and inside I just want to render a check
inside I just want to render a check icon from Lucid react so make sure you
icon from Lucid react so make sure you import the check alongside Loader 2 here
import the check alongside Loader 2 here and let's give it a class name of h-4
and let's give it a class name of h-4 W-4 and text- white and let's try that
W-4 and text- white and let's try that out now so I'm going to refresh and when
out now so I'm going to refresh and when I select an image it should be selected
I select an image it should be selected but it is not let's see and debug why
but it is not let's see and debug why that is not happening here so let's see
that is not happening here so let's see set selected image ID image
ID let's go ahead and debug uh why this is
let's go ahead and debug uh why this is happening so set selected
aage let's first see if this is working this on click so conso log image ID
this on click so conso log image ID selected image let's try that
selected image let's try that first so I'm going to go ahead inside of
first so I'm going to go ahead inside of my inspect element and when I click here
my inspect element and when I click here okay it obviously sends the correct
okay it obviously sends the correct ID and and it's definitely sets it in
ID and and it's definitely sets it in the store here and then in here we check
the store here and then in here we check if that ID is matching but for some
if that ID is matching but for some reason uh it's not showing oh I think
reason uh it's not showing oh I think it's because I have to put this below
it's because I have to put this below the image component I think it's because
the image component I think it's because of that let's try this is then going to
work yes that's it as you can see now when I click on something it shows
when I click on something it shows a little check little check icon in the
a little check little check icon in the middle perfect so that is uh what we
middle perfect so that is uh what we needed and now I can remove did I remove
needed and now I can remove did I remove the console log I did uh great what I
the console log I did uh great what I want to do now is I want to add the
want to do now is I want to add the errors component because remember we are
errors component because remember we are also passing in the errors in this so
also passing in the errors in this so let's go all the way to the bottom uh
let's go all the way to the bottom uh just by the end of this div and render
just by the end of this div and render the form errors component from do/ form
the form errors component from do/ form errors so just make sure you imported it
errors so just make sure you imported it from do/
from do/ form errors here and let's go ahead and
form errors here and let's go ahead and let's pass in the
let's pass in the ID to be image and errors errors like
ID to be image and errors errors like that great and now I have to add a
that great and now I have to add a little hidden input here which is
little hidden input here which is actually going to uh SE trigger when we
actually going to uh SE trigger when we select a specific image so for that I'm
select a specific image so for that I'm going to use the input type radio so
going to use the input type radio so just write a native HTML input element
just write a native HTML input element and give it a type of radio
and give it a type of radio like
like that and now let's go ahead and give it
that and now let's go ahead and give it an ID of ID a name of ID and let's give
an ID of ID a name of ID and let's give it a class name of hidden let's also go
it a class name of hidden let's also go ahead and write checked to be selected
ahead and write checked to be selected image ID to be image. ID
image ID to be image. ID whoops let's go ahead and give it on
whoops let's go ahead and give it on change to well just be an empty actually
change to well just be an empty actually I don't think we even need to pass
I don't think we even need to pass unchange and let's give it a disabled
unchange and let's give it a disabled prop of
prop of pending and we also need to give it a
pending and we also need to give it a value of the image which we select right
value of the image which we select right so uh let's take a look at our Network
so uh let's take a look at our Network request for that I can just go here
request for that I can just go here let's take a look at everything we have
let's take a look at everything we have so we're not going to be working with
so we're not going to be working with any of this stuff what we're going to
any of this stuff what we're going to save in the database actually is just
save in the database actually is just the actual uh let me try and find act
the actual uh let me try and find act actual URLs so we have Raw full regular
actual URLs so we have Raw full regular small so these are sizes right so we're
small so these are sizes right so we're going to save a couple of them and we're
going to save a couple of them and we're going to do this in this way so in this
going to do this in this way so in this input type radio give it a value off and
input type radio give it a value off and open up back Texs so first is going to
open up back Texs so first is going to have the image ID and then we're going
have the image ID and then we're going to add a pipe and we're going to open
to add a pipe and we're going to open this template literal again and write
this template literal again and write image. url. Thum open a pipe again and
image. url. Thum open a pipe again and then we're going to have image. urls .
then we're going to have image. urls . full then open a pipe again and then
full then open a pipe again and then we're going to write image. your image.
we're going to write image. your image. links.
links. HTML and then image. user.name that's
HTML and then image. user.name that's going to be the last one so let me zoom
going to be the last one so let me zoom out so you can see this in one line so
out so you can see this in one line so value is image. ID pipe image URLs thumb
value is image. ID pipe image URLs thumb pipe image Ur is full pipe image URLs uh
pipe image Ur is full pipe image URLs uh sorry image links HTML pipe and image
sorry image links HTML pipe and image username so these pipes are important
username so these pipes are important because in the server action we're going
because in the server action we're going to extract them uh we're going to split
to extract them uh we're going to split this string by pipes and then the each
this string by pipes and then the each item in the array is going to represent
item in the array is going to represent the ID the thumbnail the full image the
the ID the thumbnail the full image the HTML link to link to that image and the
HTML link to link to that image and the user that created that image so make
user that created that image so make sure you do it exactly like this if you
sure you do it exactly like this if you have a feeling you did something wrong
have a feeling you did something wrong you can always visit my GitHub find the
you can always visit my GitHub find the form uh picker component and just copy
form uh picker component and just copy the value which I assign to this hidden
the value which I assign to this hidden radio right here great so let me refresh
radio right here great so let me refresh now let's just confirm that nothing uh
now let's just confirm that nothing uh weird is happening great so why did we
weird is happening great so why did we have to add that input so now when we
have to add that input so now when we submit our form we're going to have
submit our form we're going to have access to the ID of that image uh inside
access to the ID of that image uh inside of our form data so let's go ahead and
of our form data so let's go ahead and let's go back inside of the uh form
let's go back inside of the uh form popover so let's go into form pop over
popover so let's go into form pop over here in here we have the onsubmit and we
here in here we have the onsubmit and we extract the title so now let's write con
extract the title so now let's write con image to be form data. getet image as
image to be form data. getet image as string and let's conso log the
string and let's conso log the image and I'm going to comment out the
image and I'm going to comment out the execute function for now so it doesn't
execute function for now so it doesn't mess with anything else so I'm going to
mess with anything else so I'm going to open my terminal now when I select an
open my terminal now when I select an image and click create there we go you
image and click create there we go you can see my object uh that image is a
can see my object uh that image is a string
string which has a lot of URLs and each of them
which has a lot of URLs and each of them is separated by a pipe so that's exactly
is separated by a pipe so that's exactly what we wanted to achieve
what we wanted to achieve perfect so why is this working how do we
perfect so why is this working how do we know that we can access the selected
know that we can access the selected image inside of form data. getet image
image inside of form data. getet image so if you take a look at our form picker
so if you take a look at our form picker here we pass the ID to be image and
here we pass the ID to be image and inside of our form picker we assign that
inside of our form picker we assign that ID and that name to this hidden input
ID and that name to this hidden input component which user uh clicks uh which
component which user uh clicks uh which is checked when the selected image ID
is checked when the selected image ID matches right and we do that by clicking
matches right and we do that by clicking on the outer div so that's why this is
on the outer div so that's why this is working perfect so now what we have to
working perfect so now what we have to do is we have to modify our schema
do is we have to modify our schema Prisma to actually accept all of these
Prisma to actually accept all of these values so let's go back inside of our
values so let's go back inside of our Prisma schema so where is it Prisma
Prisma schema so where is it Prisma schema and let's now modify this board a
schema and let's now modify this board a bit so besides the ID it's also going to
bit so besides the ID it's also going to have organization ID and that is going
have organization ID and that is going to be a string it's going to have the
to be a string it's going to have the title and then it's going to have image
title and then it's going to have image ID which is a string and let's just
ID which is a string and let's just write lowercase D image Thum URL which
write lowercase D image Thum URL which is a string and db. text so it can
is a string and db. text so it can accept a very long uh amount of
accept a very long uh amount of characters then image full your also a
characters then image full your also a string nb. text then we're going to have
string nb. text then we're going to have image username string nb. text as well
image username string nb. text as well and image link HTML to be string db.
and image link HTML to be string db. text as well and while we are here let's
text as well and while we are here let's also add the created at be date time and
also add the created at be date time and default now and let's also add the
default now and let's also add the updated ad to be date time at updated ad
updated ad to be date time at updated ad perfect and this is what I like to do is
perfect and this is what I like to do is I like to keep all of those aligned the
I like to keep all of those aligned the same
same right just a small little cosmetic here
right just a small little cosmetic here okay uh great and now we have to update
okay uh great and now we have to update our schema and let's go ahead inside of
our schema and let's go ahead inside of our terminal here and first I want to
our terminal here and first I want to show you how to reset your entire
show you how to reset your entire database so just make sure you're doing
database so just make sure you're doing this in development you know uh make
this in development you know uh make sure you're not doing this in production
sure you're not doing this in production database even though you should have
database even though you should have necessary systems in place to prevent
necessary systems in place to prevent this from even attempting to happen so
this from even attempting to happen so let's write npx Prisma
let's write npx Prisma migrate reset so this will reset the
migrate reset so this will reset the entire database and as you can see it's
entire database and as you can see it's asking us if we are sure so just go
asking us if we are sure so just go ahead and press Y and now it's going to
ahead and press Y and now it's going to reset the entire database so the reason
reset the entire database so the reason I'm resetting the database is because we
I'm resetting the database is because we have a bunch of those boards created
have a bunch of those boards created from before uh which don't have all the
from before uh which don't have all the necessary data which we need and now we
necessary data which we need and now we can can write MPX Prisma DB push and
can can write MPX Prisma DB push and then that is going to assign this new
then that is going to assign this new schema Prisma to your uh mySQL database
schema Prisma to your uh mySQL database and let's also just in case run npx
and let's also just in case run npx Prisma generate so we locally have all
Prisma generate so we locally have all of those new types perfect so now we're
of those new types perfect so now we're ready to actually save that in the
ready to actually save that in the database so now we have to modify our
database so now we have to modify our schema a bit but not Prisma schema but
schema a bit but not Prisma schema but the action schema for create board so go
the action schema for create board so go inside of schema. Cs here and alongside
inside of schema. Cs here and alongside title we also need to validate the image
title we also need to validate the image to be a string and let's give it a
to be a string and let's give it a required error of image is required and
required error of image is required and let's also give it an invalid type error
let's also give it an invalid type error of image is required as well uh great
of image is required as well uh great and now let's head back inside of index.
and now let's head back inside of index. vs and you can see we already have some
vs and you can see we already have some errors here because our uh well
errors here because our uh well requirements to create a board have
requirements to create a board have changed so first things first besides
changed so first things first besides user idid I also want to extract the
user idid I also want to extract the current organization ID and we will not
current organization ID and we will not proceed further if that is not available
proceed further if that is not available as well as the user ID great and now
as well as the user ID great and now besides extracting title from the data
besides extracting title from the data we're also going to extract the image
we're also going to extract the image from the data and then let's go ahead
from the data and then let's go ahead and let's write
and let's write const open up an array like this and
const open up an array like this and write image dosit and we're going to
write image dosit and we're going to split by that pipe element because
split by that pipe element because inside of our form picker as you can see
inside of our form picker as you can see in the value here we separate all of
in the value here we separate all of these items by a pipe element so let's
these items by a pipe element so let's go ahead and now extract all of them so
go ahead and now extract all of them so they're going to appear in the array by
they're going to appear in the array by The order they are in so first we have
The order they are in so first we have the ID then the thumbnail then the full
the ID then the thumbnail then the full then the HTML link and then the username
then the HTML link and then the username so let's go ahead and extract the image
so let's go ahead and extract the image ID the image thumb URL then let's
ID the image thumb URL then let's extract image full URL then image usern
extract image full URL then image usern name then image link HTML actually I
name then image link HTML actually I think it's it goes link and then user so
think it's it goes link and then user so let's just reverse this two so first
let's just reverse this two so first link and then user perfect and now let's
link and then user perfect and now let's go ahead and let's write um if there is
go ahead and let's write um if there is no image ID or if there is no image
no image ID or if there is no image thumb URL or if there is no image full
thumb URL or if there is no image full URL or if there is no image username or
URL or if there is no image username or if there is no image link HTML in in
if there is no image link HTML in in case any of those is missing return an
case any of those is missing return an error missing Fields failed to create
board all right and now let's go ahead and let's modify our DB board create
and let's modify our DB board create here so we're going to pass in the title
here so we're going to pass in the title the organization ID the image ID the
the organization ID the image ID the image thumb URL the image full URL image
image thumb URL the image full URL image username and image link h HTML and let's
username and image link h HTML and let's just see if we have this image link HTML
just see if we have this image link HTML it's right here and it seems like our
it's right here and it seems like our has a little error here could be because
has a little error here could be because it's differently stored in the schema
it's differently stored in the schema let me just revisit my Prisma schema
let me just revisit my Prisma schema here so image link HTML this should be
here so image link HTML this should be um available here so image link HTML can
um available here so image link HTML can I just pass it like this oh so I think
I just pass it like this oh so I think we
we extracted it incorrectly oh yes we
extracted it incorrectly oh yes we extracted it with lowercase uh tml so
extracted it with lowercase uh tml so let's just remap this to be HTML in
let's just remap this to be HTML in capitals and then also modify the if
capitals and then also modify the if Clause to use that and then we can
Clause to use that and then we can safely use it here perfect uh and I
safely use it here perfect uh and I think that should be
think that should be it yes that looks like uh it's good
it yes that looks like uh it's good enough and what I want to do next is
enough and what I want to do next is well I want to help you out a bit just
well I want to help you out a bit just in case you know this is is a bit
in case you know this is is a bit complicated what we're doing with the
complicated what we're doing with the images so go ahead and just add a
images so go ahead and just add a console log here and I just want to want
console log here and I just want to want you to copy this array and paste it here
you to copy this array and paste it here and I just want you to confirm that you
and I just want you to confirm that you have all of those here and actually
have all of those here and actually change it from array to be an object
change it from array to be an object it's going to be easier for you to
it's going to be easier for you to notice if something is missing so let's
notice if something is missing so let's go ahead and try this out now I'm going
go ahead and try this out now I'm going to go ahead and open my terminal
to go ahead and open my terminal here uh and make sure you do mpm run
here uh and make sure you do mpm run Dev because we updated our Prisma schema
Dev because we updated our Prisma schema so let's go ahead and try this
so let's go ahead and try this now so once I select a image and write
now so once I select a image and write test and click
test and click create uh is there an error
create uh is there an error happening let's see why is it not
happening let's see why is it not working
working whoa let's try
whoa let's try test form
test form popover okay let's debug let's go back
popover okay let's debug let's go back inside of of our form popover
inside of of our form popover component where we called oh it's
component where we called oh it's because I logged out the execute okay so
because I logged out the execute okay so yeah and passing the image here as well
yeah and passing the image here as well so I forgot to log out the execute
so I forgot to log out the execute okay uh let's try it out again so I'm
okay uh let's try it out again so I'm going to prepare my terminal here just
going to prepare my terminal here just because I want to see my image selection
because I want to see my image selection so when I click create there we go
so when I click create there we go confirm that you have image username
confirm that you have image username confirm that you have image link HTML
confirm that you have image link HTML full URL thumb URL on image none of this
full URL thumb URL on image none of this should be undefined if some some of
should be undefined if some some of these are undefined I highly advise you
these are undefined I highly advise you that you double check that your form
that you double check that your form picker is exactly as mine is with this
picker is exactly as mine is with this pipes right here and inside of the index
pipes right here and inside of the index right here confirm that you don't have
right here confirm that you don't have any misspells in the name here and that
any misspells in the name here and that you're using them properly throughout
you're using them properly throughout this if clause and as well as here but I
this if clause and as well as here but I mean you're obviously going to get some
mean you're obviously going to get some errors if anything is missing perfect so
errors if anything is missing perfect so we can remove this conso log and mine
we can remove this conso log and mine was successfully created I don't know if
was successfully created I don't know if you saw the message so I'm going to go
you saw the message so I'm going to go ahead and just run npx Prisma
ahead and just run npx Prisma studio so we can actually see this in
studio so we can actually see this in the database and there we go look at my
the database and there we go look at my board right here I have an ID I have an
board right here I have an ID I have an organization ID the title image ID image
organization ID the title image ID image thumbnail full URL username of the
thumbnail full URL username of the author the hot link and we have created
author the hot link and we have created that and updated
that and updated perfect so this is what we needed to do
perfect so this is what we needed to do to finish up our uh form popover so just
to finish up our uh form popover so just two more things that I want to do here
two more things that I want to do here first I want to go back inside of my
first I want to go back inside of my components form form submit and I'm
components form form submit and I'm going to change this variant in the
going to change this variant in the props to be a default primary like this
props to be a default primary like this so I want it to be this bluish color the
so I want it to be this bluish color the second thing I want is that we when we
second thing I want is that we when we successfully uh create a
successfully uh create a board I want this pop over to close so
board I want this pop over to close so let's go ahead and do that now we have
let's go ahead and do that now we have to go back inside of our uh form pop
to go back inside of our uh form pop over here and we have to create a ref uh
over here and we have to create a ref uh called close ref so let's go above this
called close ref so let's go above this use action and add const close ref to be
use action and add const close ref to be use ref from react make sure you add the
use ref from react make sure you add the use ref here I'm just going to move it
use ref here I'm just going to move it here to the top all right and let's go
here to the top all right and let's go ahead and give it an element
ahead and give it an element ref which you can also import from react
ref which you can also import from react open pointy brackets and inside just
open pointy brackets and inside just write a type of button and by default it
write a type of button and by default it is uh going to be null and now we have
is uh going to be null and now we have to assign this close ref to the pop over
to assign this close ref to the pop over close so find the pop over close right
close so find the pop over close right here it's wrapping our button and give
here it's wrapping our button and give it a ref off close ref and then here on
it a ref off close ref and then here on success what we're going to do
success what we're going to do after we well we no longer need this
after we well we no longer need this console logs right we now have the
console logs right we now have the toaster and what I want to do here now
toaster and what I want to do here now is do close ref. current question mark
is do close ref. current question mark uh doclick
uh doclick like that perfect so now if you try it
like that perfect so now if you try it out and click
out and click here after it's success there we go you
here after it's success there we go you can see the board now closes perfect and
can see the board now closes perfect and I want to add one more thing I want to
I want to add one more thing I want to add the router here so const router use
add the router here so const router use router from next SL navigation let me
router from next SL navigation let me show you where I imported that I'm just
show you where I imported that I'm just going to move it here with the big
going to move it here with the big Imports and then I want to do router.
Imports and then I want to do router. [Music]
[Music] push slash board
push slash board data. like this so make sure it's board
data. like this so make sure it's board and not boards and I think this is
and not boards and I think this is incorrect because my data oh my data is
incorrect because my data oh my data is unknown oh that's not good let's try
unknown oh that's not good let's try that out let's go inside of actions
that out let's go inside of actions create board and let's look at oh yeah
create board and let's look at oh yeah we have some errors it
we have some errors it seems okay let's take a look at types
seems okay let's take a look at types here board why is there an error
here board why is there an error here
here H oh all right all I had to do is press
H oh all right all I had to do is press command shift b or control shift B and
command shift b or control shift B and then type reload window here it looks
then type reload window here it looks like that our types were a bit
like that our types were a bit outdated because this board which we use
outdated because this board which we use you can see now has all of these new
you can see now has all of these new items but looks like something happened
items but looks like something happened with Visual Studio code uh intellisense
with Visual Studio code uh intellisense and since we updated our Prisma schema
and since we updated our Prisma schema it didn't really sync up right now there
it didn't really sync up right now there are no errors here and in my form
are no errors here and in my form popover you can see that it says the
popover you can see that it says the data ID is a string perfect so let's
data ID is a string perfect so let's just try this out as well now it should
just try this out as well now it should redirect me to a 404 page there we go so
redirect me to a 404 page there we go so it goes to slash board and then that
it goes to slash board and then that board ID which is a 404 page and last
board ID which is a 404 page and last thing we have to do is just enable that
thing we have to do is just enable that to be open when we click on the create
to be open when we click on the create button here at the
button here at the top so let's go inside of the navbar
top so let's go inside of the navbar component so let me just close
component so let me just close everything here and let's go inside of
everything here and let's go inside of app folder platform dashboard components
app folder platform dashboard components navbar and in here let's go ahead and
navbar and in here let's go ahead and let's
let's import form popover from components form
import form popover from components form form popover and go ahead and wrap the
form popover and go ahead and wrap the button inside of form popover like
button inside of form popover like this and let's go ahead and let's give
this and let's go ahead and let's give it an align of start let's give it a
it an align of start let's give it a side of bottom and let's give it a side
side of bottom and let's give it a side off set of
off set of 18 so make sure you are on desktop mode
18 so make sure you are on desktop mode so have this big create button and there
so have this big create button and there we go go you can see how now we can do
we go go you can see how now we can do it from here as well as from here and uh
it from here as well as from here and uh we also have to wrap this mobile version
we also have to wrap this mobile version into a form
into a form popover and for that one we don't need
popover and for that one we don't need to do any additional stuff so now make
to do any additional stuff so now make sure you are on mobile mode and there we
sure you are on mobile mode and there we go it's right here perfect great so you
go it's right here perfect great so you just wrapped up creating uh the form
just wrapped up creating uh the form popover component which is used to
popover component which is used to create create boards and redirect users
create create boards and redirect users to that board what we're going to do
to that board what we're going to do next is render all of our active boards
next is render all of our active boards here inside of that great great great
here inside of that great great great job so now that we have our working form
job so now that we have our working form picker and our working input and
picker and our working input and everything is working fine let's go
everything is working fine let's go ahead and let's actually render a list
ahead and let's actually render a list of our boards so inside of your Prisma
of our boards so inside of your Prisma studio just make sure that you have a
studio just make sure that you have a couple of boards here which have all the
couple of boards here which have all the necessary fields like thumbnail URL full
necessary fields like thumbnail URL full image URL and some other stuff here
image URL and some other stuff here great so I'm going to go back inside uh
great so I'm going to go back inside uh of board list component which we have
of board list component which we have inside of the app folder platform
inside of the app folder platform dashboard organization ID components
dashboard organization ID components board list right here and let's go ahead
board list right here and let's go ahead and let's import the database because
and let's import the database because remember this is a server component so
remember this is a server component so we can go ahead and fetch the actual uh
we can go ahead and fetch the actual uh boards from here and let's also extract
boards from here and let's also extract the current organization ID from out
the current organization ID from out util which we get from clerk nextjs
util which we get from clerk nextjs which I'm just going to move here to the
which I'm just going to move here to the top and then let's say if we don't have
top and then let's say if we don't have the current organization ID let's go
the current organization ID let's go ahead and return redirect from next SL
ahead and return redirect from next SL navigation to/ select
navigation to/ select dorg and I imported next navigation from
dorg and I imported next navigation from here great so if there is no organ
here great so if there is no organ oranization ID present even though this
oranization ID present even though this shouldn't happen because our middle or
shouldn't happen because our middle or prevents it from happening but keep in
prevents it from happening but keep in mind that this one can be null or
mind that this one can be null or undefined so it's just easier to do this
undefined so it's just easier to do this check rather than you know checking it
check rather than you know checking it every time and now let's go ahead and
every time and now let's go ahead and write const boards to be await db.
write const boards to be await db. board. find
board. find many and since we're using await we also
many and since we're using await we also need to turn this board list into an
need to turn this board list into an asynchronous function and let's write
asynchronous function and let's write where organization ID that's the only
where organization ID that's the only criteria we need and let's order by
criteria we need and let's order by created at
descending great now that we have that we can go ahead and iterate over those
we can go ahead and iterate over those boards so let's go ahead and just above
boards so let's go ahead and just above the form popper here let's do boards.
the form popper here let's do boards. map get the individual board here and
map get the individual board here and let's go ahead and add a link from next
let's go ahead and add a link from next slash link so I just added that import
slash link so I just added that import here let me move it to the top and let's
here let me move it to the top and let's go ahead and give it some props so HRA
go ahead and give it some props so HRA is going to be slash board SL individual
is going to be slash board SL individual board ID then we're going to have a key
board ID then we're going to have a key which is board ID so let me just make
which is board ID so let me just make that the first attribute
that the first attribute here great then we're going to have a
here great then we're going to have a style which is going to be background
style which is going to be background image and let's open backs to be
image and let's open backs to be URL and inside let's go ahead and access
URL and inside let's go ahead and access the board
the board image thumb URL so uh we save the thumb
image thumb URL so uh we save the thumb URL purposely because it takes only a
URL purposely because it takes only a few seconds to load that small image but
few seconds to load that small image but then later we also save the full image
then later we also save the full image so that we can actually uh load it when
so that we can actually uh load it when it comes to well loading a board
it comes to well loading a board entirely and let's add a class name here
entirely and let's add a class name here which is group relative aspect D video
which is group relative aspect D video BG Das no-
BG Das no- repeat like that BG Das Center uh we're
repeat like that BG Das Center uh we're also going to have BG Das
also going to have BG Das cover and let's also add BG Sky 700 in
cover and let's also add BG Sky 700 in case it's still uh loading rounded small
case it's still uh loading rounded small h- full w- full padding two and let's go
h- full w- full padding two and let's go ahead and add overflow
ahead and add overflow hidden like that that great and now
hidden like that that great and now inside of the link let's go ahead and
inside of the link let's go ahead and let open a div here which is actually
let open a div here which is actually going to be a self closing tag so a
going to be a self closing tag so a div like this and let's add a class name
div like this and let's add a class name to it to be absolute inser Z BG black
30 and group- hover is going to be BG Black slash 40 and
Black slash 40 and transition and now let's add a paragraph
transition and now let's add a paragraph which is going to render the actual uh
which is going to render the actual uh board title and let's give it a class
board title and let's give it a class name to be
name to be relative font semi bold and text
relative font semi bold and text white there we go and here we have a
white there we go and here we have a list of our boards now and you can see
list of our boards now and you can see how when we hover we have a nice little
how when we hover we have a nice little uh darkened effect and when we click on
uh darkened effect and when we click on individual one it redirects us to that
individual one it redirects us to that uh specific board ID per perfect so what
uh specific board ID per perfect so what I want to create now uh is a little
I want to create now uh is a little loading state right right now as you can
loading state right right now as you can see it's just plain like this so let's
see it's just plain like this so let's go ahead uh and let's create an actual
go ahead uh and let's create an actual loading state for that and then we're
loading state for that and then we're going to wrap the entire thing in
going to wrap the entire thing in suspense so it doesn't block the UI
suspense so it doesn't block the UI while it's loading so let's go to the
while it's loading so let's go to the bottom of the board list and write board
bottom of the board list and write board list. skeleton to be function
list. skeleton to be function skeleton board list and let's go go
skeleton board list and let's go go ahead and return a div with a class name
ahead and return a div with a class name to be grid grid
to be grid grid ds-2 small grid
ds-2 small grid ds-3 large grid ds-4 and GAP 4 and
ds-3 large grid ds-4 and GAP 4 and inside let's render the skeleton
inside let's render the skeleton component from at/ components UI
component from at/ components UI skeleton and let's go ahead and give it
skeleton and let's go ahead and give it a class name of aspect D video h- full
a class name of aspect D video h- full w-o and padding off two and let's go
w-o and padding off two and let's go ahead uh and just copy this perhaps
ahead uh and just copy this perhaps eight times like this let's try that out
eight times like this let's try that out now well actually we don't actually use
now well actually we don't actually use this skeleton anywhere so before we go
this skeleton anywhere so before we go ahead uh and do that let's head back
ahead uh and do that let's head back make sure you save this in the board
make sure you save this in the board list and let's head back inside of page.
list and let's head back inside of page. vsx which renders the board list and
vsx which renders the board list and let's go ahead and wrap it inside of
let's go ahead and wrap it inside of suspense from react so make sure you
suspense from react so make sure you import
import suspense like this and wrap the board
suspense like this and wrap the board list inside of suspense and go ahead and
list inside of suspense and go ahead and give it a
give it a fullback to be board list. skeleton
fullback to be board list. skeleton which we just created like that and
which we just created like that and let's see this now so when I
let's see this now so when I whoa so when I refresh there we go it's
whoa so when I refresh there we go it's very fast but for a second you can see
very fast but for a second you can see how there is a skeleton in place of my
how there is a skeleton in place of my boards here yeah especially when we
boards here yeah especially when we switch between organizations you can see
switch between organizations you can see how they're still loading perfect so
how they're still loading perfect so exactly what we wanted to achieve so we
exactly what we wanted to achieve so we are now officially well uh regarding UI
are now officially well uh regarding UI done with this page right here we can
done with this page right here we can create new boards what we'll focus on
create new boards what we'll focus on now is creating this 404 page which uh
now is creating this 404 page which uh well is going to render the individual
well is going to render the individual uh board where we're going to be able to
uh board where we're going to be able to change the title of the board delete the
change the title of the board delete the board and and then we're slowly going to
board and and then we're slowly going to go ahead and implement the drag and drop
go ahead and implement the drag and drop functionality and then when we get to
functionality and then when we get to that when we get to specific card and
that when we get to specific card and task functionality that's when we're
task functionality that's when we're going to add the activity and then we're
going to add the activity and then we're going to wrap up both the activity here
going to wrap up both the activity here and also in the individual card so we're
and also in the individual card so we're going to leave the activity for a bit
going to leave the activity for a bit later and then we're going to wrap it up
later and then we're going to wrap it up all with of course stripe subscription
all with of course stripe subscription and limiting the actual boards great
and limiting the actual boards great great
great job so now let's go ahead and let's fix
job so now let's go ahead and let's fix this for 44 page but just before we do
this for 44 page but just before we do that I forgot about one thing that I
that I forgot about one thing that I want to do and that's when we when we
want to do and that's when we when we are in a specific organization I want to
are in a specific organization I want to display the name of that organization in
display the name of that organization in the tab right here currently it only
the tab right here currently it only says task ify right so let's go ahead
says task ify right so let's go ahead and see how we can do that because we're
and see how we can do that because we're going to do the same thing when we click
going to do the same thing when we click on a specific board but first I want to
on a specific board but first I want to do it for the current organization so
do it for the current organization so let's go inside of the app folder
let's go inside of the app folder platform dashboard organiz ation and
platform dashboard organiz ation and organization ID and in here we have
organization ID and in here we have layout.
layout. TSX so first thing I want to do is
TSX so first thing I want to do is simply go inside of my terminal and I
simply go inside of my terminal and I will just shut down the Prisma studio
will just shut down the Prisma studio and write npm install low Dash like this
and write npm install low Dash like this and I believe we also going to need to
and I believe we also going to need to install the types for that so let me
install the types for that so let me just try and import X from low Dash yes
just try and import X from low Dash yes we also need the types so get back in
we also need the types so get back in the terminal and after you do low Dash
the terminal and after you do low Dash do npm install D npm install DD at types
do npm install D npm install DD at types SL low Dash as well so we have the types
SL low Dash as well so we have the types and that's it we can close this and
and that's it we can close this and there we go and from low Dash go ahead
there we go and from low Dash go ahead and extract start case and now let's
and extract start case and now let's also go ahead uh and create another
also go ahead uh and create another function here export
function here export asynchronous function generate
metadata and go ahead and extract organization slug from out from clerk
organization slug from out from clerk nextjs so let me just move that here and
nextjs so let me just move that here and let's go ahead and return title to be
let's go ahead and return title to be start case organization slug or we're
start case organization slug or we're just going to put in
just going to put in organization like this great and let's
organization like this great and let's save this and let's see if it it's
save this and let's see if it it's working and there we go take a look at
working and there we go take a look at my tab now it says another and we have a
my tab now it says another and we have a pipe and then it says tasky if I switch
pipe and then it says tasky if I switch to a new organization now it says test
to a new organization now it says test pipe task ify so how does it generate
pipe task ify so how does it generate that pipe tasky how does it know that
that pipe tasky how does it know that well if you revisit our main layout here
well if you revisit our main layout here you can see that we created a template
you can see that we created a template right so when we add a new title uh
right so when we add a new title uh inside of a layout uh inside of a
inside of a layout uh inside of a different layout which is not the root
different layout which is not the root layout it keeps the title but it moves
layout it keeps the title but it moves it you know here to the side and we
it you know here to the side and we actually fill only this variables it
actually fill only this variables it keeps the pipe in between perfect so now
keeps the pipe in between perfect so now what I want to do is I want to create uh
what I want to do is I want to create uh this 404 page right here so as you can
this 404 page right here so as you can see the URL for that is slash board and
see the URL for that is slash board and then a specific ID so let's go inside of
then a specific ID so let's go inside of the app folder here inside of dashboard
the app folder here inside of dashboard and in here create a new folder called
and in here create a new folder called board and then create another folder and
board and then create another folder and this time it's going to have Dynamic ID
this time it's going to have Dynamic ID so board ID like that and let's go ahead
so board ID like that and let's go ahead and write page. CSX inside and let's do
and write page. CSX inside and let's do const board ID
const board ID page and let's go ahead and return a div
page and let's go ahead and return a div saying board ID and don't forget to
saying board ID and don't forget to export default board ID
export default board ID page great so once you save that you
page great so once you save that you should no longer be having any errors
should no longer be having any errors but it should also be a completely blank
but it should also be a completely blank screen right and you still have the
screen right and you still have the ability to do that from here and now if
ability to do that from here and now if I'm not mistaken when you create a new
I'm not mistaken when you create a new one from here you should also get
one from here you should also get redirected yes as you can see my
redirected yes as you can see my redirect is working as well my URL
redirect is working as well my URL changed but still this is the page that
changed but still this is the page that well has no content inside currently and
well has no content inside currently and the reason the board ID is not visible
the reason the board ID is not visible is because it is hidden outside uh
is because it is hidden outside uh inside of this nov bar here so we're
inside of this nov bar here so we're going to have to move it uh inside of
going to have to move it uh inside of our layout so let's just leave the board
our layout so let's just leave the board ID page like this for now and now I want
ID page like this for now and now I want to go ahead and actually create uh our
to go ahead and actually create uh our board ID layout so inside of board ID
board ID layout so inside of board ID create a layout. vsx like that and let's
create a layout. vsx like that and let's do uh const board ID
do uh const board ID layout and let's extract the children
layout and let's extract the children and we can immediately map the children
and we can immediately map the children to be react. react
to be react. react node and let's just return a div here
node and let's just return a div here which renders the
which renders the children let's export default board ID
children let's export default board ID layout and after you save there should
layout and after you save there should be no more errors and now let's go ahead
be no more errors and now let's go ahead and let's add a main element here around
and let's add a main element here around the children and let's give it a class
the children and let's give it a class name name of relative and pt28 and h-
name name of relative and pt28 and h- full and there we go now we can see that
full and there we go now we can see that board ID text Here and Now what I want
board ID text Here and Now what I want to do uh is I want to fetch the current
to do uh is I want to fetch the current board by ID so let's go ahead and do the
board by ID so let's go ahead and do the following besides the children we're
following besides the children we're also going to have access to the prams
also going to have access to the prams because remember layout is a server
because remember layout is a server component so every server component
component so every server component including layout also always has access
including layout also always has access to pams so let's add Pam to the types
to pams so let's add Pam to the types here and we know what it's going to be
here and we know what it's going to be inside there's going to be a board ID
inside there's going to be a board ID which is a string how do we know that
which is a string how do we know that well because this board ID matches
well because this board ID matches exactly what we named our folder so
exactly what we named our folder so ensure that you have no typos and I
ensure that you have no typos and I wrote board ID with a capital I so that
wrote board ID with a capital I so that is also very important if it's lowercase
is also very important if it's lowercase you're not going to be able to access it
you're not going to be able to access it like this then you're going to have to
like this then you're going to have to be able to access it like this instead
be able to access it like this instead so make sure that you keep track of uh
so make sure that you keep track of uh cases and everything great and now let's
cases and everything great and now let's go ahead and let's get our organization
go ahead and let's get our organization ID from out and we did that using clerk
ID from out and we did that using clerk nextjs if there is no organization ID we
nextjs if there is no organization ID we can just redirect so we do that from
can just redirect so we do that from next SL navigation and just redirect to
next SL navigation and just redirect to select
select organization there we go make sure you
organization there we go make sure you have next navigation
have next navigation imported uh and now uh what I want to do
imported uh and now uh what I want to do is fetch the current board so const
is fetch the current board so const board is a weight DB I imported that uh
board is a weight DB I imported that uh from at SL lib DB so let me just move it
from at SL lib DB so let me just move it here and since we use await it means we
here and since we use await it means we have to make this an asynchronous
have to make this an asynchronous component so aate database uh. board.
component so aate database uh. board. find
find unique where ID is prams board ID and
unique where ID is prams board ID and the organization ID is the one which the
the organization ID is the one which the user is currently uh
user is currently uh using great and if there is no board
using great and if there is no board this is a cool thing that I just
this is a cool thing that I just recently discovered in next 14 uh you
recently discovered in next 14 uh you can manually trigger a 404 just by using
can manually trigger a 404 just by using not found which you can import from
not found which you can import from navigation just like that you redirect
navigation just like that you redirect to the not found page so there we go and
to the not found page so there we go and we can of course style the not found
we can of course style the not found page but you know we'll leave that at
page but you know we'll leave that at the end uh if we have time to do it uh
the end uh if we have time to do it uh all right so make sure you import not
all right so make sure you import not found and now let's go ahead and uh give
found and now let's go ahead and uh give this div a
this div a style to be background image open backx
style to be background image open backx URL and go ahead and use that board to
URL and go ahead and use that board to get image full URL so this is where
get image full URL so this is where we're going to use that full image which
we're going to use that full image which we stored and give it a class name of
we stored and give it a class name of relative h- full vg- no- repeat
relative h- full vg- no- repeat BG Das cover and BG Das Center like this
BG Das cover and BG Das Center like this and there we go now we can see our
and there we go now we can see our beautiful image here and let's try uh
beautiful image here and let's try uh and click on another image here there we
and click on another image here there we go you can see how all of our images are
go you can see how all of our images are now loading in their full size after we
now loading in their full size after we click on a specific board perfect so
click on a specific board perfect so exactly what we wanted what I want to do
exactly what we wanted what I want to do now is that when I click on a specific
now is that when I click on a specific board I want to change the name to well
board I want to change the name to well the same thing I I just did with
the same thing I I just did with organization right I wanted to say test
organization right I wanted to say test in the tab right here so the user knows
in the tab right here so the user knows that that's the uh that's the board that
that that's the uh that's the board that they have so we're going to do that in
they have so we're going to do that in the board ID layout as well so let's go
the board ID layout as well so let's go ahead and do export asynchronous
ahead and do export asynchronous function generate
function generate metadata and metadata of course also has
metadata and metadata of course also has access to the par
access to the par so let's map them params is an object
so let's map them params is an object which holds the board ID which is a
which holds the board ID which is a string and it's not an arrow function so
string and it's not an arrow function so just open it like this and let's go
just open it like this and let's go ahead and extract the organization ID
ahead and extract the organization ID from out which we already have
from out which we already have imported if there is no organization ID
imported if there is no organization ID in that case just return title to be
in that case just return title to be board so a generic title nothing more
board so a generic title nothing more nothing less and then let let's attempt
nothing less and then let let's attempt to fetch the board so const board is
to fetch the board so const board is await db. board. find
await db. board. find unique and it's the exact same query we
unique and it's the exact same query we just did below so ID is pam. board ID
just did below so ID is pam. board ID and org
and org ID and let's just return title to be
ID and let's just return title to be board question mark. tile or just board
board question mark. tile or just board generic as we did above and there we go
generic as we did above and there we go you can take a look now and in your tab
you can take a look now and in your tab you should see the title of your exact
you should see the title of your exact board so if I click on test one 2 3 my
board so if I click on test one 2 3 my tab now says test one 2 3 perfect uh
tab now says test one 2 3 perfect uh great great job so that's what I wanted
great great job so that's what I wanted us to do what we're going to do next uh
us to do what we're going to do next uh is we're going to whoops what we're
is we're going to whoops what we're going to do next is we're going to go
going to do next is we're going to go ahead and create a little navigation bar
ahead and create a little navigation bar at uh here so we're going to able to so
at uh here so we're going to able to so we're going to be able to rename the
we're going to be able to rename the board if
board if needed so inside of the board ID layout
needed so inside of the board ID layout here we're going to add a new component
here we're going to add a new component called board navbar so just above this
called board navbar so just above this main element which is wrapping our
main element which is wrapping our children we're going to add uh a board
children we're going to add uh a board knv bar like this whoa I completely
knv bar like this whoa I completely butchered that sorry so board knv bar
butchered that sorry so board knv bar like this and if you save you of course
like this and if you save you of course we're going to get an error because
we're going to get an error because board knv bar does not exist so let's go
board knv bar does not exist so let's go inside of the board ID folder and create
inside of the board ID folder and create another underscore components folder
another underscore components folder inside and let's create dboard dnvb bar.
inside and let's create dboard dnvb bar. vsx and in here let's go ahead and let's
vsx and in here let's go ahead and let's write con board navbar actually export
write con board navbar actually export const board
const board Navar and let's just return a div saying
Navar and let's just return a div saying board
Navar like that go back to the board ID layout and now import import that from
layout and now import import that from slore components board navbar and when
slore components board navbar and when you save you should no longer have any
you save you should no longer have any errors what I want to do before we head
errors what I want to do before we head inside of the board navbar is actually
inside of the board navbar is actually just uh darkening this uh uh background
just uh darkening this uh uh background a little bit so it's great that we can
a little bit so it's great that we can see it but I just want to darken it a
see it but I just want to darken it a bit because we're going to have you know
bit because we're going to have you know lists here and the lighter the image the
lists here and the lighter the image the Lesser it's actually uh going to be
Lesser it's actually uh going to be visible so let's go ahead just below
visible so let's go ahead just below this
this um just below this board uh navbar
um just below this board uh navbar create a div element which is going to
create a div element which is going to be a self closing tag and give it a
be a self closing tag and give it a class name of absolute inset zero and
class name of absolute inset zero and bg-10 so we just darkened it by a little
bg-10 so we just darkened it by a little bit it's not even noticeable all right
bit it's not even noticeable all right and now inside of this board navbar I
and now inside of this board navbar I want to go ahead and just Define uh the
want to go ahead and just Define uh the actual uh props here so interface board
actual uh props here so interface board navbar
navbar props and let's go ahead uh and let's
props and let's go ahead uh and let's accept the ID to be a
accept the ID to be a string so let's go ahead and do the same
string so let's go ahead and do the same thing here let's extract ID and let's
thing here let's extract ID and let's write word navbar props like that and
write word navbar props like that and then let's just go ahead and pass in the
then let's just go ahead and pass in the ID to be pam. board
ID to be pam. board ID because the board navbar uh is still
ID because the board navbar uh is still a server component so we can actually uh
a server component so we can actually uh repeat this fetch from here so let's go
repeat this fetch from here so let's go ahead and Mark this as an asynchronous
ahead and Mark this as an asynchronous function let's go ahead and import the
function let's go ahead and import the database util from s/ lib DB and in here
database util from s/ lib DB and in here let's go ahead and import that but this
let's go ahead and import that but this time we can immediately get the ID from
time we can immediately get the ID from the props and we just have to find a way
the props and we just have to find a way to get organization ID and lucky for us
to get organization ID and lucky for us that's very easy uh just just using out
that's very easy uh just just using out like this and let's go ahead and make
like this and let's go ahead and make this org ID like this the reason we can
this org ID like this the reason we can do that here is because we're going to
do that here is because we're going to redirect already here if we don't have
redirect already here if we don't have the organization ID so no need to do
the organization ID so no need to do that uh otherwise uh great and now let's
that uh otherwise uh great and now let's go ahead and let's give this div a class
go ahead and let's give this div a class name of w- full height of
name of w- full height of 14 Z 40 pixels sorry 40 BG black
14 Z 40 pixels sorry 40 BG black sl50 fixed top
sl50 fixed top 14 Flex items Center
14 Flex items Center px-6 Gap
px-6 Gap dx-4 and
dx-4 and text- white great so now as you can see
text- white great so now as you can see we have a nice little knv bar below our
we have a nice little knv bar below our first nav bar here and actually I
first nav bar here and actually I noticed that we are kind of repeating a
noticed that we are kind of repeating a lot of stuff here so how about we change
lot of stuff here so how about we change that I mean there's no reason that we
that I mean there's no reason that we cannot pass the existing
cannot pass the existing board right directly in here right so
board right directly in here right so let's actually do that I think it's kind
let's actually do that I think it's kind of going to save us some time so let's
of going to save us some time so let's go ahead and pass in this board
go ahead and pass in this board here and let's go ahead and accept the
here and let's go ahead and accept the data to be board from Prisma client and
data to be board from Prisma client and then we are working with data right and
then we are working with data right and then we don't need any of this great I
then we don't need any of this great I think it's just a bit simpler all right
think it's just a bit simpler all right and now let's go ahead and let's create
and now let's go ahead and let's create a component which is going to properly
a component which is going to properly render the title of which is this board
render the title of which is this board and when we click on it it's going to
and when we click on it it's going to allow us to rename the board so we're
allow us to rename the board so we're also going to have to create the actual
also going to have to create the actual uh well server action for that so we're
uh well server action for that so we're going to do that here inside of board
going to do that here inside of board knobb bar
knobb bar so let's create a component called board
so let's create a component called board title form so it's going to be in the
title form so it's going to be in the same level as this board Novar so let's
same level as this board Novar so let's write board- title- form. CSX like that
write board- title- form. CSX like that let's go ahead and let's mark it as use
let's go ahead and let's mark it as use client let's export const board
client let's export const board title
title form and let's go ahead and let's return
form and let's go ahead and let's return a button component like this and let's
a button component like this and let's immediately create an interface for IT
immediately create an interface for IT board
board title form props and inside of here we
title form props and inside of here we accept expect the data which is a type
accept expect the data which is a type of board from Prisma
of board from Prisma client so let's immediately assign those
client so let's immediately assign those props
props here word title form
here word title form props we extract the data and then
props we extract the data and then inside of this button let's go ahead uh
inside of this button let's go ahead uh and let's render data. tile and let's go
and let's render data. tile and let's go ahead and give this button a uh class
ahead and give this button a uh class name of font D bold text large h-o w-o
name of font D bold text large h-o w-o p-1 and
p-1 and px-2 and let me just reorder my imports
px-2 and let me just reorder my imports a bit uh all right let's save this and
a bit uh all right let's save this and let's go back inside of the board nav
let's go back inside of the board nav bar here and instead of this text here
bar here and instead of this text here let's render the uh well the board title
let's render the uh well the board title form form do/ board title form make sure
form form do/ board title form make sure you have that imported here and let's
you have that imported here and let's pass in the data great and now you can
pass in the data great and now you can see I have a big button here which says
see I have a big button here which says test which is the name of my board and
test which is the name of my board and now I want to go inside of the button
now I want to go inside of the button component so I can add a new variant
component so I can add a new variant which is going to be more suitable for
which is going to be more suitable for this kind of transparent KN bar here
this kind of transparent KN bar here so let's go ahead inside of components
so let's go ahead inside of components UI and find the button. vsx and just
UI and find the button. vsx and just below this
below this primary uh type of variant or whatever
primary uh type of variant or whatever is your last one go ahead and add a new
is your last one go ahead and add a new one called
one called transparent we're going to give it a BG
transparent we're going to give it a BG of
of transparent text is going to be
transparent text is going to be white and on Hover BG is going to be
white and on Hover BG is going to be white slash2
white slash2 and now go back to board title form
and now go back to board title form which is inside of your board ID
which is inside of your board ID components board title form and give
components board title form and give this button a
this button a variant of transparent and there we go
variant of transparent and there we go now it looks much better it's flush with
now it looks much better it's flush with the nov bar but when we hover there is a
the nov bar but when we hover there is a very obvious action which we can do here
very obvious action which we can do here so now let's actually make it do
so now let's actually make it do something so I'm going to go ahead and
something so I'm going to go ahead and import use state
import use state from react let me just move it here and
from react let me just move it here and let's go ahead and let's add a constant
let's go ahead and let's add a constant is editing and set is editing here to be
is editing and set is editing here to be used State and by default let's give it
used State and by default let's give it a false value and now I want to create a
a false value and now I want to create a constant disable editing which is a
constant disable editing which is a function which is very simply going to
function which is very simply going to set is editing to be
set is editing to be false great and now
false great and now let's go ahead uh and let's create an
let's go ahead uh and let's create an equivalent enable
editing and I'm just going to add a quick comment here to say to do Focus
quick comment here to say to do Focus inputs but since we don't have any
inputs but since we don't have any inputs at the moment let's just do set
inputs at the moment let's just do set is editing to be
is editing to be true all right and now let's go ahead
true all right and now let's go ahead and give this button and on click to be
and give this button and on click to be enable
enable editing and and now in here let's write
editing and and now in here let's write if is
if is editing in that case we're going to go
editing in that case we're going to go ahead and actually render a form so
ahead and actually render a form so return a form element and let's just
return a form element and let's just leave the action empty for now and let's
leave the action empty for now and let's give it a class name of flex items
give it a class name of flex items Center and GAP
Center and GAP X2 and let's add the form input
X2 and let's add the form input component from at/ components SL form
component from at/ components SL form form input like this
form input like this and let's go ahead and give it an ID of
and let's go ahead and give it an ID of title let's give it an on blur on blur
title let's give it an on blur on blur well actually it's just an empty Arrow
well actually it's just an empty Arrow function for now but we're going to have
function for now but we're going to have some blur action here happening let's
some blur action here happening let's give it the default value to be board.
give it the default value to be board. sorry
sorry data.
data. tile and let's give it a class name to
tile and let's give it a class name to be text LG font bold px- Open brackets 7
be text LG font bold px- Open brackets 7 pixels
pixels py-1
py-1 h-7 vg-
h-7 vg- transparent Focus D visible outline
transparent Focus D visible outline dnone Focus Das visible is going to have
dnone Focus Das visible is going to have ring
ring transparent and we're going to have
transparent and we're going to have border
border none great and now let's go ahead uh and
none great and now let's go ahead uh and let's create a couple of refs here so
let's create a couple of refs here so right here at the top I'm going to add
right here at the top I'm going to add const form ref to be use ref from react
const form ref to be use ref from react and also element ref from react so make
and also element ref from react so make sure you have element ref use ref and
sure you have element ref use ref and use state from react and let's give this
use state from react and let's give this first type of element ref to be form and
first type of element ref to be form and by default it is null and then let's add
by default it is null and then let's add const input ref to be used ref again
const input ref to be used ref again element
element ref attach type of input and null and
ref attach type of input and null and now let's assign these two refs properly
now let's assign these two refs properly so the form ref is obviously the form
so the form ref is obviously the form ref and the ref for the input is input
ref and the ref for the input is input ref great and now let's go ahead and
ref great and now let's go ahead and let's create uh let's modify this enable
let's create uh let's modify this enable editing here to actually Focus right so
editing here to actually Focus right so I'm going to add a little uh set time
I'm going to add a little uh set time out here
and let's do input ref. Curren Focus input ref. current
Focus input ref. current select and let's go ahead and try this
select and let's go ahead and try this out now so when we click here there we
out now so when we click here there we go you can see how we switched to an
go you can see how we switched to an input type
input type perfect now let's just go ahead and
perfect now let's just go ahead and prepare the form for submitting so I'm
prepare the form for submitting so I'm just going to go ahead and create a new
just going to go ahead and create a new constant on submit we're going to get
constant on submit we're going to get the form data which is a type of form
data and let's go ahead and just conso log I am
log I am submitted and we can also immediately
submitted and we can also immediately extract the title to be form data. get
extract the title to be form data. get title as string and let's log that as
title as string and let's log that as well and now we can pass that to be the
well and now we can pass that to be the action of our
action of our form great so let's try it out I'm going
form great so let's try it out I'm going to open my console here and when I write
to open my console here and when I write 1 through three and press enter there we
1 through three and press enter there we go it says I am submitted one two three
go it says I am submitted one two three perfect and now it's time for us to
perfect and now it's time for us to actually uh go ahead and we create the
actually uh go ahead and we create the actual form
actual form action but just before we we do that uh
action but just before we we do that uh I just want to fix this little like an
I just want to fix this little like an outline which we have visible here so I
outline which we have visible here so I mean if you like it you can leave it but
mean if you like it you can leave it but I think in the demo I didn't have any
I think in the demo I didn't have any outline so I just want to show you how
outline so I just want to show you how to resolve that we have to go inside of
to resolve that we have to go inside of the components folder inside the VII and
the components folder inside the VII and find the input from shaten and in here
find the input from shaten and in here we have the focus visible ring offset
we have the focus visible ring offset and let's go ahead and change it from
and let's go ahead and change it from two to zero and I think that once we
two to zero and I think that once we modify that there we go the offset is no
modify that there we go the offset is no longer visible and while we are here
longer visible and while we are here let's also change the rounded MD to be
let's also change the rounded MD to be rounded SM like this so This Way It just
rounded SM like this so This Way It just fits more to what uh shatan actually uh
fits more to what uh shatan actually uh not shaten but the Trello actually does
not shaten but the Trello actually does perfect so now we have this kind of
perfect so now we have this kind of Flushed uh experience when changing a
Flushed uh experience when changing a board there is no difference between you
board there is no difference between you know the original one uh and this one
know the original one uh and this one very nice great and now let's go ahead
very nice great and now let's go ahead and let's actually uh well just wrap up
and let's actually uh well just wrap up this board title form on blur here right
this board title form on blur here right so when it when on blur occurs what I
so when it when on blur occurs what I want to do is I want to manually trigger
want to do is I want to manually trigger the submit as well so we can do const on
blur form ref. current request submit so that's going to trigger this
submit so that's going to trigger this function then and let's go ahead and
function then and let's go ahead and just pass it
here and I'm going to go ahead and open my
my console so I just want to give the kind
console so I just want to give the kind of a neat experience here if user type
of a neat experience here if user type something goes outside I want that to
something goes outside I want that to save right because that's how Trello
save right because that's how Trello behaves as well perfect so we have all
behaves as well perfect so we have all of that set up and now we are actually
of that set up and now we are actually ready to create our server action which
ready to create our server action which is is going to modify uh this new title
is is going to modify uh this new title from
from here so I'm going to call that action uh
here so I'm going to call that action uh update board so let me close everything
update board so let me close everything here let me go inside of actions and
here let me go inside of actions and let's create a new folder update board
let's create a new folder update board and we can actually remove this delete
and we can actually remove this delete board all the uh server action which we
board all the uh server action which we have and inside of this update board
have and inside of this update board first let's create schema. TS so inside
first let's create schema. TS so inside of schema we're going to need to import
of schema we're going to need to import Zod of
Zod of course and then let's Define export
course and then let's Define export const update board to be z. object and
const update board to be z. object and inside all of the inputs we expect so
inside all of the inputs we expect so obviously we expect the title which is a
obviously we expect the title which is a string it has a required error of title
string it has a required error of title is
is required and it has an invalid type
required and it has an invalid type error and I'm just going to say title is
error and I'm just going to say title is required as well for this one and let's
required as well for this one and let's also keep the minimum value of three to
also keep the minimum value of three to stay true to the creation of the board
stay true to the creation of the board right title is too short in case this
right title is too short in case this happens but besides the title we also
happens but besides the title we also expect the user to pass in an ID which
expect the user to pass in an ID which we're just going to Define as a type of
we're just going to Define as a type of string so using the ID we're going to
string so using the ID we're going to confirm which board does the user want
confirm which board does the user want to update and now let's go ahead and
to update and now let's go ahead and let's create the types so go ahead and
let's create the types so go ahead and types. vs here and again let's import Z
types. vs here and again let's import Z from Zod let's import board from Prisma
from Zod let's import board from Prisma SL client let's import action state from
SL client let's import action state from lib create save action and let's import
lib create save action and let's import update board from do/ schema and now we
update board from do/ schema and now we can export type input type to be z.
can export type input type to be z. infer whoops z. infer type of update
infer whoops z. infer type of update board and Export type return type
board and Export type return type to be uh action State input type and
to be uh action State input type and board great and now let's finally go uh
board great and now let's finally go uh inside and create whoa and create
inside and create whoa and create index. DS let's mark this as use
index. DS let's mark this as use server and let's go ahead and write
server and let's go ahead and write const Handler to be an asynchronous
const Handler to be an asynchronous function which accepts the data which is
function which accepts the data which is a type of in put type from do/ types and
a type of in put type from do/ types and returns a promise which gives us a type
returns a promise which gives us a type of return type again from do/ type and
of return type again from do/ type and let's go ahead and return this Arrow
let's go ahead and return this Arrow function
function here and let's extract the user
here and let's extract the user ID and organization ID from out using
ID and organization ID from out using Clerk nextjs and I will just separate
Clerk nextjs and I will just separate those two here in the
those two here in the inputs let's check if we are missing a
inputs let's check if we are missing a user ID or if we are missing the
user ID or if we are missing the organization ID if either of those is
organization ID if either of those is true let's return an error saying
unauthorized great and now let's go ahead and let's extract the title and
ahead and let's extract the title and the ID from the
the ID from the data and let's write uh let board here
data and let's write uh let board here just so we prepare it and let's open a
just so we prepare it and let's open a try and catch block here so inside of
try and catch block here so inside of the try block
the try block let's assign the board to be await
let's assign the board to be await DB which we can import from s/ lib DB
DB which we can import from s/ lib DB I'm going to move it here so await db.
I'm going to move it here so await db. board. update
board. update where ID matches and of course
where ID matches and of course organization ID matches as well and
organization ID matches as well and let's use data to be title so using this
let's use data to be title so using this organization ID we have prevented anyone
organization ID we have prevented anyone from outside of this organization to
from outside of this organization to update this board so only the person
update this board so only the person from uh which has the proper
from uh which has the proper organization ID inside of the AL util
organization ID inside of the AL util which cannot be mocked or Faked in any
which cannot be mocked or Faked in any way uh can actually update this board so
way uh can actually update this board so if you watch my previous tutorials this
if you watch my previous tutorials this is similar to checking that the current
is similar to checking that the current type of user which is trying to up
type of user which is trying to up update this entity has a matching user
update this entity has a matching user ID but since this time we're not working
ID but since this time we're not working with user IDs we are working on an
with user IDs we are working on an organization base so that's why we are
organization base so that's why we are checking for the organization ID so this
checking for the organization ID so this is a kind of security for that U great
is a kind of security for that U great and now let's catch the error
and now let's catch the error here and let's just do return error
here and let's just do return error failed to
update all right and now let's go ahead and let's revalidate our path so import
and let's revalidate our path so import that from next
that from next slash and let's revalidate the pack path
slash and let's revalidate the pack path to be slash board and then the
to be slash board and then the individual ID which we extracted from
individual ID which we extracted from the data so it's immediately updated and
the data so it's immediately updated and let's return data to be the
let's return data to be the board perfect and then let's export con
board perfect and then let's export con update board to be create save action
update board to be create save action passing the update board schema so you
passing the update board schema so you can import that from do/ schema let me
can import that from do/ schema let me just move it here so make sure you have
just move it here so make sure you have create safe action from at/ Li create sa
create safe action from at/ Li create sa action and
action and schema and in the second argument pass
schema and in the second argument pass in the
in the Handler great and now we can go back uh
Handler great and now we can go back uh inside of our board title
inside of our board title form so let's go ahead and find where
form so let's go ahead and find where that is so inside of the app folder
that is so inside of the app folder platform dashboard organization
platform dashboard organization organization ID uh sorry board board ID
organization ID uh sorry board board ID components board title form right right
components board title form right right here let's go ahead and import that
here let's go ahead and import that action so update board from actions uh
action so update board from actions uh update board and let's also import the
update board and let's also import the use
use action from hooks use
action from hooks use action all right and let's go right here
action all right and let's go right here I'm going to do this at top level here
I'm going to do this at top level here let's extract
let's extract execute from use action and let's pass
execute from use action and let's pass in the update
in the update board and let's go ahead and extract the
board and let's go ahead and extract the on success here we get the new
on success here we get the new data and inside let's call the toast
data and inside let's call the toast which we imported from soner I'm going
which we imported from soner I'm going to move it to the top
to move it to the top here so toast.
here so toast. success and let's immediately say so
success and let's immediately say so open backs board open
open backs board open annotations and we're going to spell out
annotations and we're going to spell out the exact typ title of this board so
the exact typ title of this board so data. title so board the title of the
data. title so board the title of the board update so the user can immediately
board update so the user can immediately see that we successfully reflected uh
see that we successfully reflected uh those changes right and let's go ahead
those changes right and let's go ahead uh and call the disable editing
here great and now let's go ahead and use this execute so I'm going to go
use this execute so I'm going to go inside of the onsubmit here and let
inside of the onsubmit here and let let's go ahead and let's call
execute let's pass in the title and pass in the ID to be
in the ID to be data. ID like that um great and just
data. ID like that um great and just before we try out one more thing that I
before we try out one more thing that I quickly want to do I want to keep my
quickly want to do I want to keep my title inside of State rather than uh
title inside of State rather than uh inside rather than directly accessing it
inside rather than directly accessing it from data so we can do optimistic
from data so we can do optimistic updates on it so go ahead and write con
updates on it so go ahead and write con title set title use state by default to
title set title use state by default to be data. title and then let's just find
be data. title and then let's just find if we use data. title anywhere we use it
if we use data. title anywhere we use it right here in the form input so scroll
right here in the form input so scroll to where we have the if is editing
to where we have the if is editing clause and change the data. title to be
clause and change the data. title to be the title from State and also same thing
the title from State and also same thing in the button here make sure it uses the
in the button here make sure it uses the title from State and now what we can do
title from State and now what we can do is insided here on success uh before we
is insided here on success uh before we disable the editing let's do set title
disable the editing let's do set title to be the new data. tile great and let's
to be the new data. tile great and let's also extract on
also extract on error get the
error get the error and let's do toast. error the
error and let's do toast. error the error uh great and I think this should
error uh great and I think this should pretty much be it let's try it out now
pretty much be it let's try it out now so right now it's called test and now
so right now it's called test and now I'm going to change it I'm going to
I'm going to change it I'm going to press enter and you can see it's pending
press enter and you can see it's pending and it says board change it updated and
and it says board change it updated and if I refresh the name stays so another
if I refresh the name stays so another change here and this time I'm not going
change here and this time I'm not going to press enter instead I'm going to blur
to press enter instead I'm going to blur and there we go same thing board another
and there we go same thing board another change updated perfect and now what I
change updated perfect and now what I want to create is a little action here
want to create is a little action here on the side which is going to be used to
on the side which is going to be used to delete the
delete the board so let's go back inside of the
board so let's go back inside of the board navbar
board navbar component I'm going to close everything
component I'm going to close everything here and let's find board Novar so it's
here and let's find board Novar so it's located inside of app platform dashboard
located inside of app platform dashboard board board ID components board Das
board board ID components board Das navbar and in here just below our
navbar and in here just below our component board title form which we just
component board title form which we just wrapped up let's add a div with a class
wrapped up let's add a div with a class name of ml AO so this way we push it all
name of ml AO so this way we push it all the way to the right and and inside
the way to the right and and inside let's render board options and let's
let's render board options and let's pass in data actually we're only going
pass in data actually we're only going to be needing the ID so we can pass in
to be needing the ID so we can pass in the ID to be
the ID to be data. ID and let's go inside of the
data. ID and let's go inside of the underscore components and immediately
underscore components and immediately create board options. TSX let's mark
create board options. TSX let's mark them as use client and let's export con
them as use client and let's export con board options here let's extract the
board options here let's extract the ID let's create a quick interface board
ID let's create a quick interface board options props to accept the ID which is
options props to accept the ID which is a string and let's just map those
a string and let's just map those here and let's just return a div
options go back to the board Novar and now you can import the options from do/
now you can import the options from do/ board options the same way we did with
board options the same way we did with the title form and you should no longer
the title form and you should no longer have any errors and you should see the
have any errors and you should see the options text in this second nav bar here
options text in this second nav bar here here and now let's go ahead and let's
here and now let's go ahead and let's import everything we need from the
import everything we need from the popover component so import from s/
popover component so import from s/ components UI
components UI popover we're going to need the popover
itself we're going to need the popover close the popover content and the
close the popover content and the popover trigger great and now let's go
popover trigger great and now let's go ahead uh and let's change this entire
ahead uh and let's change this entire thing to be a popover
thing to be a popover let's add the popover trigger here let's
let's add the popover trigger here let's mark it as a
mark it as a child inside let's add a button
child inside let's add a button component which we can import from s/
component which we can import from s/ components UI button make sure you add
components UI button make sure you add that and let's simply render more
that and let's simply render more horizontal from Lucid react as I added
horizontal from Lucid react as I added right here but I'm just going to
right here but I'm just going to separate it here at the top so more
separate it here at the top so more horizontal is an icon and let's go ahead
horizontal is an icon and let's go ahead and give it a class class name of h-4
and give it a class class name of h-4 and
and W-4 and let's give this button a class
W-4 and let's give this button a class name of h-o and w-o and padding of Two
name of h-o and w-o and padding of Two and a variant of transparent so it fits
and a variant of transparent so it fits the look of our invisible Navar here
the look of our invisible Navar here there we go that looks quite nice and
there we go that looks quite nice and now outside of the popover trigger let's
now outside of the popover trigger let's add the
content now this content right here has the class name of px0 padding top of
has the class name of px0 padding top of three padding bottom of
three padding bottom of three and a side of bottom and a line of
three and a side of bottom and a line of start so let me just collapse
start so let me just collapse this so it's nicer to look
at Great and inside of the popover content let's go ahead and create a div
content let's go ahead and create a div which is going to say board
which is going to say board actions and inside of here here let's
actions and inside of here here let's give it a class name text small font
give it a class name text small font medium text Center text neutral 600 and
medium text Center text neutral 600 and padding bottom of four and I think we
padding bottom of four and I think we can already take a look at it so if I
can already take a look at it so if I click here there we go we have a nice
click here there we go we have a nice text which says board actions now let's
text which says board actions now let's go ahead and let's add the close button
go ahead and let's add the close button for this popover so after this div here
for this popover so after this div here at the popover
at the popover close let's give it a prop as ch
close let's give it a prop as ch and inside open up a button again and
and inside open up a button again and render the X icon from Lucid react so
render the X icon from Lucid react so just make sure you add that
just make sure you add that import
import whoa okay it's a self closing tag let's
whoa okay it's a self closing tag let's give it a class name of H4 and W4 let's
give it a class name of H4 and W4 let's give its button a class
give its button a class name let me just collapse so we have
name let me just collapse so we have more room h- aouto w- AO padding to off
more room h- aouto w- AO padding to off solute top-2 wr-2 and text- neutral
solute top-2 wr-2 and text- neutral D600 and let's give it a variant of
D600 and let's give it a variant of ghost and now we should have the close
ghost and now we should have the close button visible there we go and it
button visible there we go and it works great and now let's go ahead and
works great and now let's go ahead and render uh some options for us here so
render uh some options for us here so after the popover close let's go ahead
after the popover close let's go ahead and let's add a button element again and
and let's add a button element again and let's write delete this board and let's
let's write delete this board and let's give it a
give it a variant of
variant of ghost just let's not misspell variant
ghost just let's not misspell variant and let's give it on click for now to
and let's give it on click for now to just be an empty Arrow function uh and
just be an empty Arrow function uh and let's give it a class name of rounded
let's give it a class name of rounded dnone w- full h- aouto p-2
dnone w- full h- aouto p-2 px-5 justify Das start font Das normal
px-5 justify Das start font Das normal and text small
and text small like that and we should have a nice
like that and we should have a nice little action visible now which says
little action visible now which says delete this board and now let's go ahead
delete this board and now let's go ahead and let's create an action a server
and let's create an action a server action to actually delete our
action to actually delete our board so we can do that quite easily
board so we can do that quite easily because we have existing actions here so
because we have existing actions here so I'm going to reuse this one to update
I'm going to reuse this one to update the board so I'm going to copy the
the board so I'm going to copy the entire folder and paste it inside of the
entire folder and paste it inside of the actions folder and I will rename the
actions folder and I will rename the folder to delete board and first let's
folder to delete board and first let's go inside of the schema to change what
go inside of the schema to change what we need so previously we needed uh well
we need so previously we needed uh well the title right but this time we only
the title right but this time we only need the ID so you can change it to this
need the ID so you can change it to this and let's change this schema to be
and let's change this schema to be delete
delete board now let's go ahead inside of our
board now let's go ahead inside of our types. Cs and let's change the input
types. Cs and let's change the input type to be delete board and we also have
type to be delete board and we also have to modify the type of here the rest can
to modify the type of here the rest can stay the same now let's go back inside
stay the same now let's go back inside of index and let's go ahead and import
of index and let's go ahead and import delete board from here and scroll all
delete board from here and scroll all the way down and add it as the first
the way down and add it as the first argument in the create save action and
argument in the create save action and looks like I have a typo so it's delete
looks like I have a typo so it's delete board and let's also rename the actual
board and let's also rename the actual function from update board to delete
function from update board to delete board great
board great and now let's go ahead and let's remove
and now let's go ahead and let's remove the title from here we don't need it the
the title from here we don't need it the authentication stays the same and now
authentication stays the same and now what we're going to do is we're going to
what we're going to do is we're going to call db. board. delete and we are not
call db. board. delete and we are not going to be passing any data inside
going to be passing any data inside instead we're going to trying to delete
instead we're going to trying to delete the board which has this ID and the
the board which has this ID and the organization ID which the currently
organization ID which the currently logged in user has so if someone else is
logged in user has so if someone else is trying to do that we're not going to
trying to do that we're not going to allow if they're not from the same
allow if they're not from the same organization and instead of fail to
organization and instead of fail to update let's say fail to delete and now
update let's say fail to delete and now let's revalidate the path not bored but
let's revalidate the path not bored but let's go ahead and do slash
let's go ahead and do slash organization and then let's use the org
organization and then let's use the org ID and we're not going to return
ID and we're not going to return anything instead we're going to redirect
anything instead we're going to redirect so call that from next SL navigation I'm
so call that from next SL navigation I'm just going to move it here so we're
just going to move it here so we're going to be using the read direct and
going to be using the read direct and let's go ahead uh and let's go to slash
let's go ahead uh and let's go to slash organization slash organization
organization slash organization ID like this perfect so now that we have
ID like this perfect so now that we have this action let's head back inside of
this action let's head back inside of the board
the board options where was it right here and
options where was it right here and let's import everything we need so we're
let's import everything we need so we're going to need the delete board from
going to need the delete board from actions delete board we're going to to
actions delete board we're going to to need use action from hooks use
need use action from hooks use action inside of here let's go ahead and
action inside of here let's go ahead and let's define that so we're going to
let's define that so we're going to extract execute and is loading from use
extract execute and is loading from use action let's pass in the delete board
action let's pass in the delete board option let's modify the on error call
option let's modify the on error call back if we have an error we're going to
back if we have an error we're going to call Toast which we can import from
call Toast which we can import from soner and let's call toast. error and
soner and let's call toast. error and Light directly log that error and I'm
Light directly log that error and I'm just going to move this soner to the top
just going to move this soner to the top here and the reason we're not going to
here and the reason we're not going to be doing anything with the on success uh
be doing anything with the on success uh well there's no need right because we're
well there's no need right because we're going to directly
going to directly redirect as you saw in the action back
redirect as you saw in the action back to the organization page so the user is
to the organization page so the user is clearly going to see that the delution
clearly going to see that the delution was successful and now let's go ahead uh
was successful and now let's go ahead uh and let's create the on delete
and let's create the on delete Handler so const on
Handler so const on delete
delete execute and pass in the
execute and pass in the ID and now let's go ahead and get this
ID and now let's go ahead and get this is loading and let's make this button
is loading and let's make this button disabled if it's loading and let's
disabled if it's loading and let's change this empty on click to be on
change this empty on click to be on delete great so now let's go ahead and
delete great so now let's go ahead and let's just confirm this so inside of my
let's just confirm this so inside of my task ify I have a board called another
task ify I have a board called another change and let's go ahead and deleted
change and let's go ahead and deleted from here it's loading and there we go
from here it's loading and there we go it is no longer here perfect so we
it is no longer here perfect so we successfully finished uh our initial uh
successfully finished uh our initial uh options for manipulating the board
options for manipulating the board creating the board and deleting the
creating the board and deleting the board in the end great great job what
board in the end great great job what we're going to be doing next is uh well
we're going to be doing next is uh well finally rendering some lists here and
finally rendering some lists here and some
some cards all right so what I want to work
cards all right so what I want to work on now is of course this actual content
on now is of course this actual content here where we're going to be able to
here where we're going to be able to create new lists and also new cards so
create new lists and also new cards so in order to do that first thing that I
in order to do that first thing that I want to do is I want to go inside of our
want to do is I want to go inside of our schema so we going to go ahead and
schema so we going to go ahead and create the list model and the card model
create the list model and the card model so let's go ahead and do the list first
so let's go ahead and do the list first go ahead and write model list add the ID
go ahead and write model list add the ID which is going to be a string and ID and
which is going to be a string and ID and the default value of uu ID below that go
the default value of uu ID below that go ahead and give this list a title which
ahead and give this list a title which is also a string and also go ahead and
is also a string and also go ahead and give it an order which is an integer so
give it an order which is an integer so an order is going to be what we will
an order is going to be what we will modify whenever we drag and drop into a
modify whenever we drag and drop into a different position right and when we
different position right and when we initially fetch the lists we're going to
initially fetch the lists we're going to do order by and then we're going to use
do order by and then we're going to use this order field great and now let's
this order field great and now let's create a relation with a specific board
create a relation with a specific board so a list needs to exist inside a
so a list needs to exist inside a specific board so board ID is going to
specific board so board ID is going to be a string and then board is going to
be a string and then board is going to be a type of board which is going to be
be a type of board which is going to be a
a relation which is going to have
relation which is going to have fields which are board ID which
fields which are board ID which reference to the ID and on the lead
reference to the ID and on the lead we're going to Cascade so let me go
we're going to Cascade so let me go ahead and zoom out so you can see this
ahead and zoom out so you can see this uh in one line right here so now that we
uh in one line right here so now that we created an ID a relation with the board
created an ID a relation with the board we have to go back inside of the board
we have to go back inside of the board and create a relation with the list
and create a relation with the list there so go back inside of this board
there so go back inside of this board here and very simply just add lists and
here and very simply just add lists and go ahead and add an individual list and
go ahead and add an individual list and an array and there we go and now we have
an array and there we go and now we have to add at add index here for board ID to
to add at add index here for board ID to get rid of that warning so this is it
get rid of that warning so this is it this is the code without any warnings
this is the code without any warnings oror errors so we created a relation
oror errors so we created a relation with the boards using this board ID
with the boards using this board ID referencing to the ID of the actual
referencing to the ID of the actual board and when we delete a board we
board and when we delete a board we instructed that this list is also to be
instructed that this list is also to be deleted cascading great and now we have
deleted cascading great and now we have to create a card model so let's go ahead
to create a card model so let's go ahead and do that just below go ahead and
and do that just below go ahead and write model
write model card it's also going to have an ID of
card it's also going to have an ID of string at ID and uu
string at ID and uu ID let's go ahead and give it a title
ID let's go ahead and give it a title which is a string an order which is
which is a string an order which is again an integer and let's give it a
again an integer and let's give it a description which is a optional string
description which is a optional string and DB text and let me just align uh
and DB text and let me just align uh this items like you don't have to do
this items like you don't have to do this of course it doesn't matter it just
this of course it doesn't matter it just needs to be in one line and now let's go
needs to be in one line and now let's go ahead and create a relation with the
ahead and create a relation with the list so list ID is going going to be a
list so list ID is going going to be a string and then we repeat exactly what
string and then we repeat exactly what we did Above So list is a type of list
we did Above So list is a type of list and has a
and has a relation
relation Fields list ID references ID and on
Fields list ID references ID and on delete
delete Cascade and let me just zoom out so you
Cascade and let me just zoom out so you can see so list at relation Fields list
can see so list at relation Fields list ID references ID and on delete Cascade
ID references ID and on delete Cascade and now we go back inside of here in
and now we go back inside of here in side of the list and we add the relation
side of the list and we add the relation with the cards very simply by adding
with the cards very simply by adding card and an array and then we have to do
card and an array and then we have to do the at at
the at at index list ID here and there we go again
index list ID here and there we go again this is the code with no warnings or
this is the code with no warnings or errors so pause the screen and confirm
errors so pause the screen and confirm you have the same great and what I want
you have the same great and what I want to add now is just the created ad and
to add now is just the created ad and updated at Fields so let's go back
updated at Fields so let's go back inside of the list here and after the
inside of the list here and after the cards I want to add created at to be
cards I want to add created at to be date time and have default value of now
date time and have default value of now an updated at is going to be a date time
an updated at is going to be a date time as well and have at updated
as well and have at updated at so just like that and we can copy and
at so just like that and we can copy and paste this inside of the card model as
paste this inside of the card model as well after the list
well after the list relation great so now that we have that
relation great so now that we have that we are ready to push them uh to our
we are ready to push them uh to our database so so let's go ahead inside of
database so so let's go ahead inside of the terminal here and shut down your
the terminal here and shut down your application and first let's go ahead and
application and first let's go ahead and let's actually clear our entire database
let's actually clear our entire database so npx Prisma migrate reset so this is
so npx Prisma migrate reset so this is going to remove all the existing boards
going to remove all the existing boards that we have
that we have inside uh after that is done go ahead
inside uh after that is done go ahead and run npx Prisma DB
and run npx Prisma DB push like
push like this wait a couple of seconds for this
this wait a couple of seconds for this to align and let's do MPX Prisma
to align and let's do MPX Prisma generate great and then mpm run Dev
generate great and then mpm run Dev finally perfect so again if you have a
finally perfect so again if you have a feeling like you did something wrong you
feeling like you did something wrong you can always visit my GitHub uh you can
can always visit my GitHub uh you can even find the exact commit where I wrote
even find the exact commit where I wrote this if that's how you prefer it and you
this if that's how you prefer it and you can double check that your code is
can double check that your code is correct also if for any reason your
correct also if for any reason your Prisma doesn't have a syntax like mine
Prisma doesn't have a syntax like mine like this is yellow this is blue uh you
like this is yellow this is blue uh you can go ahead inside of the extensions
can go ahead inside of the extensions and install the Prisma extension even
and install the Prisma extension even though I think the visual studio code
though I think the visual studio code will recommend that for you
will recommend that for you automatically perfect now that we have
automatically perfect now that we have our lists let's go ahead and let's
our lists let's go ahead and let's revisit our board page but just before
revisit our board page but just before we do that we have to refresh our local
we do that we have to refresh our local host and this should now yield a 404
host and this should now yield a 404 because I reset my entire database here
because I reset my entire database here there we go so this is a 404 so I'm
there we go so this is a 404 so I'm going to go slash to localhost 3000 and
going to go slash to localhost 3000 and that's going to redirect me back to my
that's going to redirect me back to my organizations here so let me just go
organizations here so let me just go ahead and create a new test organization
ahead and create a new test organization sorry a new test board and make sure you
sorry a new test board and make sure you are in a board ID page like this now
are in a board ID page like this now let's go back inside the app folder
let's go back inside the app folder platform dashboard let's go inside of
platform dashboard let's go inside of the board board ID and here in the page.
the board board ID and here in the page. CSX where we just render the board ID I
CSX where we just render the board ID I actually want to extract the individual
actually want to extract the individual ID of the board and using that I want to
ID of the board and using that I want to fetch all the lists if has so let's go
fetch all the lists if has so let's go ahead and write an interface for board
ahead and write an interface for board ID page
ID page props so we accept the params which is
props so we accept the params which is an object which has the board ID which
an object which has the board ID which is a string and now let's go ahead and
is a string and now let's go ahead and extract those here so board ID page
extract those here so board ID page props and we can safely extract the pars
props and we can safely extract the pars and now let's mark this entire function
and now let's mark this entire function as an asynchronous function and let's go
as an asynchronous function and let's go ahead and let's let's import out from
ahead and let's let's import out from Clerk nextjs and let's also import DB
Clerk nextjs and let's also import DB from at/ lib DB and now inside of here
from at/ lib DB and now inside of here first thing I want to check if I have
first thing I want to check if I have access to the current organization ID
access to the current organization ID using the out
using the out helper if there is no organization IID
helper if there is no organization IID in that case we are going to redirect
in that case we are going to redirect using next SL navigation so let me move
using next SL navigation so let me move that here we're going to redirect the
that here we're going to redirect the user to
user to to/ select organization select or for
to/ select organization select or for short otherwise let's go ahead and let's
short otherwise let's go ahead and let's attempt to fetch all the lists this
attempt to fetch all the lists this board has so const lists is going to be
board has so const lists is going to be await db. list we now uh list we now
await db. list we now uh list we now have it because we did npx Prisma DB
have it because we did npx Prisma DB push and npx Prisma generate and now we
push and npx Prisma generate and now we can do db. list. find
can do db. list. find many and in here let's write board
many and in here let's write board board ID should be pam. board ID and now
board ID should be pam. board ID and now to confirm that only the people inside
to confirm that only the people inside of this organization can uh load this
of this organization can uh load this lists we're also going to write if board
lists we're also going to write if board itself so the Rel the relation so the
itself so the Rel the relation so the board that this list was created in also
board that this list was created in also has the matching organization ID of the
has the matching organization ID of the currently logged in user and let's also
currently logged in user and let's also go ahead and write include and we're
go ahead and write include and we're immediately going to fetch the cards and
immediately going to fetch the cards and let's order the cards by their
order ascending like that so our cards also have the matching uh order property
also have the matching uh order property as our lists have and last thing we have
as our lists have and last thing we have to do is well order the list themselves
to do is well order the list themselves so again order ascending perhaps this
so again order ascending perhaps this order field is a bit confusingly named
order field is a bit confusingly named uh maybe I should have named it position
uh maybe I should have named it position so you can just imagine its named
so you can just imagine its named position if it's confusing you right
position if it's confusing you right basically what it is meant to represent
basically what it is meant to represent is the exact order uh how it's going to
is the exact order uh how it's going to be displayed on the user's screen and
be displayed on the user's screen and this order field is what's going to be
this order field is what's going to be modified every time we drag and drop uh
modified every time we drag and drop uh the card or the list itself right for
the card or the list itself right for the list it's actually going to be quite
the list it's actually going to be quite simple because for the drag and dropping
simple because for the drag and dropping the lists we can only move the order
the lists we can only move the order right it's either going to be first or
right it's either going to be first or in between or last something like that
in between or last something like that but with cards we're also going to be
but with cards we're also going to be able to drop to another list and then
able to drop to another list and then we're going to be able to modify this
we're going to be able to modify this list ID as well so it's going to be a
list ID as well so it's going to be a bit more complicated so just ensure that
bit more complicated so just ensure that you of course have all the necessary
you of course have all the necessary fields in your schema Prisma for that if
fields in your schema Prisma for that if you do none of this should throw an
you do none of this should throw an error for you and it should work just
error for you and it should work just fine we of course don't have any list so
fine we of course don't have any list so this is just going to be empty but you
this is just going to be empty but you know it's good enough for now we can go
know it's good enough for now we can go ahead head and style this div now and
ahead head and style this div now and write a class name to be padding 4 W
write a class name to be padding 4 W sorry H full and overflow X
sorry H full and overflow X AO and inside we're going to go ahead
AO and inside we're going to go ahead and render the list
and render the list container and this list container is
container and this list container is going to be a component which we are
going to be a component which we are going to create which is going to have
going to create which is going to have the board ID which is pam. board
the board ID which is pam. board ID and it's going to have the data which
ID and it's going to have the data which is the lists which we loaded now let's
is the lists which we loaded now let's go ahead and let's quickly uh create
go ahead and let's quickly uh create this list container so inside of this
this list container so inside of this underscore components where we have the
underscore components where we have the board knv bar board options and board
board knv bar board options and board title form create a new file list-
title form create a new file list- container. TSX and what I want to do in
container. TSX and what I want to do in here is just quickly mark this as use
here is just quickly mark this as use client create an interface list
client create an interface list container props get the data uh which is
container props get the data uh which is going to be list from Prisma client and
going to be list from Prisma client and word ID which is a
word ID which is a string and now export const list
string and now export const list container let's go ahead and assign the
container let's go ahead and assign the props list container
props list container props and let's extract the data and the
props and let's extract the data and the board ID and inside let's just return a
board ID and inside let's just return a div list
container great now go back to page. CSX and now you can import that fromt slore
and now you can import that fromt slore component
component list- container and there we go now we
list- container and there we go now we have a text list container in here but
have a text list container in here but something in my interface is actually uh
something in my interface is actually uh incorrect here so what I want to
incorrect here so what I want to create is a specific type so as you can
create is a specific type so as you can see this list is actually a type of list
see this list is actually a type of list with cards inside of them but in my list
with cards inside of them but in my list container I just Define them as list and
container I just Define them as list and since we're going to work with that type
since we're going to work with that type list with cards a lot of times in the
list with cards a lot of times in the project I want to create a reusable type
project I want to create a reusable type for it so let's go to the root of our
for it so let's go to the root of our project let me just expand my
project let me just expand my screen all right let's go to the root of
screen all right let's go to the root of our project I'm going to close
our project I'm going to close everything and create a new file not in
everything and create a new file not in not in any folder so let me just close
not in any folder so let me just close everything so go to the root of your
everything so go to the root of your project and create a new file types.
project and create a new file types. Cs and in here let's go ahead and let's
Cs and in here let's go ahead and let's import card and list
import card and list from at
from at Prisma client and let's export type list
Prisma client and let's export type list with cards to be a list and open an
with cards to be a list and open an object cards
object cards card and let's go ahead and let's export
card and let's go ahead and let's export type card with list because we're going
type card with list because we're going to work with that as well to be a type
to work with that as well to be a type of card and list list like that and now
of card and list list like that and now let's head back back inside of our list
let's head back back inside of our list container component which we have an app
container component which we have an app platform dashboard board underscore
platform dashboard board underscore components list container where we
components list container where we defined to be an array of list but it's
defined to be an array of list but it's actually going to be an array of list
actually going to be an array of list with cards from types like that and now
with cards from types like that and now we have a correct type here perfect so
we have a correct type here perfect so now we're ready to create uh well our
now we're ready to create uh well our first kind of item here which is going
first kind of item here which is going to be the list list
to be the list list form so inside of this list container
form so inside of this list container right here let's instead use an ordered
right here let's instead use an ordered list like that and then inside let's go
list like that and then inside let's go ahead and let's add a little self
ahead and let's add a little self closing div which is just going to
closing div which is just going to represent the empty space at the end of
represent the empty space at the end of our x axis so add a class name Flex
our x axis so add a class name Flex shrink Z and withth of one so this is
shrink Z and withth of one so this is going to help uh when it comes to
going to help uh when it comes to scrolling when we have more items and
scrolling when we have more items and our X overflow activates this is going
our X overflow activates this is going to create a little bit of padding at
to create a little bit of padding at that overflow rather than it being flush
that overflow rather than it being flush to the end of the screen and above that
to the end of the screen and above that create a list form component which is
create a list form component which is just going to have a self closing tag
just going to have a self closing tag now let's go inside of this underscore
now let's go inside of this underscore components here and let's create a new
components here and let's create a new file list- form. TSX let's mark it as
file list- form. TSX let's mark it as use
use client and let's export con list
client and let's export con list form for now to just be a div same list
form for now to just be a div same list form go back to the list container and
form go back to the list container and import the list form from slash list
import the list form from slash list form and you should no longer be having
form and you should no longer be having uh any errors let's just see what is
uh any errors let's just see what is going on here okay so I just had to
going on here okay so I just had to refresh and now it says list form now
refresh and now it says list form now before we go on to creating list form I
before we go on to creating list form I want to create a reusable component
want to create a reusable component called list
called list wrapper so let's go ahead inside of
wrapper so let's go ahead inside of underscore components and create a new
underscore components and create a new file list wrapper.
file list wrapper. CSX in here go ahead and import uh well
CSX in here go ahead and import uh well actually let's create an interface list
actually let's create an interface list of rapper props and let's get the
of rapper props and let's get the children to be react react
children to be react react node and let's export con list
wrapper to accept those children so list wrapper props accept the
wrapper props accept the children and let's go ahead and return a
children and let's go ahead and return a list element which will render the
children and let's give it a class name of shrink Z H4 and W of 272 pixels and
of shrink Z H4 and W of 272 pixels and select Das
select Das none all right and now let's go back
none all right and now let's go back inside of the list form and let's go
inside of the list form and let's go ahead and actually start styling this so
ahead and actually start styling this so inside of the list form go ahead and
inside of the list form go ahead and import our list wrapper which we just
import our list wrapper which we just created and go ahead and replace this
created and go ahead and replace this divs with the list
divs with the list wrapper and now inside we're going to go
wrapper and now inside we're going to go ahead and open up a form
ahead and open up a form element let's go ahead and give this
element let's go ahead and give this form element a class name of w full
form element a class name of w full padding of three rounded medium BG white
padding of three rounded medium BG white space y4 and Shadow medium and there we
space y4 and Shadow medium and there we go you can already see a little uh box
go you can already see a little uh box here perfect now let's go inside of here
here perfect now let's go inside of here uh and let's create uh a little
uh and let's create uh a little button which is going to say uh add a
button which is going to say uh add a list inside of this button let's go
list inside of this button let's go ahead and give it a class name of w-
ahead and give it a class name of w- full rounded medium BG white
full rounded medium BG white sl80 uh hover vg- white
sl80 uh hover vg- white sl50
sl50 transition padding of three Flex items
transition padding of three Flex items Center f- medium and text small and one
Center f- medium and text small and one thing that I actually want to do is I
thing that I actually want to do is I want to remove this form wrapper so
want to remove this form wrapper so we're going to do that if only if we are
we're going to do that if only if we are editing right I got ahead of myself so
editing right I got ahead of myself so it should actually just be a button
it should actually just be a button inside of the list wrapper like that uh
inside of the list wrapper like that uh great and Below uh Above This add a list
great and Below uh Above This add a list text let's add a little plus from Lucid
text let's add a little plus from Lucid react so make sure you import the plus
react so make sure you import the plus and let's give it a class name of h-4
and let's give it a class name of h-4 W-4 and margin right of two like that
W-4 and margin right of two like that and there we go look at our nice little
and there we go look at our nice little button here and now when we click on it
button here and now when we click on it this is going to transform into our form
this is going to transform into our form so let's do that now now in order to do
so let's do that now now in order to do that we of course have to add some
that we of course have to add some states some refs so let's go ahead and
states some refs so let's go ahead and prepare all of that here so we're going
prepare all of that here so we're going to import use State use uh we're going
to import use State use uh we're going to need use ref and element ref from
to need use ref and element ref from react so let's go ahead and
react so let's go ahead and prepare is editing and set is editing to
prepare is editing and set is editing to be used State and by default false let's
be used State and by default false let's go ahead and let's prepare a form ref
go ahead and let's prepare a form ref which is going to be use ref with
element of form by default it is
form by default it is null let's copy and paste this one and
null let's copy and paste this one and let's rename this one to be input ref
let's rename this one to be input ref and element ref of type
and element ref of type input and I just like to separate my
input and I just like to separate my let's put refs first and then the use
let's put refs first and then the use State uh great and now let's go ahead
State uh great and now let's go ahead and let's just create a couple of
and let's just create a couple of handlers like we did for uh the editing
handlers like we did for uh the editing of the board title here in our board
of the board title here in our board navbar so let's write const enable
navbar so let's write const enable editing to Be an Arrow function which
editing to Be an Arrow function which calls this set is editing to be true and
calls this set is editing to be true and then callose the set
timeout and let's just do input ref question mark current actually we
ref question mark current actually we can directly access the current and then
can directly access the current and then Focus like that and now let's write cons
Focus like that and now let's write cons disable editing to be another arrow
disable editing to be another arrow function which is very simply just going
function which is very simply just going to set is editing to be
to set is editing to be false great and now I want to write uh
false great and now I want to write uh const on key
const on key down to have an event of keyboard
down to have an event of keyboard event and if e do key is is the escape
event and if e do key is is the escape button we want to disable editing so
button we want to disable editing so this is just one of the things we're
this is just one of the things we're going to do to kind of copy that feel of
going to do to kind of copy that feel of using Trello and feeling like it's
using Trello and feeling like it's actually Trello right we're not just
actually Trello right we're not just going to visually copy it I also want to
going to visually copy it I also want to copy all the NIT trick it has like when
copy all the NIT trick it has like when you click on escape the entire thing
you click on escape the entire thing closes right so we're going to keep
closes right so we're going to keep track of details like those and now we
track of details like those and now we can actually use a little use event
can actually use a little use event listener from use hooks if you remember
listener from use hooks if you remember which we installed in the beginning of
which we installed in the beginning of the tutorial uh we needed it for use
the tutorial uh we needed it for use local storage so let's go ahead and use
local storage so let's go ahead and use it even further right if we have it
it even further right if we have it let's use this use event listener here
let's use this use event listener here uh to listen to key down and call the on
uh to listen to key down and call the on key
key down and let's also add use on click
down and let's also add use on click outside again from use hooks TS so use
outside again from use hooks TS so use on click outside is going to list listen
on click outside is going to list listen to form ref and then it's just going to
to form ref and then it's just going to do disable editing if we click outside
do disable editing if we click outside the form ref great and now let's go
the form ref great and now let's go ahead and let's actually uh assign this
ahead and let's actually uh assign this button to do something so give it an
button to do something so give it an onclick option to call the enable
onclick option to call the enable editing like that and now let's go ahead
editing like that and now let's go ahead and let's write if uh is editing in that
and let's write if uh is editing in that case I want to go ahead and I want to
case I want to go ahead and I want to create that form so return again a list
create that form so return again a list wrapper and inside let's render the form
wrapper and inside let's render the form element let's give this form element a
element let's give this form element a ref of form ref let's give it a class
ref of form ref let's give it a class name of w- full padding of three rounded
name of w- full padding of three rounded medium BG white space y4 and Shadow
medium BG white space y4 and Shadow MD like that inside of the form let's
MD like that inside of the form let's write our form in component from add/
write our form in component from add/ components form form input so make sure
components form form input so make sure you import this and let me just move it
you import this and let me just move it here
here above uh like that great and now let's
above uh like that great and now let's go inside of this form input and let's
go inside of this form input and let's give it an a ref of input ref let's go
give it an a ref of input ref let's go ahead and give it an ID of
ahead and give it an ID of title class name is going to be text
title class name is going to be text small px2 py1 H7
small px2 py1 H7 font
font medium border
medium border transparent cover border input Focus
transparent cover border input Focus border input and transition so we're
border input and transition so we're practically going to match what this
practically going to match what this button looks like so I want to make a
button looks like so I want to make a very smooth transition when it turns
very smooth transition when it turns into an input I don't want the UI to
into an input I don't want the UI to jump around too much so that's why I'm
jump around too much so that's why I'm kind of using this weird values because
kind of using this weird values because when I developed this I just did a bunch
when I developed this I just did a bunch of trial and error you know with CSS to
of trial and error you know with CSS to see what looks the best so if you're
see what looks the best so if you're confused and you think oh well I would
confused and you think oh well I would never guess these class names well yeah
never guess these class names well yeah you wouldn't guess these class names
you wouldn't guess these class names this is what you would do in trial and
this is what you would do in trial and error that's what mostly styling is for
error that's what mostly styling is for me so I just want to clear that up in
me so I just want to clear that up in case you think I somehow know this up
case you think I somehow know this up front I don't know uh and now let's give
front I don't know uh and now let's give it a placeholder here
it a placeholder here enter list
enter list title and let's give it uh well that's
title and let's give it uh well that's all we can give it for now I think so
all we can give it for now I think so let's go ahead and try if I click here
let's go ahead and try if I click here there we go you can see how it turns
there we go you can see how it turns into a nice little input and I click
into a nice little input and I click outside it goes back uh well it goes
outside it goes back uh well it goes back to being uh a button perfect so now
back to being uh a button perfect so now I want to create a little button uh
I want to create a little button uh below it so let's go ahead uh and inside
below it so let's go ahead uh and inside of this
of this form well I actually want to do another
form well I actually want to do another thing uh I want to go ahead and create a
thing uh I want to go ahead and create a hidden input here so a hidden
hidden input here so a hidden value and let's go ahead and give it a
value and let's go ahead and give it a value of pams board ID and I didn't add
value of pams board ID and I didn't add params so let's go ahead and let's get
params so let's go ahead and let's get our
our params use params from next SL
params use params from next SL navigation make sure you import that and
navigation make sure you import that and now let's go here and just get prams
now let's go here and just get prams board ID like that and give it a name of
board ID like that and give it a name of board ID so this way we could have
board ID so this way we could have technically also fetched this inside of
technically also fetched this inside of our own submit function or we can add it
our own submit function or we can add it to a input so then we can work purely
to a input so then we can work purely with form data it's however you want it
with form data it's however you want it to be and now below this but still
to be and now below this but still inside of this form element create a div
inside of this form element create a div with a class name of flex items Center
with a class name of flex items Center and GAP X
and GAP X one and go ahead and render the form
one and go ahead and render the form submit from add/ components form form
submit from add/ components form form submit so make sure you add this import
submit so make sure you add this import as well and inside of here write add
as well and inside of here write add list and below that add a button
list and below that add a button component which you can import from
component which you can import from components UI
components UI button so just make sure you do uh that
button so just make sure you do uh that as well great where are we we are here
as well great where are we we are here and inside of this button render the X
and inside of this button render the X icon from Lucid react as I added right
icon from Lucid react as I added right here next to my plus icon great and
here next to my plus icon great and let's give this x a class name of H5 and
let's give this x a class name of H5 and W5 and let's give this button a class
W5 and let's give this button a class name of uh actually no class name but
name of uh actually no class name but let's give it an onclick to be disabled
editing and let's go ahead and give it a size of
and let's go ahead and give it a size of small and a variant of
small and a variant of ghost and let me close this all right
ghost and let me close this all right and let's try that out now when I go
and let's try that out now when I go here when I click there we go you can
here when I click there we go you can see how it immediately focuses on the
see how it immediately focuses on the input we can click on ADD list or we can
input we can click on ADD list or we can click here we can also kind of Click
click here we can also kind of Click outside and then it closes we can click
outside and then it closes we can click the escape and it closes as well perfect
the escape and it closes as well perfect so what we have to create now is an
so what we have to create now is an actual action which is going to is an
actual action which is going to is an actual server action which is going to
actual server action which is going to be called uh when well um when we click
be called uh when well um when we click submit so let's go ahead and let's copy
submit so let's go ahead and let's copy one of the existing actions which we
one of the existing actions which we have so I'm going to actually close
have so I'm going to actually close everything for now let's go inside of
everything for now let's go inside of the actions folder and I want to copy
the actions folder and I want to copy the update board let's go ahead and copy
the update board let's go ahead and copy it and let's rename it to create list
it and let's rename it to create list like this and let's go inside of schema.
like this and let's go inside of schema. THS first so we're definitely going to
THS first so we're definitely going to require the title and instead of
require the title and instead of requiring an ID we're going to require
requiring an ID we're going to require board ID so the title rules can actually
board ID so the title rules can actually stay the same it's required that a list
stay the same it's required that a list has a title and let's just put a minimum
has a title and let's just put a minimum length of three this of course doesn't
length of three this of course doesn't matter for example I'm pretty sure all
matter for example I'm pretty sure all lists should allow lower than three for
lists should allow lower than three for example if you want your list to be
example if you want your list to be named QA that's just two letters but you
named QA that's just two letters but you know if you want to test out validation
know if you want to test out validation you can leave it at three it's purely
you can leave it at three it's purely your choice and let's rename this to
your choice and let's rename this to create
create list all right so make just make sure
list all right so make just make sure you have validation for title and
you have validation for title and validation for board ID so that's what
validation for board ID so that's what we expect to for the user to enter and
we expect to for the user to enter and now inside of types dots let's rename
now inside of types dots let's rename this from update board to create list
this from update board to create list and let's use it here as our input type
and let's use it here as our input type now go back inside of index.ts here and
now go back inside of index.ts here and let's go ahead and slowly modify this so
let's go ahead and slowly modify this so first we have this schema error instead
first we have this schema error instead of update board it is create list and we
of update board it is create list and we have to use it all the way to the bottom
have to use it all the way to the bottom here in create save action so change
here in create save action so change this to create list and rename the
this to create list and rename the action to be create list as well and now
action to be create list as well and now let's go to the beginning of the action
let's go to the beginning of the action and see if we have to modify anything so
and see if we have to modify anything so the validation can actually stay EX
the validation can actually stay EX exactly the same we don't need to modify
exactly the same we don't need to modify that and now from our data we're
that and now from our data we're actually going to get the title and
actually going to get the title and board ID and we're not going to be
board ID and we're not going to be creating a new board we're going to be
creating a new board we're going to be creating a new list so let's prepare the
creating a new list so let's prepare the let list instead and then in the tri
let list instead and then in the tri block we are assigning to that new list
block we are assigning to that new list constant and calling AWA DB board. list
constant and calling AWA DB board. list and do
and do create that me this means we're not
create that me this means we're not going to be using the work Clause at all
going to be using the work Clause at all instead we're going to be passing the
instead we're going to be passing the data only we're going to pass in uh well
data only we're going to pass in uh well the title we're going to pass in the
the title we're going to pass in the board ID but we're actually missing a
board ID but we're actually missing a couple of stuff right because I also
couple of stuff right because I also have to pass in the order which now I
have to pass in the order which now I can only hard code so let's go ahead and
can only hard code so let's go ahead and do the following first thing I'm going
do the following first thing I'm going to do is attempt to fetch the board
to do is attempt to fetch the board which where this list is supposedly uh
which where this list is supposedly uh to be created so const
to be created so const board await DB board find
board await DB board find unique where
unique where ID is board ID and organization ID of
ID is board ID and organization ID of the current user
the current user matches and now if we don't have a board
matches and now if we don't have a board in that case let's return an
in that case let's return an error board not found so this will
error board not found so this will prevent any Outsiders from trying to
prevent any Outsiders from trying to create a new list inside of your
create a new list inside of your organization's board and well in general
organization's board and well in general check if by the time you attempted to
check if by the time you attempted to create this list maybe the board was
create this list maybe the board was deleted so this is going to prevent that
deleted so this is going to prevent that from happening now that we have the
from happening now that we have the board what we can do is we can attempt
board what we can do is we can attempt to fetch the last list inside of that
to fetch the last list inside of that board so that we can properly assign the
board so that we can properly assign the newest order of this list so let's write
newest order of this list so let's write const last list to be await DB list find
const last list to be await DB list find first
first where board ID is board
where board ID is board ID order Y is order as descending my
ID order Y is order as descending my apologies and select
apologies and select whoa we just need the order so we just
whoa we just need the order so we just want to hear about what is the last list
want to hear about what is the last list in our uh board and then when we have
in our uh board and then when we have our last list we can generate a constant
our last list we can generate a constant new order to say if we have the last
new order to say if we have the last list in that case let's do last list.
list in that case let's do last list. order plus one otherwise one
order plus one otherwise one like that and then we can copy this new
like that and then we can copy this new order and paste it inside of here
order and paste it inside of here perfect and now let's go inside of the
perfect and now let's go inside of the error and instead of fail to update
error and instead of fail to update let's do fail to
let's do fail to create and instead of revalidate path uh
create and instead of revalidate path uh ID let's use the board ID which we
ID let's use the board ID which we extract from the data and let's return
extract from the data and let's return data which is a list and now yeah we
data which is a list and now yeah we actually have to modify our type because
actually have to modify our type because we are expecting
we are expecting uh board I believe so let's just revisit
uh board I believe so let's just revisit our types. CS yes so let's go ahead and
our types. CS yes so let's go ahead and instead of importing board let's import
instead of importing board let's import list from Prisma client and put list as
list from Prisma client and put list as the second argument in our action State
the second argument in our action State and now as you can see we have no errors
and now as you can see we have no errors inside of our save action Handler
inside of our save action Handler perfect so now let's go ahead and let's
perfect so now let's go ahead and let's find
find our our app platform dashboard board
our our app platform dashboard board components list form
components list form and let's go ahead and let's import
and let's go ahead and let's import everything we need so we're going to
everything we need so we're going to need use action from hooks use action
need use action from hooks use action and we're going to need create list from
and we're going to need create list from actions create list so make sure you
actions create list so make sure you have used action and create list and
have used action and create list and let's go all the way to the top here uh
let's go all the way to the top here uh and actually I don't want to do it here
and actually I don't want to do it here I want to do it after these disable
I want to do it after these disable editing right so I want them to be in
editing right so I want them to be in the scope first and then let's extract
the scope first and then let's extract execute and field
execute and field errors from use action let's pass in
errors from use action let's pass in create
create list and now let's go ahead uh and let's
list and now let's go ahead uh and let's actually add some existing um well
actually add some existing um well callbacks right so on
callbacks right so on success we get the data and let's just
success we get the data and let's just do toast which we can import from soner
do toast which we can import from soner so just make sure you do that I'm going
so just make sure you do that I'm going to move it uh to the top all right so we
to move it uh to the top all right so we have that and let's do toast. success
have that and let's do toast. success open backck and let's write list open
open backck and let's write list open annotations and let's write list data.
annotations and let's write list data. tile created so we immediately show that
tile created so we immediately show that we fetched and uh well uh have the name
we fetched and uh well uh have the name of the new list and then let's do
of the new list and then let's do disable
editing and what I want to do then is also call router. refresh
also call router. refresh so let's just go ahead and do const
so let's just go ahead and do const router to be us router from next
router to be us router from next navigation so make sure you import use
navigation so make sure you import use router from next navigation and then in
router from next navigation and then in here let's do router. refresh so we
here let's do router. refresh so we refresh all of our server components and
refresh all of our server components and on error let's fetch the
on error let's fetch the error and do toast
error and do toast error and the error
error and the error itself uh great and now that we have
itself uh great and now that we have that let's go ahead and let's create uh
that let's go ahead and let's create uh our onsubmit function so const on submit
our onsubmit function so const on submit accepts the form data which is form
accepts the form data which is form data and in here we have the title which
data and in here we have the title which is form data get title as
is form data get title as string and we also have the board ID
string and we also have the board ID because we placed it in this hidden
because we placed it in this hidden input here right and let's go ahead and
input here right and let's go ahead and let's do
let's do execute title and board
execute title and board ID while like that and then let's use
ID while like that and then let's use this onsubmit to be an
this onsubmit to be an action of this board right here and
action of this board right here and let's also go ahead and give this form
let's also go ahead and give this form input errors of field errors uh and did
input errors of field errors uh and did we extract the field errors we did so
we extract the field errors we did so make sure you extract field errors from
make sure you extract field errors from use action and let's try it out now so
use action and let's try it out now so I'm going to refresh here and first
I'm going to refresh here and first let's try something too short there we
let's try something too short there we go we have an error that title is too
go we have an error that title is too short and now let's try something like
short and now let's try something like this and there we go it says that list
this and there we go it says that list has been created perfect uh we cannot
has been created perfect uh we cannot see it here because we don't do any
see it here because we don't do any iteration over uh any Fields right but
iteration over uh any Fields right but what we can do is we can visit our
what we can do is we can visit our Prisma studio so let me do that npx
Prisma studio so let me do that npx Prisma Studio that's going to open it
Prisma Studio that's going to open it here and let's go ahead and see and in
here and let's go ahead and see and in my list collections it looks like I have
my list collections it looks like I have one and as you can see it has the
one and as you can see it has the correct title it has the correct order
correct title it has the correct order since it's the first one and it has a
since it's the first one and it has a relation with the board exactly as we
relation with the board exactly as we want it perfect great great job so what
want it perfect great great job so what we're going to do next uh we're going to
we're going to do next uh we're going to create uh well a kind of loop over our
create uh well a kind of loop over our lists and actually render them out here
lists and actually render them out here great great
great great job so now let's go ahead and let's
job so now let's go ahead and let's actually iterate over the over our lists
actually iterate over the over our lists here so we don't only have this button
here so we don't only have this button but we also have our newly created lists
but we also have our newly created lists so in order to do that let's go ahead
so in order to do that let's go ahead and let's visit our actual board page so
and let's visit our actual board page so go ahead inside of the app platform
go ahead inside of the app platform dashboard and you should have the board
dashboard and you should have the board here and inside of board ID we have
here and inside of board ID we have page. CSX and in here we actually load
page. CSX and in here we actually load the lists and all of the lists cards but
the lists and all of the lists cards but we don't actually go over them anywhere
we don't actually go over them anywhere so go inside the list container right
so go inside the list container right here which we created and let's go ahead
here which we created and let's go ahead and let's store this data inside of a
and let's store this data inside of a state so cons data set data or actually
state so cons data set data or actually we can call it uh ordered data and set
we can call it uh ordered data and set ordered data and go and write use State
ordered data and go and write use State data so the reason I'm doing this is
data so the reason I'm doing this is because when we Implement drag and drop
because when we Implement drag and drop we're going to need to have a kind of a
we're going to need to have a kind of a source of Truth which we are going to
source of Truth which we are going to modify locally before we do it in the
modify locally before we do it in the database we could directly do it in the
database we could directly do it in the database and skip this local part but
database and skip this local part but then it would kind of have a bad user
then it would kind of have a bad user experience because with drag and drop
experience because with drag and drop it's very important that we have a
it's very important that we have a working optimistic mutation usually
working optimistic mutation usually optimistic mutation is only used you
optimistic mutation is only used you know for specific cases where you kind
know for specific cases where you kind of want to improve the experience like
of want to improve the experience like when you like something but it's not the
when you like something but it's not the end of the world if you don't do
end of the world if you don't do optimistic updates when you like
optimistic updates when you like something right but when it comes to
something right but when it comes to drag and drop it's a really bad
drag and drop it's a really bad experience to have to wait for a second
experience to have to wait for a second and then you know your content switches
and then you know your content switches back to where you dragged it from and
back to where you dragged it from and then after a second it only updates so
then after a second it only updates so this way we're going to use optimistic
this way we're going to use optimistic update and we're going to immediately
update and we're going to immediately add the use effect
add the use effect here so that when the data updates we're
here so that when the data updates we're going to go ahead and change set ordered
going to go ahead and change set ordered data again and pass in the new data so
data again and pass in the new data so this is is going to ensure those
this is is going to ensure those optimistic updates and when later we
optimistic updates and when later we implement the actual drag and drop we
implement the actual drag and drop we can directly mutate the data using set
can directly mutate the data using set ordered data um great and now that we
ordered data um great and now that we have that let's go ahead inside of this
have that let's go ahead inside of this ordered list and just before we do uh
ordered list and just before we do uh the list form let's go ahead and let's
the list form let's go ahead and let's iterate over data actually ordered data
iterate over data actually ordered data do map and now we are working with an
do map and now we are working with an individual list and let's also pass in
individual list and let's also pass in the index while we are here so go ahead
the index while we are here so go ahead and open this and return a list
and open this and return a list item which is going to have a key which
item which is going to have a key which is list.
is list. ID index which is list. ID as
ID index which is list. ID as well uh sorry index is going to be index
well uh sorry index is going to be index my apologies and we're also going to
my apologies and we're also going to have the actual data which is going to
have the actual data which is going to be the entire list great and if you save
be the entire list great and if you save you're going to get an error because we
you're going to get an error because we currently don't have the list list item
currently don't have the list list item so go inside of underscore components
so go inside of underscore components where we have the list container and
where we have the list container and create a new file uh list- item. TSX and
create a new file uh list- item. TSX and go ahead and Mark this as Ed client and
go ahead and Mark this as Ed client and Export con list item
Export con list item here and return a div saying list
here and return a div saying list item and go back to the list container
item and go back to the list container and now you can import the list item
and now you can import the list item from /list item and when you save uh you
from /list item and when you save uh you should be having a list item here like
should be having a list item here like this so now uh let's go ahead uh and
this so now uh let's go ahead uh and let's just quickly give this ordered
let's just quickly give this ordered list a class
list a class name of flex Gap X3 and
name of flex Gap X3 and H4 and now as you can see the list item
H4 and now as you can see the list item has moved next to this button to add a
has moved next to this button to add a list which is exactly what we want now
list which is exactly what we want now let's create an interface for the list
let's create an interface for the list item so we don't have this props errors
item so we don't have this props errors here so interface list item
here so interface list item props is going to uh accept the actual
props is going to uh accept the actual data which is list with cards from
data which is list with cards from types so let me just go ahead oh my God
types so let me just go ahead oh my God okay list with cards and we're going to
okay list with cards and we're going to have an index which is a number and
have an index which is a number and we're going to use the index later uh
we're going to use the index later uh when we Implement drag and drop and now
when we Implement drag and drop and now let's uh assign those props so list item
let's uh assign those props so list item props and we can extract data and the
props and we can extract data and the index uh great so now let's go ahead uh
index uh great so now let's go ahead uh and let's change this div to be a list
and let's change this div to be a list item and give it a class name of shrink
item and give it a class name of shrink 0 h- full and
0 h- full and w272 pixels and select
w272 pixels and select none great and now inside of here I want
none great and now inside of here I want to go ahead and just write a simple div
to go ahead and just write a simple div element and give it a class name of w-
element and give it a class name of w- full rounded
full rounded mdbg Dash and go ahead and write hash
mdbg Dash and go ahead and write hash 1f uh 1 f24 2
1f uh 1 f24 2 F4 Shadow MD and padding bottom of two
F4 Shadow MD and padding bottom of two great and now inside uh well for now we
great and now inside uh well for now we can just uh add a list header component
can just uh add a list header component like this and if you save you're going
like this and if you save you're going to get an error for that as well so
to get an error for that as well so let's go back inside of the components
let's go back inside of the components and create a list - header. vsx like
and create a list - header. vsx like that Mark it is use client and Export
that Mark it is use client and Export con list
con list header and return a div saying list
header and go back to the list item and then you can import the list header from
then you can import the list header from SL list header and when you save you
SL list header and when you save you should no longer uh be having an
should no longer uh be having an error and I made a small mistake here in
error and I made a small mistake here in the list item component so the
the list item component so the background should be 1 F and then it
background should be 1 F and then it should be
should be directly uh sorry it should be F1 F2 to4
directly uh sorry it should be F1 F2 to4 like this and there we go so now you
like this and there we go so now you should be seeing a whsh color like this
should be seeing a whsh color like this so not exactly white but just a slight
so not exactly white but just a slight uh gradient on it perfect and now we can
uh gradient on it perfect and now we can go inside of our list header component
go inside of our list header component here and let's give this div a class
here and let's give this div a class name of padding top of two PX of two as
name of padding top of two PX of two as well text small font semi bold Flex
well text small font semi bold Flex justify between items start and GAP x
justify between items start and GAP x two great and now inside instead of
two great and now inside instead of rendering list header let's go ahead and
rendering list header let's go ahead and open up a new div which is going to
open up a new div which is going to write list header here and give it a
write list header here and give it a class name of w- full text small px-
class name of w- full text small px- 2.5
2.5 py-1
py-1 h-7 font D medium and Border D
h-7 font D medium and Border D transparent like that uh great and now
transparent like that uh great and now let's go ahead and let's actually create
let's go ahead and let's actually create an interface for the list
an interface for the list header so interface list header props is
header so interface list header props is going to accept a data which is a list
going to accept a data which is a list like that and we can import the list
like that and we can import the list from Prisma client and let's just assign
from Prisma client and let's just assign those here so list header props let's
those here so list header props let's extract the list and then we can go
extract the list and then we can go ahead sorry data not
ahead sorry data not list and then we can go ahead and
list and then we can go ahead and actually render data. Tyle here uh and
actually render data. Tyle here uh and we forgot to pass that so go back to
we forgot to pass that so go back to list item and pass data from here and
list item and pass data from here and there we go you can see how now uh if I
there we go you can see how now uh if I attempt to create a new list it should
attempt to create a new list it should appear right here there we go you can
appear right here there we go you can see how it's right next to this one and
see how it's right next to this one and if I do it again it will appear here
if I do it again it will appear here perfect so that is working now what I
perfect so that is working now what I want to do now is I want to implement uh
want to do now is I want to implement uh an ability to rename lists so when we
an ability to rename lists so when we click on the title I want it to switch
click on the title I want it to switch to an
to an input so inside of the list header
input so inside of the list header component let's me just expand this so
component let's me just expand this so go back inside of the list header
go back inside of the list header component and let's go ahead and let's
component and let's go ahead and let's import the things we need like use State
import the things we need like use State use ref element ref
use ref element ref from
from react and the first thing I want to do
react and the first thing I want to do is I want to store the title inside of
is I want to store the title inside of its own state so I can optimistically
its own state so I can optimistically update that as well so const title set
update that as well so const title set title is going to be use State data.
title is going to be use State data. title and then let's replace this data
title and then let's replace this data the title to use the state version of
the title to use the state version of that uh great besides this state we're
that uh great besides this state we're obviously going to have the is editing
obviously going to have the is editing and set is editing from use State and by
and set is editing from use State and by default is going to be false and now
default is going to be false and now let's go ahead and let's add form ref to
let's go ahead and let's add form ref to we use ref with element
we use ref with element ref of form and give it a null for the
ref of form and give it a null for the default value copy and paste that and
default value copy and paste that and replace um this one to be the input ref
replace um this one to be the input ref and the element ref for that is input
and the element ref for that is input and then let's go ahead and let's create
and then let's go ahead and let's create const enable
const enable editing do set is editing to be true and
editing do set is editing to be true and then set
then set timeout so set timeout is obviously not
timeout so set timeout is obviously not the best solution for this it's kind of
the best solution for this it's kind of like a hack but it gets the job done for
like a hack but it gets the job done for now uh you can explore flush sync if
now uh you can explore flush sync if you're interested in that but the react
you're interested in that but the react docs mentioned that it should only be
docs mentioned that it should only be used as a last resort in all of the
used as a last resort in all of the attempts that I've tried using using it
attempts that I've tried using using it it worked perfectly of course it can
it worked perfectly of course it can probably vary depending if a device is
probably vary depending if a device is slower but I think for a tutorial it's
slower but I think for a tutorial it's it's just enough and I mean there is
it's just enough and I mean there is nothing no important functionality
nothing no important functionality inside of this set time out it just
inside of this set time out it just focuses on the input to you know save
focuses on the input to you know save the user from clicking twice so let's
the user from clicking twice so let's focus on the input ref using the current
focus on the input ref using the current and focus and input
and focus and input ref current select I actually think we
ref current select I actually think we can only like we actually don't have to
can only like we actually don't have to do both of them we're going to try it
do both of them we're going to try it out now I think we we only actually need
out now I think we we only actually need select uh great but we're going to try
select uh great but we're going to try that in a minute let's now just continue
that in a minute let's now just continue and let's write the disable editing so
and let's write the disable editing so con disable
con disable editing set is editing is going to be
editing set is editing is going to be false great and now let's go ahead and
false great and now let's go ahead and let's just add the use event listener
let's just add the use event listener from use hooks so make sure you import
from use hooks so make sure you import uh that and let me just align those so
uh that and let me just align those so use event listener from use Hooks and
use event listener from use Hooks and I'm going to go ahead and pass in the
I'm going to go ahead and pass in the key down element and on key down
key down element and on key down function now let's define const on key
function now let's define const on key down to accept the event which is a type
down to accept the event which is a type of keyboard event and then inside if e
of keyboard event and then inside if e do key is escape in that case we're
do key is escape in that case we're going to submit uh this request so form
going to submit uh this request so form ref. current question Mark request
ref. current question Mark request submit that's what we're going to
submit that's what we're going to do great and now let's go ahead uh and
do great and now let's go ahead uh and let's modify this a bit so inside of
let's modify this a bit so inside of this div let's go ahead and add if is
this div let's go ahead and add if is editing in that case just return return
editing in that case just return return a paragraph which says form otherwise
a paragraph which says form otherwise and then wrap this entire div
and then wrap this entire div here like that and now let's just go
here like that and now let's just go ahead and let's give this div a on click
ahead and let's give this div a on click to be enable editing like that and let
to be enable editing like that and let me just indent
me just indent this all right and already now when I
this all right and already now when I try and click on this it should change
try and click on this it should change to the text form like that perfect and
to the text form like that perfect and what we're going to do now is we're
what we're going to do now is we're going to actually well make the input
going to actually well make the input here but we're also going to try and add
here but we're also going to try and add the necessary padding and stuff so that
the necessary padding and stuff so that when we click it doesn't get like this
when we click it doesn't get like this little jump right I want it to stay the
little jump right I want it to stay the same size as the original card so let's
same size as the original card so let's just ref refresh this so everything is
just ref refresh this so everything is in the original title form and let's add
in the original title form and let's add the actual native HTML form element here
the actual native HTML form element here and let's go ahead and give it a class
and let's go ahead and give it a class name of flex D1 and px2
name of flex D1 and px2 pixels and let's write an input inside
pixels and let's write an input inside and let's uh go ahead and make it a
and let's uh go ahead and make it a hidden input with an ID of ID name of ID
hidden input with an ID of ID name of ID and value data. ID let's copy the hidden
and value data. ID let's copy the hidden input and and let's change this one to
input and and let's change this one to be board ID and name board ID and the
be board ID and name board ID and the value is data. board ID like that and
value is data. board ID like that and then let's add the form input component
then let's add the form input component from add/ components form form input so
from add/ components form form input so make sure you have this input right here
make sure you have this input right here which I've separated and inside of here
which I've separated and inside of here go ahead and give it a ref of input ref
go ahead and give it a ref of input ref give it an on blur uh well for now let's
give it an on blur uh well for now let's just make it an empty function give it
just make it an empty function give it an ID of title a placeholder of enter
an ID of title a placeholder of enter list
list title
whoa and a default value of title from the state so let's try that out now when
the state so let's try that out now when I refresh here and click here there we
I refresh here and click here there we go you can see how it turns into an
go you can see how it turns into an input and now we're just going to go
input and now we're just going to go ahead and kind of style it so it flushes
ahead and kind of style it so it flushes with the original uh
with the original uh well card right and it it kind of
well card right and it it kind of doesn't jump as much with this outline
doesn't jump as much with this outline thing so let's try and do that we're
thing so let's try and do that we're going to give it a couple of class names
going to give it a couple of class names here so focus in the form input and
here so focus in the form input and let's give it a class name of text small
let's give it a class name of text small PX is going to be 7 pixels py is going
PX is going to be 7 pixels py is going to be one H is going to be seven font is
to be one H is going to be seven font is going to be medium border is going to be
going to be medium border is going to be transparent on Hover border is going to
transparent on Hover border is going to be input on Focus border is going to be
be input on Focus border is going to be input
input transition and truncate and let's also
transition and truncate and let's also add BG
add BG transparent and focus BG white like that
transparent and focus BG white like that so let's try that out now when I refresh
so let's try that out now when I refresh and when I click here there we go you
and when I click here there we go you can see how it looks kind of flush
can see how it looks kind of flush perfect and what I want to do now is
perfect and what I want to do now is well uh the actual server action which
well uh the actual server action which is going to well trigger uh the
is going to well trigger uh the submit so let's let's go ahead and copy
submit so let's let's go ahead and copy an existing server action so let me just
an existing server action so let me just collapse everything here go inside of
collapse everything here go inside of actions and let's copy update board and
actions and let's copy update board and let's rename it to update
let's rename it to update list and then first let's go inside of
list and then first let's go inside of schema. Cs so what do we expect we
schema. Cs so what do we expect we definitely expect the title and the
definitely expect the title and the rules for the title can stay the same we
rules for the title can stay the same we expect the ID of the list but we also
expect the ID of the list but we also expect a board ID so we know where to
expect a board ID so we know where to update this great and now let's go ahead
update this great and now let's go ahead and let's modify the types from here
and let's modify the types from here sorry the types inside of update list
sorry the types inside of update list and we will not be expecting uh we will
and we will not be expecting uh we will not be expecting the board to return
not be expecting the board to return we'll be expecting the list to be
we'll be expecting the list to be returned and looks like we forgot to
returned and looks like we forgot to rename the schema so it's not going to
rename the schema so it's not going to be update board it's going to be update
be update board it's going to be update list and then we have to modify that
list and then we have to modify that from here and here uh as well so let me
from here and here uh as well so let me just copy this right that and now let's
just copy this right that and now let's go ahead and inside of index here change
go ahead and inside of index here change this to update list as well go all the
this to update list as well go all the way down change it to here and instead
way down change it to here and instead of update board is going to be update
of update board is going to be update list perfect so the uh authorization can
list perfect so the uh authorization can stay the same and from the data we're
stay the same and from the data we're going to extract the title the ID and
going to extract the title the ID and also the board ID and instead of
also the board ID and instead of updating the board we are updating the
updating the board we are updating the list so let's assign the list here and
list so let's assign the list here and we're doing a wait db. list. update
we're doing a wait db. list. update where ID is ID board ID is board ID and
where ID is ID board ID is board ID and board has the matching organization ID
board has the matching organization ID and we are just passing in the title
and we are just passing in the title here perfect and we're going to change
here perfect and we're going to change this to well this can stay the same
this to well this can stay the same failed to update and we're going to make
failed to update and we're going to make sure that the path we are we are
sure that the path we are we are revalidating is board ID like that and
revalidating is board ID like that and in the end let's just return the list
in the end let's just return the list perfect and now let's head back inside
perfect and now let's head back inside of
of our where is it inside of our app folder
our where is it inside of our app folder platform dashboard board components and
platform dashboard board components and we have list header here and let's go
we have list header here and let's go ahead and actually add this stuff so
ahead and actually add this stuff so after disable editing I'm going to add
after disable editing I'm going to add use action which we can import from at/
use action which we can import from at/ hooks use action and update list from
hooks use action and update list from actions update list so let me just show
actions update list so let me just show you that I added the use action and
you that I added the use action and update list all right make sure you have
update list all right make sure you have those two and now here let's go ahead
those two and now here let's go ahead and let's extract the
execute execute like that and let's go ahead oh I misspelled it again
ahead oh I misspelled it again execute all right fourth try and now
execute all right fourth try and now let's go ahead and add on success
let's go ahead and add on success message here and a data and in here we
message here and a data and in here we can just display a nice message using
can just display a nice message using our toast which you can import from
our toast which you can import from sonor like this let me just move it uh
sonor like this let me just move it uh to the top
to the top here and let's call toast. success here
here and let's call toast. success here open backx renamed to open annotations
open backx renamed to open annotations and let's render the new name like that
and let's render the new name like that and then let's set the new title so we
and then let's set the new title so we op mystically updated to data. title
op mystically updated to data. title here and let's disable
here and let's disable editing in case we have an error let's
editing in case we have an error let's get that error and let's toast error
get that error and let's toast error error great and now let's go ahead and
error great and now let's go ahead and let's write uh the const handle submit
let's write uh the const handle submit here which accepts the form
here which accepts the form data which is form
data which is form data and let's get the title so form
data and let's get the title so form data. getet
data. getet title as string let's copy this two more
title as string let's copy this two more times the second one is going to be the
times the second one is going to be the ID and the third one is going to be
ID and the third one is going to be board
board ID and let's go ahead and let's do if
ID and let's go ahead and let's do if title is equal to data. title we can
title is equal to data. title we can just disable editing right no need to do
just disable editing right no need to do an API request here and then let's do
an API request here and then let's do execute and let's pass in the title ID
execute and let's pass in the title ID and the board
and the board ID and now let's also add the const on
ID and now let's also add the const on blur here to call form current
blur here to call form current request submit and it's actually form
R all right and now uh let's go ahead and let's assign all of these things so
and let's assign all of these things so our form needs to have let just collapse
our form needs to have let just collapse these
these items so our form needs to have a ref of
items so our form needs to have a ref of form
submit and I think that should be it our main input here has the ref but we also
main input here has the ref but we also need to pass the on blur here and I
need to pass the on blur here and I think that should be it let's also just
think that should be it let's also just add a little button element
add a little button element here which is going to be a type of
here which is going to be a type of submit and
submit and hidden all right and let's try that out
hidden all right and let's try that out I'm going to refresh this I'm going to
I'm going to refresh this I'm going to go and change the title and I can press
go and change the title and I can press enter and there we go it's updating and
enter and there we go it's updating and it's changed and we have a success
it's changed and we have a success message and when I refresh the title has
message and when I refresh the title has been saved let's try another one and
been saved let's try another one and this time I will not press enter instead
this time I will not press enter instead I'm just going to blur and it still
I'm just going to blur and it still works and let's try if I click Escape
works and let's try if I click Escape same thing it updates perfect so you
same thing it updates perfect so you have finished um renaming and updating
have finished um renaming and updating the list now we're going to go ahead and
the list now we're going to go ahead and create options tag so we can copy the
create options tag so we can copy the list but also delete it if needed great
list but also delete it if needed great great
great job so now that we finished our list
job so now that we finished our list header I want to go ahead and add a
header I want to go ahead and add a little options here uh on the right side
little options here uh on the right side so that when we click here it opens a
so that when we click here it opens a little popover and allows us to you know
little popover and allows us to you know add a new card to this list delete this
add a new card to this list delete this list or copy this list in order to do
list or copy this list in order to do that let's make sure we are in the list
that let's make sure we are in the list header component which we just wrapped
header component which we just wrapped up and after this uh if else Clause all
up and after this uh if else Clause all the way here at the bottom let's add
the way here at the bottom let's add list options component of course if you
list options component of course if you save you're going to get an error
save you're going to get an error because it doesn't exist but let's
because it doesn't exist but let's already prepare and let's give it a data
already prepare and let's give it a data of data like that and let's also give it
of data like that and let's also give it on ADD card and for now now let's just
on ADD card and for now now let's just make this an empty function we don't
make this an empty function we don't have the form to add a card so there's
have the form to add a card so there's nothing we can do here but I just want
nothing we can do here but I just want to make sure we don't forget about this
to make sure we don't forget about this functionality and now inside of this
functionality and now inside of this underscore components folder create a
underscore components folder create a new file list options. DSX go ahead and
new file list options. DSX go ahead and Mark this as use client and Export con
Mark this as use client and Export con list
list options and return a div list
options and return a div list options and now let's quickly create an
options and now let's quickly create an in interace list options
in interace list options props and let's define the data to be
props and let's define the data to be list from Prisma client and on ADD card
list from Prisma client and on ADD card let's just make it an empty void and now
let's just make it an empty void and now in here let's go ahead and assign those
in here let's go ahead and assign those props to list options props and let's
props to list options props and let's destructure the data and on ADD
destructure the data and on ADD car great let's go back to the list
car great let's go back to the list header and now we can import this list
header and now we can import this list options from /list options like this and
options from /list options like this and you should no longer be having any
you should no longer be having any errors and now in your card on the right
errors and now in your card on the right side you should have the text which says
side you should have the text which says list options and we're now going to
list options and we're now going to modify it so it's actually well a drop-
modify it so it's actually well a drop- down menu so first thing I want to do is
down menu so first thing I want to do is I want to go ahead and import everything
I want to go ahead and import everything we need from add/ components UI
we need from add/ components UI popover so let's go ahead and import the
popover so let's go ahead and import the popover itself let's import the popover
popover itself let's import the popover content the popover trigger and the pop
content the popover trigger and the pop over close and now inside of here I want
over close and now inside of here I want to replace the div with the popover
to replace the div with the popover component and inside I want to add a
component and inside I want to add a popover
popover trigger and I want to render a button
trigger and I want to render a button component from at/ components uui button
component from at/ components uui button so just make sure you add this as well
so just make sure you add this as well and let's mark this as child and let's
and let's mark this as child and let's give this button a class name of
give this button a class name of h-o w-o and padding two and the variant
h-o w-o and padding two and the variant of
of ghost and now let's go ahead and let's
ghost and now let's go ahead and let's add more horizontal from Lucid react so
add more horizontal from Lucid react so just make sure you have this import from
just make sure you have this import from Lucid react more horizontal and let's
Lucid react more horizontal and let's give it a class name of h-4 and
give it a class name of h-4 and W-4 and now you should have a nice
W-4 and now you should have a nice little button here which will open the
little button here which will open the pop hour content which we are going to
pop hour content which we are going to create in a minute so below the popover
create in a minute so below the popover trigger let's add the popover content
trigger let's add the popover content here and let's give it a class name of
here and let's give it a class name of px0 padding top three and padding bottom
px0 padding top three and padding bottom of three let's give it a side of bottom
of three let's give it a side of bottom and a line of
and a line of start great inside of the popover
start great inside of the popover content now let's add a div which is
content now let's add a div which is going to say list actions and let's give
going to say list actions and let's give this div a class name of text small font
this div a class name of text small font medium text Center text neutral 600 and
medium text Center text neutral 600 and P adding bottom of four and now you can
P adding bottom of four and now you can already try and click on one of these
already try and click on one of these and you should have a nice list actions
and you should have a nice list actions popover so now we're going to go ahead
popover so now we're going to go ahead and actually add uh some items here so
and actually add uh some items here so before we do that let's add our popover
before we do that let's add our popover close here and let's go ahead and add a
close here and let's go ahead and add a button inside and let's render the X
button inside and let's render the X from Lucid react so make sure you add
from Lucid react so make sure you add the X here all right let's give this x a
the X here all right let's give this x a class name name of h-4 and W-4 and let's
class name name of h-4 and W-4 and let's give this button a class name of H AO
give this button a class name of H AO w-o padding to top uh sorry
w-o padding to top uh sorry absolute uh top to right to and tax-
absolute uh top to right to and tax- neutral D600 and the variant of ghost
neutral D600 and the variant of ghost and let's also just go ahead and give
and let's also just go ahead and give this a prop as child to this popover
this a prop as child to this popover close and now our popover should be
close and now our popover should be having a close button here which works
having a close button here which works great and now we're ready to actually
great and now we're ready to actually render some actions Inside So Below this
render some actions Inside So Below this popover close add a button component and
popover close add a button component and write add card like this and now let's
write add card like this and now let's go ahead and give this an onclick on ADD
go ahead and give this an onclick on ADD card which is as you remember an empty
card which is as you remember an empty function for now and let's write a class
function for now and let's write a class name here rounded dnone w- full h- aouto
name here rounded dnone w- full h- aouto padding to px5 justify start font normal
padding to px5 justify start font normal text
text small and give it a
small and give it a variant of
variant of ghost and let's check that out now and
ghost and let's check that out now and there we go we have a nice button which
there we go we have a nice button which says add a card perfect so uh besides
says add a card perfect so uh besides add a card we're going to have two more
add a card we're going to have two more options one is to copy a list and the
options one is to copy a list and the other one is to delete a list so just
other one is to delete a list so just below this button let's actually open a
below this button let's actually open a little form element here so there are
little form element here so there are obviously multiple ways we can do this
obviously multiple ways we can do this we can directly add a button right and
we can directly add a button right and then we can use our use action hook and
then we can use our use action hook and just call the execute on click or we can
just call the execute on click or we can use the form right so these server
use the form right so these server actions have enabled us to do a lot more
actions have enabled us to do a lot more so let's go ahead and give this an input
inside which is a hidden has a name of ID an ID uh of ID and value of data ID
ID an ID uh of ID and value of data ID and let's copy and paste it and let's
and let's copy and paste it and let's replace this one to have a name of board
replace this one to have a name of board ID and this one board ID as well and
ID and this one board ID as well and value is going to be data. board ID and
value is going to be data. board ID and inside we're just going to render the
inside we're just going to render the form
form submit uh which we imported from at/
submit uh which we imported from at/ components form form submit
components form form submit and inside Let's uh render copy list
and inside Let's uh render copy list like this and now what we have to do is
like this and now what we have to do is give this form submit a variant of ghost
give this form submit a variant of ghost and we can just actually copy the class
and we can just actually copy the class name from the this button which has add
name from the this button which has add a card so you can just paste it here and
a card so you can just paste it here and let's try that out now so when I open
let's try that out now so when I open there we go we now have a copy list
there we go we now have a copy list which is going to trigger a form if you
which is going to trigger a form if you click now it's just going to refresh the
click now it's just going to refresh the entire page but later we're going to add
entire page but later we're going to add an action to the form which is going to
an action to the form which is going to properly execute what it needs to
properly execute what it needs to execute and since we are using the form
execute and since we are using the form submit option this is automatically
submit option this is automatically going to be disabled while it is loading
going to be disabled while it is loading perfect and now let's go ahead and let's
perfect and now let's go ahead and let's add a little separator after we end this
add a little separator after we end this form so separator which you can import
form so separator which you can import from s/ components UI separator so just
from s/ components UI separator so just confirm that this is your import and not
confirm that this is your import and not the radic one and let's go ahead and
the radic one and let's go ahead and just copy and paste the entire form like
just copy and paste the entire form like this and just paste it below the
this and just paste it below the separator and we can keep the ID and the
separator and we can keep the ID and the board ID and this one is not going to
board ID and this one is not going to say copy list but delete this
say copy list but delete this list like that and let's try it out now
list like that and let's try it out now and when we click here as you can see we
and when we click here as you can see we have options to add a card copy the list
have options to add a card copy the list or delete the list what we have to do
or delete the list what we have to do now is create these two actions copy
now is create these two actions copy list and delete this
list and delete this list so the easier one one of the two
list so the easier one one of the two options to do first is the delete list
options to do first is the delete list especially because we already have a
especially because we already have a function to delete the board so let's go
function to delete the board so let's go ahead and let's copy the server action
ahead and let's copy the server action for deleting the board so we have delete
for deleting the board so we have delete board go ahead and copy it and let's
board go ahead and copy it and let's rename it to delete list and first let's
rename it to delete list and first let's go inside of the schema to Define
go inside of the schema to Define everything we need for it so we need the
everything we need for it so we need the ID but we also need the board ID so make
ID but we also need the board ID so make sure you add that and let's rename this
sure you add that and let's rename this to delete list now let's go inside of
to delete list now let's go inside of the types and let's change this to
the types and let's change this to delete list as well and let's change
delete list as well and let's change this to be Z infer type of delete list
this to be Z infer type of delete list and the same is for our return type we
and the same is for our return type we are expecting a list and not a board now
are expecting a list and not a board now we can go back inside of index.ts for
we can go back inside of index.ts for the delete list and first let's fix this
the delete list and first let's fix this wrong import so now it is delete list
wrong import so now it is delete list and we can copy that and paste it all
and we can copy that and paste it all the way here at the bottom and change
the way here at the bottom and change this from delete board to be um delete
this from delete board to be um delete list great and now let's go ahead and
list great and now let's go ahead and modify this so the this can of course
modify this so the this can of course stay the same and from the data we now
stay the same and from the data we now uh D structure the ID and board ID and
uh D structure the ID and board ID and we are working with list not board so
we are working with list not board so let's define let list here and let's
let's define let list here and let's assign it here and let's go ahead and
assign it here and let's go ahead and attempt to delete a list so db. list.
attempt to delete a list so db. list. delete where we have the matching ID we
delete where we have the matching ID we have the matching board ID and also the
have the matching board ID and also the board relation has the matching
board relation has the matching organization ID as our current user so
organization ID as our current user so no file play can be done here we can
no file play can be done here we can leave the fail to delete here and let's
leave the fail to delete here and let's change this to revalidate the board and
change this to revalidate the board and the board ID and instead of redirecting
the board ID and instead of redirecting let's return data
let's return data list just like that perfect and we can
list just like that perfect and we can remove the redirect import from here now
remove the redirect import from here now that we have this finished let's go
that we have this finished let's go ahead and let's actually use it inside
ahead and let's actually use it inside of our list options so let's find list
of our list options so let's find list options as you can see it's located in
options as you can see it's located in platform dashboard board board ID
platform dashboard board board ID underscore components we have list
underscore components we have list options here and let's go ahead and
options here and let's go ahead and let's import everything we need here so
let's import everything we need here so we need the
we need the import use action from hooks use action
import use action from hooks use action and and we also need the delete list
and and we also need the delete list from actions delete list so make sure
from actions delete list so make sure you have use action and delete list here
you have use action and delete list here now let's go ahead outside of the return
now let's go ahead outside of the return function and let's call the execute and
function and let's call the execute and let's remap it to execute delete because
let's remap it to execute delete because we're going to have multiple executes
we're going to have multiple executes here in this component let's call the
here in this component let's call the use action
use action here let's pass in the delete list and
here let's pass in the delete list and let's add some callbacks so on success
let's add some callbacks so on success or going to get the data and let's go
or going to get the data and let's go ahead and call Toast which we can import
ahead and call Toast which we can import from soner so make sure you have the
from soner so make sure you have the toast which I will add here there we go
toast which I will add here there we go so let's call toast. success and let's
so let's call toast. success and let's go ahead and open back tis and write
go ahead and open back tis and write list data. title
list data. title deleted like that and let's add on error
deleted like that and let's add on error so in case we have a server error here
so in case we have a server error here let's do toast. error and log that error
let's do toast. error and log that error perfect and now let's go ahead and let's
perfect and now let's go ahead and let's create a function const on delete to get
create a function const on delete to get the form data which is a type of form
the form data which is a type of form data let's get the ID to be form data
data let's get the ID to be form data get ID as string copy and paste this and
get ID as string copy and paste this and replace it with board ID for the second
replace it with board ID for the second one and then execute
one and then execute delete ID and board ID
delete ID and board ID great and now let's go ahead and let's
great and now let's go ahead and let's add this on theit as the form action for
add this on theit as the form action for our uh last form here which says delete
our uh last form here which says delete this list so in this form give it an
this list so in this form give it an option action on delete and let's now
option action on delete and let's now try it out here so I'm going to refresh
try it out here so I'm going to refresh I'll pick this last list I will click
I'll pick this last list I will click delete this list there we go it's
delete this list there we go it's pending and it says list has been
pending and it says list has been deleted perfect one thing that can
deleted perfect one thing that can happen though is that this popover stays
happen though is that this popover stays open after we delete something I've seen
open after we delete something I've seen it happen a couple of times and it will
it happen a couple of times and it will certainly be visible once we do the copy
certainly be visible once we do the copy list so I already want to prepare for
list so I already want to prepare for that by creating a ref for the close
that by creating a ref for the close button and then programmatically calling
button and then programmatically calling it so let's go ahead and add const close
it so let's go ahead and add const close ref to be use ref from react element ref
ref to be use ref from react element ref again from react button and give it
again from react button and give it default value of null so let me just
default value of null so let me just confirm that you have this import use
confirm that you have this import use ref and element ref from react and I
ref and element ref from react and I will import them right here so element
will import them right here so element ref and use ra now let's go ahead and
ref and use ra now let's go ahead and assign the close ref here to the popover
assign the close ref here to the popover close component where we uh provided the
close component where we uh provided the as child prop and we have the button
as child prop and we have the button with the X icon inside and let's go
with the X icon inside and let's go ahead and give it a ref close ref and
ahead and give it a ref close ref and then inside of our on success right here
then inside of our on success right here we can call the close
we can call the close ref current question mark click and
ref current question mark click and right now I think nothing should change
right now I think nothing should change everything should still work as we
everything should still work as we expect but we just ensure that that
expect but we just ensure that that popover always gets closed great what I
popover always gets closed great what I want to do next is create the server
want to do next is create the server action for copying a specific
action for copying a specific list so we can just copy this delete
list so we can just copy this delete list server action which we just created
list server action which we just created so let's go into actions delete list and
so let's go into actions delete list and let's copy it and let's rename it to
let's copy it and let's rename it to copy list like that let's first resolve
copy list like that let's first resolve the schema which we need so that
the schema which we need so that actually stays exactly the same but
actually stays exactly the same but let's just rename it to copy list like
let's just rename it to copy list like that now let's go inside of our types
that now let's go inside of our types and let's fix the schema so it is copy
and let's fix the schema so it is copy list and let's properly assign it for
list and let's properly assign it for our input type here the return type is
our input type here the return type is correct because we are going to be
correct because we are going to be expecting a
expecting a list and now let's go inside of the
list and now let's go inside of the index.ts here and let's change the copy
index.ts here and let's change the copy list here as well go all the way to the
list here as well go all the way to the bottom and place it here and rename the
bottom and place it here and rename the function to copy list great and now
function to copy list great and now let's go ahead and actually copy the
let's go ahead and actually copy the list so this can of course stay the same
list so this can of course stay the same we are also extracting the ID and board
we are also extracting the ID and board ID we are defining let list here and now
ID we are defining let list here and now we're just going to do things slightly
we're just going to do things slightly different in the tri block so first I
different in the tri block so first I want to find the exact list we are
want to find the exact list we are trying to copy so const list to copy is
trying to copy so const list to copy is await DB list find
await DB list find unique where ID is matching board ID is
unique where ID is matching board ID is matching and the board relation has the
matching and the board relation has the matching organization ID as our current
matching organization ID as our current user outside of the where object let's
user outside of the where object let's also add include cards true because when
also add include cards true because when we copy a list we also want to copy all
we copy a list we also want to copy all the cards right so let's go ahead and
the cards right so let's go ahead and check if there is no list to copy in
check if there is no list to copy in that case return an error list not
that case return an error list not found because we cannot proceed further
found because we cannot proceed further and then let's go ahead and let's see
and then let's go ahead and let's see which one is the last list in this board
which one is the last list in this board because when we copy a list we need to
because when we copy a list we need to give it a proper order position so let's
give it a proper order position so let's write const last list to be await DB
write const last list to be await DB list find
list find first where we have a matching board ID
first where we have a matching board ID order by
order by the order property in descending mode
the order property in descending mode and let's just select the order that is
and let's just select the order that is all we need and make sure you put order
all we need and make sure you put order true great and now let's write con new
true great and now let's write con new order to be last list question mark last
order to be last list question mark last list. order + one or just one and now we
list. order + one or just one and now we can finally uh create a new list so list
can finally uh create a new list so list is equal to await DB list create
is equal to await DB list create data board ID is list to copy. board ID
data board ID is list to copy. board ID title is going to be open backck the
title is going to be open backck the original title from list to copy. title
original title from list to copy. title but we're going to append a little copy
but we're going to append a little copy uh annotation so we know it's copied and
uh annotation so we know it's copied and order is going to be the New Order and
order is going to be the New Order and then we're going to also create all of
then we're going to also create all of the cards so let's go ahead and open the
the cards so let's go ahead and open the cards object create many here and pass
cards object create many here and pass in data to be list to copy do cards. map
in data to be list to copy do cards. map get the individual card from here open
get the individual card from here open an immediate object and return the title
an immediate object and return the title to be card. tile let's also add a
to be card. tile let's also add a description to be card.
description to be card. description and
description and order to be card. order great and as you
order to be card. order great and as you can see the object fully matches what we
can see the object fully matches what we expect and let's also return include
expect and let's also return include cards true at the end great and now
cards true at the end great and now let's go ahead and inside of this
let's go ahead and inside of this instead of fail to delete we're going to
instead of fail to delete we're going to say failed to
say failed to copy perfect and the revalidate path is
copy perfect and the revalidate path is correct and the return data is correct
correct and the return data is correct as well great now let's go back inside
as well great now let's go back inside of our list options component where we
of our list options component where we just added the executed Elite and we can
just added the executed Elite and we can just copy and paste the entire thing
just copy and paste the entire thing here and this time let's rename the
here and this time let's rename the execute to be execute copy and let's use
execute to be execute copy and let's use the copy list from at/ actions copy list
the copy list from at/ actions copy list which we just created so I'm just going
which we just created so I'm just going to move it here alongside the leete list
to move it here alongside the leete list so make sure you have the copy list
so make sure you have the copy list imported and now let's go ahead and
imported and now let's go ahead and let's write list data. title copied and
let's write list data. title copied and we can leave the close ref to be closed
we can leave the close ref to be closed on error should yield on error that is
on error should yield on error that is all right and now as we did with on
all right and now as we did with on delete let's copy and paste it here and
delete let's copy and paste it here and let's just call on copy this time we
let's just call on copy this time we also extract the ID and board ID and
also extract the ID and board ID and let's just call the proper execution so
let's just call the proper execution so execute
execute copy great and now let's go ahead and
copy great and now let's go ahead and assign this on copy to this first form
assign this on copy to this first form where we have the copy list text so give
where we have the copy list text so give it an action on copy perfect and now
it an action on copy perfect and now let's try this out so I'm going to
let's try this out so I'm going to refresh my page I will create an
refresh my page I will create an original list here and then I'm going to
original list here and then I'm going to go ahead and click copy list and let's
go ahead and click copy list and let's see if that is working and it is working
see if that is working and it is working perfect exactly what we wanted great
perfect exactly what we wanted great great job what we're going to do next is
great job what we're going to do next is we're going to create a form to create
we're going to create a form to create cards or tasks in an individual list and
cards or tasks in an individual list and then we're finally going to go and learn
then we're finally going to go and learn how to reorder these using drag and drop
how to reorder these using drag and drop so now let's go ahead and let's create
so now let's go ahead and let's create the functionality to create cards inside
the functionality to create cards inside of lists so for that I want to go back
of lists so for that I want to go back inside of the list item component so
inside of the list item component so inside of this underscore components in
inside of this underscore components in the board ID folder find the list item
the board ID folder find the list item and inside of here first thing I want to
and inside of here first thing I want to do is I want to define the is editing
do is I want to define the is editing and set is editing to be from use state
and set is editing to be from use state with a default value of false and make
with a default value of false and make sure you import use state from react
sure you import use state from react then let's go ahead uh and let's add con
then let's go ahead uh and let's add con text area form sorry ref to be use ref
text area form sorry ref to be use ref from react as well as
from react as well as element ref from react and give it a
element ref from react and give it a type of text area and let's give it a
type of text area and let's give it a default value of null so just make sure
default value of null so just make sure you have element ref and use ref
you have element ref and use ref imported and let me just separate those
imported and let me just separate those two like that and now let's create our
two like that and now let's create our functions so cons disable editing is
functions so cons disable editing is very simply going to turn the set is
very simply going to turn the set is editing to
editing to false and now let's go ahead and Define
false and now let's go ahead and Define con enable editing which is going to be
con enable editing which is going to be set is editing true and also set time
set is editing true and also set time out here and now let's just focus on the
out here and now let's just focus on the text area ref a text area ref current
text area ref a text area ref current Focus like this
Focus like this great and now inside of our list header
great and now inside of our list header we can now add uh on ADD card here to be
we can now add uh on ADD card here to be enable editing because remember we have
enable editing because remember we have a another action inside of our list
a another action inside of our list options which is used to add a new card
options which is used to add a new card so that's why we are defining this
so that's why we are defining this States inside of the list item rather
States inside of the list item rather than a new component which we are going
than a new component which we are going to create here which will be called card
to create here which will be called card form so now we have to add this props
form so now we have to add this props inside of the list header so let's go
inside of the list header so let's go ahead and Define on ADD card to be a
ahead and Define on ADD card to be a void like this and then let's use that
void like this and then let's use that prop let's also extract it here from the
prop let's also extract it here from the props alongside data and let's find the
props alongside data and let's find the list options where we if I remember
list options where we if I remember correctly passed an empty function for
correctly passed an empty function for add a card well now it's going to be add
add a card well now it's going to be add a card there we go so we can go back
a card there we go so we can go back inside of the list item now and uh now
inside of the list item now and uh now outside uh
outside uh sorry inside of this div just below the
sorry inside of this div just below the list header let's add a card form which
list header let's add a card form which we don't yet have so when we save we're
we don't yet have so when we save we're going to get an error and let's already
going to get an error and let's already prepare here by giving it a ref of text
prepare here by giving it a ref of text area ref let's give it is editing prop
area ref let's give it is editing prop to be is editing enable
to be is editing enable editing to be enable editing disable
editing to be enable editing disable editing B disable
editing B disable [Music]
[Music] editing and let's also pass in list ID
editing and let's also pass in list ID which is going to be data. ID like that
which is going to be data. ID like that great and now let's go inside of here
great and now let's go inside of here and create a new component card- form.
and create a new component card- form. ppsx let's mark it as use client and
ppsx let's mark it as use client and Export con card
Export con card form and return a div card
form and return a div card form and now let's just quickly create
form and now let's just quickly create the interface card form props which is
the interface card form props which is going to accept the list ID which is a
going to accept the list ID which is a string enable editing which is just a
string enable editing which is just a void disable editing which is exactly
void disable editing which is exactly the same and is editing which is a
the same and is editing which is a Boolean and let's go ahead and assign
Boolean and let's go ahead and assign those props so card form
those props so card form props and let's extract list ID enable
props and let's extract list ID enable editing disable editing and is editing
editing disable editing and is editing here let's go back inside of the list
here let's go back inside of the list item and now we can import card form
item and now we can import card form from do/ card form the same way we did
from do/ card form the same way we did with a list header here and you should
with a list header here and you should no longer be having any errors and here
no longer be having any errors and here underneath the header you should now
underneath the header you should now have a text which says card form so
have a text which says card form so let's go ahead and modify the card form
let's go ahead and modify the card form so it actually uh looks like something
so it actually uh looks like something and one thing that we forgot to do is
and one thing that we forgot to do is the ref forwarding here so let's go
the ref forwarding here so let's go ahead and change this a bit so I'm going
ahead and change this a bit so I'm going to remove this prop assignment here and
to remove this prop assignment here and instead I'm going to mark this entire
instead I'm going to mark this entire thing as forward ref which we can import
thing as forward ref which we can import from react and let's open uh another
from react and let's open uh another brackets here and let's end the brackets
brackets here and let's end the brackets here at the bottom and then let's open
here at the bottom and then let's open pointy brackets for the forward ref and
pointy brackets for the forward ref and let's give it html text area element and
let's give it html text area element and comma let's give them card form props
comma let's give them card form props and now let's go ahead and inside of
and now let's go ahead and inside of this well to get rid of these errors
this well to get rid of these errors first thing let's do is card form.
first thing let's do is card form. display name to be card form like that
display name to be card form like that and now alongside this uh props here add
and now alongside this uh props here add a comma and exclude the ref itself like
a comma and exclude the ref itself like that perfect so now what I want to do is
that perfect so now what I want to do is render uh well a button here to say add
render uh well a button here to say add a card so let's give this div a class
a card so let's give this div a class name of padding top and
name of padding top and px2 let's open a button from at/
px2 let's open a button from at/ components UI buttons so let me just
components UI buttons so let me just separate those Imports here inside of
separate those Imports here inside of the button let's write add a card let's
the button let's write add a card let's go ahead and close the button and let's
go ahead and close the button and let's also add a plus icon from Lucid react so
also add a plus icon from Lucid react so make sure you have the plus icon from
make sure you have the plus icon from Lucid react let's give this plus icon a
Lucid react let's give this plus icon a class name of h-4 W-4 and margin right
class name of h-4 W-4 and margin right off two let's give this button on click
off two let's give this button on click to be enable
to be enable editing let's give it a class name of h-
editing let's give it a class name of h- AO px2 py 2 1.5 W full justify start
AO px2 py 2 1.5 W full justify start text muted foreground and text small
text muted foreground and text small let's give it a size of small and a
let's give it a size of small and a variant of ghost like that and there we
variant of ghost like that and there we go now we have a nice little button here
go now we have a nice little button here which says add a card and it actually
which says add a card and it actually switches the state from is editing false
switches the state from is editing false to is editing true but right now we
to is editing true but right now we haven't created any functionality when
haven't created any functionality when that happens
that happens so what I want to do next is I want to
so what I want to do next is I want to create a reusable component called uh
create a reusable component called uh form text area so let's go ahead and do
form text area so let's go ahead and do that I'm going to go inside I'm going to
that I'm going to go inside I'm going to close everything in fact and I will go
close everything in fact and I will go inside of my components folder let me
inside of my components folder let me just find it here and create a new file
just find it here and create a new file actually let's go inside of the form and
actually let's go inside of the form and create form D text area. vsx let's mark
create form D text area. vsx let's mark this as use client and let's go ahead
this as use client and let's go ahead and write an interface form text area
and write an interface form text area props which is an ID an optional label a
props which is an ID an optional label a placeholder a required
placeholder a required prop a disabled prop errors which is
prop a disabled prop errors which is going to be a record string string array
going to be a record string string array or
or undefined let's go ahead and give it a
undefined let's go ahead and give it a class name which is a string on blur
class name which is a string on blur which is going to be an optional void
which is going to be an optional void and on click which is going to be an
and on click which is going to be an optional void as well and on key down
optional void as well and on key down which is going to be an optional
which is going to be an optional keyboard event handler from react so
keyboard event handler from react so make sure you import that which is going
make sure you import that which is going to work on html text area element or
to work on html text area element or undefined and let's give it a default
undefined and let's give it a default value which is an optional string so a
value which is an optional string so a lot of props for this one and now let's
lot of props for this one and now let's go ahead and write export const form
go ahead and write export const form text area to be
text area to be forward ref which you can import from
forward ref which you can import from react as well and let's immediately add
react as well and let's immediately add form
form text uh and let's actually call it form
text uh and let's actually call it form text area with a lowercase a like this
text area with a lowercase a like this so form text area. display name is going
so form text area. display name is going to be form text
to be form text area now let's go inside the forward ref
area now let's go inside the forward ref let's go ahead and open parenthesis
let's go ahead and open parenthesis and let's go here and let's give it an
and let's go here and let's give it an html text area element and form text
html text area element and form text area props like that so basically uh
area props like that so basically uh this two let me go ahead and try and
this two let me go ahead and try and collapse those or perhaps I can do it so
collapse those or perhaps I can do it so basically these two elements html text
basically these two elements html text area element and form text area props
area element and form text area props which we created now go ahead and open
which we created now go ahead and open parenthesis again and immediately let's
parenthesis again and immediately let's go ahead and destructure the ID label
go ahead and destructure the ID label placeholder require IR D disabled errors
placeholder require IR D disabled errors on blur onclick and let's not make a
on blur onclick and let's not make a typo in the on blur
typo in the on blur here besides on click we're going to
here besides on click we're going to have on key
have on key down class name and default value now
down class name and default value now outside of this props let's extract the
outside of this props let's extract the ref and then uh before we end this
ref and then uh before we end this parentheses open uh an arrow function
parentheses open uh an arrow function like this and go ahead and return a div
like this and go ahead and return a div and that should get rid of your errors
and that should get rid of your errors so make sure you do forward ref assign
so make sure you do forward ref assign the proper type to it open parenthesis
the proper type to it open parenthesis and then open another parenthesis which
and then open another parenthesis which represent the props and inside you can
represent the props and inside you can immediately destructure the ID label
immediately destructure the ID label placeholder all of that stuff and after
placeholder all of that stuff and after that add a comma and add a ref and then
that add a comma and add a ref and then open an arrow function and just make
open an arrow function and just make sure you have an additional parenthesis
sure you have an additional parenthesis here to close the forward ref which we
here to close the forward ref which we opened great let's give this div a class
opened great let's give this div a class name of space y 2 and w- full and now
name of space y 2 and w- full and now let's add a new div with a class name
let's add a new div with a class name space y1 and w- full as well and inside
space y1 and w- full as well and inside if we have the label we're going to go
if we have the label we're going to go ahead and render the label from do/ UI
ahead and render the label from do/ UI label and I'm just going to replace that
label and I'm just going to replace that to use slash
to use slash components and let me separate it all
components and let me separate it all right let's go ahead and close the label
right let's go ahead and close the label here and inside let's run under the
here and inside let's run under the label like this and let's also give this
label like this and let's also give this an else to be null let's go ahead and
an else to be null let's go ahead and give this lab label an HTML 4 element to
give this lab label an HTML 4 element to be ID plus name to be text extra small
be ID plus name to be text extra small font semi bold and text neutral 700 like
font semi bold and text neutral 700 like that and now let's go ahead inside of
that and now let's go ahead inside of the shet CN sorry inside of the terminal
the shet CN sorry inside of the terminal and I'm just going to go ahead and add
and I'm just going to go ahead and add npx Shad CN UI my latest add text area
npx Shad CN UI my latest add text area like this let's wait a second for this
like this let's wait a second for this to install Perfect and now uh just below
to install Perfect and now uh just below this label here go ahead and enter the
this label here go ahead and enter the text area from do/ UI text area which I
text area from do/ UI text area which I again I'm going to replace to use add
again I'm going to replace to use add slash components like this and inside of
slash components like this and inside of this text area let's go ahead and assign
this text area let's go ahead and assign all the props we need so on key down is
all the props we need so on key down is going to be Onkey down on blur is going
going to be Onkey down on blur is going to on blur on click is going to be on
to on blur on click is going to be on click ref is going to be ref which we
click ref is going to be ref which we forward then we're going to have
forward then we're going to have required prop which is required we're
required prop which is required we're going to have the placeholder which is
going to have the placeholder which is placeholder we're going to have name
placeholder we're going to have name which is ID we're going to have ID which
which is ID we're going to have ID which is ID we're going to have the disabled
is ID we're going to have the disabled prop which is going to be the disabled
prop which is going to be the disabled prop and we're going to have class name
prop and we're going to have class name which is going to be dynamic so go ahead
which is going to be dynamic so go ahead and import CN from /li
and import CN from /li SLU and inside of here go ahead and open
SLU and inside of here go ahead and open parenthesis and first let's uh write the
parenthesis and first let's uh write the default classes which are going to be
default classes which are going to be resize dnone Focus - visible uh ring- Z
resize dnone Focus - visible uh ring- Z Focus D
Focus D visible ring- offset d0 again then we're
visible ring- offset d0 again then we're going to have a separate ring- Z Focus
going to have a separate ring- Z Focus ring- Z and outline dnone and we're
ring- Z and outline dnone and we're going to have Shadow small like that and
going to have Shadow small like that and now add a comma and paste in the class
now add a comma and paste in the class Name Pro like that and let's also add
Name Pro like that and let's also add area- described by to be uh open back
area- described by to be uh open back the
the id- error so we have the accessibility
id- error so we have the accessibility setup and let's give it a default value
setup and let's give it a default value of default value perfect and outside of
of default value perfect and outside of this div go ahead and render the form
this div go ahead and render the form errors which you can import from do/
errors which you can import from do/ form errors so we already have that
form errors so we already have that component like this and let's go ahead
component like this and let's go ahead and let's pass in the ID to be ID and
and let's pass in the ID to be ID and errors to be
errors to be errors great and now one more thing that
errors great and now one more thing that I want to do is I want to use the use
I want to do is I want to use the use form status hook so uh it's disabled
form status hook so uh it's disabled while the form is pending so extract
while the form is pending so extract pending from use form status which you
pending from use form status which you can import uh from react Dom let me just
can import uh from react Dom let me just move it here to the top like that and
move it here to the top like that and then let's use this pending here uh in
then let's use this pending here uh in the disabled prop so we're going to use
the disabled prop so we're going to use either pending or disabled like that
either pending or disabled like that perfect so now that we have our for text
perfect so now that we have our for text area set up we can head back inside of
area set up we can head back inside of our card form component which we started
our card form component which we started creating where we added this add a card
creating where we added this add a card button and a plus sign so that's again
button and a plus sign so that's again located in uh app platform dashboard
located in uh app platform dashboard board board ID components card Dash form
board board ID components card Dash form right here so now let's go ahead and
right here so now let's go ahead and modify this so that
modify this so that if is editing in that case go ahead and
if is editing in that case go ahead and return a form
return a form element which is going to
element which is going to have a class name of
have a class name of m-1 m-1 pa- 0.5 px-1 and space
m-1 m-1 pa- 0.5 px-1 and space y4 and inside render the form text area
y4 and inside render the form text area from at/ components form uh text area
from at/ components form uh text area which we just created and let's go ahead
which we just created and let's go ahead and let's give it an ID of title let's
and let's give it an ID of title let's give it uh an on key down for now to
give it uh an on key down for now to just be an empty Arrow function let's
just be an empty Arrow function let's give it a ref of ref which we are
give it a ref of ref which we are forwarding uh let's give it a
forwarding uh let's give it a placeholder of enter a title for this
placeholder of enter a title for this card and let's go ahead and let's give
card and let's go ahead and let's give it a class name uh well we actually I
it a class name uh well we actually I don't think we need to do uh this class
don't think we need to do uh this class name instead let's just pass in the
name instead let's just pass in the errors to be
errors to be errors uh and we don't have the errors
errors uh and we don't have the errors yet so how about we don't give them just
yet so how about we don't give them just yet and looks like we have a missing
yet and looks like we have a missing onclick property so let's go back inside
onclick property so let's go back inside the form text area and let's just ensure
the form text area and let's just ensure that on click is optional so just add a
that on click is optional so just add a little question mark here my apologies
little question mark here my apologies and there we go now you should have uh
and there we go now you should have uh no errors here perfect uh below this
no errors here perfect uh below this form text area add an input which is
form text area add an input which is going to be
going to be hidden the ID of this input is going to
hidden the ID of this input is going to be a list ID the name is going to be
be a list ID the name is going to be list ID and the value is going to be
list ID and the value is going to be list ID which we have in the props Here
list ID which we have in the props Here and Now what I want to do is go below
and Now what I want to do is go below this input and open up a div with the
this input and open up a div with the class name of flex items Center and GAP
class name of flex items Center and GAP X1 and let's go ahead and add a form
X1 and let's go ahead and add a form submit
submit option which you can import from s/
option which you can import from s/ components form form submit as I did
components form form submit as I did right here and let's write add card and
right here and let's write add card and below that go ahead and add a button
below that go ahead and add a button component which we already have imported
component which we already have imported if I'm not mistaken that's right we do
if I'm not mistaken that's right we do all right uh and inside of this button
all right uh and inside of this button component render the X from Lucid react
component render the X from Lucid react so just make sure you have X alongside
so just make sure you have X alongside Plus in your Lucid react Imports here
Plus in your Lucid react Imports here and give it a class name of h-5 and
and give it a class name of h-5 and w-5 and go ahead and give this button an
w-5 and go ahead and give this button an on click disable editing a size of small
on click disable editing a size of small and a
and a variant
variant of ghost like this and let's see what we
of ghost like this and let's see what we come up with here so now when I click on
come up with here so now when I click on add a card there we go I should have a
add a card there we go I should have a beautiful text area here uh which says
beautiful text area here uh which says enter a title for this card and we can
enter a title for this card and we can close it from here as well and when we
close it from here as well and when we open it is immediately focused perfect
open it is immediately focused perfect so what we have to do now is we have to
so what we have to do now is we have to create an action which is going to
create an action which is going to create the
create the card so as always let's call copy an
card so as always let's call copy an existing server action for this so for
existing server action for this so for example let's do create list and let's
example let's do create list and let's paste it here let's rename it to create
paste it here let's rename it to create card let's go inside of the schema first
card let's go inside of the schema first and let's rename the schema to create a
and let's rename the schema to create a card like this and what we
card like this and what we expect uh inside of the create card here
expect uh inside of the create card here is the title which can well the rules
is the title which can well the rules can stay the same we expect the board ID
can stay the same we expect the board ID but we also expect the list ID like this
but we also expect the list ID like this now let's go inside of types. DS and
now let's go inside of types. DS and let's change this to create card from
let's change this to create card from the schema and let's assign that to our
the schema and let's assign that to our input type and our return type should
input type and our return type should work with card now so replace that as
work with card now so replace that as well and then go inside of index. vs and
well and then go inside of index. vs and let's do the same thing so create card
let's do the same thing so create card from the schema and we scroll all the
from the schema and we scroll all the way down create save Action Now uses
way down create save Action Now uses create card and the function name should
create card and the function name should be create create card great and now
be create create card great and now inside of this Handler let's see what we
inside of this Handler let's see what we have to do so the authorization can stay
have to do so the authorization can stay the same from the data we D structure
the same from the data we D structure the title the board ID but also the list
the title the board ID but also the list ID and we are no longer working with the
ID and we are no longer working with the list instead we are working with the
list instead we are working with the card so inside of the tri block let's go
card so inside of the tri block let's go ahead and remove everything here so make
ahead and remove everything here so make sure the tri block is empty and let's go
sure the tri block is empty and let's go ahead and first let's attempt to fetch
ahead and first let's attempt to fetch the list Where We Are trying to insert
the list Where We Are trying to insert this card so const list is a wait DB
this card so const list is a wait DB list find
list find unique where ID is list ID and board
unique where ID is list ID and board uses the same organization ID as the
uses the same organization ID as the currently logged in user if there is no
currently logged in user if there is no list return an error which says list not
list return an error which says list not found great so that is our protection
found great so that is our protection and now let's go ahead and let's get the
and now let's go ahead and let's get the last card inside of this list so we know
last card inside of this list so we know what order to give this new card even if
what order to give this new card even if it is the first one we need to do that
it is the first one we need to do that check so con last Card wait DB card find
check so con last Card wait DB card find first where we have a matching list ID
first where we have a matching list ID and order by order
and order by order descending and select order
descending and select order true and now let's define the new order
true and now let's define the new order to be last card if we have that it's
to be last card if we have that it's going to be last card order plus one
going to be last card order plus one otherwise it's the first card so we can
otherwise it's the first card so we can just put the order to be one and now
just put the order to be one and now finally let's define that the card is
finally let's define that the card is await DB hard create let's give it the
await DB hard create let's give it the data in which where we will Define the
data in which where we will Define the title The List ID and the order to be
title The List ID and the order to be New
Order uh perfect so that is it it the error can stay the same the the
error can stay the same the the revalidation is correct and the return
revalidation is correct and the return Returns the card and not the list and
Returns the card and not the list and that should be it perfect we can now go
that should be it perfect we can now go back inside of our card form component
back inside of our card form component so inside of the board idore components
so inside of the board idore components we have card form it's alongside all of
we have card form it's alongside all of those list header list items all of
those list header list items all of those stuff we just worked on so we just
those stuff we just worked on so we just finish this button which triggers the is
finish this button which triggers the is editing and changes it to a form so now
editing and changes it to a form so now let's go ahead and let's import
let's go ahead and let's import everything we need uh to trigger the
everything we need uh to trigger the action so we need the use action Hook
action so we need the use action Hook from hooks use action and we need the
from hooks use action and we need the create card from actions create card uh
create card from actions create card uh and let's see if that's going to be it I
and let's see if that's going to be it I would also like to
would also like to import use ref from react uh as well as
import use ref from react uh as well as element
element ref and let's also import
ref and let's also import keyboard uh event Handler like that uh
keyboard uh event Handler like that uh great and oh I already started this
great and oh I already started this react so yeah let's not have two Imports
react so yeah let's not have two Imports let's just move the forward drive to be
let's just move the forward drive to be here so no need to have uh multiple
here so no need to have uh multiple Imports of exactly the same thing in
Imports of exactly the same thing in fact I think it will throw uh an error
fact I think it will throw uh an error uh okay and I think this should be uh
uh okay and I think this should be uh enough for now now let's go ahead and
enough for now now let's go ahead and just also add uh use params from next SL
just also add uh use params from next SL navigation like that and let's go ahead
navigation like that and let's go ahead here inside of the card form and let's
here inside of the card form and let's add const params to be used params which
add const params to be used params which we imported const form ref to be use ref
we imported const form ref to be use ref element
element ref of form by default it's going to be
ref of form by default it's going to be null and let's write
null and let's write const use action uh create C
const use action uh create C hard and let's go ahead and execute uh
hard and let's go ahead and execute uh and extract execute and fi
and extract execute and fi errors great and now let's write const
errors great and now let's write const on key down which is event keyboard
on key down which is event keyboard event if event. key is escape in that
event if event. key is escape in that case we're going to disable
editing all right so we have that done now let's go ahead uh and let's add the
now let's go ahead uh and let's add the use on click outside which you can
use on click outside which you can import from use hooks DS I'm just going
import from use hooks DS I'm just going to move it right here so import use on
to move it right here so import use on click outside uh and also import use
click outside uh and also import use event listener here so we're just doing
event listener here so we're just doing some uh user experience features now
some uh user experience features now right when they click Escape when they
right when they click Escape when they blur uh when they click enter when they
blur uh when they click enter when they click Escape all of those things so
click Escape all of those things so inside of the use on click outside we're
inside of the use on click outside we're going to pass in the form ref and we're
going to pass in the form ref and we're going to trigger disable
going to trigger disable editing then we're going to write use
editing then we're going to write use event listener
event listener here which we imported to listen to key
here which we imported to listen to key down listener and then we're going to
down listener and then we're going to trigger the on key
trigger the on key down and now let's create const on text
down and now let's create const on text area key down to be a type of keyboard
area key down to be a type of keyboard event handler which handles the HTML
event handler which handles the HTML area element HTML text area element it
area element HTML text area element it accepts the event
accepts the event prop and open an arrow function and
prop and open an arrow function and let's check if event. key is enter and
let's check if event. key is enter and if we did not press the shift key at the
if we did not press the shift key at the same time in that case prevent default
same time in that case prevent default and instead let's go ahead and
and instead let's go ahead and request uh request submit for the form
request uh request submit for the form so we're going to consider if an user
so we're going to consider if an user presses enter inside of the text area
presses enter inside of the text area which by default just opens a new line
which by default just opens a new line we're going to submit this form because
we're going to submit this form because that's what Trello does except if they
that's what Trello does except if they are holding shift in that case they're
are holding shift in that case they're going to go into a new line and finally
going to go into a new line and finally let's create const on submit to be form
let's create const on submit to be form data which is a type of form
data which is a type of form data let's extract the title to be form
data let's extract the title to be form data get title as string let's copy this
data get title as string let's copy this two times let's replace the second one
two times let's replace the second one to be list ID and the third one to be
to be list ID and the third one to be board ID let's go ahead and let's call
board ID let's go ahead and let's call the execute prop and let's give it a
the execute prop and let's give it a title list ID and board
title list ID and board ID perfect let's go ahead and let's uh
ID perfect let's go ahead and let's uh assign the ref to this form to be form
assign the ref to this form to be form ref and let's give it an
ref and let's give it an action on submit great and now uh we
action on submit great and now uh we also have to pass this on text area key
also have to pass this on text area key down inside of the form text area so go
down inside of the form text area so go inside of the if is editing Clause find
inside of the if is editing Clause find the form text area and replace this
the form text area and replace this empty Arrow function with on text area
empty Arrow function with on text area key down and let's also give it errors
key down and let's also give it errors to be field errors which we now have
to be field errors which we now have because we added uh this function uh
because we added uh this function uh execute and field errors and now let's
execute and field errors and now let's go ahead and let's import toast from
go ahead and let's import toast from sonor package and let's go ahead and add
sonor package and let's go ahead and add some callbacks so on
success is going to get the data and we can call post. success open btics card
can call post. success open btics card open annotations data.
open annotations data. title created like this and let's do
title created like this and let's do form ref. current reset so it clears the
form ref. current reset so it clears the form and let's add on error here to get
form and let's add on error here to get the error and call Toast error with the
the error and call Toast error with the contents of the error perfect so I think
contents of the error perfect so I think this should be it now since we don't
this should be it now since we don't have any UI to display the cards I'm
have any UI to display the cards I'm going to go ahead and run npx Prisma
going to go ahead and run npx Prisma Studio here so it opens on Local Host uh
Studio here so it opens on Local Host uh 5555 so let me just prepare that here
5555 so let me just prepare that here I'm going to refresh this I'm going to
I'm going to refresh this I'm going to paste the Prisma Studio here and let me
paste the Prisma Studio here and let me focus on the card collection here right
focus on the card collection here right now as you can see there are no rows
now as you can see there are no rows inside of my table so I have opened the
inside of my table so I have opened the card rows and let's go ahead and try
card rows and let's go ahead and try this out so I'm going to go and write
this out so I'm going to go and write test I'm going to click add and it looks
test I'm going to click add and it looks like something is not working as it
like something is not working as it should let's go ahead and uh thebug y so
should let's go ahead and uh thebug y so if I let's see already when I open there
if I let's see already when I open there seems to be an
seems to be an error all right let's see why it is not
error all right let's see why it is not working so first thing I'm going to do
working so first thing I'm going to do is I'm going to confirm that this
is I'm going to confirm that this onsubmit is actually uh having all the
onsubmit is actually uh having all the values so T title list ID and and board
values so T title list ID and and board ID so let's try that out now so I'm
ID so let's try that out now so I'm going to open the console here and let
going to open the console here and let me just focus uh all
me just focus uh all right let's write test here and when we
right let's write test here and when we click test oh the board ID is null oh oh
click test oh the board ID is null oh oh all right so we forgot to pass the board
all right so we forgot to pass the board ID that's why the function uh is not
ID that's why the function uh is not working
working here uh so let's go
here uh so let's go ahead and and let's pass the board ID so
ahead and and let's pass the board ID so we have to do that I believe I believe
we have to do that I believe I believe we can actually do it inside of the uh
we can actually do it inside of the uh onsubmit here because yeah we can use
onsubmit here because yeah we can use the params here so we added this use
the params here so we added this use params but we're not using it but we
params but we're not using it but we needed to extract the current board ID
needed to extract the current board ID so we can either add another hidden
so we can either add another hidden input field or we can use the pam. board
input field or we can use the pam. board ID here directly and also write it as
ID here directly and also write it as string like like this and let's see if
string like like this and let's see if that's going to improve this so when I
that's going to improve this so when I write test here and click add a card
write test here and click add a card there we go card test has been created
there we go card test has been created let's try one more time and there we go
let's try one more time and there we go the form is cleared and I have a
the form is cleared and I have a notification that card has been created
notification that card has been created let's check my Prisma studio now and I
let's check my Prisma studio now and I should have two records and I do and
should have two records and I do and they have the correct order they have no
they have the correct order they have no description and they have a relation
description and they have a relation with the list great great job so you
with the list great great job so you finished uh creating cards what we have
finished uh creating cards what we have to do next is we have to iterate over
to do next is we have to iterate over the cards and actually show them in
the cards and actually show them in individual
individual list so rendering the cards is actually
list so rendering the cards is actually quite simple because we already have
quite simple because we already have them because if you remember our page.
them because if you remember our page. CSX we also include the cards when we
CSX we also include the cards when we load the lists so inside of the list
load the lists so inside of the list container here when we pass the data to
container here when we pass the data to the list item we already have access to
the list item we already have access to all the cards so let's go just above
all the cards so let's go just above this card form here and let's add an
this card form here and let's add an ordered list element so this is the list
ordered list element so this is the list item component make sure you are in list
item component make sure you are in list item here and just below the list header
item here and just below the list header and above the card form go ahead and add
and above the card form go ahead and add an ordered list and let's give it a
an ordered list and let's give it a class name which is going to be dynamic
class name which is going to be dynamic so import CN from /lib
so import CN from /lib SLS and let's give it some default
SLS and let's give it some default classes which are going to be mx1 px1 py
classes which are going to be mx1 px1 py 0.5 flex flex-all and GAP Y 2 and then
0.5 flex flex-all and GAP Y 2 and then add a comma and let's add a dynamic
add a comma and let's add a dynamic class which is going to be if data.
class which is going to be if data. cards. length is zero in that case we're
cards. length is zero in that case we're going to have is larger than zero in
going to have is larger than zero in that case we're going to add a little
that case we're going to add a little space from the top otherwise we're not
space from the top otherwise we're not going to add any space from the top
going to add any space from the top perfect and now let's go into inside of
perfect and now let's go into inside of this ordered list and let's do data.
this ordered list and let's do data. cards. map let's get the individual card
cards. map let's get the individual card and the index and let's go ahead and
and the index and let's go ahead and render the card item component which we
render the card item component which we don't yet have but we're going to create
don't yet have but we're going to create it in a second let's pass in the index
it in a second let's pass in the index which is an index key which is card. ID
which is an index key which is card. ID and data which is the card itself
and data which is the card itself perfect so inside of this very same uh
perfect so inside of this very same uh so let me close everything about this
so let me close everything about this all right so inside of this folder where
all right so inside of this folder where we have list header list item basically
we have list header list item basically everything and our newly created card
everything and our newly created card form now let's create card item. vsx
form now let's create card item. vsx let's go ahead and let's mark this as
let's go ahead and let's mark this as use client and let's export const card
use client and let's export const card item
item here and return a div card
here and return a div card item and now let's just quickly create
item and now let's just quickly create an interface card item props to accept
an interface card item props to accept the the data which is the card from
the the data which is the card from Prisma client and an index which is a
Prisma client and an index which is a number and now we can go ahead and D
number and now we can go ahead and D structure this props and add them
structure this props and add them here so data and index like that and
here so data and index like that and let's go back inside of our list item
let's go back inside of our list item and now we can import the card item from
and now we can import the card item from SLC card item as I did right here and
SLC card item as I did right here and when you save you should be having no
when you save you should be having no errors and now let's just go ahead and
errors and now let's just go ahead and style this so instead of rendering card
style this so instead of rendering card item we're going to render data.
item we're going to render data. title and let's go ahead and give this a
title and let's go ahead and give this a class name of truncate border 2 border
class name of truncate border 2 border transparent hover border
transparent hover border black py2 PX three text small BG
black py2 PX three text small BG white rounded MD and Shadow SM and if I
white rounded MD and Shadow SM and if I take a look now there we go we we have
take a look now there we go we we have our cards here perfect and if I create a
our cards here perfect and if I create a new one here there we go it is
new one here there we go it is immediately visible in my list perfect
immediately visible in my list perfect what we have to do next is finally
what we have to do next is finally Implement our drag and drop so we're
Implement our drag and drop so we're going to do that before we do opening a
going to do that before we do opening a card uh in a model right we're going to
card uh in a model right we're going to do that later and just one more thing
do that later and just one more thing that I want to do before we wrap up is
that I want to do before we wrap up is give this div a roll of a
button like that so now when you try you can see that you have uh a like a
can see that you have uh a like a clickable element right and you're going
clickable element right and you're going to be able to drag it or you're going to
to be able to drag it or you're going to be able to click on it right now none of
be able to click on it right now none of those works but what's cool is that uh
those works but what's cool is that uh we revalidate this path and we do
we revalidate this path and we do router. refresh so the server components
router. refresh so the server components are all up to date and we have the
are all up to date and we have the newest data here perfect great great
newest data here perfect great great job so now that we have all the elements
job so now that we have all the elements we need we are ready to install uh the
we need we are ready to install uh the drag and drop package and implement the
drag and drop package and implement the functionality so head inside of your
functionality so head inside of your terminal and let's go ahead and run mpm
terminal and let's go ahead and run mpm install at
install at hello Das panga SL DND D so this is the
hello Das panga SL DND D so this is the same as react beautiful DND D but the
same as react beautiful DND D but the old one has been deprecated and this one
old one has been deprecated and this one is up to date great so make sure you
is up to date great so make sure you have your project running and let's go
have your project running and let's go ahead inside of our list container
ahead inside of our list container component which is in the platform
component which is in the platform dashboard board components and in here
dashboard board components and in here we have the list container and let's go
we have the list container and let's go ahead and let's import everything you
ahead and let's import everything you need from the DND so I'm going to import
need from the DND so I'm going to import drag drop context and
drag drop context and dropable from at hello Pangia DND D like
dropable from at hello Pangia DND D like that that and now let's go ahead and
that that and now let's go ahead and let's drag this entire thing inside of
let's drag this entire thing inside of drag drop
drag drop context like that and let's go ahead and
context like that and let's go ahead and let's add an on drag end which for now
let's add an on drag end which for now is just going to be an empty Arrow
is just going to be an empty Arrow function now let's go ahead and wrap
function now let's go ahead and wrap this ordered list element inside of a
this ordered list element inside of a dropable component so dropable here WP
dropable component so dropable here WP the wrap the entire ordered list inside
the wrap the entire ordered list inside of that and go ahead and give it a
of that and go ahead and give it a droppable ID of lists and give it a type
droppable ID of lists and give it a type of list and direction of
horizontal and now go inside and the structure the provided prop and wrap the
structure the provided prop and wrap the ordered list again inside of this new
ordered list again inside of this new element and now that we have the
element and now that we have the provided prop we can go ahead and pass
provided prop we can go ahead and pass it to the ordered list here so let's go
it to the ordered list here so let's go ahead above the class name and pass in
ahead above the class name and pass in provided. dropable props so spread them
provided. dropable props so spread them like I did and give it a ref of
like I did and give it a ref of provided do inner ref like this and then
provided do inner ref like this and then go ahead above the list form and render
go ahead above the list form and render provided.
provided. placeholder like that now that we
placeholder like that now that we implemented this drag and drop context
implemented this drag and drop context and droppable in the list container we
and droppable in the list container we have to do the same thing for the list
have to do the same thing for the list item so let's go inside of this
item so let's go inside of this component list item and let's import
component list item and let's import everything we need from drag and drop so
everything we need from drag and drop so that's going to be draggable and
that's going to be draggable and droppable from DND like this and let's
droppable from DND like this and let's go ahead and let's wrap the entire
go ahead and let's wrap the entire component inside of draggable so
component inside of draggable so draggable
draggable all the way to the end of the list
element and let's give it a dragable ID of data. ID and an index of index and
of data. ID and an index of index and now we have to destructure the provided
now we have to destructure the provided as we did so open curly brackets open
as we did so open curly brackets open normal brackets get the provided and
normal brackets get the provided and wrap the entire list inside of that
wrap the entire list inside of that element go ahead and indent this a bit
element go ahead and indent this a bit and now let's go ahead and pass
and now let's go ahead and pass everything we need to the list item here
everything we need to the list item here so go ahead and spread
so go ahead and spread provided. dragable props and ref is
provided. dragable props and ref is going to be
going to be provided do inner ref like this and now
provided do inner ref like this and now inside of this div which wraps the list
inside of this div which wraps the list header let's go ahead and give it
header let's go ahead and give it another
another property which is going to be a spread
property which is going to be a spread of provided. drag handle props so when
of provided. drag handle props so when the user grabs on this div they're going
the user grabs on this div they're going to activate the drag and drop perfect
to activate the drag and drop perfect and now what we have to do is go below
and now what we have to do is go below this list header here and wrap the
this list header here and wrap the ordered list of Cards into
ordered list of Cards into dropable so just go ahead and wrap
dropable so just go ahead and wrap that let's go ahead and give dropable
that let's go ahead and give dropable the dropable ID of data. ID and let's
the dropable ID of data. ID and let's give it a type of card and then we have
give it a type of card and then we have to destructure the element
to destructure the element provided and wrap the entire ordered
provided and wrap the entire ordered list inside of that now that we have the
list inside of that now that we have the provided right here let's give this
provided right here let's give this ordered list a ref of provided do inner
ordered list a ref of provided do inner ref let's just not misspell inner ref
ref let's just not misspell inner ref and let's go ahead and spread provided
and let's go ahead and spread provided do droppable
do droppable props and now just at the end of this
props and now just at the end of this ordered list go ahead and render
ordered list go ahead and render provided. placeholder like that and I
provided. placeholder like that and I think that already we should be able to
think that already we should be able to at least visually see the drag and drop
at least visually see the drag and drop as you can see we can drag and drop of
as you can see we can drag and drop of course it's not working because when it
course it's not working because when it finishes the UND drag end we don't
finishes the UND drag end we don't actually do
actually do anything and one more thing that we have
anything and one more thing that we have to do to enable the cards from being
to do to enable the cards from being draggable and droppable because right
draggable and droppable because right now if I grab on the card card you can
now if I grab on the card card you can see that the entire list gets dragged we
see that the entire list gets dragged we have to go inside of the individual card
have to go inside of the individual card item and let's go ahead and again import
item and let's go ahead and again import dragable from DND D so import dragable
dragable from DND D so import dragable from D and D like this and we have to
from D and D like this and we have to wrap the entire div inside of
wrap the entire div inside of dragable so let's go ahead and do that
dragable so let's go ahead and do that let's indent the element let's give the
let's indent the element let's give the draggable uh ID
draggable uh ID to be data. ID and an index of index and
to be data. ID and an index of index and then let's go ahead and destructure the
then let's go ahead and destructure the provided element and wrap the entire div
provided element and wrap the entire div inside of that and then we can give this
inside of that and then we can give this div a spread of
div a spread of provided draggable props and provided
provided draggable props and provided drag handle props like that and let's
drag handle props like that and let's also give it a ref of provided
also give it a ref of provided inner W like this and now I believe we
inner W like this and now I believe we should be able to drag the cards as well
should be able to drag the cards as well there we go we can now drag cards or we
there we go we can now drag cards or we can grab on the uh list and then we can
can grab on the uh list and then we can grab list but again it's not working
grab list but again it's not working right now you can see that it resets its
right now you can see that it resets its position after we let go that's because
position after we let go that's because we haven't done anything inside of our
we haven't done anything inside of our on drag end but as you can see still it
on drag end but as you can see still it works if I try to rename something I can
works if I try to rename something I can click on the option here so all of those
click on the option here so all of those things are working perfect and now let's
things are working perfect and now let's actually implement the functionality
actually implement the functionality first to locally uh or optimistically
first to locally uh or optimistically mutate uh the state of orders and then
mutate uh the state of orders and then we're going to do the request on the
we're going to do the request on the back
back end so we have to go back inside of the
end so we have to go back inside of the list container so let me just expand
list container so let me just expand this and let's go inside of list
this and let's go inside of list container. TSX inside of this board ID
container. TSX inside of this board ID components here and now we have to
components here and now we have to modify this on drag function before we
modify this on drag function before we do that I just want to create a generic
do that I just want to create a generic reorder function which we are going to
reorder function which we are going to reuse so let's do
reuse so let's do function reorder which is accepting a
function reorder which is accepting a generic
generic T has a list as a prop which is an array
T has a list as a prop which is an array of the generic it has a start index
of the generic it has a start index which is a number and an end index which
which is a number and an end index which is a number as well let's go ahead and
is a number as well let's go ahead and open this function let's create the
open this function let's create the result which is going to be an array
result which is going to be an array from the list we provide and then let's
from the list we provide and then let's destructure the removed from result
destructure the removed from result splice start index and
splice start index and one and now let's do result. splice and
one and now let's do result. splice and index zero and removed and returned
index zero and removed and returned result so this is going to reorder them
result so this is going to reorder them by indexes and now we have to create a
by indexes and now we have to create a very big function on drag and so we're
very big function on drag and so we're going to add comments inside so it's
going to add comments inside so it's easier to follow exactly what it does so
easier to follow exactly what it does so let's go ahead and let's create const on
let's go ahead and let's create const on drag end
drag end function and let's replace that with
function and let's replace that with this empty function here so now on drag
this empty function here so now on drag end in inside of our drag drop context
end in inside of our drag drop context should be handling this so first let's
should be handling this so first let's go ahead and extract everything we need
go ahead and extract everything we need from our prop result which let's give it
from our prop result which let's give it a type of
a type of any let's go ahead and let's destructure
any let's go ahead and let's destructure the the
the the destination let's destructure the source
destination let's destructure the source and the type first we're going to check
and the type first we're going to check if we can even do this so if it happens
if we can even do this so if it happens that we don't have destination we can
that we don't have destination we can already return the function there is
already return the function there is nothing else we can do with this now
nothing else we can do with this now let's go ahead and let's write a comment
let's go ahead and let's write a comment dropped in the same position or maybe if
dropped in the same position or maybe if dropped in the same position so if a
dropped in the same position so if a user picks up a card or a list and drops
user picks up a card or a list and drops them in the same position we don't have
them in the same position we don't have to do anything and we can check that
to do anything and we can check that with an if clause which is going to say
with an if clause which is going to say if destination do dropable ID is equal
if destination do dropable ID is equal to
Source droppable ID and if
droppable ID and if destination. index is equal to source.
destination. index is equal to source. index in that case we can break the
index in that case we can break the function meaning that there's nothing we
function meaning that there's nothing we have to do if the user picks up an item
have to do if the user picks up an item and drops them in the very same place
and drops them in the very same place and now we're going to create uh what
and now we're going to create uh what happens if user is moving a list so
happens if user is moving a list so let's write a comment user moves a list
let's write a comment user moves a list let's write if type is list so we know
let's write if type is list so we know that because we gave this dropable a
that because we gave this dropable a type of list the same way that in list
type of list the same way that in list item we have a dropable element
item we have a dropable element right here with a type of card so we
right here with a type of card so we know what exactly we're dropping so go
know what exactly we're dropping so go back inside of the list container and if
back inside of the list container and if the user moves a list we're going to do
the user moves a list we're going to do the following first we're going to
the following first we're going to reorder the items using our generic
reorder the items using our generic reorder function inside we're going to
reorder function inside we're going to pass the ordered
pass the ordered data which we have from right here so
data which we have from right here so that's our initial
that's our initial data we're passing the ordered data then
data we're passing the ordered data then the source. index and then destination.
the source. index and then destination. index and then let's do map let's get
index and then let's do map let's get the item and the index and let's go
the item and the index and let's go ahead and return an immediate object
ahead and return an immediate object where we spread the existing item but
where we spread the existing item but change the order to the New Order using
change the order to the New Order using the index and all we have to do then is
the index and all we have to do then is set ordered data to be the items like
set ordered data to be the items like that and I'm going to write a to-do
that and I'm going to write a to-do comment uh well uh trigger server action
comment uh well uh trigger server action so we have to create a server action
so we have to create a server action which is also going to update this on
which is also going to update this on the back end for now we're only going to
the back end for now we're only going to do the optimistic mutation here on the
do the optimistic mutation here on the front end so I think we can already try
front end so I think we can already try it out if I drag and drop the original
it out if I drag and drop the original there we go the original now stays
there we go the original now stays exactly where we place it of course if
exactly where we place it of course if we refresh it goes back to its original
we refresh it goes back to its original position that's because we don't do the
position that's because we don't do the server action for this yet great so now
server action for this yet great so now we have to do the same thing but for the
we have to do the same thing but for the card right so let's go outside of this
card right so let's go outside of this if here and write a comment uh user
if here and write a comment uh user moves a
moves a card in that case let's do if type
card in that case let's do if type is
is card and let me try and expand the code
card and let me try and expand the code even more all right so if type is card
even more all right so if type is card let's go ahead and just create new
let's go ahead and just create new ordered data to be a copy of the
ordered data to be a copy of the existing order data so we can mutate on
existing order data so we can mutate on this and let's go ahead and first get
this and let's go ahead and first get the source and destination list so cons
the source and destination list so cons Source list is new data New Order data.
Source list is new data New Order data. find and we search through all the lists
find and we search through all the lists we get the list ID and we compare it to
we get the list ID and we compare it to Source droppable ID so let me zoom out
Source droppable ID so let me zoom out so you can see that in one line we get
so you can see that in one line we get the source list sorry we attempt to get
the source list sorry we attempt to get the store list Source list using the New
the store list Source list using the New Order data and we use the find method to
Order data and we use the find method to get the list and we compare the existing
get the list and we compare the existing list ID with Source droppable ID and now
list ID with Source droppable ID and now let's go ahead and create the
let's go ahead and create the destination list so con Des list is
destination list so con Des list is going to be again new ordered data. find
going to be again new ordered data. find we get the individual list and we
we get the individual list and we compare the list ID this time with
compare the list ID this time with destination. droppable ID like that so
destination. droppable ID like that so let me zoom out so you can see both of
let me zoom out so you can see both of those so Source list is comparing the
those so Source list is comparing the list ID from the New Order data with the
list ID from the New Order data with the drop with the source and the destination
drop with the source and the destination list is with destination droppable ID
list is with destination droppable ID all of those props you can find you can
all of those props you can find you can see that we have the droppable ID lists
see that we have the droppable ID lists here and the list item we have uh
here and the list item we have uh different dropable IDs to be the data.
different dropable IDs to be the data. ID all right and now let's go ahead and
ID all right and now let's go ahead and let's check in case that Source list or
let's check in case that Source list or destination list wasn't found inside of
destination list wasn't found inside of this iteration using find so if we don't
this iteration using find so if we don't have the source list or if we don't have
have the source list or if we don't have the destination list
the destination list list there's nothing we can do so let's
list there's nothing we can do so let's just break the function and now we have
just break the function and now we have to handle the cases if uh the cards
to handle the cases if uh the cards property doesn't exist on a specific
property doesn't exist on a specific list right if the list is empty it can
list right if the list is empty it can happen that it doesn't have cards so
happen that it doesn't have cards so check if cards exists on the source list
check if cards exists on the source list we're going to do if there is no Source
we're going to do if there is no Source list. cards in that case let's go ahead
list. cards in that case let's go ahead and manually add them so Source list.
and manually add them so Source list. cards is an empty array now let's go
cards is an empty array now let's go ahead and check if cards exist on the
ahead and check if cards exist on the destination list so in here
destination list so in here if exclamation point destination list.
if exclamation point destination list. cards manually add cards to be an empty
cards manually add cards to be an empty array like that great so now we can work
array like that great so now we can work with that and now we have to handle the
with that and now we have to handle the case if the user moves the card within
case if the user moves the card within the same list list and if they move the
the same list list and if they move the card uh in a different list because we
card uh in a different list because we have to modify their list ID in that
have to modify their list ID in that case so we can safely send that to the
case so we can safely send that to the back end so first let's do uh moving the
back end so first let's do uh moving the card in the same list like this so we
card in the same list like this so we can do that by checking if Source
can do that by checking if Source droppable ID is identical to destination
droppable ID is identical to destination droppable
droppable ID let's go ahead and let's reorder our
ID let's go ahead and let's reorder our cards using the generic reorder function
cards using the generic reorder function so const
reordered parts are reorder function and let's pass in the source list. cards
let's pass in the source list. cards source. index and
source. index and destination.
destination. index and now let's change the order of
index and now let's change the order of each card reordered cards do for each we
each card reordered cards do for each we have the car and the
have the car and the index and let's write write card. order
index and let's write write card. order to be the new
index and let's just not misspell index all right and then let's assign that to
all right and then let's assign that to the source list cards so Source list
the source list cards so Source list cards is now reordered cards with the
cards is now reordered cards with the new indexes and now let's do set ordered
new indexes and now let's do set ordered data to be the new ordered
data to be the new ordered data like that and let's go ahead and
data like that and let's go ahead and add a comment here to do and we're going
add a comment here to do and we're going to write um well trigger server action
to write um well trigger server action so we can actually save this so now if
so we can actually save this so now if I'm not mistaken we should be able to
I'm not mistaken we should be able to locally keep the state of cards there we
locally keep the state of cards there we go SDF is now on top and I can move it
go SDF is now on top and I can move it all the way to the back and it doesn't
all the way to the back and it doesn't reset one through3 is here I can move it
reset one through3 is here I can move it all the way to the back and it doesn't
all the way to the back and it doesn't reset but if I refresh of course it goes
reset but if I refresh of course it goes back because we have to do the server
back because we have to do the server action great so now we have this and now
action great so now we have this and now we have to handle the case if the user
we have to handle the case if the user moves the card to another list so let's
moves the card to another list so let's go ahead uh and add a little comment
go ahead uh and add a little comment here uh user moves the card to another
here uh user moves the card to another list and let's open an else statement
list and let's open an else statement here because this is the end of our if
here because this is the end of our if Clause as you can see right here so
Clause as you can see right here so meaning that if the dropable ID is
meaning that if the dropable ID is different from the destination's
different from the destination's dropable ID it means that we are using a
dropable ID it means that we are using a new list to drop this in so let's go
new list to drop this in so let's go ahead and let's remove card from the
ahead and let's remove card from the source list so const moved card is going
source list so const moved card is going to be Source list. cards. splice source.
to be Source list. cards. splice source. index and
index and one like that and then let's go ahead
one like that and then let's go ahead and assign the new list ID to the uh new
and assign the new list ID to the uh new card or actually moved card so we're
card or actually moved card so we're going to do that using moved card list
going to do that using moved card list ID is going to be
ID is going to be destination.
destination. dropable
dropable ID and now let's add the card to
ID and now let's add the card to the destination
the destination list so we successfully just change the
list so we successfully just change the card ID card cards list ID and now let's
card ID card cards list ID and now let's use the destination list. cards. splice
use the destination list. cards. splice so we can insert it in between the
so we can insert it in between the destination index and moved card like
destination index and moved card like that and now we have to change the order
that and now we have to change the order of the cards as well because remember
of the cards as well because remember when we drop it inside another uh list
when we drop it inside another uh list we can also reorder the entire list we
we can also reorder the entire list we can put it on top or in the middle or on
can put it on top or in the middle or on the bottom so let's write Source
the bottom so let's write Source list do cards. for each get the card the
list do cards. for each get the card the index and card. order is the
index and card. order is the index uh whoops let me just go
index uh whoops let me just go uh I somehow switched my components so
uh I somehow switched my components so back in the list container this is where
back in the list container this is where we were Source list. cards for each and
we were Source list. cards for each and apply the new index as the card order
apply the new index as the card order great and now we have to update the
great and now we have to update the order for each card in the destination
order for each card in the destination list so let's go ahead and do
list so let's go ahead and do destination list. cards for each get the
destination list. cards for each get the card and the index again and let's just
card and the index again and let's just right card. order is the new
right card. order is the new index and now we can do uh set ordered
index and now we can do uh set ordered data to be new ordered data and let's
data to be new ordered data and let's write to do trigger server action and
write to do trigger server action and now this should be locally working as
now this should be locally working as well so let's try it out if I drag one
well so let's try it out if I drag one to three in the middle of this it should
to three in the middle of this it should stay and it does if I move this one at
stay and it does if I move this one at as the first in this one it works as
as the first in this one it works as well perfect so you can try with as many
well perfect so you can try with as many combinations as you want you can try in
combinations as you want you can try in between the same list and everything
between the same list and everything seems to be working perfect so we are
seems to be working perfect so we are officially ready to now do this on the
officially ready to now do this on the back end as well because locally it
back end as well because locally it works again if you're having any
works again if you're having any problems you can always visit uh my
problems you can always visit uh my original GitHub and look at the exact
original GitHub and look at the exact code from here perfect so now we have to
code from here perfect so now we have to create two server actions one is to
create two server actions one is to update the card order and the other one
update the card order and the other one is to update the list order so let's go
is to update the list order so let's go ahead and do
ahead and do that so as always let's copy an existing
that so as always let's copy an existing server action so I'm going to close
server action so I'm going to close everything going to actions and I'm
everything going to actions and I'm going to copy uh the create
going to copy uh the create list and let's rename it to update list
list and let's rename it to update list order like this and first let's go ahead
order like this and first let's go ahead and modify the schema so let's change
and modify the schema so let's change this to update list order and it's no
this to update list order and it's no longer going to be having a title so it
longer going to be having a title so it needs a word ID and it accepts the item
needs a word ID and it accepts the item which is z. array of Z doob and inside
which is z. array of Z doob and inside we expect the ID to be z. string the
we expect the ID to be z. string the title to be z. string as well we expect
title to be z. string as well we expect the order to be z. number the created ad
the order to be z. number the created ad to be z.e and the updated ad to be z.
to be z.e and the updated ad to be z. date as well
all right now let's go inside of types and let's use the update list order
and let's use the update list order schema as our input type and instead of
schema as our input type and instead of a single list we're going to be
a single list we're going to be expecting an array of lists at the end
expecting an array of lists at the end so just make sure you add an array here
so just make sure you add an array here and now let's go inside of the index
and now let's go inside of the index right here and let's modify the update
right here and let's modify the update list order here as well let's move it
list order here as well let's move it all the way down to update list list
all the way down to update list list order and let's change this to update
order and let's change this to update list order like that perfect now let's
list order like that perfect now let's go inside of the actual Handler the
go inside of the actual Handler the authentication can stay the same and
authentication can stay the same and let's go ahead and modify this so
let's go ahead and modify this so instead of title we are now uh
instead of title we are now uh extracting the items and it's not going
extracting the items and it's not going to be list it's going to be lists like
to be list it's going to be lists like this let's go ahead and clear everything
this let's go ahead and clear everything inside of our Tri function so make sure
inside of our Tri function so make sure the tri function is empty and let's go
the tri function is empty and let's go ahead and create a transaction so const
ahead and create a transaction so const transaction is items. map get the
transaction is items. map get the individual list from here and let's
individual list from here and let's do db. update sorry db. list. update
do db. update sorry db. list. update where ID is list. ID and board matches
where ID is list. ID and board matches the organization ID and let's pass in
the organization ID and let's pass in the data to be order list.
the data to be order list. order like that so I just want to bring
order like that so I just want to bring to your attention that I did not add any
to your attention that I did not add any curly brackets here so this goes
curly brackets here so this goes directly after uh the arrow function
directly after uh the arrow function like this I just moved it to a new line
like this I just moved it to a new line so I directly returned this function so
so I directly returned this function so make sure you don't put any uh curly
make sure you don't put any uh curly brackets otherwise I think you have to
brackets otherwise I think you have to return this function uh great and now
return this function uh great and now let's go ahead and write lists to be
let's go ahead and write lists to be await
await db. dollar
db. dollar sign TR
sign TR transaction and pass in the transaction
transaction and pass in the transaction inside like that and in the error let's
inside like that and in the error let's go ahead and let's say fail to
go ahead and let's say fail to reorder and then we're going to go ahead
reorder and then we're going to go ahead and return the lists there we go and now
and return the lists there we go and now we can use this inside of our list
we can use this inside of our list container so let's go back inside of the
container so let's go back inside of the list container which where we just wrote
list container which where we just wrote this big on drag end function and let's
this big on drag end function and let's go ahead and import
go ahead and import the use action from hooks use action and
the use action from hooks use action and let's also import update list order from
let's also import update list order from actions update list order and now inside
actions update list order and now inside of here I'm going to go ahead and I'm
of here I'm going to go ahead and I'm going to get that uh well action so
going to get that uh well action so const use action update list
const use action update list order and let's get the execute so
order and let's get the execute so execute is going to be execute
execute is going to be execute update list order so we know exactly
update list order so we know exactly which one of those it is let's go ahead
which one of those it is let's go ahead and open the callbacks and on
and open the callbacks and on success let's we are not going to work
success let's we are not going to work with data instead we're just going to
with data instead we're just going to call the toast from sonor so just make
call the toast from sonor so just make sure you add uh this
sure you add uh this import and we're going to say toast.
import and we're going to say toast. success list
reordered and if we have an error
error we're going to go ahead and log that
we're going to go ahead and log that error in a toast and now we can use the
error in a toast and now we can use the execute list order once we finish our
execute list order once we finish our list reordering so go inside of UND drag
list reordering so go inside of UND drag end and in here we have a clause if user
end and in here we have a clause if user moves a list and in here we have set
moves a list and in here we have set order data for this new items and then
order data for this new items and then we have a comment to do trigger server
we have a comment to do trigger server action let's remove the comment let's
action let's remove the comment let's call execute update list order let's
call execute update list order let's pass in the items and the board ID like
pass in the items and the board ID like this and
this and now we should be able to uh move the
now we should be able to uh move the original in the place of this one and it
original in the place of this one and it should be saved in the database so if I
should be saved in the database so if I drag and drop it like
drag and drop it like this there we go list has been reordered
this there we go list has been reordered and if I refresh the original stays in
and if I refresh the original stays in this place where we move it let's try
this place where we move it let's try that one more time list has been
that one more time list has been reordered if I refresh the original
reordered if I refresh the original stays in the same place perfect so now
stays in the same place perfect so now we have to create the same thing but for
we have to create the same thing but for updating the card
updating the card order so let's go ahead and copy the
order so let's go ahead and copy the existing update list order because it's
existing update list order because it's the closest to what we need so I'm just
the closest to what we need so I'm just going to close this stuff I'm just going
going to close this stuff I'm just going to leave the list container open so we
to leave the list container open so we can quickly move back to it let's find
can quickly move back to it let's find the update list order copy it and paste
the update list order copy it and paste it inside the actions and rename it to
it inside the actions and rename it to update card
update card order and inside focus on the schema
order and inside focus on the schema first like this so let's rename this to
first like this so let's rename this to update card order like that and we
update card order like that and we expect the board I we don't expect the
expect the board I we don't expect the board ID we expect the list ID like this
board ID we expect the list ID like this and then let's go ahead and confirm the
and then let's go ahead and confirm the object so we need an ID title order but
object so we need an ID title order but we also need the list ID which is z.
we also need the list ID which is z. string
string like this so let's go ahead now inside
like this so let's go ahead now inside of types and let's import update card
of types and let's import update card order and paste it here and we are no
order and paste it here and we are no longer working with lists instead of
longer working with lists instead of we're working with cards so import the
we're working with cards so import the card and place it here so it's going to
card and place it here so it's going to be an array of cards in our return type
be an array of cards in our return type and now let's go inside of the index and
and now let's go inside of the index and let's change this schema to be import
let's change this schema to be import card order let's go all the way down and
card order let's go all the way down and change this to import card order as well
change this to import card order as well and the function should be update card
and the function should be update card order as well perfect so now let's go
order as well perfect so now let's go ahead and modify this index here so the
ahead and modify this index here so the this authorization can stay the same and
this authorization can stay the same and we're no longer extracting the board ID
we're no longer extracting the board ID instead we're extracting the list ID and
instead we're extracting the list ID and the let is going to be updated cards
the let is going to be updated cards like this so let's go ahead and let's
like this so let's go ahead and let's remove everything inside of the tri
remove everything inside of the tri block
block and let's go ahead uh and write db.
and let's go ahead uh and write db. card.
card. update where ID is card.
update where ID is card. ID oh my apologies so we have to run a
ID oh my apologies so we have to run a transaction actually so const
transaction actually so const transaction is items. map and then we
transaction is items. map and then we have the individual card and again don't
have the individual card and again don't open the curly brackets you know just
open the curly brackets you know just immediately start writing a function DB
immediately start writing a function DB card update
card update where ID is card. ID list is board
where ID is card. ID list is board organizational ID so we ensure no file
organizational ID so we ensure no file place can be done and then we pass in
place can be done and then we pass in the data which is the New Order from
the data which is the New Order from card. order and we pass in the list ID
card. order and we pass in the list ID which is card list ID like
which is card list ID like that perfect so we have our transaction
that perfect so we have our transaction and now let's go ahead and write con
and now let's go ahead and write con update updated cards to be await DB
update updated cards to be await DB sorry not const but we just modify the
sorry not const but we just modify the updated cards from here await
updated cards from here await db. open the
db. open the transaction and pass in the transaction
transaction and pass in the transaction inside great and let's go ahead okay
inside great and let's go ahead okay this can stay the same and return the
this can stay the same and return the data to be updated cards and I just
data to be updated cards and I just realized that we might actually need the
realized that we might actually need the board ID from here as well so let's go
board ID from here as well so let's go back inside of our schema here and let's
back inside of our schema here and let's request board ID to be z. string as
request board ID to be z. string as well and now let's go here inside of the
well and now let's go here inside of the index and let's destructure the board ID
index and let's destructure the board ID from it as well and let's go ahead and
from it as well and let's go ahead and yeah this can stay the same
yeah this can stay the same perfect it actually seems to me like we
perfect it actually seems to me like we don't need this list ID at all so let's
don't need this list ID at all so let's remove the list ID and let's also remove
remove the list ID and let's also remove it from the schema so we only work with
it from the schema so we only work with board ID here all right and now let's go
board ID here all right and now let's go back inside of the list container and in
back inside of the list container and in the same way we created the execute
the same way we created the execute update list order let's create the
update list order let's create the update card order so I'm going to go
update card order so I'm going to go ahead and I'm going to import update
ahead and I'm going to import update card order from update card order which
card order from update card order which we just created so let's just ensure
we just created so let's just ensure that we did that properly uh the name is
that we did that properly uh the name is update card order correct correct great
update card order correct correct great so we have the update card order and we
so we have the update card order and we can go ahead and we can copy this use
can go ahead and we can copy this use action here so let's just paste it below
action here so let's just paste it below and let's give the Alias the execute
and let's give the Alias the execute update card order and let's use the
update card order and let's use the update card order like that and let's
update card order like that and let's give the success message to be card
give the success message to be card reordered and now let's use the execute
reordered and now let's use the execute uh card order instead of the uh
uh card order instead of the uh well list Order Perfect so let's go
well list Order Perfect so let's go ahead and find where we have to do that
ahead and find where we have to do that so we handled the case if the user moves
so we handled the case if the user moves a list now we are here where the user
a list now we are here where the user moves a card so let's go ahead and find
moves a card so let's go ahead and find the first to-do here so moving the card
the first to-do here so moving the card in the same list let's go ahead and
in the same list let's go ahead and change this to do trigger server action
change this to do trigger server action to call the execute update card order
to call the execute update card order and let's pass in uh the board
and let's pass in uh the board ID do we do we have the board ID in here
ID do we do we have the board ID in here let me just confirm we have the board ID
let me just confirm we have the board ID in the props all right so we can use
in the props all right so we can use that so you can just pass in uh the
that so you can just pass in uh the board ID we have it in the props and
board ID we have it in the props and let's give it items to be reordered
let's give it items to be reordered cards and that should be working so this
cards and that should be working so this is a case if user moves the card in the
is a case if user moves the card in the same list let's see if that saves in the
same list let's see if that saves in the database now so I'm going to move 1 two
database now so I'm going to move 1 two 3 to the bottom of the list and let's
3 to the bottom of the list and let's see if we get a success message there we
see if we get a success message there we go card reordered and if I refresh 1
go card reordered and if I refresh 1 through three is still at the same place
through three is still at the same place perfect but if I try this for example
perfect but if I try this for example well there's no success message because
well there's no success message because we don't handle that case so if I
we don't handle that case so if I refresh it's going to go back to here
refresh it's going to go back to here perfect so reordering in between the
perfect so reordering in between the same list seems to be working just fine
same list seems to be working just fine now we have to handle the case if the
now we have to handle the case if the user drags it in the other one and we
user drags it in the other one and we don't have to create any new server
don't have to create any new server action for that one we can instead just
action for that one we can instead just go ahead and find the case it's right
go ahead and find the case it's right here below it so where we wrote the
here below it so where we wrote the execute update card order we have a
execute update card order we have a comment user moves the card to another
comment user moves the card to another list so scroll here and we have a to-do
list so scroll here and we have a to-do trigger the server action so let's add
trigger the server action so let's add execute update card order let's give it
execute update card order let's give it the board ID to be board ID and let's
the board ID to be board ID and let's pass in the new items to be destination
pass in the new items to be destination list
list cards like that perfect let's try right
cards like that perfect let's try right out for the Moment of Truth now I'm
out for the Moment of Truth now I'm going to drag and drop 1 two three in
going to drag and drop 1 two three in the middle of this board I should be
the middle of this board I should be having a notification any second the
having a notification any second the card is in the middle if I refresh the
card is in the middle if I refresh the card stays in the middle and let's try
card stays in the middle and let's try it with this one moving it to the top
it with this one moving it to the top let's wait there we go we have a
let's wait there we go we have a notification I refresh and it's still
notification I refresh and it's still here let's try if I can now move it in
here let's try if I can now move it in between this seems to be working as well
between this seems to be working as well I refresh and it is still here let's try
I refresh and it is still here let's try with a new card so new card let's see if
with a new card so new card let's see if it's going to be created it is right
it's going to be created it is right here I will immediately move it here and
here I will immediately move it here and that seems to be working as well I can
that seems to be working as well I can refresh let's try and rotate this items
refresh let's try and rotate this items that seems to be working as well let's
that seems to be working as well let's move the new card to the top here and
move the new card to the top here and that seems to be working perfect so I
that seems to be working perfect so I think we just did some good Q&A here we
think we just did some good Q&A here we handled all the cases perfect what we
handled all the cases perfect what we have to do now is that when we click on
have to do now is that when we click on a specific item or card it opens a model
a specific item or card it opens a model with more information about it great
with more information about it great great
great job so now that we've wrapped up our
job so now that we've wrapped up our drag and drop which is working
drag and drop which is working flawlessly let's go ahead and implement
flawlessly let's go ahead and implement the functionality that when we click on
the functionality that when we click on a card we open a model which then
a card we open a model which then displays the content of the card and
displays the content of the card and from then we can re we can rename it we
from then we can re we can rename it we can add a description and we can also do
can add a description and we can also do some actions here so let's go ahead
some actions here so let's go ahead inside of our hooks folder here and I
inside of our hooks folder here and I want to copy and paste the use mobile
want to copy and paste the use mobile sidebar and let's rename it to use card
sidebar and let's rename it to use card model so I copied the use mobile sidebar
model so I copied the use mobile sidebar and rename it to this and let's change
and rename it to this and let's change this mobile sidebar to be uh card model
this mobile sidebar to be uh card model like this so we have the card model
like this so we have the card model store we have the use card model and
store we have the use card model and card model store here in the create
card model store here in the create types and I want to modify it a bit by
types and I want to modify it a bit by adding an optional ID here and whenever
adding an optional ID here and whenever we open it we're going to pass in the ID
we open it we're going to pass in the ID of the card we want to open and then
of the card we want to open and then let's explicitly add the ID to be
let's explicitly add the ID to be undefined in the initial State and in
undefined in the initial State and in the in on open let's extract the ID and
the in on open let's extract the ID and let's go ahead and set the ID and when
let's go ahead and set the ID and when we close let's reset the ID to be
we close let's reset the ID to be undefined great and now what I want to
undefined great and now what I want to do uh is I want to actually create the
do uh is I want to actually create the card
card model in order to do that we're going to
model in order to do that we're going to need to have the dialogue components
need to have the dialogue components from shat CN so let's go ahead and run
from shat CN so let's go ahead and run npx
npx chassen UI latest ad dialogue like this
chassen UI latest ad dialogue like this and let's wait a second for all of this
and let's wait a second for all of this to install and let's do mpm run Dev
to install and let's do mpm run Dev again and just refresh the Local Host if
again and just refresh the Local Host if you Shu down your app now let's go
you Shu down your app now let's go inside of the component and create a new
inside of the component and create a new folder called models and inside let's go
folder called models and inside let's go ahead and create a new folder uh card
ahead and create a new folder uh card model and inside create an index. DSX
model and inside create an index. DSX because we're going to have multiple
because we're going to have multiple components uh inside of this models of
components uh inside of this models of course so let's go ahead and let's mark
course so let's go ahead and let's mark this as use
this as use client and let's export const card model
client and let's export const card model here and what I want to do is I want to
here and what I want to do is I want to return a dialogue from component CI
return a dialogue from component CI dialogue so just make sure you don't
dialogue so just make sure you don't accidentally use um the from the radics
accidentally use um the from the radics and then let's render the dialogue
and then let's render the dialogue content from components UI dialogue and
content from components UI dialogue and inside I'm just going to say I am a
inside I'm just going to say I am a model like this and let me just add the
model like this and let me just add the semicolons here and now let's connect it
semicolons here and now let's connect it to that use card model hook so const use
to that use card model hook so const use card model is use card model and let's
card model is use card model and let's actually change change this to be card
actually change change this to be card model and now let's go ahead uh actually
model and now let's go ahead uh actually let's not do it like this let's
let's not do it like this let's individually get each one so let's do
individually get each one so let's do the ID use card model State and we pick
the ID use card model State and we pick the state. ID let's get the is open be
the state. ID let's get the is open be use card model get the state and do
use card model get the state and do state is open and let's do cons on close
state is open and let's do cons on close to be use card model get the State and
to be use card model get the State and State on close and now we can apply
State on close and now we can apply those props to the dialogue itself so
those props to the dialogue itself so open is is open and on open change is
open is is open and on open change is going to trigger on close like
going to trigger on close like this now let's create a model provider
this now let's create a model provider so inside of the components folder
so inside of the components folder create a new folder called providers
create a new folder called providers like that and inside create a new file
like that and inside create a new file model- provider. TSX let's mark it as
model- provider. TSX let's mark it as use client and let's export const model
use client and let's export const model provider here and let's return a
provider here and let's return a fragment and inside we're going to add
fragment and inside we're going to add our card model from Models card model
our card model from Models card model like this and I'm just going to change
like this and I'm just going to change this to use
this to use components and now I want to go ahead
components and now I want to go ahead and protect it from hydration errors so
and protect it from hydration errors so let's give this a use state with the
let's give this a use state with the default value of false and let's go
default value of false and let's go ahead and extract is mounted and set is
ahead and extract is mounted and set is mounted and then in here I'm calling a
mounted and then in here I'm calling a use
use effect which is going to set the is
mounted to true and then I'm adding an if Clause that if we are not mounted we
if Clause that if we are not mounted we just return null so this ensures that
just return null so this ensures that this component is and everything inside
this component is and everything inside is only rendered on the client because
is only rendered on the client because use effect can only be rendered on the
use effect can only be rendered on the client so unless this is mounted has
client so unless this is mounted has been turned to True by the initial Mount
been turned to True by the initial Mount here uh it will not be rendered and thus
here uh it will not be rendered and thus it will not be creating any
it will not be creating any inconsistencies when it comes to server
inconsistencies when it comes to server and the client thus preventing hydration
and the client thus preventing hydration errors now let's assign this model
errors now let's assign this model provider to our platform layout so go in
provider to our platform layout so go in the app folder platform layout. DSX and
the app folder platform layout. DSX and just below the toaster go ahead and add
just below the toaster go ahead and add the
the model Provider from components providers
model Provider from components providers model provider let me just align my uh
model provider let me just align my uh Imports a bit there we go so just make
Imports a bit there we go so just make sure you have
sure you have that now we have to go back inside of
that now we have to go back inside of card item component so inside of
card item component so inside of platform dashboard board board ID
platform dashboard board board ID components you have a little card item
components you have a little card item component and let's go ahead and use uh
component and let's go ahead and use uh our card model hook so const card model
our card model hook so const card model is use card model and we can import use
is use card model and we can import use card model from at hooks use card
card model from at hooks use card model and let's go ahead and give this
model and let's go ahead and give this button and on
button and on click which is an arrow function which
click which is an arrow function which calls card model on open and passes in
calls card model on open and passes in the data ID so we know exactly uh which
the data ID so we know exactly uh which uh well card to load so when I click on
uh well card to load so when I click on one now there we go you can see how we
one now there we go you can see how we have a nice little model here perfect
have a nice little model here perfect but it seems like this back backround is
but it seems like this back backround is just a bit too light for me so what I
just a bit too light for me so what I want to do is I want to go inside of the
want to do is I want to go inside of the dialogue itself let me close everything
dialogue itself let me close everything and let's go inside of components UI
and let's go inside of components UI dialogue right
dialogue right here and I want to find the dialogue
here and I want to find the dialogue overlay right here and in here we use BG
overlay right here and in here we use BG background 80 I want to use BG black
background 80 I want to use BG black like that and I think that should look
like that and I think that should look just a a bit better when I click on
just a a bit better when I click on something there we go it has a black
something there we go it has a black background now and our model is opened
background now and our model is opened perfect and now what I want to create is
perfect and now what I want to create is a component which is going to display
a component which is going to display the title of the model of the card but
the title of the model of the card but before we can actually display any
before we can actually display any information we have to fetch the
information we have to fetch the information from somewhere up until now
information from somewhere up until now we only fetched information using the
we only fetched information using the server component and directly accessing
server component and directly accessing the database but due to the nature of
the database but due to the nature of this drag and drop component which
this drag and drop component which requires the items to be client
requires the items to be client components we're going to have to create
components we're going to have to create an API route and to call that API route
an API route and to call that API route we're going to be using react 10stack
we're going to be using react 10stack query so let's go ahead and let me just
query so let's go ahead and let me just close everything here and let's go
close everything here and let's go inside of our terminal and let's do mpm
inside of our terminal and let's do mpm install at 10stack Dash SL react D query
install at 10stack Dash SL react D query like this let's wait for a second uh for
like this let's wait for a second uh for this to install let's do mpm run Dev
this to install let's do mpm run Dev again and let's go back inside of our
again and let's go back inside of our providers in our Global components here
providers in our Global components here and create a new file query provider.
and create a new file query provider. DSX let's mark this as use client and
DSX let's mark this as use client and let's go ahead and import use state from
let's go ahead and import use state from react and let's import query
react and let's import query client and let's import query client
client and let's import query client Provider from at tack
Provider from at tack react query and then export const query
react query and then export const query provider which has children and go ahead
provider which has children and go ahead and give the children a type of react.
and give the children a type of react. react
node and inside of here let's assign the query client in a state and let's make
query client in a state and let's make it be uh the the new query client so
it be uh the the new query client so let's just the new query
let's just the new query client and then let's just return the
client and then let's just return the query
query client
client provider let's render the children
provider let's render the children inside and let's passing the client to
inside and let's passing the client to be query
be query client like
client like that now let's assign this new query
that now let's assign this new query provider back inside of our platform
provider back inside of our platform layout so inside of the app folder we
layout so inside of the app folder we have the platform and layout right here
have the platform and layout right here and let's go ahead and let's wrap
and let's go ahead and let's wrap everything inside of our your query
everything inside of our your query Provider from components providers query
Provider from components providers query provider so make sure you do it from
provider so make sure you do it from here and including the children as
here and including the children as well so uh what this this is not going
well so uh what this this is not going to turn everything inside into client
to turn everything inside into client components just because we've wrapped
components just because we've wrapped this as client because there is a way to
this as client because there is a way to render server components like a bunch of
render server components like a bunch of our children are inside of client
our children are inside of client components and you do that by passing
components and you do that by passing them as children and that is not going
them as children and that is not going to change the fact that children are
to change the fact that children are server components just in case you had
server components just in case you had that doubt so let's just refresh the
that doubt so let's just refresh the Local Host here to confirm that all of
Local Host here to confirm that all of this is still working to confirm that
this is still working to confirm that our server components are still server
our server components are still server components and as you can see they are
components and as you can see they are because this was loaded directly from
because this was loaded directly from the database perfect and now what I want
the database perfect and now what I want to do is I want to create an API route
to do is I want to create an API route to fetch a specific card so let's go
to fetch a specific card so let's go ahead in and let's go inside of the A
ahead in and let's go inside of the A app folder and create a new folder API
app folder and create a new folder API and then inside create another folder
and then inside create another folder called cards and inside of that create a
called cards and inside of that create a new folder with Dynamic ID card ID and
new folder with Dynamic ID card ID and instead of page since this is an API we
instead of page since this is an API we create route. DS so now let's go ahead
create route. DS so now let's go ahead and let's export asynchronous function
and let's export asynchronous function get let's get the request which is a
get let's get the request which is a type of request and we can destructure
type of request and we can destructure the params so let's give them a type of
the params so let's give them a type of pams and get the card ID which is a
pams and get the card ID which is a string just confirm that your card ID
string just confirm that your card ID exactly matches the folder name card ID
exactly matches the folder name card ID otherwise it's not going to be visible
otherwise it's not going to be visible here go ahead and open a try and catch
here go ahead and open a try and catch block and we can resolve uh the error
block and we can resolve uh the error first so let's just do return new next
first so let's just do return new next response from next SLS server and let's
response from next SLS server and let's pass in internal error
pass in internal error and a status of
and a status of 500 inside of the tri block first let's
500 inside of the tri block first let's attempt to get the user ID in the
attempt to get the user ID in the organization ID from the out Handler so
organization ID from the out Handler so make sure you import clerk from
make sure you import clerk from nextjs if we don't have user ID or if we
nextjs if we don't have user ID or if we don't have organization ID you can
don't have organization ID you can return new next response an authorized
return new next response an authorized with a status of
with a status of 401 and now let's let's go ahead and
401 and now let's let's go ahead and let's import our database from at/ lib
let's import our database from at/ lib DB and in here let's go and fetch the
DB and in here let's go and fetch the card so const card is await DB card find
card so const card is await DB card find unique where ID is prms card ID list has
unique where ID is prms card ID list has a board which has the matching
a board which has the matching organization ID so only members of the
organization ID so only members of the board can access this and let's include
board can access this and let's include a list but the only thing you're going
a list but the only thing you're going to need from the list where this card is
to need from the list where this card is is the title so write Select Title true
is the title so write Select Title true so no need for any uh additional
so no need for any uh additional information and now let's just write uh
information and now let's just write uh return next response. Json card great so
return next response. Json card great so now we have our API request
now we have our API request ready now let's create our reusable
ready now let's create our reusable fetcher so I'm going to close everything
fetcher so I'm going to close everything go inside of the lib folder and create a
go inside of the lib folder and create a new file fetcher. DS and in here export
new file fetcher. DS and in here export const fetcher which takes in the URL
const fetcher which takes in the URL which is a string uses the native Fetch
which is a string uses the native Fetch and passes in the URL and then
and passes in the URL and then transforms the result to Json so it's
transforms the result to Json so it's readable for us like this in one
readable for us like this in one line great and now we can go back inside
line great and now we can go back inside of our components models card model
of our components models card model right here and let's go ahead and let's
right here and let's go ahead and let's inut import the used query from tan
inut import the used query from tan stack query so import use Query from tan
stack query so import use Query from tan stack query like this and let's write
stack query like this and let's write const use Query and let's define what we
const use Query and let's define what we expect from it which is card with list
expect from it which is card with list which we have defined in our at/ types
which we have defined in our at/ types so let me show you that you should have
so let me show you that you should have card with list which is a card from
card with list which is a card from Prisma client and it it includes a list
Prisma client and it it includes a list and let's go ahead now and let's
and let's go ahead now and let's actually Define the query key to be card
actually Define the query key to be card and ID and let's do the query function
and ID and let's do the query function to be a void which calls our fetcher
to be a void which calls our fetcher from at SL lib fetcher and passes in
from at SL lib fetcher and passes in backtick SL API cards and then pass in
backtick SL API cards and then pass in the individual ID like that and then
the individual ID like that and then inside of your const you're going to
inside of your const you're going to have data which we're going to uh save
have data which we're going to uh save as card data like this perfect and now
as card data like this perfect and now let's go ahead inside of here and let's
let's go ahead inside of here and let's render data sorry card data question
render data sorry card data question mark title like this and let's try it
mark title like this and let's try it out so I'm going to refresh this and
out so I'm going to refresh this and when I click on new card there we go it
when I click on new card there we go it says new card if I click on one two 3 uh
says new card if I click on one two 3 uh it shows one two 3 and you can debug
it shows one two 3 and you can debug this this if something's not working uh
this this if something's not working uh by going inside of your network tab here
by going inside of your network tab here and let's go
and let's go to SL cards is all let's try
to SL cards is all let's try again all right and you just type in
again all right and you just type in card here and let's click on a card and
card here and let's click on a card and there we go you can see the exact ID
there we go you can see the exact ID that I'm passing and the exact result
that I'm passing and the exact result that I received back perfect so now we
that I received back perfect so now we can go ahead and we can finally create
can go ahead and we can finally create our header comp component from here so
our header comp component from here so let's go ahead and do
let's go ahead and do that so I want to go and open the
that so I want to go and open the browser here where I have my card model
browser here where I have my card model and inside I want to create a new file
and inside I want to create a new file which is going to be named
which is going to be named header.
header. DSX and let's go ahead and Mark this as
DSX and let's go ahead and Mark this as use client and let's export const
use client and let's export const header and let's return a div sing
header and let's return a div sing header let's go back inside ins side of
header let's go back inside ins side of the uh index component here and what I
the uh index component here and what I want to do is I want to render the
want to do is I want to render the header component and I want to pass in
header component and I want to pass in the data to be card data and let's
the data to be card data and let's import the header component from dot
import the header component from dot slhe header so this one right here and
slhe header so this one right here and let me just uh join this Imports because
let me just uh join this Imports because they are of the same nature like that
they are of the same nature like that and now let's create the types for the
and now let's create the types for the header so we can accept this uh data so
header so we can accept this uh data so inside of the header create an interface
inside of the header create an interface header props which accepts the data
header props which accepts the data which is card with list which we have
which is card with list which we have inside of our types let's go ahead and
inside of our types let's go ahead and give them header props here and the
give them header props here and the structure the data like that perfect so
structure the data like that perfect so what I want to do now uh is I want to go
what I want to do now uh is I want to go ahead and render this but I I kind of
ahead and render this but I I kind of want it to be uh an input by default so
want it to be uh an input by default so we're going to see what I have in mind
we're going to see what I have in mind so let's give it a class name of flex
so let's give it a class name of flex items start Gap x 3 margin bottom of 6
items start Gap x 3 margin bottom of 6 and W
and W full and now I want to go ahead and add
full and now I want to go ahead and add a layout icon from Lucid react so just
a layout icon from Lucid react so just make sure uh you import that and it is a
make sure uh you import that and it is a self- closing tag and let's go ahead and
self- closing tag and let's go ahead and give it a class name of h-5 w-5 margin
give it a class name of h-5 w-5 margin top one and text neutral
top one and text neutral 700 and now let's create a div here with
700 and now let's create a div here with a class name of w full and let's open up
a class name of w full and let's open up a native form element and let's render
a native form element and let's render the form input from components form form
the form input from components form form input like this and inside of it we have
input like this and inside of it we have to give it an ID of title we have to
to give it an ID of title we have to give it a ref of input ref uh which we
give it a ref of input ref uh which we don't yet have so let's not do that just
don't yet have so let's not do that just yet uh and let's go ahead and give it
yet uh and let's go ahead and give it well first thing I want to do is I want
well first thing I want to do is I want to store the
to store the title inside of use State here so give
title inside of use State here so give it a data. title like that and make sure
it a data. title like that and make sure you import use state from react because
you import use state from react because we're going to do some optimistic update
we're going to do some optimistic update uh on that as well so now we can give
uh on that as well so now we can give this a default value of the title from
this a default value of the title from the state and let's go ahead and give it
the state and let's go ahead and give it a class name of font semi bold text
a class name of font semi bold text extra large
extra large px1 text neutral
px1 text neutral 700 BG
700 BG transparent border
transparent border transparent relative minus left minus
transparent relative minus left minus 1.5 W so sorry width of
1.5 W so sorry width of 95% and on Focus visible I wanted to
95% and on Focus visible I wanted to have a background of white on Focus
have a background of white on Focus visible I also wanted to have a border
visible I also wanted to have a border input a margin bottom of 0 .5 and
input a margin bottom of 0 .5 and truncate like that perfect so now as you
truncate like that perfect so now as you can see when I click on a specific card
can see when I click on a specific card uh so let's just refresh so we ignore
uh so let's just refresh so we ignore this uh okay let's try it like this
this uh okay let's try it like this let's use data question mark
let's use data question mark title like that or actually think now
title like that or actually think now yeah now nothing's going to be displayed
yeah now nothing's going to be displayed here all right let's let's just continue
here all right let's let's just continue developing and we will see the results
developing and we will see the results uh in a moment so in order to fix that
uh in a moment so in order to fix that little bug let's go ahead and let's
little bug let's go ahead and let's create a skeleton here so header.
create a skeleton here so header. skeleton is a function header
skeleton is a function header skeleton which returns a div with a
skeleton which returns a div with a class name of flex items start Gap X3
class name of flex items start Gap X3 and margin bottom of six and inside we
and margin bottom of six and inside we render the skeleton which we can import
render the skeleton which we can import uh from at/ components UI skeleton as I
uh from at/ components UI skeleton as I did right here and let's go ahead and
did right here and let's go ahead and let's give this skeleton a class name of
let's give this skeleton a class name of h-6 w-6 margin top one and BG neutral
h-6 w-6 margin top one and BG neutral 200 like that and then below that let's
200 like that and then below that let's open up a div and inside let's render
open up a div and inside let's render another
another skeleton let's give it a class name of
skeleton let's give it a class name of H-24 actually width of 24 height of six
H-24 actually width of 24 height of six margin bottom of one and BG new control
margin bottom of one and BG new control 200 copy and paste this below and let's
200 copy and paste this below and let's give this one a width of 12 a height of
give this one a width of 12 a height of four and it doesn't need any margin
four and it doesn't need any margin bottom and now we can use this header
bottom and now we can use this header skeleton here so go ahead here and
skeleton here so go ahead here and remove the question mark for data. tile
remove the question mark for data. tile it's always going to have it and now go
it's always going to have it and now go back to the index where we have an error
back to the index where we have an error because the data is possibly undefined
because the data is possibly undefined so let's go ahead and do the following
so let's go ahead and do the following if we don't have car data in that that
if we don't have car data in that that case we're going to go ahead and render
case we're going to go ahead and render header. skeleton like that otherwise
header. skeleton like that otherwise we're going to go ahead and render the
we're going to go ahead and render the actual header like this so let's try
actual header like this so let's try that out now I'm going to refresh here
that out now I'm going to refresh here and when I click you can see how for a
and when I click you can see how for a second I have a nice little loading
second I have a nice little loading skeleton and then it loads the exact
skeleton and then it loads the exact card which I expect perfect let's head
card which I expect perfect let's head back inside of our header component and
back inside of our header component and let's give it all the necessary stuff it
let's give it all the necessary stuff it needs to successfully update this so I'm
needs to successfully update this so I'm going to go ahead and I'm going to add
going to go ahead and I'm going to add the query client from use Query client
the query client from use Query client which we are going to use to refresh the
which we are going to use to refresh the data once we successfully update it
data once we successfully update it using the server action so let me move
using the server action so let me move that to the Global Imports at top we're
that to the Global Imports at top we're also going to need the pams so use pams
also going to need the pams so use pams which we can get from uh next navigation
which we can get from uh next navigation as I did uh right
as I did uh right here besides this we're also going to
here besides this we're also going to need to create a ref so let's write
need to create a ref so let's write const
const input ref to be use ref from react get
input ref to be use ref from react get the element ref from react as well and
the element ref from react as well and give it a type of input and inside give
give it a type of input and inside give it a default value of null so just make
it a default value of null so just make sure that from react you have the
sure that from react you have the element ref use ref and use State great
element ref use ref and use State great so now we have those and let's go ahead
so now we have those and let's go ahead and just create one more which is going
and just create one more which is going to be on blur so const on blur is going
to be on blur so const on blur is going to call the input ref. current
to call the input ref. current form request
form request submit all right and now uh let's go
submit all right and now uh let's go ahead and let's uh give this form input
ahead and let's uh give this form input what it needs so ref is going to be
what it needs so ref is going to be input ref on blur is going to be on
input ref on blur is going to be on blur and we already gave it the uh the
blur and we already gave it the uh the default value so that's good and now
default value so that's good and now let's just go ahead and let's create Con
let's just go ahead and let's create Con on submit here with form
on submit here with form data which is is a type of form
data which is is a type of form data and let's conso log form data.
data and let's conso log form data. getet
getet title so now and of course let's assign
title so now and of course let's assign this to be the action of this form so if
this to be the action of this form so if we try this out already I'm going to
we try this out already I'm going to open my inspect element here and if I
open my inspect element here and if I try and change this to something and
try and change this to something and press enter there we go you can see the
press enter there we go you can see the exact form uh field which is going to be
exact form uh field which is going to be submitted to the server action which we
submitted to the server action which we are yet to to create before we do that
are yet to to create before we do that let's go to the end of this form
let's go to the end of this form function and let's create a paragraph
function and let's create a paragraph which is going to render in list open a
which is going to render in list open a span element data list title like this
span element data list title like this and let's go ahead and give this
and let's go ahead and give this paragraph a class name of text small and
paragraph a class name of text small and text muted foreground and let's give
text muted foreground and let's give this span element a class name of
this span element a class name of underlined like that let's try it out
underlined like that let's try it out now I when I refresh and click here
now I when I refresh and click here there we go you can see it says new card
there we go you can see it says new card and then uh where my list is at and you
and then uh where my list is at and you can see how flush this input is it's
can see how flush this input is it's barely noticeable there are almost no
barely noticeable there are almost no you know flickering and stuff when you
you know flickering and stuff when you click and when you blur perfect so now
click and when you blur perfect so now let's go ahead and let's actually uh
let's go ahead and let's actually uh create that server
create that server action so let's copy an existing server
action so let's copy an existing server action I'm going to go ahead and find
action I'm going to go ahead and find update board that's the closest one and
update board that's the closest one and let's rename it to update card like that
let's rename it to update card like that let's go inside of schema so we tell it
let's go inside of schema so we tell it exactly what we expect so let's change
exactly what we expect so let's change this to be update card and alongside
this to be update card and alongside title we're also going to need a board
title we're also going to need a board ID which is going to be z. string we're
ID which is going to be z. string we're also going to need a description which
also going to need a description which is z.
is z. optional z. string and then we're going
optional z. string and then we're going to go ahead head and give it a required
to go ahead head and give it a required error of description is required we're
error of description is required we're going to give it an invalid type error
going to give it an invalid type error of description is required as well and
of description is required as well and let's also give it a minimum value of
let's also give it a minimum value of three just so we can see those errors
three just so we can see those errors and a message of description is too
and a message of description is too short all right and so we have the board
short all right and so we have the board ID the description the title and we have
ID the description the title and we have the ID of the card we are trying to
the ID of the card we are trying to update perfect now let's go inside of
update perfect now let's go inside of the types and let's get the update card
the types and let's get the update card from here and let's add it to the type
from here and let's add it to the type of great and now let's go ahead inside
of great and now let's go ahead inside of the index let's change the schema to
of the index let's change the schema to use the update
use the update card whoa let's go all the way down and
card whoa let's go all the way down and add the update card and change the
add the update card and change the function name to update card as well and
function name to update card as well and now inside we have to modify the Handler
now inside we have to modify the Handler so this can stay the same and in here
so this can stay the same and in here we're going to be extracting the
we're going to be extracting the following so we will extract the ID the
following so we will extract the ID the board ID and the rest of the values
board ID and the rest of the values we're going to keep in a constant values
we're going to keep in a constant values which we will spread like this and we're
which we will spread like this and we're not going to be working with a board but
not going to be working with a board but with a card so change this let to be a
with a card so change this let to be a card and then in here let's go ahead and
card and then in here let's go ahead and let's simply change the card to be await
let's simply change the card to be await DB card. update where ID matches the ID
DB card. update where ID matches the ID and the list has a
and the list has a board which has the matching
board which has the matching organization ID and then in the title
organization ID and then in the title we're just going to go ahead and spread
we're just going to go ahead and spread the values like that perfect and we can
the values like that perfect and we can leave this to be failed to update and
leave this to be failed to update and let's use the board ID here and in the
let's use the board ID here and in the data let's return the card and it seems
data let's return the card and it seems like I forgot to update my types here
like I forgot to update my types here yes so go back inside of the types and
yes so go back inside of the types and change from using the board to using an
change from using the board to using an individual card as our return type great
individual card as our return type great and now let's go back inside of the
and now let's go back inside of the header component inside of models card
header component inside of models card model so go inside of header here and
model so go inside of header here and let's go ahead and let's import the use
let's go ahead and let's import the use action from hooks use action and let's
action from hooks use action and let's import update card from actions update
import update card from actions update card and now let's go ahead uh and
card and now let's go ahead uh and actually uh we create this uh uh hook
actually uh we create this uh uh hook right so right here I'm going to call
right so right here I'm going to call the execute and I'm going to call the
the execute and I'm going to call the use action I'm passing create sorry
use action I'm passing create sorry update card here like that and then
update card here like that and then inside of this onsubmit let's go ahead
inside of this onsubmit let's go ahead and change this to be a constant
and change this to be a constant title and let's do execute and let's
title and let's do execute and let's pass in the title but I believe uh that
pass in the title but I believe uh that it also needs a couple of uh more things
it also needs a couple of uh more things here so let's fetch the board
here so let's fetch the board ID to be pams board ID as string
ID to be pams board ID as string remember we have the prams from used
remember we have the prams from used pams here so make sure you have that uh
pams here so make sure you have that uh all right and let's pass in the board ID
all right and let's pass in the board ID and the ID is going to be data. ID like
and the ID is going to be data. ID like that perfect uh and let's change this to
that perfect uh and let's change this to be as string and let's also prevent
be as string and let's also prevent updates if the title matches the current
updates if the title matches the current data. title so we can then just break
data. title so we can then just break the function instead perfect so that
the function instead perfect so that should now be working how about we try
should now be working how about we try it out and one more thing that I just
it out and one more thing that I just want to do is uh add the callbacks right
want to do is uh add the callbacks right so first let me just go ahead and let's
so first let me just go ahead and let's import the
import the soner uh sorry toast from soner and then
soner uh sorry toast from soner and then inside of the used action let's go ahead
inside of the used action let's go ahead and add on success let's get the data
and add on success let's get the data from here and first thing I want to do
from here and first thing I want to do is reinal the query so query client.
is reinal the query so query client. invalidate queries and let's go ahead
invalidate queries and let's go ahead and pass in the query key to be
and pass in the query key to be card and data. ID like that and then
card and data. ID like that and then let's call post.
let's call post. success and open backt renamed to open
success and open backt renamed to open annotations and render data.
annotations and render data. title and set title to be the new data.
title and set title to be the new data. tile and let's add on error here
tile and let's add on error here error and inside uh well we can just
error and inside uh well we can just call the toast. error and pass in the
call the toast. error and pass in the error so let's try it out now I'm going
error so let's try it out now I'm going to go
to go here choose a
here choose a card change the name press enter and
card change the name press enter and there we go rename to change the name
there we go rename to change the name and it's immediately reflected here and
and it's immediately reflected here and I can also click just outside and that
I can also click just outside and that does the exact same thing perfect so now
does the exact same thing perfect so now we're going to go ahead and create some
we're going to go ahead and create some additional actions like changing the
additional actions like changing the description uh copying the card or
description uh copying the card or deleting the card and then we're going
deleting the card and then we're going to go ahead and learn how to uh create
to go ahead and learn how to uh create activity
activity logs so now I want to create the
logs so now I want to create the description component so let's go ahead
description component so let's go ahead and just prepare for that it's going to
and just prepare for that it's going to be very similar as our header component
be very similar as our header component is and the first thing I want to do of
is and the first thing I want to do of course uh is just create a little
course uh is just create a little skeleton for it so we can render it
skeleton for it so we can render it conditionally so go inside of the card
conditionally so go inside of the card model and create a new file description
model and create a new file description PSX let's go ahead and Mark this as used
PSX let's go ahead and Mark this as used client let's export cons
client let's export cons description we can immediately create an
description we can immediately create an interface for
interface for it description props and you already
it description props and you already know that we accept the data which is a
know that we accept the data which is a type of card with
type of card with list now let's go ahead and assign those
list now let's go ahead and assign those props here description props and let's
props here description props and let's destructure the data and for now let's
destructure the data and for now let's just return a
just return a div rendering data title or perhaps it
div rendering data title or perhaps it would be smarter to render the
would be smarter to render the description even though we don't
description even though we don't actually have the description since it
actually have the description since it is optional so this will be the first
is optional so this will be the first time the user can actually add a
time the user can actually add a description and now let's go ahead and
description and now let's go ahead and let's just create a proper skeleton for
let's just create a proper skeleton for this one so I'm going to write
this one so I'm going to write description do skeleton is equal to a
description do skeleton is equal to a function which is a description
skeleton and let's go ahead and return a div let's give this div a class name of
div let's give this div a class name of flex items start Gap X3 and W pool
flex items start Gap X3 and W pool inside of this div let's render our
inside of this div let's render our skeleton from add/ components UI
skeleton from add/ components UI skeleton I will just align it like that
skeleton I will just align it like that let's give this a class name of
let's give this a class name of h-6 w-6 BG neutral 200 and now let's
h-6 w-6 BG neutral 200 and now let's create another div here with a class
create another div here with a class name of w- full let's go ahead and copy
name of w- full let's go ahead and copy the skeleton and put it inside here and
the skeleton and put it inside here and let's modify its class name a bit so I'm
let's modify its class name a bit so I'm going to leave the BG neutral 200 but
going to leave the BG neutral 200 but I'm going to add the width to be 24 the
I'm going to add the width to be 24 the height to be six and margin bottom two
height to be six and margin bottom two let's copy and paste this one and let's
let's copy and paste this one and let's also uh leave the BG neutral and let's
also uh leave the BG neutral and let's add W full here let's give it a height
add W full here let's give it a height of 78
of 78 pixels and that should be it let's just
pixels and that should be it let's just save this now and now let's go back
save this now and now let's go back inside of the index
inside of the index here and inside the dialog content let's
here and inside the dialog content let's create a proper grid which is going to
create a proper grid which is going to render this
render this description so the reason I want to
description so the reason I want to create a grid because our layout is
create a grid because our layout is going to look like this on this side
going to look like this on this side we're going to have the description and
we're going to have the description and then we're going to have the activity
then we're going to have the activity log down here but here in the corner
log down here but here in the corner we're going to have actions to delete
we're going to have actions to delete this card or to copy it and on mobile
this card or to copy it and on mobile mode they're going to collapse
mode they're going to collapse underneath each other so for that I
underneath each other so for that I think it's best that we use the grid uh
think it's best that we use the grid uh functionality so let's go outside of
functionality so let's go outside of this conditional which renders the
this conditional which renders the header and let's add that grid so give
header and let's add that grid so give it a class name of grid grid calls one
it a class name of grid grid calls one on mobile device so they go underneath
on mobile device so they go underneath each other and on medium device is going
each other and on medium device is going to be grid calls 4 and on medium devices
to be grid calls 4 and on medium devices they're also going to have a gap of four
they're also going to have a gap of four between another and now let's give this
between another and now let's give this description right column a larger call
description right column a larger call span so I wanted to take three out of
span so I wanted to take three out of four possible places which we Define
four possible places which we Define here on the well medium mode right so
here on the well medium mode right so call span 3
call span 3 and then inside let's render a div which
and then inside let's render a div which is going to use the full width of that
is going to use the full width of that column and now inside I'm just going to
column and now inside I'm just going to oh and let's also add space Y6 because
oh and let's also add space Y6 because we're going to have multiple items
we're going to have multiple items inside but for now let's do the same
inside but for now let's do the same thing with the above so if we don't have
thing with the above so if we don't have card data in that case let's go ahead
card data in that case let's go ahead and render description from do/
and render description from do/ description which we just created so
description which we just created so just make sure you have all the
just make sure you have all the necessary uh exports here so you can
necessary uh exports here so you can import it properly so we're going to
import it properly so we're going to render description.
render description. skeleton otherwise we're going to render
skeleton otherwise we're going to render the description and we're going to pass
the description and we're going to pass in the data which is card
in the data which is card data and now I think we should already
data and now I think we should already be seeing uh a nice little loading
be seeing uh a nice little loading skeleton here let's try this again there
skeleton here let's try this again there we go you can see how for a second we
we go you can see how for a second we have a nice little space for the
have a nice little space for the description here so let's go ahead uh
description here so let's go ahead uh and style that now inside of this
and style that now inside of this description uh component right here so
description uh component right here so let's give this top div a class name of
let's give this top div a class name of flex items Center actually let's make it
flex items Center actually let's make it items start Gap X3 and W full and just
items start Gap X3 and W full and just as I wrote this I think I remembered
as I wrote this I think I remembered when I was watching the previous part of
when I was watching the previous part of the tutorial that in my header component
the tutorial that in my header component I made a typo yes I saw this in the
I made a typo yes I saw this in the video uh so if you made the same typo I
video uh so if you made the same typo I don't think you did but just make sure
don't think you did but just make sure this says items start right so let's go
this says items start right so let's go back in the description now so Flex item
back in the description now so Flex item start Gap X3 and full width and now
start Gap X3 and full width and now let's add an icon a line left from Lucid
let's add an icon a line left from Lucid react so just make sure you have this
react so just make sure you have this import it is a self closing tag and
import it is a self closing tag and let's give it a class name of H5 W5
let's give it a class name of H5 W5 margin top of
margin top of 0.5 and
0.5 and text neutral
text neutral 700 below this icon let's open open a
700 below this icon let's open open a div which takes in the full width and
div which takes in the full width and inside let's add a little paragraph
inside let's add a little paragraph which is going to be the label
which is going to be the label description and let's go ahead uh and
description and let's go ahead uh and give it a class name of font bold
give it a class name of font bold actually semi bold might be better and
actually semi bold might be better and text neutral 700 and margin
text neutral 700 and margin bottom of two so let me just try that
bottom of two so let me just try that out to see how it looks there we go it
out to see how it looks there we go it looks quite nice so this first skeleton
looks quite nice so this first skeleton is obviously representing that icon and
is obviously representing that icon and the description text and now we're going
the description text and now we're going to add that big big box here at the
to add that big big box here at the bottom um which is actually going to be
bottom um which is actually going to be the description so this is going to be
the description so this is going to be conditional it's going to be a text area
conditional it's going to be a text area if we are editing it otherwise it's
if we are editing it otherwise it's going to be a plain div so for now let's
going to be a plain div so for now let's just make it a div and then later we're
just make it a div and then later we're going to make it conditional so inside
going to make it conditional so inside of this div let's give it a roll of
of this div let's give it a roll of buttons so that it indicates that the
buttons so that it indicates that the user can click on it and let's give it a
user can click on it and let's give it a class name of Min height of 78 pixels BG
class name of Min height of 78 pixels BG neutral 200 text small font medium py3
neutral 200 text small font medium py3 PX 3.5 and rounded medium and inside
PX 3.5 and rounded medium and inside let's render the data description or
let's render the data description or since it's possible that we don't have
since it's possible that we don't have one let's add a placeholder add a more
one let's add a placeholder add a more detailed
detailed description
description all right and as you can see this is how
all right and as you can see this is how it looks now so we have a nice little
it looks now so we have a nice little but button that we can click here and
but button that we can click here and inside and right here we're going to
inside and right here we're going to have our actions right so let's go ahead
have our actions right so let's go ahead uh and now we create the functionality
uh and now we create the functionality to enable editing right for that we need
to enable editing right for that we need to add all of those things like enable
to add all of those things like enable editing uh and refs so let's go ahead
editing uh and refs so let's go ahead and prepare all of that here so first
and prepare all of that here so first things first is editing and set is
things first is editing and set is editing
editing they're going to come from used
they're going to come from used State and by default they are false so
State and by default they are false so make sure you import used state from
make sure you import used state from react and while we are here let's also
react and while we are here let's also import use ref and element
import use ref and element ref now let's go ahead and let's create
ref now let's go ahead and let's create the text area ref and the form ref so
the text area ref and the form ref so const text area ref is use
const text area ref is use ref with a type of element
ref with a type of element ref which uses the text area and by
ref which uses the text area and by default let's give it null
default let's give it null and let's copy and paste this and now
and let's copy and paste this and now create a form ref which uses the form
create a form ref which uses the form element all right and now I just want to
element all right and now I just want to add uh the query client and the params
add uh the query client and the params so const query
so const query client is use Query client from 10stack
client is use Query client from 10stack react query so make sure you have this
react query so make sure you have this import I will just order them by length
import I will just order them by length all right and we're also going to have
all right and we're also going to have the params so cons param is use perams
the params so cons param is use perams from next navigation so let me just add
from next navigation so let me just add this as well great so we have everything
this as well great so we have everything uh we need here and now we can go ahead
uh we need here and now we can go ahead and create our enable editing function
and create our enable editing function so const enable editing is an arrow
so const enable editing is an arrow function which set is editing to true
function which set is editing to true and set
and set timeout and then we're going to uh focus
timeout and then we're going to uh focus on the text area ref using the current
on the text area ref using the current Focus now let's go ahead and let's
Focus now let's go ahead and let's create the disable
create the disable editing and that is very simply going to
editing and that is very simply going to set editing
set editing false great and now let's create our on
false great and now let's create our on key down so const on key down is going
key down so const on key down is going to take in the event which is the
to take in the event which is the keyboard uh
keyboard uh event and if event key is
event and if event key is escape in that case let's go ahead and
escape in that case let's go ahead and disable the editing so little bit of
disable the editing so little bit of good user experience on our side and now
good user experience on our side and now let's go ahead and let's add the uh use
let's go ahead and let's add the uh use event listener from use hooks TS and
event listener from use hooks TS and while we are here let's also import use
while we are here let's also import use on click outside from use hooks so add
on click outside from use hooks so add both of those use event listener is
both of those use event listener is going to be listening on the key down
going to be listening on the key down event and it's going to call on key down
event and it's going to call on key down and now let's add use on click outside
and now let's add use on click outside of the form ref and that is going to
of the form ref and that is going to trigger the disable
trigger the disable editing great and let's go ahead and
editing great and let's go ahead and create a con on submit here which takes
create a con on submit here which takes in the form data which is a type of form
in the form data which is a type of form data and inside of here let's go ahead
data and inside of here let's go ahead and let's destructure the description so
and let's destructure the description so form data get
form data get description as string and let's also get
description as string and let's also get the board ID to be params board ID as
the board ID to be params board ID as string great and let's add a comment to
string great and let's add a comment to do execute because we didn't add that
do execute because we didn't add that yet perfect and now we can go ahead and
yet perfect and now we can go ahead and modify this div to render conditionally
modify this div to render conditionally so let's wrap this inside of curly
so let's wrap this inside of curly brackets and let's write if is editing
brackets and let's write if is editing let's go ahead and let's just render a
let's go ahead and let's just render a return of a
return of a paragraph let's just make it like this
paragraph let's just make it like this oh actually this represents the return
oh actually this represents the return my apologies so inside let's render a
my apologies so inside let's render a paragraph which says
paragraph which says editing and then let's wrap this in the
editing and then let's wrap this in the else Clause the entire div uh which
else Clause the entire div uh which renders uh this like
renders uh this like that uh actually like this let's just
that uh actually like this let's just see so it should be just this div and
see so it should be just this div and close the else cluse and you can remove
close the else cluse and you can remove this data the description here like that
this data the description here like that so two Dives here and one div here and
so two Dives here and one div here and we can indent this I believe yes all
we can indent this I believe yes all right so now let's give this uh div an
right so now let's give this uh div an on click enable
on click enable editing great and now let's go ahead and
editing great and now let's go ahead and change this not be a paragraph but
change this not be a paragraph but instead be a form component and let's go
instead be a form component and let's go ahead and give it a ref of form ref
ahead and give it a ref of form ref let's give it a class name of space
let's give it a class name of space Y2 now inide let's render the form text
Y2 now inide let's render the form text area component which we created recently
area component which we created recently so from add/ components form form text
so from add/ components form form text area and let's give it all the necessary
area and let's give it all the necessary prop so we need the ID which is a
prop so we need the ID which is a description we need a class name which
description we need a class name which is W full and margin top of two we need
is W full and margin top of two we need a placeholder which is add a more
a placeholder which is add a more detailed
detailed description we need errors well we're
description we need errors well we're going to have them later and let's give
going to have them later and let's give it a default value of data. description
it a default value of data. description or
or undefined and outside of the form text
undefined and outside of the form text area create a div which is going to hold
area create a div which is going to hold our submit button and our cancel button
our submit button and our cancel button so give it a class name of flex items
so give it a class name of flex items Center and GAP
Center and GAP X2 let's render uh the form submit
X2 let's render uh the form submit component uh yes form submit component
component uh yes form submit component from add/ components form submit so make
from add/ components form submit so make sure you add uh this import as well and
sure you add uh this import as well and inside let's just write save and now
inside let's just write save and now let's go ahead and render a regular
let's go ahead and render a regular button here which we can import from
button here which we can import from add/ components uh UI button like I did
add/ components uh UI button like I did here and inside of here we're going to
here and inside of here we're going to render cancel and now let's go ahead and
render cancel and now let's go ahead and give this all the necessary types so
give this all the necessary types so type is going to be button on click is
type is going to be button on click is going to be disable editing size is
going to be disable editing size is going to be small and variant is going
going to be small and variant is going to be ghost and now I think when I click
to be ghost and now I think when I click in here there we go we have a nice
in here there we go we have a nice little description to write in here
little description to write in here perfect so now let's go ahead and
perfect so now let's go ahead and actually create uh the execution for
actually create uh the execution for that and we don't have to to create any
that and we don't have to to create any new server action we can reuse our
new server action we can reuse our existing action called uh update card
existing action called uh update card because in the schema you can see that
because in the schema you can see that we already prepared for accepting the
we already prepared for accepting the the optional description inside and in
the optional description inside and in the index we just spread whatever values
the index we just spread whatever values the user passes us so that way we cover
the user passes us so that way we cover both the title editing and the
both the title editing and the description editing so I'm going to go
description editing so I'm going to go here and I will import use
here and I will import use action from hooks use AC action and I'm
action from hooks use AC action and I'm going to import update
card from actions update card and now let's go ahead uh and I'm going to go
let's go ahead uh and I'm going to go right here and I'm going to execute this
right here and I'm going to execute this I'm going to extract execute from use
I'm going to extract execute from use action I'm going to pass in the update
action I'm going to pass in the update card
card component and let's go ahead and let's
component and let's go ahead and let's get the on success
here and let's call the toast from soner so let's move the soner to the top
so let's move the soner to the top here and let's go ahead and uh where is
here and let's go ahead and uh where is it it's right here so we can also use
it it's right here so we can also use the data here so data.
the data here so data. success and we're just going to open
success and we're just going to open backs and we're going to write uh card
backs and we're going to write uh card open
open annotations data title updated so we
annotations data title updated so we don't need to render the entire new
don't need to render the entire new description because it can be pretty
description because it can be pretty large and inside of here I also want to
large and inside of here I also want to call the query client and I want to
call the query client and I want to invalidate the
queries which is the card and data. ID and let's add the on error which
ID and let's add the on error which takes in the error and that is just
takes in the error and that is just going to well display that error in
going to well display that error in atast and let's also extract the field
atast and let's also extract the field errors from here and then we can use
errors from here and then we can use that to pass in the this form text area
that to pass in the this form text area as
as errors great uh all right so now I just
errors great uh all right so now I just think we need to add some uh refs here
think we need to add some uh refs here so let's uh actually we need to add the
so let's uh actually we need to add the action here so action to this form is
action here so action to this form is going to be uh the on submit right I
going to be uh the on submit right I think that's what we called it right
think that's what we called it right here and now let's use the execute and
here and now let's use the execute and let's pass in the description and the
let's pass in the description and the board
ID uh let's see if I did this correctly so we have the execute like that and
so we have the execute like that and something seems to not be working oh I
something seems to not be working oh I think it's because of
think it's because of this board ID uh it seems to be
this board ID uh it seems to be missing it seems to be missing uh the
missing it seems to be missing uh the title oh or it's missing the ID yes ID
title oh or it's missing the ID yes ID which is data. ID like that but I still
which is data. ID like that but I still think we have to modify the schema
think we have to modify the schema because in our action yes in schema we
because in our action yes in schema we always
always require uh we always require the title
require uh we always require the title so let's make the title optional as well
so let's make the title optional as well so go inside of your schema of update
so go inside of your schema of update card and just modify the title can be
card and just modify the title can be optional like that and just wrap the
optional like that and just wrap the entire thing in Brackets and add a
entire thing in Brackets and add a little comma at the
little comma at the end great like this and now we have no
end great like this and now we have no errors inside of here uh great and I
errors inside of here uh great and I think we did not assign this textt area
think we did not assign this textt area ref anywhere so let's also give that to
ref anywhere so let's also give that to the form text area here ref text area
the form text area here ref text area ref and let's try it out now so I'm just
ref and let's try it out now so I'm just going to refresh everything I'm going to
going to refresh everything I'm going to go in a random component here I will add
go in a random component here I will add a original description I'm going to
a original description I'm going to click save and it seems like it is
click save and it seems like it is working perfect and let's go ahead and
working perfect and let's go ahead and refresh now to see if that's true there
refresh now to see if that's true there we go it stays right here the only thing
we go it stays right here the only thing I want to change is after I save I want
I want to change is after I save I want to disable the editing so let's just go
to disable the editing so let's just go ahead and do that so I'm going to go
ahead and do that so I'm going to go right here and call disable
right here and call disable editing and I think uh that's going to
editing and I think uh that's going to be enough to uh trigger it back to your
be enough to uh trigger it back to your original so I'm going to modify it again
original so I'm going to modify it again I'm going to click save and there we go
I'm going to click save and there we go it moves back to this perfect so we
it moves back to this perfect so we finished uh we finish the description
finished uh we finish the description now now I want to create my actions and
now now I want to create my actions and my actions skeleton so let's go back to
my actions skeleton so let's go back to our index
model and outside of this div which represents this column we're going to
represents this column we're going to create a new one and inside actually we
create a new one and inside actually we don't have to create a div at all
don't have to create a div at all instead we're just going to render the
instead we're just going to render the actions here right so just leave a
actions here right so just leave a little space here and let's go inside
little space here and let's go inside the card model and create actions. TSX
the card model and create actions. TSX let's mark it as use client and let's
let's mark it as use client and let's export const actions here and the
export const actions here and the actions are going to have its own div
actions are going to have its own div and don't forget to return
this actions and what I want to do first is I want to create the skeleton for my
is I want to create the skeleton for my actions so actions. skeleton is equal to
actions so actions. skeleton is equal to function actions
function actions skeleton and let's return a div with a
skeleton and let's return a div with a class name space Y 2 and margin top of
class name space Y 2 and margin top of two and in here let's render the
two and in here let's render the skeleton component which we can import
skeleton component which we can import from add components UI skeleton let's go
from add components UI skeleton let's go ahead and give it a class name of uh
ahead and give it a class name of uh width 20 height 4 BG neutral 200 let's
width 20 height 4 BG neutral 200 let's copy this two more times uh the second
copy this two more times uh the second one is going to have a full
one is going to have a full width and the height of eight and the
width and the height of eight and the same is going to be for the one
below now let's go back inside of the index right here and now we can
index right here and now we can conditionally render that so if we don't
conditionally render that so if we don't have card data in that case uh well
have card data in that case uh well let's do it like this so if we don't
let's do it like this so if we don't have card data we're going to render
have card data we're going to render actions from do/ actions. skeleton so
actions from do/ actions. skeleton so make sure you import the actions in the
make sure you import the actions in the same way we did description and the
same way we did description and the Heather so just make sure you have all
Heather so just make sure you have all the necessary uh exports in here all
the necessary uh exports in here all right and otherwise we're going to
right and otherwise we're going to render actions but we're going to pass
render actions but we're going to pass in the data which is card data we don't
in the data which is card data we don't have the types defined for that yet but
have the types defined for that yet but I think it should be enough and you can
I think it should be enough and you can see when I expand they go to the side
see when I expand they go to the side here so that's exactly what I want and
here so that's exactly what I want and when I refresh you can see how I have a
when I refresh you can see how I have a nice skeleton for them on the side
nice skeleton for them on the side perfect so now let's create the
perfect so now let's create the interface and let's wrap up uh the
interface and let's wrap up uh the actions so the interface I think you
actions so the interface I think you already know what it's going to be
already know what it's going to be interface actions props data card with
interface actions props data card with list from
list from types all right and let's go ahead and
types all right and let's go ahead and assign those props actions
assign those props actions props data and let's just make it
props data and let's just make it properly spelled there we go and now
properly spelled there we go and now let's go ahead uh and style this UI so
let's go ahead uh and style this UI so our first div is going to have a class
our first div is going to have a class name of space Y2 and margin top of two
name of space Y2 and margin top of two and then inside we're going to have a
and then inside we're going to have a paragraph which will say actions so
paragraph which will say actions so let's just give it a class name of text
let's just give it a class name of text extra small and font semi bold and then
extra small and font semi bold and then below that we're going to have two
below that we're going to have two button elements so just make sure you
button elements so just make sure you import the button from here and the
import the button from here and the first one is going to say copy and it's
first one is going to say copy and it's going to have a copy icon from Lucid
going to have a copy icon from Lucid react uh above the text right so before
react uh above the text right so before the text make sure you import copy from
the text make sure you import copy from Lucid react and while we are here let's
Lucid react and while we are here let's also import the trash icon from it react
also import the trash icon from it react and let's go ahead and give this copy a
and let's go ahead and give this copy a class name of H4 W4 and margin right of
class name of H4 W4 and margin right of two and now let's give this uh button a
two and now let's give this uh button a variant uh of well we have to create a
variant uh of well we have to create a new variant because whichever variant we
new variant because whichever variant we actually use for this model simply won't
actually use for this model simply won't look good so before we do that let's go
look good so before we do that let's go inside of our button component so inside
inside of our button component so inside of the components folder UI
of the components folder UI button after transparent go ahead and
button after transparent go ahead and create
create gray and let's give it a BG of neutral
gray and let's give it a BG of neutral 200 text secondary
200 text secondary foreground hover BG neutral 300 like
foreground hover BG neutral 300 like this so if you uh misspelled it or
this so if you uh misspelled it or something or if it's not working you can
something or if it's not working you can always visit my GitHub and just take a
always visit my GitHub and just take a look at it here and now let's give it a
look at it here and now let's give it a gray variant like that and as you can
gray variant like that and as you can see we now don't have any errors uh and
see we now don't have any errors uh and let's give it a class name of of w full
let's give it a class name of of w full and justify start but I also want to
and justify start but I also want to give it a new size uh and let's actually
give it a new size uh and let's actually go ahead back inside of the button so we
go ahead back inside of the button so we practice how to modify this size as well
practice how to modify this size as well so go back inside of your button after
so go back inside of your button after you've added this gray variant go inside
you've added this gray variant go inside of the size here and create an inline
of the size here and create an inline size and let's give it an H AO PX of 2
size and let's give it an H AO PX of 2 py of 1.5 and text
py of 1.5 and text small great and now let's go inside the
small great and now let's go inside the actions and give it the size of inline
actions and give it the size of inline great so that is our button now and we
great so that is our button now and we can copy and paste the button one below
can copy and paste the button one below another and change this one to be delete
another and change this one to be delete and use the trash icon which we already
and use the trash icon which we already imported so let's try that out now when
imported so let's try that out now when I click here there we go actions copy
I click here there we go actions copy and delete perfect uh so now we actually
and delete perfect uh so now we actually have to create the uh execution methods
have to create the uh execution methods of those actions
of those actions so we can actually kind of reuse both of
so we can actually kind of reuse both of those actions right because they're not
those actions right because they're not going to be very different from another
going to be very different from another uh and let's just quickly visit okay so
uh and let's just quickly visit okay so I added text small to the in line okay
I added text small to the in line okay everything is fine so we can close
everything is fine so we can close everything and let's go inside our
everything and let's go inside our actions and let's for example copy um
actions and let's for example copy um well we can do the copy list why not and
well we can do the copy list why not and let's change it to copy
let's change it to copy card let's go inside of the schema and
card let's go inside of the schema and let's rename it to copy card like this
let's rename it to copy card like this and what what's everything uh we need
and what what's everything uh we need inside of this so we need the ID and we
inside of this so we need the ID and we need the board ID I think that is uh
need the board ID I think that is uh fine enough now let's go inside of our
fine enough now let's go inside of our types and let's change this to copy
types and let's change this to copy card like that and change this to be a
card like that and change this to be a board sorry card and returns a
board sorry card and returns a card and inside of the index
card and inside of the index here
here let's also change the copy card from
let's also change the copy card from this schema all the way down here and
this schema all the way down here and change this to copy card as well and now
change this to copy card as well and now the authorization can stay the same we
the authorization can stay the same we are
are extracting I believe yeah ID and board
extracting I believe yeah ID and board ID that's all we need but we're going to
ID that's all we need but we're going to be working with a card so just change
be working with a card so just change this and now let's remove everything
this and now let's remove everything inside of the tri
inside of the tri block so it's completely empty and let's
block so it's completely empty and let's go ahead and first get the card to copy
go ahead and first get the card to copy so await DB card find
so await DB card find unique where ID matches and list board
unique where ID matches and list board has the same organization
has the same organization ID if there is no card to copy we can
ID if there is no card to copy we can just return an
just return an error card not
error card not found great now that we have uh our card
found great now that we have uh our card let's find the last card in this list so
let's find the last card in this list so const last card await DB card B first
where list ID is card to copy list ID so we didn't need to manually pass the list
we didn't need to manually pass the list ID because we can fetch it using this
ID because we can fetch it using this existing card we want to copy and let's
existing card we want to copy and let's order by order
order by order descending and all we need to select is
descending and all we need to select is the order
itself and now let's write the New Order if we have the last card it is
Order if we have the last card it is last card. order + one otherwise it's
last card. order + one otherwise it's just one and let's write card to be
just one and let's write card to be await DB card
await DB card create and let's use the data here let's
create and let's use the data here let's give it a title to be open back Tex and
give it a title to be open back Tex and we're going to copy the original uh card
we're going to copy the original uh card to copy. title and we're going to give
to copy. title and we're going to give it a little tag copy at the end let's
it a little tag copy at the end let's give it a description to be card to
give it a description to be card to copy. description so that can stay
copy. description so that can stay exactly the same order new order and
exactly the same order new order and list ID is going to be card to
list ID is going to be card to copy. list
copy. list ID great and now we are ready to return
ID great and now we are ready to return that card right here perfect and while
that card right here perfect and while we are here I think we can already go
we are here I think we can already go ahead and create our server action for
ahead and create our server action for deleting a card so let's just copy and
deleting a card so let's just copy and paste this and change it to delete
paste this and change it to delete card let's go inside of the schema here
card let's go inside of the schema here and change this to delete card as well I
and change this to delete card as well I believe uh we only need uh the the board
believe uh we only need uh the the board ID and the ID as we did in the previous
ID and the ID as we did in the previous one so let's immediately go inside of
one so let's immediately go inside of our types and let me just change oh yeah
our types and let me just change oh yeah I had a typle delete card inside of the
I had a typle delete card inside of the schema and let's give this here this is
schema and let's give this here this is here perfect go inside the index let's
here perfect go inside the index let's use the delete card schema let's go all
use the delete card schema let's go all the way down change it to delete card
the way down change it to delete card and the function rename to delete card
and the function rename to delete card as well perfect so now I believe this
as well perfect so now I believe this will be even easier so we can just we
will be even easier so we can just we can remove everything inside of the tri
can remove everything inside of the tri block for the delete card because all we
block for the delete card because all we need to do is card is wait DB card
need to do is card is wait DB card delete where ID is matching and the link
delete where ID is matching and the link board has the matching organization ID
board has the matching organization ID that's all we need perfect and let's
that's all we need perfect and let's change the error to be failed to delete
change the error to be failed to delete and we pass in the card perfect so we
and we pass in the card perfect so we now have our two new actions and now we
now have our two new actions and now we are ready to go back uh inside of our
are ready to go back uh inside of our actions do I have an error here I
actions do I have an error here I believe it's just let me just I'm going
believe it's just let me just I'm going I just pressed command shift p and
I just pressed command shift p and reload my window to see if this will go
reload my window to see if this will go away there we go it's no longer red okay
away there we go it's no longer red okay no errors so let's go inside of
no errors so let's go inside of components models actions right here and
components models actions right here and let's import everything we need so we
let's import everything we need so we need the use action from hooks use
need the use action from hooks use action we need the copy card from
action we need the copy card from actions copy card and we also need the
actions copy card and we also need the delete card from actions delete
delete card from actions delete card like that perfect so let's go ahead
card like that perfect so let's go ahead and add those here so
and add those here so use
use action copy
action copy card and let's write it execute to be
card and let's write it execute to be execute copy
execute copy card let's copy and paste this and this
card let's copy and paste this and this one is going to be execute delete card
one is going to be execute delete card and he's going to use the delete card
and he's going to use the delete card action let's go ahead uh and I believe
action let's go ahead uh and I believe we need some params here so let's import
we need some params here so let's import use
use params
params from next
from next navigation and let's add the params here
navigation and let's add the params here so use
params and let's create const on copy to Be an Arrow function which is going to
Be an Arrow function which is going to get the board ID from prams board ID as
get the board ID from prams board ID as string and let's go ahead and call the
string and let's go ahead and call the execute copy card pass in the ID to be
execute copy card pass in the ID to be data. ID and board ID
data. ID and board ID and we can copy and paste this
and we can copy and paste this function and rename it to on delete and
function and rename it to on delete and this is going to call execute delete
this is going to call execute delete card like that and now uh what I want to
card like that and now uh what I want to extract from
extract from here is also going to be well the uh is
here is also going to be well the uh is loading right so is loading for this one
loading right so is loading for this one and is loading for this one as well and
and is loading for this one as well and let's change it yes so is loading copy
let's change it yes so is loading copy and this one is going to be is loading
delete and let's now assign those actions to our buttons so the first one
actions to our buttons so the first one is for copy so onclick on this button is
is for copy so onclick on this button is going to use on copy and disabled if is
going to use on copy and disabled if is loading copy and now let's copy these
loading copy and now let's copy these two attributes and let's place them here
two attributes and let's place them here so this one is going to be on
so this one is going to be on delete and is loading delete
delete and is loading delete great I just want to add some call backs
great I just want to add some call backs now and also when we delete a card I
now and also when we delete a card I want to close the model right so let's
want to close the model right so let's go ahead and let's import our uh card
go ahead and let's import our uh card model from use card
model from use card model so you can import that from hooks
model so you can import that from hooks use card model and let's also import
use card model and let's also import toast from soner so we can display a
toast from soner so we can display a success message so first let's do the
success message so first let's do the copy card so on success here I want to
copy card so on success here I want to go ahead and log toast success and let's
go ahead and log toast success and let's write card open annotations data. tile
write card open annotations data. tile copy
it and I believe yeah we can close the model on copy as well so card model.on
model on copy as well so card model.on close I think that would kind of make
close I think that would kind of make sense on
sense on error let's get the
error let's get the error and let's do toast. error error
and now let me just copy these two actions here and just open an object
actions here and just open an object here and paste them here so this is
here and paste them here so this is going to be card data title deleted and
going to be card data title deleted and it's also going to close everything and
it's also going to close everything and I think that should be it let's try it
I think that should be it let's try it out so I'm just going to refresh just in
out so I'm just going to refresh just in case I will get this one through three
case I will get this one through three first I'm going to try to copy there we
first I'm going to try to copy there we go it's disabled and 1 two 3 copy now
go it's disabled and 1 two 3 copy now let's try delete and it it's deleted
let's try delete and it it's deleted perfect so we can now delete our cards
perfect so we can now delete our cards we can copy our cards and we can also
we can copy our cards and we can also add the description let's actually try
add the description let's actually try with description so I have this and if I
with description so I have this and if I click
click copy there we go the description copies
copy there we go the description copies as well perfect so what we're going to
as well perfect so what we're going to do next is we're finally going to
do next is we're finally going to implement our activity tab here with
implement our activity tab here with audit logs and then we're going to
audit logs and then we're going to implement stripe great great job so now
implement stripe great great job so now that we have all the functionality uh
that we have all the functionality uh finished in our board I want to go ahead
finished in our board I want to go ahead and create audit logs which are
and create audit logs which are basically logs of activity which will be
basically logs of activity which will be well created every time we do some kind
well created every time we do some kind of change like renaming a card copying a
of change like renaming a card copying a card which is essentially creating a
card which is essentially creating a card right deleting a card editing the
card right deleting a card editing the description all of those things will
description all of those things will have its own activity and then you're
have its own activity and then you're going to be able to see that so just
going to be able to see that so just like like in jira or original Trello
like like in jira or original Trello you're going to be able to see exactly
you're going to be able to see exactly what happened with a specific card and
what happened with a specific card and of course we're also going to add that
of course we're also going to add that activity right here in this tab which
activity right here in this tab which says activity which is currently a 404
says activity which is currently a 404 page and before we do that I just
page and before we do that I just quickly want to expand this model right
quickly want to expand this model right here because I have a feeling it's just
here because I have a feeling it's just a bit too small so let's see how we can
a bit too small so let's see how we can do that I want to go inside of
do that I want to go inside of components you u i dialogue right here
components you u i dialogue right here and we already modified this BG black
and we already modified this BG black here but let's take a look uh right here
here but let's take a look uh right here in the dialog content and in here you
in the dialog content and in here you can find the max with LG how about we
can find the max with LG how about we change that to 3 Excel instead and now I
change that to 3 Excel instead and now I believe that our model there we go is
believe that our model there we go is slightly bigger so we have more room for
slightly bigger so we have more room for this activity right here perfect so
this activity right here perfect so where I want to go now what I want to do
where I want to go now what I want to do now now is first let's shut down the app
now now is first let's shut down the app because we're going to be modifying not
because we're going to be modifying not Port Terminal because we're going to be
Port Terminal because we're going to be modifying our schema so shut down your
modifying our schema so shut down your app and let's go ahead inside of prismas
app and let's go ahead inside of prismas schema. Prisma right here and all the
schema. Prisma right here and all the way at the bottom let's add a new model
way at the bottom let's add a new model model audit
model audit log but in order to use that first we're
log but in order to use that first we're going to have to create a couple of
going to have to create a couple of enums so first let's create an enum
enums so first let's create an enum action so we're going to have the
action so we're going to have the possibility of logging create
possibility of logging create actions delete actions actually let's
actions delete actions actually let's write update first so update actions and
write update first so update actions and delete actions and we're also going to
delete actions and we're also going to have an entity type which is going to be
have an entity type which is going to be connected to each of those actions which
connected to each of those actions which are
are board
board list and
list and card great and now let's go back inside
card great and now let's go back inside of the audit log here and let's give it
of the audit log here and let's give it an ID of string
an ID of string ID and a default value of uu ID now
ID and a default value of uu ID now let's give it an organization ID which
let's give it an organization ID which is a string let's give it an action
is a string let's give it an action which is the type of action which we
which is the type of action which we just defined above entity ID which is
just defined above entity ID which is just going to be a string entity type
just going to be a string entity type which is going to be a type of entity
which is going to be a type of entity type enum which we defined above the
type enum which we defined above the same as action we're also going to have
same as action we're also going to have the user ID which created that we're
the user ID which created that we're going to have the user image
going to have the user image string DB text and username string DB
string DB text and username string DB text as well and we're going to have the
text as well and we're going to have the created ads so we can order them by
created ads so we can order them by latest with the default value of
latest with the default value of now and we're going to have the updated
now and we're going to have the updated at which is also a date time with the
at which is also a date time with the value at updated at and I'm just going
value at updated at and I'm just going to go ahead uh and align all of this
to go ahead uh and align all of this things I just like them to be aligned
things I just like them to be aligned you don't have to do this of course but
you don't have to do this of course but you know I think prettier does this for
you know I think prettier does this for you but I'm not using prettier in my
you but I'm not using prettier in my tutorials because I don't want you to
tutorials because I don't want you to miss out on any changes in the code uh
miss out on any changes in the code uh great so now that we have this we have
great so now that we have this we have to push it in the database so let's go
to push it in the database so let's go ahead inside of the terminal here and
ahead inside of the terminal here and let's run npx Prisma DB push so this is
let's run npx Prisma DB push so this is going to add this new model inside side
going to add this new model inside side of our uh database and now we also have
of our uh database and now we also have to do npx Prisma generate so we locally
to do npx Prisma generate so we locally have this and again make sure you've
have this and again make sure you've shut down your app so now run it again
shut down your app so now run it again because if you kept it running uh well
because if you kept it running uh well there's a chance that it wouldn't be
there's a chance that it wouldn't be registered and then you're going to have
registered and then you're going to have some weird errors until you restart your
some weird errors until you restart your app and now I want to go inside of the
app and now I want to go inside of the lib here and I want to create a new file
lib here and I want to create a new file create audit log. CS so we're going to
create audit log. CS so we're going to have a reusable function whenever we
have a reusable function whenever we want to create a log and let's go ahead
want to create a log and let's go ahead and import out and current user from
and import out and current user from at Clerk
at Clerk nextjs and let's import action and
nextjs and let's import action and entity type from at
entity type from at Prisma client and let me just change
Prisma client and let me just change this to be out like that let's import
this to be out like that let's import database from uh well I'm going to
database from uh well I'm going to change it to lib like this and let's
change it to lib like this and let's create an interface props to accept the
create an interface props to accept the entity ID which is a string entity
entity ID which is a string entity type which is entity type entity
type which is entity type entity title which is a string and action which
title which is a string and action which is an
is an action and now let's go ahead and let's
action and now let's go ahead and let's create this function so export const
create this function so export const audit uh create audit
audit uh create audit log is going to be an asynchronous
log is going to be an asynchronous action which calls the
action which calls the props and now let's open a try and catch
props and now let's open a try and catch block so in case this fails we don't
block so in case this fails we don't want to break the entire function right
want to break the entire function right we just want it to live in its own scope
we just want it to live in its own scope so console log let's write audit log
so console log let's write audit log error and let's log that error here and
error and let's log that error here and now in here first let's extract the or
now in here first let's extract the or organization ID from out and now let's
organization ID from out and now let's extract the entire user from await
extract the entire user from await current user so both of these are
current user so both of these are imported from Clerk
imported from Clerk nextjs and let's go ahead and check if
nextjs and let's go ahead and check if we don't have the user or if we don't
we don't have the user or if we don't have organization ID in that case throw
have organization ID in that case throw new error user not
new error user not found and now let's go ahead and let's
found and now let's go ahead and let's extract entity ID entity type
extract entity ID entity type entity title and
entity title and action from
action from props and let's do await DB audit log
props and let's do await DB audit log which we now have create and give it a
which we now have create and give it a data of organization ID entity idid
data of organization ID entity idid entity type entity
entity type entity title uh did I misspell that entity
title uh did I misspell that entity title uh looks like something uh is is
title uh looks like something uh is is wrong here did I forgot to add it to my
wrong here did I forgot to add it to my schema let's go inside of Prisma schema
schema let's go inside of Prisma schema here yes I forgot so add entity Title
here yes I forgot so add entity Title Here which is also a type of string like
Here which is also a type of string like that and now what we have to do is shut
that and now what we have to do is shut down our app since we modifi the schema
down our app since we modifi the schema run npx Prisma generate again so that's
run npx Prisma generate again so that's going to fix it here as you can see now
going to fix it here as you can see now we no longer have that entity title
we no longer have that entity title error and then we also have to do do MPX
error and then we also have to do do MPX Prisma DB hush to synchronize it with
Prisma DB hush to synchronize it with the actual database on planet scale or
the actual database on planet scale or wherever you're running your database
wherever you're running your database and then mpm run Dev and everything
and then mpm run Dev and everything should be fine great besides entity
should be fine great besides entity title uh we also need to pass the action
title uh we also need to pass the action we need to pass the user ID which is
we need to pass the user ID which is user. ID we need to pass in the user
user. ID we need to pass in the user image which is going to be user question
image which is going to be user question mark image URL and we're also going to
mark image URL and we're also going to need the user name which is going to be
need the user name which is going to be user first name plus empty space user
user first name plus empty space user last
last name like that so that's it that is our
name like that so that's it that is our audit log action so now let's go ahead
audit log action so now let's go ahead and add it to a very simple action like
and add it to a very simple action like for example creating a
for example creating a card so in order to prepare to see
card so in order to prepare to see whether this is working or not I'm going
whether this is working or not I'm going to go ahead and also open my Prisma
to go ahead and also open my Prisma studio so npx Prisma Studio
studio so npx Prisma Studio make sure you have this
make sure you have this running and let's just paste it uh in
running and let's just paste it uh in here so I have no audit logs you can see
here so I have no audit logs you can see it says zero here so I'm going to click
it says zero here so I'm going to click here and focus on that so there are no
here and focus on that so there are no rows in this table and now let's go
rows in this table and now let's go ahead and let's close this and let's go
ahead and let's close this and let's go into a very simple action uh create card
into a very simple action uh create card and let's go inside of Index right here
and let's go inside of Index right here and let's go all the way to the bottom
and let's go all the way to the bottom here where we assign uh the await DB
here where we assign uh the await DB card create to the card uh constant and
card create to the card uh constant and below that we're going to add await
below that we're going to add await create audit log which you can import
create audit log which you can import from s/ Li create audit log as I did
from s/ Li create audit log as I did right here I'm just going to move it
right here I'm just going to move it here and let's go ahead and give it
here and let's go ahead and give it everything we need so create audit
everything we need so create audit log entity ID is going to be card ID
log entity ID is going to be card ID we're going to have entity title which
we're going to have entity title which is going to be card. title we're going
is going to be card. title we're going to have have entity type which is going
to have have entity type which is going to be entity type which you can import
to be entity type which you can import from at Prisma client so entity type.
from at Prisma client so entity type. card and we're going to have an action
card and we're going to have an action which is going to be action which you
which is going to be action which you can again import from Prisma client.
can again import from Prisma client. create like that so let me just show you
create like that so let me just show you my imports so I also added this action
my imports so I also added this action and entity type from Prisma client and
and entity type from Prisma client and create save action from as/ Li create
create save action from as/ Li create save action perfect so let's try out if
save action perfect so let's try out if this is working I'm going to refresh
this is working I'm going to refresh this in my Prisma Studio I have no audit
this in my Prisma Studio I have no audit logs right now so let's just wait for
logs right now so let's just wait for this to uh okay and let's create a new
this to uh okay and let's create a new card test let's click
card test let's click create it's creating and there we go the
create it's creating and there we go the card has been created let's check my
card has been created let's check my Prisma Studio here I will refresh the
Prisma Studio here I will refresh the audit log and there we go I have the
audit log and there we go I have the organization ID the action is create I
organization ID the action is create I have the entity ID I have the entity
have the entity ID I have the entity type card and the entity title matches
type card and the entity title matches my card and I also have the user ID user
my card and I also have the user ID user image username everything I need perfect
image username everything I need perfect so now we are ready to create an API
so now we are ready to create an API route uh so we can fetch that activity
route uh so we can fetch that activity inside of a card model and display it
inside of a card model and display it here so let's go back inside of our API
here so let's go back inside of our API folder I'm going to close everything
folder I'm going to close everything here go inside of app API inside of card
here go inside of app API inside of card ID and create a new folder logs and
ID and create a new folder logs and inside create a new route. CS
inside create a new route. CS let's export asynchronous function get
let's export asynchronous function get here let's get the request which is a
here let's get the request which is a type of request let's extract the
type of request let's extract the params which are a type of params which
params which are a type of params which have the card ID which is a string just
have the card ID which is a string just confirm of course that this card ID
confirm of course that this card ID matches the card ID folder let's open
matches the card ID folder let's open try and catch block let's log the error
try and catch block let's log the error here and and let's just return new next
here and and let's just return new next response internal
response internal error with a status of
error with a status of 500 now let's oh we have to import next
500 now let's oh we have to import next response from next SL server so make
response from next SL server so make sure you do that and in here we're going
sure you do that and in here we're going to go ahead and extract the user ID and
to go ahead and extract the user ID and the organization ID from the out library
the organization ID from the out library from Clerk nextjs and while we are here
from Clerk nextjs and while we are here let's also import the database from s/
let's also import the database from s/ lib DB and now let's go ahead and check
lib DB and now let's go ahead and check if we don't have the user ID or if we
if we don't have the user ID or if we don't have the organization ID return
don't have the organization ID return new next response and
new next response and authorized with a status of
authorized with a status of 401 and now let's get con audit logs to
401 and now let's get con audit logs to be await DB audit log find
be await DB audit log find many and let's go ahead and write where
many and let's go ahead and write where the organization ID matches entity ID is
the organization ID matches entity ID is prem's card ID and entity type is entity
prem's card ID and entity type is entity type which you can import from Prisma
type which you can import from Prisma client entity type. card like that and
client entity type. card like that and let's order by created at descending and
let's order by created at descending and for the card model we're just going to
for the card model we're just going to take three so only the latest three are
take three so only the latest three are going to be visible there but in the
going to be visible there but in the activity page you're going to be able to
activity page you're going to be able to see all of them
see all of them great now let's go back inside of the
great now let's go back inside of the card model so components models card
card model so components models card model inside of the index here and in
model inside of the index here and in here we already fetch the card data so
here we already fetch the card data so we can copy and paste this function and
we can copy and paste this function and replace this to be audit logs data and
replace this to be audit logs data and let's change this query key to be card-
let's change this query key to be card- logs let's change the fetcher to go to/
logs let's change the fetcher to go to/ API cards
API cards slid and then slash logs and change the
slid and then slash logs and change the expected output to be audit log from
expected output to be audit log from Prisma client and add a little array at
Prisma client and add a little array at the end so I imported audit log from
the end so I imported audit log from here great and now that we have these
here great and now that we have these audit logs it's time for us to render
audit logs it's time for us to render them so inside of here let's create a
them so inside of here let's create a new component called activity. DSX let's
new component called activity. DSX let's mark it this use client and let's export
mark it this use client and let's export con
con activity and let's return a div saying
activity and let's return a div saying activity
activity here now what I want to do is create a
here now what I want to do is create a little skeleton for it so let's write
little skeleton for it so let's write activity. skeleton to be function
activity. skeleton to be function activity
activity skeleton and let's return a div
skeleton and let's return a div inside which is going to have a class
inside which is going to have a class name of flex items start Gap X3 and W4
name of flex items start Gap X3 and W4 it's going to have a skeleton which you
it's going to have a skeleton which you can import from s/ components UI
can import from s/ components UI skeleton and go ahead and give this
skeleton and go ahead and give this skeleton a class name of BG neutral 200
skeleton a class name of BG neutral 200 and let's also go ahead and give it a
and let's also go ahead and give it a height of six and a width of six now
height of six and a width of six now let's go ahead and create a div with a
let's go ahead and create a div with a class name of w full and inside we're
class name of w full and inside we're going to have uh two more
going to have uh two more skeletons like this let's go ahead and
skeletons like this let's go ahead and give this one a width of 24 and a height
give this one a width of 24 and a height of six and let's let's give the bottom
of six and let's let's give the bottom one a width of
one a width of full and height of
full and height of 10 like that and let's give this upper
10 like that and let's give this upper one a margin bottom of two as well
one a margin bottom of two as well perfect so let me just save that and now
perfect so let me just save that and now let's go back inside of our index here
let's go back inside of our index here and here we render the card data so
and here we render the card data so let's copy and paste this below and
let's copy and paste this below and let's change this to use the audit logs
let's change this to use the audit logs data and it's going to render the
data and it's going to render the activity from dot slash activity and
activity from dot slash activity and activity here and we're going to pass in
activity here and we're going to pass in uh the items here to be act audit log
uh the items here to be act audit log data like that so let's just go ahead
data like that so let's just go ahead and quickly create an interface for the
and quickly create an interface for the activity here so
activity here so interface activity props is going to
interface activity props is going to have items which are a type of audit log
have items which are a type of audit log from Prisma client perfect and I think
from Prisma client perfect and I think we should not be having uh any error
we should not be having uh any error here let's see we we seem to be having
here let's see we we seem to be having uh errors oh we didn't assign that yes
uh errors oh we didn't assign that yes so all the activity props and extract
so all the activity props and extract the items uh great so now when you click
the items uh great so now when you click on a card let me just refresh when you
on a card let me just refresh when you click on a card there we go you have
click on a card there we go you have this uh lower loading state right here
this uh lower loading state right here and it seems something uh is happening
and it seems something uh is happening with our request let's check that out so
with our request let's check that out so right here audit logs data API cards ID
right here audit logs data API cards ID logs and let's check my API route so app
logs and let's check my API route so app API card ID
API card ID logs export asynchronous function get oh
logs export asynchronous function get oh I forgot to return the audit logs uh so
I forgot to return the audit logs uh so let's go back inside of the route for
let's go back inside of the route for logs here and after we fetch the logs
logs here and after we fetch the logs let's return next response Json audit
let's return next response Json audit logs like that all right and now let's
logs like that all right and now let's refresh this and let's pick another one
refresh this and let's pick another one and there we go now you can see how it
and there we go now you can see how it stops loading after a certain
stops loading after a certain time now let's go back inside of our
time now let's go back inside of our activity component inside of our uh card
activity component inside of our uh card model here and let's go ahead and give
model here and let's go ahead and give it some Styles so class name is going to
it some Styles so class name is going to be Flex items start Gap X3 and width of
be Flex items start Gap X3 and width of full we're going to render the activity
full we're going to render the activity icon from Lucid react and make sure you
icon from Lucid react and make sure you import it as activity icon or if you
import it as activity icon or if you can't do that because they have the
can't do that because they have the activity icon but you can see it clashes
activity icon but you can see it clashes with the name of our component so you
with the name of our component so you can always remap it using as activity
can always remap it using as activity icon or you can just simply import the
icon or you can just simply import the activity icon but you know just in case
activity icon but you know just in case they change it in the future or
they change it in the future or something and now let's give this
something and now let's give this activity icon a class name of flex items
activity icon a class name of flex items start Gap X3 uh sorry no I I copied the
start Gap X3 uh sorry no I I copied the one from above so uh height of five
one from above so uh height of five width of five margin top of 0. five text
width of five margin top of 0. five text neutral
neutral 700 let's create a div with a class name
700 let's create a div with a class name of with
of with full let's create a paragraph which says
full let's create a paragraph which says activity and let's go ahead and give
activity and let's go ahead and give this paragraph a class name of font semi
this paragraph a class name of font semi bold text neutral 700 and margin bottom
bold text neutral 700 and margin bottom of two and then let's create an ordered
of two and then let's create an ordered list here with a class name of margin
list here with a class name of margin top 2 and space y4 and in here let's
top 2 and space y4 and in here let's iate over our items like this and let's
iate over our items like this and let's just create a paragraph item item and
just create a paragraph item item and this should be item of course so item uh
this should be item of course so item uh dot well let's just say um entity title
dot well let's just say um entity title for now just so we see something here
for now just so we see something here let's try it out so not all of our cards
let's try it out so not all of our cards are going to have activity just the
are going to have activity just the recent one which we created and there we
recent one which we created and there we go you can see we have some existing
go you can see we have some existing activity here because it knows that this
activity here because it knows that this is the name of our uh component sorry of
is the name of our uh component sorry of our card and now let's create a reusable
our card and now let's create a reusable activity item
activity item component before we do that I just
component before we do that I just quickly want to create a lib which is
quickly want to create a lib which is going to be called generate log message
going to be called generate log message so we don't have to write our message
so we don't have to write our message ourselves every time so inside of the
ourselves every time so inside of the lib folder create a new file generate
lib folder create a new file generate log message.
log message. DS and let's go ahead and import action
DS and let's go ahead and import action from Prisma client and audit log uh from
from Prisma client and audit log uh from Prisma client as a type and now export
Prisma client as a type and now export const generate
const generate log message is going to be a log audit
log message is going to be a log audit log let's go ahead and extract the
log let's go ahead and extract the action entity
action entity title and entity type from log and let's
title and entity type from log and let's do a switch on the action in case it is
do a switch on the action in case it is action. create we're going to return
action. create we're going to return backx and this should be without column
backx and this should be without column here we're going to return
here we're going to return created entity
created entity title sorry entity type to
lowercase and then we're going to uh open annotations and render The Entity
open annotations and render The Entity title so if it is the create action
title so if it is the create action we're going to say created a card and
we're going to say created a card and then the name of the card like that and
then the name of the card like that and let's go ahead and just copy this uh
let's go ahead and just copy this uh actions so let me just indent them
actions so let me just indent them properly like that so the second action
properly like that so the second action is going to be update and we're here
is going to be update and we're here we're going to say that we
we're going to say that we updated and the last one is going to be
updated and the last one is going to be delete and in here we're going to say
delete and in here we're going to say deleted and then the last one is going
deleted and then the last one is going to be uh the default case and we're just
to be uh the default case and we're just going to say that it's an unknown action
going to say that it's an unknown action because well we don't know exactly what
because well we don't know exactly what happened if for any reason uh we don't
happened if for any reason uh we don't have that great so now that we have the
have that great so now that we have the generate log message let's go inside of
generate log message let's go inside of our
our components and let's create a new file
components and let's create a new file activity item. DSX so let me just close
activity item. DSX so let me just close this so uh alongside our hint and our
this so uh alongside our hint and our logo here let's go ahead and let's
logo here let's go ahead and let's export const activity
item like this let's go ahead and create a type for it so interface activity item
a type for it so interface activity item props is going to have the data audit
props is going to have the data audit log from Prisma
log from Prisma client and now let's also import the
client and now let's also import the generate log message from lib generate
generate log message from lib generate log message and let's also go inside of
log message and let's also go inside of our terminal here and let's run npx shat
our terminal here and let's run npx shat cn- at latest add Avatar because we're
cn- at latest add Avatar because we're going to need to display the image of
going to need to display the image of the person that created uh this specific
the person that created uh this specific uh log right so okay that's
uh log right so okay that's fine and let's also import that so
fine and let's also import that so import avatar from /ui Avatar and Avatar
import avatar from /ui Avatar and Avatar image and I'm just going to replace that
image and I'm just going to replace that to use
to use components and now let's assign the
components and now let's assign the props so activity item props let's
props so activity item props let's extract the data from here and let's go
extract the data from here and let's go inside and let's return a list element
inside and let's return a list element and let's give it a class name of flex
and let's give it a class name of flex items Center and Gap
items Center and Gap X2 and let's write an avatar here which
X2 and let's write an avatar here which we have imported let's give it a class
we have imported let's give it a class name of h-8 and
name of h-8 and w-8 and let's render the Avatar
w-8 and let's render the Avatar image so we have both of those imported
image so we have both of those imported from here right and let's give this one
from here right and let's give this one a source of
a source of data user
data user image and now below that let's create a
image and now below that let's create a div with a class name flex flex-all and
div with a class name flex flex-all and space- Y
space- Y 0.5 and inside open up a paragraph and
0.5 and inside open up a paragraph and let's give this paragraph a class name
let's give this paragraph a class name of text small and text not test text
of text small and text not test text small and text muted
small and text muted foreground and inside let's open up a
foreground and inside let's open up a span and render data username and give
span and render data username and give this span a class name of font semi bold
this span a class name of font semi bold lower case and text neutral
lower case and text neutral 700 and then uh just outside of this
700 and then uh just outside of this span here add a space and run generate
span here add a space and run generate log message and paste in the data like
log message and paste in the data like that and then just outside of here we're
that and then just outside of here we're going to add a new paragraph uh which is
going to add a new paragraph uh which is going to well say when this was created
going to well say when this was created and for that we're going to need to
and for that we're going to need to install mpm uh I date FNS so just make
install mpm uh I date FNS so just make sure you install date FNS so we can work
sure you install date FNS so we can work with dates now so let's go to the top
with dates now so let's go to the top here and let's import format from date
here and let's import format from date FNS and now here we're going to go ahead
FNS and now here we're going to go ahead and render
and render format new date data. created at and
format new date data. created at and we're going to format it in a type of
we're going to format it in a type of mmm D comma y y y y and then open the
mmm D comma y y y y and then open the small annotations at and then we place
small annotations at and then we place the time H mm a like that and give this
the time H mm a like that and give this paragraph a class name of text extra
paragraph a class name of text extra small text muted foreground like that
small text muted foreground like that perfect now let's go back inside uh of
perfect now let's go back inside uh of our activity inside of the card model
our activity inside of the card model here and instead let's surrender the
here and instead let's surrender the activity item from components activity
activity item from components activity item so make sure you import that and
item so make sure you import that and let's go ahead let's give it a key of
let's go ahead let's give it a key of item. ID and let's give it data of item
item. ID and let's give it data of item like that and let's try that out now
like that and let's try that out now when I click on test there we go it says
when I click on test there we go it says that I created test and now what we have
that I created test and now what we have to do well for every new card that we
to do well for every new card that we create that's going to be true so it's
create that's going to be true so it's going to be having uh the new audit log
going to be having uh the new audit log here and now we have to add audit logs
here and now we have to add audit logs for all the other actions inside of our
for all the other actions inside of our projects so just go inside of the
projects so just go inside of the actions and we're going to go inside of
actions and we're going to go inside of each of those and add where we
each of those and add where we can so let's start with with the copy
can so let's start with with the copy card go inside of the index here and in
card go inside of the index here and in here where we assigned the new card go
here where we assigned the new card go ahead and await DB sorry a wait create
ahead and await DB sorry a wait create audit log from s/ lib create audit log
audit log from s/ lib create audit log and give it an entity uh title to be c.
and give it an entity uh title to be c. tile go ahead and give it entity ID to
tile go ahead and give it entity ID to be car. ID entity type to be entity type
be car. ID entity type to be entity type from Prisma do client so make sure you
from Prisma do client so make sure you import that card and and we're going to
import that card and and we're going to have
have action to be action again from Prisma
action to be action again from Prisma client. create like that so let me show
client. create like that so let me show you my imports I imported the create
you my imports I imported the create audit log here and I imported uh Prisma
audit log here and I imported uh Prisma client action and entity type and we're
client action and entity type and we're going to do that uh for all of those so
going to do that uh for all of those so I'm actually just going to copy this so
I'm actually just going to copy this so I can easily do it so you can copy
I can easily do it so you can copy create audit log as well after copy card
create audit log as well after copy card let's go into the side of copy list and
let's go into the side of copy list and in here where we assign our list let's
in here where we assign our list let's go here and let's import create audit
go here and let's import create audit log from lib create audit log let's use
log from lib create audit log let's use the list title let's use the list ID and
the list title let's use the list ID and import The Entity type from Prisma
import The Entity type from Prisma client and the action from the card
client and the action from the card great and let me just align uh my
great and let me just align uh my imports
imports here great all right and now let's go
here great all right and now let's go into the next one so that's going to be
into the next one so that's going to be our create board so go inside of the
our create board so go inside of the index here after we create the board go
index here after we create the board go ahead and create a new audit log use the
ahead and create a new audit log use the board title and board ID import The
board title and board ID import The Entity type and import action great now
Entity type and import action great now let's go uh
let's go uh inside and let's change the entity type
inside and let's change the entity type of course yes I think I forgot that in
of course yes I think I forgot that in the list as well so change the entity
the list as well so change the entity type here to be board right and go
type here to be board right and go inside of the list and change the entity
inside of the list and change the entity type to be list as well so that's
type to be list as well so that's important all right and the copy card
important all right and the copy card one is correct now let's go inside of
one is correct now let's go inside of create list Index right here and we do
create list Index right here and we do the same thing so find the list right
the same thing so find the list right here and let's import create audit log
here and let's import create audit log we are using the list and let's import
we are using the list and let's import The Entity type let's import the action
The Entity type let's import the action so the action is correct but the entity
so the action is correct but the entity type needs to be
type needs to be list all right now let's go inside of
list all right now let's go inside of delete board go inside of index here and
delete board go inside of index here and after we delete it go ahead and import
after we delete it go ahead and import create audit log from lib SLC create
create audit log from lib SLC create audit log we are using this board and
audit log we are using this board and entity type is going to be board and our
entity type is going to be board and our action so we make sure you import both
action so we make sure you import both entity type and the action is going to
entity type and the action is going to be board uh sorry
be board uh sorry delete all right now let's go and fix
delete all right now let's go and fix the delete card so all the way to the
the delete card so all the way to the bottom here let's add create audit log
bottom here let's add create audit log let's use the card The Entity type is
let's use the card The Entity type is card make sure you import the entity
card make sure you import the entity type and the action delete is correct
type and the action delete is correct because uh we delete here as well now
because uh we delete here as well now let's go into the delete list and we're
let's go into the delete list and we're doing the same thing so find where we
doing the same thing so find where we deleted the list in the tri block let's
deleted the list in the tri block let's import create audit log let's use use
import create audit log let's use use the list information we import The
the list information we import The Entity type we import the action The
Entity type we import the action The Entity type is list but the Del the
Entity type is list but the Del the action is correct now let's go inside uh
action is correct now let's go inside uh of the update board index
of the update board index here and let's paste that here as well
here and let's paste that here as well so after we update the board import
so after we update the board import create audit log use the new board The
create audit log use the new board The Entity type is is board and the action
Entity type is is board and the action is
is update great and let's go make sure you
update great and let's go make sure you save those files of course go inside of
save those files of course go inside of update card now and it's going to be
update card now and it's going to be pretty similar here so create AIT log
pretty similar here so create AIT log we're going to use the card import The
we're going to use the card import The Entity type which is a type of card and
Entity type which is a type of card and import the action which is still update
import the action which is still update and go inside of we're going to skip the
and go inside of we're going to skip the update card order and update list order
update card order and update list order and instead we're going to go just for
and instead we're going to go just for the update list here so when we change
the update list here so when we change the title of the list let's go ahead and
the title of the list let's go ahead and create audit log here and let's use the
create audit log here and let's use the list information and let's use the
list information and let's use the entity type from Prisma client and let's
entity type from Prisma client and let's use the action and that is the same
use the action and that is the same action perfect so now whatever we do uh
action perfect so now whatever we do uh we have of course our audit logs again
we have of course our audit logs again just make sure that you add the
just make sure that you add the necessary imports from Prisma client and
necessary imports from Prisma client and create audit log great but what happen
create audit log great but what happen so right
so right now you can see when I try let's try out
now you can see when I try let's try out so when I edit something
so when I edit something here it should give me a new oh yes we
here it should give me a new oh yes we forgot we need to revalidate this as
forgot we need to revalidate this as well so I want to go back inside of the
well so I want to go back inside of the card model so go inside of components
card model so go inside of components models card model here and we have the
models card model here and we have the header right let's go inside of the
header right let's go inside of the header first and in here we do some
header first and in here we do some query client invalidate queries so let's
query client invalidate queries so let's just copy this and let's also re re
just copy this and let's also re re invalidate the card log if that is the
invalidate the card log if that is the correct one so it's card logs so reinv
correct one so it's card logs so reinv validate the card logs and
validate the card logs and now let me just
now let me just refresh pick a random one change the
refresh pick a random one change the title and now it should reinv valid it
title and now it should reinv valid it with the new logs and they're right here
with the new logs and they're right here and they will also work it will not work
and they will also work it will not work for the description so we have to do the
for the description so we have to do the same thing in the description so copy
same thing in the description so copy the invalidate queries go inside of the
the invalidate queries go inside of the description here uh and let's see where
description here uh and let's see where is our on success it's right here so
is our on success it's right here so after we reinv validate the card also
after we reinv validate the card also reinv validate the card logs so let me
reinv validate the card logs so let me try that out now when I add a new
try that out now when I add a new description there we go it says that we
description there we go it says that we updated the
updated the card and if you're wondering about this
card and if you're wondering about this fact that sometimes it's empty well this
fact that sometimes it's empty well this should never happen right it's because
should never happen right it's because we created these cards before we
we created these cards before we implemented audit logs so after you
implemented audit logs so after you reset your database right when you're
reset your database right when you're going into production or whatever uh
going into production or whatever uh you're going to see always see some
you're going to see always see some activity here or you can use Prisma
activity here or you can use Prisma migrate reset perfect so we finished the
migrate reset perfect so we finished the activity here uh and let's just try for
activity here uh and let's just try for copy for example when I click copy here
copy for example when I click copy here it also has the created card exactly
it also has the created card exactly what we want and now we finally are able
what we want and now we finally are able to create the activity tab here which is
to create the activity tab here which is going to be quite easy because we have a
going to be quite easy because we have a lot of reusable components
lot of reusable components here so let's go so uh let me just
here so let's go so uh let me just expand this let's go inside of the app
expand this let's go inside of the app folder platform dashboard organization
folder platform dashboard organization organization ID and in here create a new
organization ID and in here create a new folder called activity like that and
folder called activity like that and inside of activity create a new file
inside of activity create a new file page. DSX and let's go ahead and write
page. DSX and let's go ahead and write const activity
const activity page let's return a div activity page
page let's return a div activity page and don't forget yet you always have to
and don't forget yet you always have to export default when it comes to pages
export default when it comes to pages and
and layouts so activity
layouts so activity page like that and let's try and click
page like that and let's try and click on activity now and there we go no
on activity now and there we go no longer we have a 404 error if you're
longer we have a 404 error if you're still seeing a 404 error confirm that
still seeing a 404 error confirm that you have named the activity correctly
you have named the activity correctly and then uh you have to confirm inside
and then uh you have to confirm inside of your nav item here in the routes that
of your nav item here in the routes that you're in in this rout routs constant
you're in in this rout routs constant that your activity also doesn't have a
that your activity also doesn't have a typo here so just some possible culprits
typo here so just some possible culprits that could happen uh great and now
that could happen uh great and now inside of this page let's go ahead and
inside of this page let's go ahead and let's give this a class name up with
let's give this a class name up with full let's go ahead and let's enter the
full let's go ahead and let's enter the info component fromt do/ components info
info component fromt do/ components info so we have it uh right here the info
so we have it uh right here the info component like that let's add the
component like that let's add the separator from components UI separator
separator from components UI separator let's give it a class name of
let's give it a class name of my2 and then let's go ahead and let's
my2 and then let's go ahead and let's render the activity
render the activity list which we don't yet have but we're
list which we don't yet have but we're going to create in a second so go ahead
going to create in a second so go ahead inside of activity here create a new
inside of activity here create a new folder underc components and inside
folder underc components and inside create
create activity dl. DSX like that it's going to
activity dl. DSX like that it's going to be server component so no need to put
be server component so no need to put use client on the top activity list
use client on the top activity list is going to be an asynchronous
function and let's extract the organization ID from out from clerk
organization ID from out from clerk nextjs if we don't have the organization
nextjs if we don't have the organization ID in that case we can redirect the user
ID in that case we can redirect the user using next navigation to select or so
using next navigation to select or so make sure you import redirect from next
make sure you import redirect from next navigation and while we are here let's
navigation and while we are here let's also import the database let's import
also import the database let's import the activ
the activ item from components activity item and
item from components activity item and let's import the skeleton from
let's import the skeleton from components UI skeleton now let's go
components UI skeleton now let's go ahead and let's fetch the all the
ahead and let's fetch the all the activity logs for this organization so
activity logs for this organization so audit logs are await DB audit log find
audit logs are await DB audit log find many and let's go ahead and write where
many and let's go ahead and write where organization ID that's all we need and
organization ID that's all we need and then let's go ahead and let's return an
then let's go ahead and let's return an ordered list here and give it a class
ordered list here and give it a class name of space one y4 and margin top of
name of space one y4 and margin top of four let's add a paragraph no activity
four let's add a paragraph no activity found inside this organization and this
found inside this organization and this is only going to be visible if there are
is only going to be visible if there are no items so we can do that using CSS
no items so we can do that using CSS using the hidden by default but only if
using the hidden by default but only if it is the last element inside of this
it is the last element inside of this ordered list it's going to be visible
ordered list it's going to be visible using the block and text extra small
using the block and text extra small text Center and text muted
text Center and text muted foreground and in side of here let's do
foreground and in side of here let's do audit logs.
audit logs. map let's get the individual log in here
map let's get the individual log in here and let's render the activity
and let's render the activity item let's give it a key of log ID and
item let's give it a key of log ID and data of log itself great and now let's
data of log itself great and now let's just go ahead uh and create the skeleton
just go ahead uh and create the skeleton for this one so activity list. skeleton
for this one so activity list. skeleton is activity list
is activity list skeleton function activity list
skeleton function activity list skeleton and let's go ahead and let's
skeleton and let's go ahead and let's return an ordered list and let's give it
return an ordered list and let's give it a skeleton here and let's write class
a skeleton here and let's write class name width of
name width of 80% and the height of 14 and let's give
80% and the height of 14 and let's give this ordered list a class name of space
this ordered list a class name of space y4 and margin a top of four and now
y4 and margin a top of four and now let's just copy it a couple of times so
let's just copy it a couple of times so let's give this one a 50% width let's
let's give this one a 50% width let's give this one a 70 % with this one an
give this one a 70 % with this one an 80% and last one a
80% and last one a 75% like that so make sure you have this
75% like that so make sure you have this skeleton now go back to the page. DSX
skeleton now go back to the page. DSX right here and let's go ahead and do the
right here and let's go ahead and do the following let's import suspense from
following let's import suspense from react let's wrap the activity list
react let's wrap the activity list inside of suspense here let's import the
inside of suspense here let's import the activity list from do/ components uh
activity list from do/ components uh activity list so let me just separate my
activity list so let me just separate my imports just a bit and now let's go
imports just a bit and now let's go ahead and give it a fullback of activity
ahead and give it a fullback of activity list. skeleton like that there we go now
list. skeleton like that there we go now we have the activity page so you can see
we have the activity page so you can see in here we shouldn't have any uh uh any
in here we shouldn't have any uh uh any activity and now let's try out and
activity and now let's try out and create a new board for example so I'm
create a new board for example so I'm going to create a random board here and
going to create a random board here and I'm going to go back to the uh activity
I'm going to go back to the uh activity here and there we go it says that I
here and there we go it says that I created a board perfect so let's try and
created a board perfect so let's try and renaming the board now and let's go
renaming the board now and let's go ahead and let's add a list here and
ahead and let's add a list here and besides the list let's also go ahead uh
besides the list let's also go ahead uh and create a card all of those things
and create a card all of those things let's uh copy a card for
let's uh copy a card for example and let's delete a card so all
example and let's delete a card so all of those things let's go back now to the
of those things let's go back now to the activity and there we go created the
activity and there we go created the card created the board created a card
card created the board created a card updated the board created a list we have
updated the board created a list we have everything we need in here uh and just
everything we need in here uh and just one more thing that I want to
one more thing that I want to do uh and this thing I think yeah it
do uh and this thing I think yeah it should be like this activity list.
should be like this activity list. skeleton or not oh I have a mistake here
skeleton or not oh I have a mistake here skeleton uh yes and I think yeah we need
skeleton uh yes and I think yeah we need to render it like this great and now
to render it like this great and now just go inside of the activity list and
just go inside of the activity list and also add order by created at descending
also add order by created at descending so it is in proper order otherwise it
so it is in proper order otherwise it doesn't give us uh too much information
doesn't give us uh too much information there so we created the board we updated
there so we created the board we updated the board created a list created a card
the board created a list created a card created a copy card and then deleted a
created a copy card and then deleted a card perfect and you can see how we have
card perfect and you can see how we have a loading state for our activity great
a loading state for our activity great great job all that's left is to create
great job all that's left is to create the
the payments all right so now that we
payments all right so now that we finished our activity it's time to get
finished our activity it's time to get to the last part of this tutorial which
to the last part of this tutorial which is implementing board limits and a
is implementing board limits and a subscription so right now we have this
subscription so right now we have this text which says that five are remaining
text which says that five are remaining but it is hardcoded and it's not
but it is hardcoded and it's not actually enforcing anything so let's go
actually enforcing anything so let's go ahead and change that first I want to go
ahead and change that first I want to go back inside of my Prisma schema right
back inside of my Prisma schema right here and let's go after our model audit
here and let's go after our model audit log let's create a model organization
log let's create a model organization limit short org limit let's give it an
ID and a default value of uu
value of uu ID now let's go ahead and give it an org
ID now let's go ahead and give it an org ID which is a string let's give it a
ID which is a string let's give it a count which is an integer and by default
count which is an integer and by default it's going to be zero and let's also
it's going to be zero and let's also ensure that this organization ID is
ensure that this organization ID is unique for every organization limit we
unique for every organization limit we want to create and after that let's add
want to create and after that let's add the created ad field which is going to
the created ad field which is going to be a date time and a default of now and
be a date time and a default of now and we're also going to have the updated ad
we're also going to have the updated ad with a date time and a default sorry
with a date time and a default sorry updated ad and let me just align all of
updated ad and let me just align all of these
these things let's try like
this and now let's go ahead and let's push this into the database so just make
push this into the database so just make sure you have the ID make sure you have
sure you have the ID make sure you have the organization ID which should be
the organization ID which should be unique make sure you have count which is
unique make sure you have count which is an integer with a default value of zero
an integer with a default value of zero let's go ahead and let's do the
let's go ahead and let's do the following npx well first as always shut
following npx well first as always shut down the app right that's important and
down the app right that's important and then let's do npx Prisma generate so
then let's do npx Prisma generate so it's locally available and then npx
it's locally available and then npx Prisma DB hush like this and I actually
Prisma DB hush like this and I actually just want to clear out the entire
just want to clear out the entire database so let's do that as well MPX
database so let's do that as well MPX Prisma migrate reset like this and
Prisma migrate reset like this and confirm that you want to reset the
confirm that you want to reset the entire database because I want to
entire database because I want to enforce this count of our boards and
enforce this count of our boards and after you do the reset you have to run
after you do the reset you have to run DB push again so I should have done that
DB push again so I should have done that first my apologies but nevertheless uh
first my apologies but nevertheless uh let's go ahead and proceed with this so
let's go ahead and proceed with this so now that we have the organization limit
now that we have the organization limit I want to go ahead and I want to create
I want to go ahead and I want to create uh a constant called boards so let's go
uh a constant called boards so let's go inside of our constants folder where we
inside of our constants folder where we already have the images and let's create
already have the images and let's create a new file board boards. DS and let's
a new file board boards. DS and let's export const Max three boards to be five
export const Max three boards to be five so from here you will be able to change
so from here you will be able to change it if you want for your application
it if you want for your application great and now I want to create a helper
great and now I want to create a helper called organization limit from where
called organization limit from where we're going to fetch all the necessary
we're going to fetch all the necessary information about how many boards there
information about how many boards there is left how many uh how to increase a
is left how many uh how to increase a board and to check if we can even create
board and to check if we can even create a new one for free so go inside of the
a new one for free so go inside of the lib folder and create a new file org
lib folder and create a new file org limit. Cs and let's go ahead and let's
limit. Cs and let's go ahead and let's import the out from clerk nextjs let's
import the out from clerk nextjs let's go ahead and import the database and I'm
go ahead and import the database and I'm just going to change the import to use
just going to change the import to use at/ lip and let's go ahead and import
at/ lip and let's go ahead and import Max free boards from constants boards
Max free boards from constants boards and first let's write a function so we
and first let's write a function so we can easily increment the available count
can easily increment the available count so whenever we create a new board we
so whenever we create a new board we have to run this function so export
have to run this function so export const increment
const increment available
available count that is going to be an
count that is going to be an asynchronous function let's go ahead and
asynchronous function let's go ahead and extract the organization ID from our out
extract the organization ID from our out helper if we don't have the organization
helper if we don't have the organization ID we can just break the function and
ID we can just break the function and let's write const organization limit to
let's write const organization limit to be await db. organization limit. find
be await db. organization limit. find unique
unique where we have the organization ID and I
where we have the organization ID and I actually think it would be better if we
actually think it would be better if we threw an error here so throw new error
threw an error here so throw new error and now
and now authorized so we want to we want to
authorized so we want to we want to break the server action if we are not
break the server action if we are not having any organization ID right we need
having any organization ID right we need to enforce this uh and now that we
to enforce this uh and now that we fetched this organization limit let's
fetched this organization limit let's check if we have it at all so if we have
check if we have it at all so if we have the organization limit in that case
the organization limit in that case let's do await DB organization limit
let's do await DB organization limit update where we have a matching
update where we have a matching organization ID and data is going to be
organization ID and data is going to be count the current organization limit
count the current organization limit which we fetched just here do count +
which we fetched just here do count + one so we increase the current value by
one so we increase the current value by one but if this is the first time they
one but if this is the first time they have created a board then this object
have created a board then this object this model doesn't not exist so we have
this model doesn't not exist so we have to create it await DB org limit. create
to create it await DB org limit. create data passing the matching organization
data passing the matching organization ID and count is fixed to one because
ID and count is fixed to one because it's the first board they created great
it's the first board they created great and now we have to create the same thing
and now we have to create the same thing but to uh decrease the uh amount so
but to uh decrease the uh amount so let's go ahead and copy this
let's go ahead and copy this function like that and let's rename it
function like that and let's rename it uh to
uh to decrease available count and let's go
decrease available count and let's go ahead and this stays the same and then
ahead and this stays the same and then we check if we have the organization
we check if we have the organization limit and then we update it and we could
limit and then we update it and we could technically do just minus one but that
technically do just minus one but that could uh take us into a negative number
could uh take us into a negative number so instead we're going to check if the
so instead we're going to check if the current organization limit count is
current organization limit count is larger than zero then we're going to do
larger than zero then we're going to do organization limit do count minus one
organization limit do count minus one otherwise we're going to go back to zero
otherwise we're going to go back to zero so this way we ensure that there's no
so this way we ensure that there's no way the new count can be lower than zero
way the new count can be lower than zero otherwise it's going to be a count of
otherwise it's going to be a count of one and now let's go ahead and create a
one and now let's go ahead and create a helper method to help us check whether
helper method to help us check whether the user can create a new free board so
the user can create a new free board so export con has
export con has available count is going to be an
available count is going to be an asynchronous
asynchronous function where we're going to fetch the
function where we're going to fetch the organization ID
again if there is no organization ID throw new
throw new error
unauthorized and then let's go ahead and let's fetch the organization limit to be
let's fetch the organization limit to be await DB organization limit find unique
await DB organization limit find unique where we have a matching organization ID
where we have a matching organization ID and then we're going to write if there
and then we're going to write if there is no organization limit or if
is no organization limit or if organization limit. count is smaller
organization limit. count is smaller than Max free boards so if they created
than Max free boards so if they created less than five boards we return true
less than five boards we return true meaning that it's okay for them to
meaning that it's okay for them to create a new one else we're going to
create a new one else we're going to return
return balse and let's go ahead and and create
balse and let's go ahead and and create the last helper which is just going to
the last helper which is just going to be a helper to actually tell us the
be a helper to actually tell us the number of free boards they've used up so
number of free boards they've used up so export const get available count is
export const get available count is again an asynchronous
again an asynchronous function which again uses the
function which again uses the organization ID using the out Helper and
organization ID using the out Helper and this time if we don't have the
this time if we don't have the organization ID we're just going to
organization ID we're just going to return zero because this is going to be
return zero because this is going to be used in our uh UI helpers right so let's
used in our uh UI helpers right so let's go ahead and fetch const get
go ahead and fetch const get organization limit to be await DB
organization limit to be await DB organization limit find
unique where we have a matching organization ID like
organization ID like that if we don't have the let's rename
that if we don't have the let's rename this to organization limit if we don't
this to organization limit if we don't have the organization I limit we're
have the organization I limit we're going to return zero otherwise we're
going to return zero otherwise we're going to return the organization limit
going to return the organization limit ount great and now that we have this we
ount great and now that we have this we can go back uh inside of our board list
can go back uh inside of our board list so let's go inside of our board list
so let's go inside of our board list component so that's inside of the app
component so that's inside of the app folder platform dashboard organ uh
folder platform dashboard organ uh organization organization ID components
organization organization ID components and in here we have the board list great
and in here we have the board list great and let's go ahead and let's do the
and let's go ahead and let's do the following so I want to import Max free
following so I want to import Max free boards from the constants and I want to
boards from the constants and I want to import get available count from at/
import get available count from at/ liborg limit like that and now what I'm
liborg limit like that and now what I'm going to do is after uh I do this
going to do is after uh I do this fetching of the boards here I'm going to
fetching of the boards here I'm going to get the available count so const
available count is going to be a wait get available
get available count like that and then let's go all
count like that and then let's go all the way to the bottom here here where we
the way to the bottom here here where we write five remaining and instead of
write five remaining and instead of writing that let's go ahead and let's do
writing that let's go ahead and let's do the following let's make this Dynamic
the following let's make this Dynamic and let's
and let's write Max fre
write Max fre boards like that minus the available
boards like that minus the available count
count remaining like that perfect so let's
remaining like that perfect so let's refresh our uh Local Host and let's
refresh our uh Local Host and let's actually make sure that it is running
actually make sure that it is running first
first mpm
runev so let's refresh this now and let's wait a second uh for this to
let's wait a second uh for this to reinitialize and it says that we have
reinitialize and it says that we have five remaining great so it's working
five remaining great so it's working because right now what we are doing is
because right now what we are doing is we are calculating 5 minus 0 because
we are calculating 5 minus 0 because take a look at our get available count
take a look at our get available count uh well first it returns zero if there
uh well first it returns zero if there is no organization ID but also if there
is no organization ID but also if there was no organization limit found in the
was no organization limit found in the database it means that we haven't even
database it means that we haven't even created the first board for anything for
created the first board for anything for us to calculate so we are reducing five
us to calculate so we are reducing five by zero which in turn is five now we
by zero which in turn is five now we have to add one of our helpers here
have to add one of our helpers here which is going to be this one increment
which is going to be this one increment the available count inside of our server
the available count inside of our server action where we create the
action where we create the board so let's go inside let me close
board so let's go inside let me close this and let's go inside of actions
this and let's go inside of actions create board right here
create board right here and after we successfully create our
and after we successfully create our board let's go ahead and uh write await
board let's go ahead and uh write await increment available count and make sure
increment available count and make sure you import increment available count
you import increment available count from at/ liborg limit like that uh but I
from at/ liborg limit like that uh but I also want to import another thing from
also want to import another thing from here which is has available count so
here which is has available count so make sure you have the increment
make sure you have the increment available count and has available count
available count and has available count from our newly created org limit util
from our newly created org limit util and now we're going to go ahead uh and
and now we're going to go ahead uh and use this has available count right after
use this has available count right after we check for uh authentication let's go
we check for uh authentication let's go ahead and write con can create to be has
ahead and write con can create to be has available count but let's also make sure
available count but let's also make sure to await this otherwise it's going to be
to await this otherwise it's going to be a promise and if we cannot create
a promise and if we cannot create meaning that we surpassed our available
meaning that we surpassed our available account we're going to return an error
account we're going to return an error saying you have reached your limit of
saying you have reached your limit of free boards please upgrade to create
free boards please upgrade to create more great and let's try that out now so
more great and let's try that out now so I believe it should already be working
I believe it should already be working let's just confirm that we properly
let's just confirm that we properly added this await increment available
added this await increment available count we did great and let's try it out
count we did great and let's try it out so I'm going to refresh this now I'm
so I'm going to refresh this now I'm going to create my first board and I'm
going to create my first board and I'm going to click create and once I'm
going to click create and once I'm redirected to the
redirected to the board and I go back there we go now it
board and I go back there we go now it says four remaining let's try and fill
says four remaining let's try and fill this up now so I'm going to create
this up now so I'm going to create another one here let's go back now it
another one here let's go back now it says three remaining great let's create
says three remaining great let's create another one and let's go back uh to
another one and let's go back uh to confirm two remaining great we are
confirm two remaining great we are getting close to getting our error there
getting close to getting our error there we go one remaining and let's go ahead
we go one remaining and let's go ahead uh and let's get the last one here one
uh and let's get the last one here one remaining there we go zero remaining and
remaining there we go zero remaining and now let's go ahead uh an attempt to
now let's go ahead uh an attempt to create and there we go we have an error
create and there we go we have an error you have reached the limits of free
you have reached the limits of free boards please upgrade to create more and
boards please upgrade to create more and what I want to create now is that if the
what I want to create now is that if the user deletes one of their free boards we
user deletes one of their free boards we want to decrease the count right so
want to decrease the count right so let's go ahead uh and do that so the
let's go ahead uh and do that so the only thing we're going to need is that
only thing we're going to need is that delete uh is that decrease available
delete uh is that decrease available count so let's find the delete board
count so let's find the delete board action right here and let's go ahead and
action right here and let's go ahead and let's
let's import
decrease available count from lib or limit here and let's go all the way down
limit here and let's go all the way down here after we delete the board and await
here after we delete the board and await decrease available count and let's try
decrease available count and let's try that out now so let me just confirm and
that out now so let me just confirm and refresh here so it says zero remaining
refresh here so it says zero remaining and I'm going to remove this board so
and I'm going to remove this board so delete this board I will get redirected
delete this board I will get redirected back and now again it says one remaining
back and now again it says one remaining and if I try I should not be getting any
and if I try I should not be getting any errors and I am not great so our front
errors and I am not great so our front end and back end is fully in sync and we
end and back end is fully in sync and we can reverse that action so now we found
can reverse that action so now we found a way to block the user from creating
a way to block the user from creating more than five boards and now we have to
more than five boards and now we have to find a way to enable them to create more
find a way to enable them to create more if they add a stripe
if they add a stripe subscription so so for that we have to
subscription so so for that we have to head back inside of our Prisma schema so
head back inside of our Prisma schema so let's close everything here let's go
let's close everything here let's go inside of prismas schema. Prisma and
inside of prismas schema. Prisma and let's create a our last Model
let's create a our last Model organization subscription so model or
organization subscription so model or subscription for short is going to have
subscription for short is going to have an ID which is a string uh and a type of
an ID which is a string uh and a type of ID with the default value of uu ID we're
ID with the default value of uu ID we're going to have an organization ID which
going to have an organization ID which is also a string and unique for every
is also a string and unique for every subscription and we're going to have the
subscription and we're going to have the stripe customer ID which is going to be
stripe customer ID which is going to be a string
a string optional unique and we're also going to
optional unique and we're also going to map it to a specific value so we can
map it to a specific value so we can access it differently so give it a name
access it differently so give it a name stripe uncore customer ID so exactly
stripe uncore customer ID so exactly what we have here but in a different
what we have here but in a different case like that so let me just separate
case like that so let me just separate this to so we can see them uh more
this to so we can see them uh more clearly and let me just uh move this
clearly and let me just uh move this here all right besides this we're going
here all right besides this we're going to have the stripe subscription ID which
to have the stripe subscription ID which is also an optional string it's also
is also an optional string it's also unique and let's map the name to be
unique and let's map the name to be stripe undor subscription _
stripe undor subscription _ ID and now let's go ahead and create
ID and now let's go ahead and create stripe price
stripe price ID which is going to be a optional
ID which is going to be a optional string as well and it's not going to be
string as well and it's not going to be unique but it's going to have a name of
unique but it's going to have a name of stripe uncore price uncore ID and last
stripe uncore price uncore ID and last one is stripe current period and date
one is stripe current period and date time optional and let's map the name to
time optional and let's map the name to be stripe current period underscore end
be stripe current period underscore end like that perfect so make sure you have
like that perfect so make sure you have all of those of course you can always
all of those of course you can always visit my GitHub to see uh the exact code
visit my GitHub to see uh the exact code as you can see I made a typo here so it
as you can see I made a typo here so it should be period
so just double check that you don't have any typos you can always confirm uh with
any typos you can always confirm uh with my source
my source code and now what we have to do is push
code and now what we have to do is push this to the database as well so let's go
this to the database as well so let's go ahead and shut down our application and
ahead and shut down our application and run npx Prisma DB
run npx Prisma DB push let's ensure that it's up to date
push let's ensure that it's up to date with our planet scale or wherever you're
with our planet scale or wherever you're hosting your MySQL and then let's do npx
hosting your MySQL and then let's do npx Prisma generate so we can locally access
Prisma generate so we can locally access this perfect now let's go ahead and
this perfect now let's go ahead and let's run mpm install stripe while we
let's run mpm install stripe while we are here great and now we don't have to
are here great and now we don't have to run the app just yet because we're going
run the app just yet because we're going to be creating a lot of Libs now so the
to be creating a lot of Libs now so the first lib I want to create is the stripe
first lib I want to create is the stripe lib so let's go inside of lib and create
lib so let's go inside of lib and create a new file stripe. DS let's
a new file stripe. DS let's import Stripe from stripe let's export
import Stripe from stripe let's export cons stripe to be new stripe and then
cons stripe to be new stripe and then we're going to give it a process.
we're going to give it a process. environment. stripe API uncore key and
environment. stripe API uncore key and put an exclamation point at the end so
put an exclamation point at the end so we're going to have to add this in a
we're going to have to add this in a moment and now let's add some options
moment and now let's add some options here so API version it can be different
here so API version it can be different if you're watching this in the future
if you're watching this in the future but you can just let typescript autoc
but you can just let typescript autoc completed for you so you can see for me
completed for you so you can see for me it's 2023 1016 for you it might be
it's 2023 1016 for you it might be something different for example my
something different for example my previous tutorials don't use this exact
previous tutorials don't use this exact version right but you can it will always
version right but you can it will always be matching to your package which you
be matching to your package which you install so if you have the newest
install so if you have the newest package which we just did in a second
package which we just did in a second when you do this you can see that you
when you do this you can see that you have an auto complete like this and I'm
have an auto complete like this and I'm going to show you another way to confirm
going to show you another way to confirm which is the newest version when once we
which is the newest version when once we get to the stripe dashboard so don't
get to the stripe dashboard so don't just don't worry for now you can just
just don't worry for now you can just write it like this if you have an error
write it like this if you have an error I believe you if you type something else
I believe you if you type something else you can see that it has an error so if
you can see that it has an error so if you wrote everything correctly you're
you wrote everything correctly you're going to have no errors and let's write
going to have no errors and let's write typescript true great and now what I
typescript true great and now what I want to create is I want to create a lib
want to create is I want to create a lib to check our subscription right to
to check our subscription right to confirm that we have an active
confirm that we have an active subscription so let's go ahead inside
subscription so let's go ahead inside the LI folder and create a new file
the LI folder and create a new file check actually let's just call it
check actually let's just call it subscription. DS and in here let's go
subscription. DS and in here let's go ahead and let's import the out from
ahead and let's import the out from clerk nextjs let's import the database
clerk nextjs let's import the database and I'm going to change this to SL lib
and I'm going to change this to SL lib and let's write a constant const day in
and let's write a constant const day in milliseconds is going to be 86
milliseconds is going to be 86 400 0 like that uh and I close that my
400 0 like that uh and I close that my apologies all right and now let's export
apologies all right and now let's export con check subscription to be an
con check subscription to be an asynchronous
asynchronous function and let's extract the
function and let's extract the organization ID from our out service if
organization ID from our out service if there is no organization ID we're just
there is no organization ID we're just going to say false meaning we don't have
going to say false meaning we don't have any any subscription here so we are not
any any subscription here so we are not ever going to say true uh optimistically
ever going to say true uh optimistically right so if we are not able to fetch the
right so if we are not able to fetch the organization ID we're just going to say
organization ID we're just going to say okay no subscription for you and now
okay no subscription for you and now let's attempt to fetch the subscription
let's attempt to fetch the subscription so const org subscription is await dbor
so const org subscription is await dbor subscription. find
subscription. find unique where we have a matching
unique where we have a matching organization ID and let's select stripe
organization ID and let's select stripe uh subscription ID to be true stripe
uh subscription ID to be true stripe stripe current period end to be true
stripe current period end to be true stripe customer ID to be true and stripe
stripe customer ID to be true and stripe price ID to be true as well and now
price ID to be true as well and now let's write if we don't have the
let's write if we don't have the subscription return
subscription return false otherwise let's go ahead and check
false otherwise let's go ahead and check if it's valid so just because we have it
if it's valid so just because we have it that's not enough we need to check if
that's not enough we need to check if it's not expired so const is
it's not expired so const is valid is going to check if organization
valid is going to check if organization subscription
subscription do stripe price
do stripe price ID exists and if organization
ID exists and if organization subscription. stripe Uh current period
subscription. stripe Uh current period and question mark. getet time and let's
and question mark. getet time and let's just add an exclamation point at the end
just add an exclamation point at the end here so we're going to compare the time
here so we're going to compare the time of the period End plus one extra day
of the period End plus one extra day right and it's larger than date. now and
right and it's larger than date. now and let me just zoom this out so you can see
let me just zoom this out so you can see it in one line so stripe current period
it in one line so stripe current period at end we get the time and we add a
at end we get the time and we add a buffer of one day right and we compare
buffer of one day right and we compare if even that is larger than the current
if even that is larger than the current date then it means that it is valid uh
date then it means that it is valid uh great so just ensure that you wrote that
great so just ensure that you wrote that and of course we have to return is valid
and of course we have to return is valid and let's turn it into a Boolean by
and let's turn it into a Boolean by adding double exclamation points at the
adding double exclamation points at the end so please don't make one right make
end so please don't make one right make sure you put double this will turn it
sure you put double this will turn it into a uh for sure sure a Boolean uh
into a uh for sure sure a Boolean uh great so we have this done and now we
great so we have this done and now we have to create our stripe API key and
have to create our stripe API key and our web hook and our actions to actually
our web hook and our actions to actually trigger the
trigger the checkout so let's go ahead and let's so
checkout so let's go ahead and let's so we just wrapped up this subscription
we just wrapped up this subscription right is valid now let's go ahead and
right is valid now let's go ahead and let's head on stripe.com and go ahead
let's head on stripe.com and go ahead and just log in and you should be seeing
and just log in and you should be seeing this dashboard and what I want you to do
this dashboard and what I want you to do is to create a new well store right so
is to create a new well store right so you can either use uh this top left
you can either use uh this top left corner and scroll all the way down to
corner and scroll all the way down to where it says new account or if this is
where it says new account or if this is your first time using stripe I think
your first time using stripe I think this will be open for you by default so
this will be open for you by default so let's call this Trello tutorial and
let's call this Trello tutorial and let's click create an account right here
let's click create an account right here and now we will be able uh to copy uh
and now we will be able uh to copy uh our API key so click on developers right
our API key so click on developers right here in the nov bar and here we have the
here in the nov bar and here we have the API I Keys Tab and in here click reveal
API I Keys Tab and in here click reveal the secret key and click again to copy
the secret key and click again to copy that secret key and now we have to put
that secret key and now we have to put that right here in our stripe API key so
that right here in our stripe API key so let's go inside of our environment file
let's go inside of our environment file and let's add stripe aior key and paste
and let's add stripe aior key and paste the key inside and double check that
the key inside and double check that your stripee API key matches exactly
your stripee API key matches exactly what you've written in the environment
what you've written in the environment file
file so now what I want to create is the user
so now what I want to create is the user interface for an actual uh you know a
interface for an actual uh you know a popover model which tells the user all
popover model which tells the user all right you need to upgrade now so let's
right you need to upgrade now so let's go ahead and do that so I'm going to go
go ahead and do that so I'm going to go inside of my Hooks and I will copy the
inside of my Hooks and I will copy the use card model and I'm going to paste it
use card model and I'm going to paste it here and I will rename it to use pro
here and I will rename it to use pro model and now let's rename this to pro
model and now let's rename this to pro model in all
model in all instances we can remove the ID from the
instances we can remove the ID from the store we can remove the ID from the
store we can remove the ID from the props here in the onopen we can remove
props here in the onopen we can remove it here as well and change this from
it here as well and change this from setting any ID so both the on close and
setting any ID so both the on close and onop and also remove the ID from here
onop and also remove the ID from here great and now let's go ahead and let's
great and now let's go ahead and let's create uh the actual uh pro model
create uh the actual uh pro model component so I'm going to go inside of
component so I'm going to go inside of components models and just create a new
components models and just create a new file pro- model
file pro- model DSX let's go ahead uh and let's mark it
DSX let's go ahead uh and let's mark it as use
as use client let's export con pro model
client let's export con pro model here and let's go ahead and start uh
here and let's go ahead and start uh styling this so I'm going to create a
styling this so I'm going to create a dialogue component from do/ dialogue and
dialogue component from do/ dialogue and I'm immediately going to add the pro
I'm immediately going to add the pro model from use pro model from our hooks
model from use pro model from our hooks right here and let's go ahead uh and
right here and let's go ahead uh and let's give it a a property open on pro
let's give it a a property open on pro model is open and let's give it on Open
model is open and let's give it on Open change to be pro model on
change to be pro model on close great and inside let's render the
close great and inside let's render the dialogue content from do/ UI dialogue
dialogue content from do/ UI dialogue again and let me just change this to use
again and let me just change this to use at/ components just Ure you're not
at/ components just Ure you're not accidentally using the radic version and
accidentally using the radic version and inside of here let's give this a class
inside of here let's give this a class name of Max V MD padding zero and
name of Max V MD padding zero and overflow
overflow hidden and now what I want you to do is
hidden and now what I want you to do is I want you to add a little image which
I want you to add a little image which you can find well anywhere really but
you can find well anywhere really but you can go inside of my GitHub and just
you can go inside of my GitHub and just find hero. SVG so let's go ahead and
find hero. SVG so let's go ahead and download this image and let's place it
download this image and let's place it inside of our public folder so let me
inside of our public folder so let me show you I prepared my public folder
show you I prepared my public folder here and I will just drag and drop that
here and I will just drag and drop that inside of the public folder great so let
inside of the public folder great so let me just close this and let's go uh back
me just close this and let's go uh back inside of our pro model here and let's
inside of our pro model here and let's go ahead and add a div here with a class
go ahead and add a div here with a class name of aspect video relative Flex items
name of aspect video relative Flex items Center and justify Center and let's go
Center and justify Center and let's go ahead and render the image from next
ahead and render the image from next slash image so make sure you add this
slash image so make sure you add this import as well and let's go ahead and
import as well and let's go ahead and give it a source of/ hero. SVG which we
give it a source of/ hero. SVG which we just added an ALT of hero class name of
just added an ALT of hero class name of object Das cover and a fill property
object Das cover and a fill property like that perfect and now I think it's
like that perfect and now I think it's time for us to actually see this model
time for us to actually see this model so let's do the following uh let's go
so let's do the following uh let's go inside of our providers so in components
inside of our providers so in components we have the providers and we have the
we have the providers and we have the model provider and just below the card
model provider and just below the card model add a pro model which you can
model add a pro model which you can import from do do/ models pro model or/
import from do do/ models pro model or/ components pro model and I quickly want
components pro model and I quickly want to go back to our use pro model hook and
to go back to our use pro model hook and just give it the default value of true
just give it the default value of true for is open and make sure that you have
for is open and make sure that you have uh the app
uh the app running and that should be enough for
running and that should be enough for you uh to finally see the actual pro
you uh to finally see the actual pro model let's just wait a second to see if
model let's just wait a second to see if that is true and there we go you should
that is true and there we go you should be seeing it should be open by default
be seeing it should be open by default and I think yeah so every time you can
and I think yeah so every time you can close it and just refresh and it should
close it and just refresh and it should be visible again so it's visible because
be visible again so it's visible because in the hook use pro model uh we added it
in the hook use pro model uh we added it to be is open True by default and we
to be is open True by default and we added it to model providers right here
added it to model providers right here so make sure you do that and also in the
so make sure you do that and also in the actual model itself make sure you're
actual model itself make sure you're using the pro model here and assigned is
using the pro model here and assigned is open here great so now let's continue uh
open here great so now let's continue uh developing on this one so outside of
developing on this one so outside of this div let's go ahead and give it a
this div let's go ahead and give it a class
class name text neutral 700 MX outo
name text neutral 700 MX outo space Y6 and padding of six like that
space Y6 and padding of six like that let's go ahead and write an H2 element
let's go ahead and write an H2 element upgrade to tasky Pro
upgrade to tasky Pro today like that and let's give this h2a
today like that and let's give this h2a class name of font semibold and text
class name of font semibold and text XL outside of the H2 element let's
XL outside of the H2 element let's create a paragraph which will say
create a paragraph which will say explore the best of tasky and give it a
explore the best of tasky and give it a class name of text extra small font semi
class name of text extra small font semi bold and text neutral
bold and text neutral 600 and outside of this paragraph let's
600 and outside of this paragraph let's create a div with a class name of PL Das
create a div with a class name of PL Das three like this and let's create uh an
three like this and let's create uh an unordered list with a class name name of
unordered list with a class name name of text SM and list
text SM and list disk and inside create a list element
disk and inside create a list element which is going to say unlimited
which is going to say unlimited boards let's also
boards let's also write Advanced checklists so basically
write Advanced checklists so basically you know just some promotional text
you know just some promotional text admin and
admin and security
security features and more right so we know that
features and more right so we know that it's actually only unlimited boards but
it's actually only unlimited boards but after you finish this part of the
after you finish this part of the tutorial you're going to know how to
tutorial you're going to know how to limit anything else you want uh in this
limit anything else you want uh in this application uh great and now outside of
application uh great and now outside of this div I want to go ahead and add a
this div I want to go ahead and add a button component so make sure you import
button component so make sure you import the button component from do/ UI button
the button component from do/ UI button here
here components and let's go ahead and let's
components and let's go ahead and let's uh write upgrade here and give it a
uh write upgrade here and give it a class name of w full and give it a
class name of w full and give it a variant of primary like that there we go
variant of primary like that there we go so now we have our nice little model
so now we have our nice little model here and now let's just change uh it's
here and now let's just change uh it's use pro model here to be closed by
use pro model here to be closed by default and now let's go inside of our
default and now let's go inside of our components form form popover here and
components form form popover here and let's go ahead and add that model so
let's go ahead and add that model so cons pro model here use pro model so
cons pro model here use pro model so make sure you import use pro model from
make sure you import use pro model from hooks use pro model and then let's go
hooks use pro model and then let's go ahead and call it if we have an error
ahead and call it if we have an error inside of this create board function so
inside of this create board function so pro model on
pro model on open and now if I try and create a new
open and now if I try and create a new board let me just refresh and if I try
board let me just refresh and if I try and create a new board there we go you
and create a new board there we go you can see that I have a popup
can see that I have a popup great now let's go ahead and create an
great now let's go ahead and create an action that when we click on this
action that when we click on this upgrade button we actually get
upgrade button we actually get redirected the stripe checkout so I want
redirected the stripe checkout so I want to begin by going a quick quickly inside
to begin by going a quick quickly inside of my lib
of my lib utils where I have my CN function and
utils where I have my CN function and let's add another reusable util called
let's add another reusable util called export
export function absolute
function absolute URL
oops which takes in the path which is the string and it
the string and it returns a combination of process.
returns a combination of process. environment. nextore _ appcore URL and
environment. nextore _ appcore URL and combines the
combines the path so we're going to use this as our
path so we're going to use this as our stripe checkout redirect path so that's
stripe checkout redirect path so that's why we need it to be uh exactly what
why we need it to be uh exactly what where our app is hosted so now make sure
where our app is hosted so now make sure you copy this environment variable go
you copy this environment variable go inside of
inside of environment environment and add it so
environment environment and add it so HTTP Local Host 3000 just make sure you
HTTP Local Host 3000 just make sure you don't put an extra slash at the end so
don't put an extra slash at the end so it needs to be like this it doesn't
it needs to be like this it doesn't matter what your Port is or wherever
matter what your Port is or wherever you're running it just make sure it
you're running it just make sure it doesn't have the end slash great and now
doesn't have the end slash great and now let's go ahead and let's copy one of the
let's go ahead and let's copy one of the existing server actions like copy card
existing server actions like copy card here and let's rename it uh to stripe
here and let's rename it uh to stripe redirect would be good so stripe
redirect would be good so stripe redirect let's go inside of stripe
redirect let's go inside of stripe redirect and let's resolve the schema
redirect and let's resolve the schema first so we're going to call it stripe
first so we're going to call it stripe redirect here and let's go ahead and
redirect here and let's go ahead and change this to be just an empty object
change this to be just an empty object because we don't need to pass anything
because we don't need to pass anything and now go to types. DS and change this
and now go to types. DS and change this to stripe redirect as well and modify it
to stripe redirect as well and modify it here and the return type is just going
here and the return type is just going to be a string right so we don't need
to be a string right so we don't need this Prisma client at all and now let's
this Prisma client at all and now let's go inside of the index here and let's
go inside of the index here and let's import uh this stripe redirect from
import uh this stripe redirect from schema let's go all the way down and
schema let's go all the way down and let's put it here and let's change this
let's put it here and let's change this function's name to be stripe redirect
function's name to be stripe redirect stripe redirect lowercase like that
stripe redirect lowercase like that great and now let's go ahead and let's
great and now let's go ahead and let's actually uh do this so I'm going to go
actually uh do this so I'm going to go ahead actually and how about I remove
ahead actually and how about I remove this entire try and catch block so all
this entire try and catch block so all the way from here I I don't even want
the way from here I I don't even want this make sure you only have uh the
this make sure you only have uh the authorization here now let's get our
authorization here now let's get our settings URL so that's going to be our
settings URL so that's going to be our absolute URL from lib utils which we
absolute URL from lib utils which we just created I've added it just here and
just created I've added it just here and let's go ahead and pass in the back Tex
let's go ahead and pass in the back Tex organization slash the current
organization slash the current organization
organization ID now let's write let the URL which we
ID now let's write let the URL which we expect the return is just going to be an
expect the return is just going to be an empty string and now let's open our try
empty string and now let's open our try and catch block here let's go inside a
and catch block here let's go inside a try and let's write const organization
try and let's write const organization subscription to be await DB organiz
subscription to be await DB organiz ation subscription find
ation subscription find unique where we have the organization
unique where we have the organization ID and now let's check if we have the
ID and now let's check if we have the organization subscription and if
organization subscription and if organization subscription has stripe
organization subscription has stripe customer ID let's go ahead and let's
customer ID let's go ahead and let's create a stripe session so we're going
create a stripe session so we're going to do that using con stripe session to
to do that using con stripe session to be await stripe. billing portal oh let's
be await stripe. billing portal oh let's also import Stripe from at/ lib stripe
also import Stripe from at/ lib stripe as I did here so if we already have a
as I did here so if we already have a subscription we're going to open the
subscription we're going to open the billing portal sessions create and
billing portal sessions create and passing the customer to be org
passing the customer to be org subscription. stripe customer ID and the
subscription. stripe customer ID and the return URL is our settings
return URL is our settings URL and now let's just return the URL to
URL and now let's just return the URL to be the strip session. URL so that's if
be the strip session. URL so that's if we already have a subscription and now
we already have a subscription and now let's write an else
let's write an else clause and in here let's go ahead and
clause and in here let's go ahead and create a new stripe session so con
create a new stripe session so con stripe session is going to be await
stripe session is going to be await stripe. checkout. sessions. create
stripe. checkout. sessions. create success URL is going to be the settings
success URL is going to be the settings URL cancel URL is going to be the
URL cancel URL is going to be the settings URL as well payment uh method
settings URL as well payment uh method types is just going to be card you can
types is just going to be card you can of course course modify this however you
of course course modify this however you want so I'm just giving you my options
want so I'm just giving you my options here mode is going to be
here mode is going to be subscription we're going to have a
subscription we're going to have a billing address to be
outo the customer email is going to be user email uh do we have the user we
user email uh do we have the user we don't have the user so let's go ahead up
don't have the user so let's go ahead up here and let's get the user await
here and let's get the user await current user from clerk nextjs so just
current user from clerk nextjs so just make sure you import current user from
make sure you import current user from here and let's also check here if we
here and let's also check here if we don't have the user in that case Also
don't have the user in that case Also let's throw an error and now we can go
let's throw an error and now we can go back here uh and let's go ahead and get
back here uh and let's go ahead and get the user email addresses the first one
the user email addresses the first one in the array. email address let's R
in the array. email address let's R light items here let's write price data
light items here let's write price data the currency is US dollar product data
the currency is US dollar product data has a name of Tas ify Pro
has a name of Tas ify Pro and description is unlimited boards for
and description is unlimited boards for your
your organization and now let's go ahead and
organization and now let's go ahead and give this product a unit amount of uh
give this product a unit amount of uh 2,000 which represents 20 recurring on
2,000 which represents 20 recurring on an interval of month so monthly interval
an interval of month so monthly interval and let's also give it a quantity of one
and let's also give it a quantity of one right here perfect and then outside of
right here perfect and then outside of this array extremely important to add
this array extremely important to add the metadata where we're going to pass
the metadata where we're going to pass the organization ID like that perfect
the organization ID like that perfect and now let's go ahead uh inside of this
and now let's go ahead uh inside of this if clause and let's write URL is equal
if clause and let's write URL is equal to stripe session. URL or an empty
to stripe session. URL or an empty string like that perfect in the catch
string like that perfect in the catch let's go ahead and let's just return
let's go ahead and let's just return error something went
error something went wrong great and now let's go ahead and
wrong great and now let's go ahead and let's revalidate the
let's revalidate the path slash
path slash organization organ organization ID which
organization organ organization ID which we have and let's just return data to be
we have and let's just return data to be the URL so either the billing portal or
the URL so either the billing portal or the new checkout
the new checkout great now let's go back inside of our
great now let's go back inside of our pro model so inside of components models
pro model so inside of components models pro model right here and let's go ahead
pro model right here and let's go ahead and let's add execute and is loading
and let's add execute and is loading from use action from hooks use action
from use action from hooks use action let's give it a stripe redirect from
let's give it a stripe redirect from actions stripe redirect right here and
actions stripe redirect right here and let's go ahead and write on success get
let's go ahead and write on success get the data and now we have to redirect the
the data and now we have to redirect the user so
user so window.location.href is now the data
window.location.href is now the data which is going to be our URL and on
which is going to be our URL and on error it's going to have an
error it's going to have an error and it's going to call Toast from
error and it's going to call Toast from sonor so import that. error and passing
sonor so import that. error and passing the error like that perfect and now
the error like that perfect and now let's go ahead uh and let's write const
let's go ahead uh and let's write const on click to be an empty Arrow function
on click to be an empty Arrow function which calls the
which calls the execute option like this with empty
execute option like this with empty object inside and now let's go ahead and
object inside and now let's go ahead and let's give this button a disabled prop
let's give this button a disabled prop if is loading and on click to be on
if is loading and on click to be on click and great let's try that out now
click and great let's try that out now so make sure you are getting an error
so make sure you are getting an error here make sure you filled up your boards
here make sure you filled up your boards let's click upgrade and we should be
let's click upgrade and we should be redirected to the stripe checkout page
redirected to the stripe checkout page and we are great great job but we cannot
and we are great great job but we cannot pay just yet if you try it's not going
pay just yet if you try it's not going to work so I just wanted to confirm that
to work so I just wanted to confirm that we get redirected to the stripe checkout
we get redirected to the stripe checkout and now we have to create the API web
and now we have to create the API web hook so let's go inside of our API
hook so let's go inside of our API folder so we have to do this inside of
folder so we have to do this inside of the API folder because this is not
the API folder because this is not something we are going to call but
something we are going to call but something that stripe is going to call
something that stripe is going to call so we need to provide them with an API
so we need to provide them with an API endpoint so let's create an API endpoint
endpoint so let's create an API endpoint called Web
called Web hook like this and in the web hook
hook like this and in the web hook create a new file route. vs let's go
create a new file route. vs let's go ahead and import Stripe from
ahead and import Stripe from stripe let's go ahead and import
stripe let's go ahead and import headers from next
headers let's go go ahead and import next
next response from next SLS
response from next SLS server and now let's import the
database and finally let's import our stripe util from lib stripe now let's
stripe util from lib stripe now let's write export asynchronous function post
write export asynchronous function post which has a request which is a type of
which has a request which is a type of request let's get the body which is a
request let's get the body which is a wait request. text let's get the
wait request. text let's get the signature
signature since this is going to be called by the
since this is going to be called by the stripe uh well dashboard or SDK we have
stripe uh well dashboard or SDK we have to confirm that it is them that's
to confirm that it is them that's calling that so let's use the headers
calling that so let's use the headers get
get stripe Das
stripe Das signature as string basically we want to
signature as string basically we want to protect this endpoint from Foul Play
protect this endpoint from Foul Play Let's Define the uh event
Let's Define the uh event here and now let's try and get the event
here and now let's try and get the event using the stripe web hooks. construct
using the stripe web hooks. construct event in the combination of our body the
event in the combination of our body the signature which we have and the process.
signature which we have and the process. environment. stripe web hook uncore
environment. stripe web hook uncore secret so we don't yet have this but
secret so we don't yet have this but we're going to add it and let's just put
we're going to add it and let's just put an exclamation point here at the end and
an exclamation point here at the end and then a catch here which is going to say
then a catch here which is going to say return new next
return new next response web hook error
response web hook error with a status of
with a status of 400 and if you want to you can extract
400 and if you want to you can extract the error from here otherwise if we
the error from here otherwise if we successfully creating this event it
successfully creating this event it means that this is then uh well we can
means that this is then uh well we can continue and actually finish the user's
continue and actually finish the user's check out so this happens after the user
check out so this happens after the user checks out so after they enter their
checks out so after they enter their credit card and everything else so let's
credit card and everything else so let's get the session from event. dat. object
get the session from event. dat. object as stripe. checkout. session
if event. type is checkout. session.
type is checkout. session. completed then let's create a
completed then let's create a subscription await do stripe sorry await
subscription await do stripe sorry await stripe.
stripe. subscriptions
subscriptions retrieve so we retrieve using the
retrieve so we retrieve using the session. subscription as a
session. subscription as a string if we don't have session
string if we don't have session metadata organization ID which we just
metadata organization ID which we just passed inside of the stripe redirect
passed inside of the stripe redirect server action that's why it said it's
server action that's why it said it's very important we are going to be throw
very important we are going to be throw a new error so next
a new error so next response org ID is
response org ID is required with a status of 400 because if
required with a status of 400 because if we don't have that metadata we don't
we don't have that metadata we don't know for which organization to create a
know for which organization to create a subscription and finally await database
subscription and finally await database or subscription create data or ID
or subscription create data or ID session
metadata. orgid stripe subscription ID is
orgid stripe subscription ID is subscription. ID stripe customer ID is
subscription. ID stripe customer ID is going to be stri uh subscription.
going to be stri uh subscription. customer as
customer as string stripe price ID is going to be
string stripe price ID is going to be subscription. items the first one in the
subscription. items the first one in the array. price . ID so let's just check if
array. price . ID so let's just check if this is correct uh
this is correct uh subscription. items. data then the first
subscription. items. data then the first one in the array like that and stripe
one in the array like that and stripe current period end is going to be a new
current period end is going to be a new date which takes in the
date which takes in the subscription current period endtimes
1,000 great and we handled if the user creates a subscription for the first
creates a subscription for the first time and now we have to handle a
time and now we have to handle a different type of event so let's go
different type of event so let's go outside of this if clause and write if
outside of this if clause and write if event. type is invoice payment succeeded
event. type is invoice payment succeeded that means that they have renewed their
that means that they have renewed their subscription so in here let's get a
subscription so in here let's get a subscription again using
subscription again using await stripe
retrieve session subscription as string and then let's do await DB organization
and then let's do await DB organization subscription update where we have a
subscription update where we have a matching stripe subscription ID to be
subscription. ID and let's update the data to the new stripe price ID which is
data to the new stripe price ID which is subscription. items. data the first one
subscription. items. data the first one do price. ID and stripe current period
do price. ID and stripe current period and it will be updated to new
and it will be updated to new date with
date with subscription current period end times
subscription current period end times 1,000 like that perfect so we finished
1,000 like that perfect so we finished our stripe web hook and very important
our stripe web hook and very important at the end of the web hook you have to
at the end of the web hook you have to return new next response null with a
return new next response null with a status of two 100 great and now we have
status of two 100 great and now we have to find a way to keep this web hook
to find a way to keep this web hook running locally
running locally so let's go ahead inside of the stripe
so let's go ahead inside of the stripe dashboard here and in here in the
dashboard here and in here in the developers tab you have web hooks go
developers tab you have web hooks go ahead and click on test in a local
ahead and click on test in a local environment you have to download the
environment you have to download the stripe CLI so you can click here if you
stripe CLI so you can click here if you haven't done that already once you have
haven't done that already once you have that continue with the video and go
that continue with the video and go inside of your terminal here and let's
inside of your terminal here and let's go ahead and open a new terminal and
go ahead and open a new terminal and let's run stripe login and now go ahead
let's run stripe login and now go ahead and click on this this link right here
and click on this this link right here and click open and that will guide you
and click open and that will guide you to allow this for your computer so click
to allow this for your computer so click allow access then you can go back inside
allow access then you can go back inside of here and in a couple of seconds you
of here and in a couple of seconds you should be seeing the completed sign
should be seeing the completed sign right here perfect so let me just clear
right here perfect so let me just clear this now and now we have to run this
this now and now we have to run this command stripe listen forward to and let
command stripe listen forward to and let me show you exactly where we're going to
me show you exactly where we're going to listen so stripe listen-- forward D2
listen so stripe listen-- forward D2 local post 3000 SL apiweb hook don't
local post 3000 SL apiweb hook don't make a mistake and forget the API
make a mistake and forget the API because it's going to be weird to debug
because it's going to be weird to debug I see this happen a lot of times so let
I see this happen a lot of times so let me zoom out so you can see it in one
me zoom out so you can see it in one line stripe listened forward to Local
line stripe listened forward to Local Host 3000 API webhook and go ahead and
Host 3000 API webhook and go ahead and press enter and after you press enter
press enter and after you press enter you're going to be seeing your web hook
you're going to be seeing your web hook signing secret right here so copy
signing secret right here so copy everything that is in bold text like
everything that is in bold text like this so make sure you copy this and then
this so make sure you copy this and then go ahead and we have to create this
go ahead and we have to create this stripe web hook secret API and uh
stripe web hook secret API and uh environment key so let's go and do that
environment key so let's go and do that I'm going to go inside of my uh
I'm going to go inside of my uh environment here and I will add stripe
environment here and I will add stripe web hook secret and paste it here so
web hook secret and paste it here so please make sure that you copy that and
please make sure that you copy that and confirm that your stripe web hook secret
confirm that your stripe web hook secret matches so stripe web hook secret needs
matches so stripe web hook secret needs to be exactly the same and again confirm
to be exactly the same and again confirm from your ter
from your ter you you must not shut this down this
you you must not shut this down this must be open whenever you want to test
must be open whenever you want to test out stripe locally confirm that you
out stripe locally confirm that you copied from the start of the Bold text
copied from the start of the Bold text to the end of The Bold text and paste it
to the end of The Bold text and paste it here and confirm that you don't have any
here and confirm that you don't have any extra wi space uh great and now what we
extra wi space uh great and now what we have to do is allow this API endpoint to
have to do is allow this API endpoint to be uh visitable without authentication
be uh visitable without authentication because we're going to be using that
because we're going to be using that signature event to confirm that it is
signature event to confirm that it is the uh well the correct party so let's
the uh well the correct party so let's go in inside of our middleware
go in inside of our middleware file we have a middleware dods here and
file we have a middleware dods here and besides this being the public route we
besides this being the public route we also need to add the API web hook so
also need to add the API web hook so let's add SL API SL web hook here like
let's add SL API SL web hook here like that and let's try this out now so I'm
that and let's try this out now so I'm going to go ahead here and let's refresh
going to go ahead here and let's refresh this and let's do it like this I want to
this and let's do it like this I want to make sure that you keep your terminal
make sure that you keep your terminal open so you can see the LS from here
open so you can see the LS from here let's attempt to create a new one and
let's attempt to create a new one and now let's click upgrade
now let's click upgrade here and now I'm going to go ahead and
here and now I'm going to go ahead and just enter some fake information so
just enter some fake information so basically if you this is your first time
basically if you this is your first time working with stripe you need to enter
working with stripe you need to enter this exact uh credit card to get a
this exact uh credit card to get a success otherwise you're going to be
success otherwise you're going to be getting failure so this doesn't matter
getting failure so this doesn't matter this doesn't matter the name doesn't
this doesn't matter the name doesn't matter but this is very important it
matter but this is very important it needs to be like this you can find more
needs to be like this you can find more examples in the documentation and let's
examples in the documentation and let's click pay And subscribe and let's see if
click pay And subscribe and let's see if we made any mistakes or if this is
we made any mistakes or if this is working as it should and there we go you
working as it should and there we go you can see I have a bunch of 200 events
can see I have a bunch of 200 events inside of my terminal here which means
inside of my terminal here which means that it is working and let's go ahead
that it is working and let's go ahead and confirm that by I'm going to open a
and confirm that by I'm going to open a new terminal here and run npx Prisma
new terminal here and run npx Prisma studio so I just want to confirm that I
studio so I just want to confirm that I now have an organization subscription so
now have an organization subscription so let me check that out and there we go I
let me check that out and there we go I have an organization subscription model
have an organization subscription model perfect I have the stripe customer ID
perfect I have the stripe customer ID price ID and current period end so we
price ID and current period end so we confirm that this is definitely working
confirm that this is definitely working now we have to modify this text here to
now we have to modify this text here to say unlimited and we also need to check
say unlimited and we also need to check in our server action to allow the user
in our server action to allow the user to create new
to create new boards so first let's visit our board
boards so first let's visit our board list component so I'm going to close
list component so I'm going to close everything here and I'm going back
everything here and I'm going back inside of the app platform dashboard
inside of the app platform dashboard organization organization ID components
organization organization ID components board list and besides the available
board list and besides the available count let's check if is pro using await
count let's check if is pro using await check subscription which we created when
check subscription which we created when we started developing the stripe lib so
we started developing the stripe lib so we have the check subscription here
we have the check subscription here where we compare with the day buffer and
where we compare with the day buffer and if it is pro we're going to modify that
if it is pro we're going to modify that text so let's go all the way here and
text so let's go all the way here and let's do the following if it is pro
let's do the following if it is pro we're going to say unlimited otherwise
we're going to say unlimited otherwise we're going to do uh this check and now
we're going to do uh this check and now when I save there we go this now says
when I save there we go this now says unlimited perfect and now we have to
unlimited perfect and now we have to modify this so let me just zoom out so
modify this so let me just zoom out so you can see so is pro unlimited
you can see so is pro unlimited otherwise we do that
otherwise we do that calculation and now what we have to do
calculation and now what we have to do is we have to modify our actions create
is we have to modify our actions create board right here so inside of the create
board right here so inside of the create board uh let's see where it is so in
board uh let's see where it is so in here we have the can create now let's
here we have the can create now let's add const is pro await check check
add const is pro await check check subscription which you can import from
subscription which you can import from lib subscription so if we cannot create
lib subscription so if we cannot create end if we are not pro only then are we
end if we are not pro only then are we going to throw an error otherwise we can
going to throw an error otherwise we can uh do this freely and let me also do one
uh do this freely and let me also do one more thing
more thing so in here I don't want to increase the
so in here I don't want to increase the available count if we are pro so if is
available count if we are pro so if is not
not pro like that
so this will only matter if the user later decides to turn off their
later decides to turn off their subscriptions so they're going to have
subscriptions so they're going to have some weird values I believe let's try it
some weird values I believe let's try it out it says unlimited let's try it out
out it says unlimited let's try it out and there we go it's working amazing
and there we go it's working amazing amazing job you finished up uh stripe
amazing job you finished up uh stripe subscription now we have to modify that
subscription now we have to modify that to say Pro here in the info component
to say Pro here in the info component as well as in this one here uh and we
as well as in this one here uh and we also have to create the billing page so
also have to create the billing page so let's go ahead and do
let's go ahead and do that so let's go and find our info
that so let's go and find our info component let me close everything here
component let me close everything here and find the info component you can find
and find the info component you can find it in the platform dashboard
it in the platform dashboard organization organization ID component
organization organization ID component so just alongside board list and in here
so just alongside board list and in here let's create an interface info props is
let's create an interface info props is Pro
Pro Boolean let's go ahead and assign those
Boolean let's go ahead and assign those props so info props and let's extract is
props so info props and let's extract is pro and now all we're going to do is
pro and now all we're going to do is change this from being hardcoded to free
change this from being hardcoded to free to is pro is going to say Pro otherwise
to is pro is going to say Pro otherwise free like that and now we have to find
free like that and now we have to find all the places where we are using uh the
all the places where we are using uh the info component so let's
info component so let's find info like this so first one is in
find info like this so first one is in the organization ID page page let me
the organization ID page page let me show you where that is so it is in the
show you where that is so it is in the organization ID folder page. vsx right
organization ID folder page. vsx right here and let's go ahead and let's get
here and let's go ahead and let's get the is Pro from await check subscription
the is Pro from await check subscription from as/ liip subscription and pass in
from as/ liip subscription and pass in is pro here great and now we have to do
is pro here great and now we have to do the same thing inside of the activity
the same thing inside of the activity page right here so let's go ahead and do
page right here so let's go ahead and do that let's turn this into an
that let's turn this into an asynchronous function let's import check
asynchronous function let's import check subscription from l subscription and
subscription from l subscription and pass is pro as a prop
pass is pro as a prop here great and I believe now there we go
here great and I believe now there we go it says that we are pro here if I go to
it says that we are pro here if I go to activity it says that we are pro here
activity it says that we are pro here but if I go to another one it says that
but if I go to another one it says that it is free and here I have five
it is free and here I have five remaining but here I have unlimited
remaining but here I have unlimited because subscription is only per
because subscription is only per organization and now we have to create
organization and now we have to create our last page
our last page billing so let's go back inside of the
billing so let's go back inside of the organization ID folder and alongside
organization ID folder and alongside activity and settings create a new
activity and settings create a new folder called
folder called billing go ahead and create page. vsx
billing go ahead and create page. vsx here and let's go ahead and do const
here and let's go ahead and do const billing
billing page and let's return a div with a class
page and let's return a div with a class name of w full let's make sure this is
name of w full let's make sure this is an asynchronous
an asynchronous component let's check is pro using a
component let's check is pro using a wait check
wait check subscription and now let's go ahead and
subscription and now let's go ahead and let's render the info component from do/
let's render the info component from do/ components info pass in the is pro
components info pass in the is pro prop like that let me just separate my
prop like that let me just separate my imports let's make sure we do export
imports let's make sure we do export default billing page and now you should
default billing page and now you should be having uh this page available so when
be having uh this page available so when I click on
I click on billing there we go it's available if
billing there we go it's available if yours is still 404 confirm that your url
yours is still 404 confirm that your url is/ billing if it's not find the nav
is/ billing if it's not find the nav item component and modify uh let me show
item component and modify uh let me show you nav item component and in here you
you nav item component and in here you have the routes and your billing should
have the routes and your billing should be/ billing make sure it doesn't have a
be/ billing make sure it doesn't have a typo and of course make sure that the
typo and of course make sure that the page uh the billing folder doesn't have
page uh the billing folder doesn't have a typo either and that it is inside of
a typo either and that it is inside of the organization ID folder uh great now
the organization ID folder uh great now let's add a separator component from
let's add a separator component from components UI separator so make sure you
components UI separator so make sure you have that as well let's give it a class
have that as well let's give it a class name of my2 and now we have to create a
name of my2 and now we have to create a subscription button component so let's
subscription button component so let's just write subscription button component
just write subscription button component like this and it's going to accept the
like this and it's going to accept the is pro argument like that let's go ahead
is pro argument like that let's go ahead inside of the billing folder and create
inside of the billing folder and create the underscore components folder and
the underscore components folder and create a new file subscription Das
create a new file subscription Das button. CSX let's go ahead and Mark this
button. CSX let's go ahead and Mark this as use
as use client export con subscription button
client export con subscription button and return a div for
and return a div for now now go back to the billing page and
now now go back to the billing page and you can import the subscription button
you can import the subscription button from do/ components subscription
from do/ components subscription button let me just align my
button let me just align my imports now let's modify the interface
imports now let's modify the interface for the subscription
for the subscription button so sub subscription button props
button so sub subscription button props is
is pro is going to be a Boolean and let's
pro is going to be a Boolean and let's go ahead and assign that so
go ahead and assign that so subscription button props and extract is
subscription button props and extract is Pro from here and now we're going to
Pro from here and now we're going to render dynamically depending on that so
render dynamically depending on that so get the button component from components
get the button component from components UI button and if is pro we're going to
UI button and if is pro we're going to say manage
say manage subscription otherwise we're going to
subscription otherwise we're going to say upgrade to Pro and let's go ahead
say upgrade to Pro and let's go ahead and give this a variant of primary like
and give this a variant of primary like this and now let's go ahead and let's
this and now let's go ahead and let's extract execute and is loading from use
extract execute and is loading from use action from hooks use action and let's
action from hooks use action and let's use the stripe redirect from actions
use the stripe redirect from actions stripe redirect because remember stripe
stripe redirect because remember stripe redirect can both handle the cases of
redirect can both handle the cases of initial upgrade and also the billing
initial upgrade and also the billing portal so let's give it a disabled prop
portal so let's give it a disabled prop of is
loading and let's go ahead and write some call backs here so on success we
some call backs here so on success we get the data and then with the data we
get the data and then with the data we have to handle window. load
have to handle window. load location. hre to be the new data if it
location. hre to be the new data if it is an error we have to handle the error
is an error we have to handle the error inside of the toast from
inside of the toast from sonor so just make sure you import the
sonor so just make sure you import the toast from
toast from sonor and now let's go ahead and let's
sonor and now let's go ahead and let's write const on click here be an arrow
write const on click here be an arrow function which which calls if is pro
function which which calls if is pro execute but otherwise it's going to open
execute but otherwise it's going to open the pro model so const pro model use pro
the pro model so const pro model use pro model which you can import from hooks
model which you can import from hooks use pro model so let's go ahead and
use pro model so let's go ahead and write else pro model on
write else pro model on open and assign the on click to the on
open and assign the on click to the on click
click here great so let's try that out now in
here great so let's try that out now in here it says manage subscription in the
here it says manage subscription in the boards here in the settings or in the
boards here in the settings or in the billing it says upgrade to Pro so if I
billing it says upgrade to Pro so if I click here I get to upgrade but if I try
click here I get to upgrade but if I try here it's actually not going to work
here it's actually not going to work we're going to get an error and let me
we're going to get an error and let me show you why it's very simple to fix
show you why it's very simple to fix inside of your terminal you have a text
inside of your terminal you have a text which says uh inside of your main
which says uh inside of your main project here uh you should be having a
project here uh you should be having a text uh where is it oh we're not logging
text uh where is it oh we're not logging any error uh basically we have to enable
any error uh basically we have to enable the billing portal so go inside of the
the billing portal so go inside of the dashboard uh let's find some normal
dashboard uh let's find some normal dashboard here and let's find the
dashboard here and let's find the billing or let's search billing portal
billing or let's search billing portal oh so type in Billing portal and go to
oh so type in Billing portal and go to settings billing customer portal and in
settings billing customer portal and in here you have to activate the test link
here you have to activate the test link so click on this button and now try this
so click on this button and now try this again manage subscription and as you can
again manage subscription and as you can see no more errors and instead I'm
see no more errors and instead I'm redirected to manage my subscription
redirected to manage my subscription amazing amazing job you finished the
amazing amazing job you finished the entire tutorial great great job if we
entire tutorial great great job if we have time I'm going to show you uh how
have time I'm going to show you uh how to deploy as well if not thank you so
to deploy as well if not thank you so much for watching the
much for watching the tutorial so it looks like I have some
tutorial so it looks like I have some time to show you how to deploy after all
time to show you how to deploy after all let's just fix a couple of bugs that we
let's just fix a couple of bugs that we have so I noticed while reviewing my
have so I noticed while reviewing my last part of the video that inside of
last part of the video that inside of lib inside of the subscription here I
lib inside of the subscription here I was saying 86,000 but I wrote 84,000 so
was saying 86,000 but I wrote 84,000 so let's be correct and make it
let's be correct and make it 86 great and one more thing that I want
86 great and one more thing that I want to do so in actions when we create a
to do so in actions when we create a board I make sure that I don't increment
board I make sure that I don't increment if I am on Pro right so we don't have
if I am on Pro right so we don't have some weird numbers so I think it would
some weird numbers so I think it would be fair to do the same thing when we
be fair to do the same thing when we delete the board so we wrapped this in
delete the board so we wrapped this in is pro so let's go ahead and check that
is pro so let's go ahead and check that here const is pro is await check
here const is pro is await check subscription from at lib subscription
subscription from at lib subscription make sure you have this and then I'm
make sure you have this and then I'm going to go ahead and only do this if
going to go ahead and only do this if I'm not pro so if it's not pro only then
I'm not pro so if it's not pro only then am I going to decrease the available
am I going to decrease the available count uh great so now let's go ahead and
count uh great so now let's go ahead and let's actually deploy this so the first
let's actually deploy this so the first thing we have to do is we have to go
thing we have to do is we have to go inside of our package Json and in here
inside of our package Json and in here we have to add a post install script so
we have to add a post install script so post install and we have to run the
post install and we have to run the command Prisma generate after we've done
command Prisma generate after we've done that we have to add that to our GitHub
that we have to add that to our GitHub so go into GitHub and click on a plus
so go into GitHub and click on a plus here and click new repository and now
here and click new repository and now let's go ahead and let's give this
let's go ahead and let's give this repository uh a name so I'm going to
repository uh a name so I'm going to give this next 13 Trello like that I'm
give this next 13 Trello like that I'm going to make it private and I'm going
going to make it private and I'm going to click create a
to click create a repository and now what we are going to
repository and now what we are going to do is open up our terminal so we don't
do is open up our terminal so we don't need anything running so you can shut
need anything running so you can shut down absolutely everything from here
down absolutely everything from here even the stripe portal and the Prisma
even the stripe portal and the Prisma studio and let's go ahead and run get ad
studio and let's go ahead and run get ad get commit let's give it a message so as
get commit let's give it a message so as you can see I have my messages in
you can see I have my messages in numbers depending on which video part
numbers depending on which video part I'm on so this would be for me 25
I'm on so this would be for me 25 deployment for you the message can just
deployment for you the message can just be you know initial commit or something
be you know initial commit or something like that and now we have to push this
like that and now we have to push this so let's go ahead and use this second
so let's go ahead and use this second option which says push to an existing
option which says push to an existing repository so copy these three lines
repository so copy these three lines right here and let's go ahead and add
right here and let's go ahead and add git remote add origin and let's go ahead
git remote add origin and let's go ahead and push this perfect and now when I
and push this perfect and now when I refresh here you should be seeing the
refresh here you should be seeing the entire project great and now let's go to
entire project great and now let's go to versel and click on add new project
versel and click on add new project right here and find that uh repository
right here and find that uh repository which you just created for me it's right
which you just created for me it's right here I'm going to click import
here I'm going to click import and what we have to do now is add all
and what we have to do now is add all the environment variables so we can do
the environment variables so we can do that quite easily by going inside of
that quite easily by going inside of environment and you can just copy
environment and you can just copy absolutely everything from here select
absolutely everything from here select the first field and paste and there we
the first field and paste and there we go that's going to fill up everything
go that's going to fill up everything here so after we deploy we're going to
here so after we deploy we're going to have to change the stripe web hook
have to change the stripe web hook secret and next public app URL but we
secret and next public app URL but we can only do that after we deploy so
can only do that after we deploy so let's go ahead and click deploy if I get
let's go ahead and click deploy if I get any errors I'm going to pause the video
any errors I'm going to pause the video and we will result olve it
and we will result olve it together so my deployment seem to have
together so my deployment seem to have failed because of this shaty and Sheet
failed because of this shaty and Sheet component that we have because the
component that we have because the property class name does not exist on
property class name does not exist on type dialogue portal props so I Googled
type dialogue portal props so I Googled the error and it looks like two weeks
the error and it looks like two weeks ago there was a comment here uh which
ago there was a comment here uh which explained that this was fixed for uh
explained that this was fixed for uh dialogue and alert dialogue but it looks
dialogue and alert dialogue but it looks like for the sheet it's only coming in
like for the sheet it's only coming in the next release but for now we can do
the next release but for now we can do this we can mpm install uh red this
this we can mpm install uh red this exact radx underlying package so let's
exact radx underlying package so let's try that I haven't tried it but I hope
try that I haven't tried it but I hope it's going to work so let's just go
it's going to work so let's just go ahead and run npm install radx ui/ react
ahead and run npm install radx ui/ react dialogue with this exact version and
dialogue with this exact version and let's go ahead and paste that inside
let's go ahead and paste that inside great let's go ahead and run get add get
great let's go ahead and run get add get commit I'm going to write deployment fix
commit I'm going to write deployment fix here and I'm going to push so make sure
here and I'm going to push so make sure you do get push and that should
you do get push and that should automatically trigger the next
automatically trigger the next deployment on ver cell so you can click
deployment on ver cell so you can click back on your projects here and then go
back on your projects here and then go ahead and select your project click on
ahead and select your project click on deployments and there we go you can see
deployments and there we go you can see that this fix deployment fix is now
that this fix deployment fix is now building so I'm going to go ahead and
building so I'm going to go ahead and pause the video again and see if we have
pause the video again and see if we have more
more problems great so that seems to have
problems great so that seems to have fixed my issues and I can now visit this
fixed my issues and I can now visit this on my deployed domain right here there
on my deployed domain right here there we go the app is here and now we have to
we go the app is here and now we have to modify our stripe web hook environment
modify our stripe web hook environment key because remember currently we
key because remember currently we created our stripe web hook inside of
created our stripe web hook inside of the test environment so now we have to
the test environment so now we have to change that to use the production
change that to use the production environment so let's click on the
environment so let's click on the developers tab right here let's click
developers tab right here let's click back here on web Hooks and here we go I
back here on web Hooks and here we go I have local listeners so now let's click
have local listeners so now let's click add endpoint for hosted endpoints here
add endpoint for hosted endpoints here and now we have to go ahead and we have
and now we have to go ahead and we have to copy uh this this URL which we have
to copy uh this this URL which we have paste it here and go to SL API SL web
paste it here and go to SL API SL web hook so make sure you don't forget the
hook so make sure you don't forget the SL API here and let's go ahead and click
SL API here and let's go ahead and click select events and we are using the
select events and we are using the checkout session completed option so
checkout session completed option so select that one and we're using the
select that one and we're using the invoice let me invoice payment succeeded
invoice let me invoice payment succeeded so if you're not sure which ones we use
so if you're not sure which ones we use you can visit the web hook itself and in
you can visit the web hook itself and in here you can see that we check for
here you can see that we check for checkout session completed and we check
checkout session completed and we check out for invoice payment succeeded so
out for invoice payment succeeded so those two are the ones we have to listen
those two are the ones we have to listen to and just click uh add events so
to and just click uh add events so confirm checkout session completed and
confirm checkout session completed and invoice payment succeeded and click add
invoice payment succeeded and click add an endpoint and then you have the
an endpoint and then you have the signing secret here and just click
signing secret here and just click reveal and go ahead and copy uh this
reveal and go ahead and copy uh this sign in
sign in secret and now we have to go back in
secret and now we have to go back in inside of versel here
inside of versel here select your project go inside of
select your project go inside of settings environment variables and go
settings environment variables and go ahead and find the stripe web hook
ahead and find the stripe web hook secret here edit it and paste the new
secret here edit it and paste the new version inside it's going to be much
version inside it's going to be much shorter like this and we have to edit uh
shorter like this and we have to edit uh the next public app URL if you try and
the next public app URL if you try and take a look now you can see it's HTTP
take a look now you can see it's HTTP Local Host 3000 but we have to copy the
Local Host 3000 but we have to copy the new deployment URL and just remove the
new deployment URL and just remove the last slash like this and click save and
last slash like this and click save and that is going to be it and after you
that is going to be it and after you change your environment variables go
change your environment variables go back to deployments select the last one
back to deployments select the last one which is working and click redeploy and
which is working and click redeploy and just confirm the redeployment and that
just confirm the redeployment and that is it great great job you finished the
is it great great job you finished the entire tutorial thank you so much for
entire tutorial thank you so much for watching remember to leave a like share
watching remember to leave a like share and subscribe and see you in the next
and subscribe and see you in the next one
Click on any text or timestamp to jump to that moment in the video
Share:
Most transcripts ready in under 5 seconds
One-Click Copy125+ LanguagesSearch ContentJump to Timestamps
Paste YouTube URL
Enter any YouTube video link to get the full transcript
Transcript Extraction Form
Most transcripts ready in under 5 seconds
Get Our Chrome Extension
Get transcripts instantly without leaving YouTube. Install our Chrome extension for one-click access to any video's transcript directly on the watch page.