This course teaches developers how to build a functional Jira clone using modern web technologies like Next.js, React, and Tailwind CSS, covering features from task management to user authentication and workspace administration.
Mind Map
Click to expand
Click to explore the full interactive mind map • Zoom, pan, and navigate
hey there my name is Antonio and in this
course I'm going to show you how to
build a fully functional Joc clone using
modern web
Technologies this project is great for
developers who want to improve their
skills in building complex web
applications and learn about project management
management
tools we'll be using nextjs react
Tailwind hono JS and aight to create our
Jura clone throughout the course you
learn how to implement various features
that you'd expect from a professional
project management platform we'll build
a my tasks page where you can view your
tasks in different formats we'll
Implement a table view for a quick
overview and a comound board for easy
drag and drop task management and
finally a calendar view for better deadline
deadline
tracking we'll create individual task
Pages where you can view and edit task
details you'll learn how to implement
features like editing task descriptions
models on the task list We'll add
filtering capabilities allowing users to
quickly find tasks based on different
criteria like the person assigned to
that task we'll also Implement a editing
system where you can change assignes
move tasks between projects and update
for user management we'll create a
members page where you can view all
members in a workpace you learn how to
implement role-based Access Control
allowing you to set members as guests
promote them to admins or remove them
from the
workspace we'll build project pages with
their own set of analytics and settings
you'll learn how to create edit and
manage projects within a workspace
creating new tasks will be a key feature
we'll Implement a task creation form
where you can set all necessary details
like name due date asse status and Associated
Associated
project to personalize The Experience
We'll add workspace settings where users
can rename their workspace and upload a
custom image we'll also create a
workspace switcher component to easily
we'll Implement an invite system with
sharable links making it easy to bring
new members on board you'll see how new
members are integrated into the
workspace and can start creating and
managing tasks right
away by the end of this course you'll
have built a functional jira clone that
you can use for your own projects or add
to your portfolio you have gained
valuable experience in creating complex
interactive web applications and now
without further Ado let's build the
project this is a quick reminder that
you can also use my platform Cod withth
antonio.com and you can watch this
entire course in chapters and you can
also Mark your
progress you can also become a premium
member and you can unlock special
courses like canva clone as well as
source code to over 20 premium projects
and over 170 hours of Premium content
let's go ahead and let's set up our
project in order to do that we first
have to ensure specific system
requirements so let's go ahead and let's
head inside of our terminal and I want
you to run the command node
DV go ahead and read the version it says
right here and double check that it is
the necessary requirement on the nextjs
installation page so at the time of
recording this video that is at least node.js
node.js
18.18 so my version node 20 will work
just fine in case you have a lower
version or don't have node installed
meaning you got an error here simply
click on this link which will open a
node.js website and you can download the
latest long-term support which would be
20.7 at the time of recording this video
there is an alternative runtime you can
install if you want to which is
something that I'm going to do in this
tutorial so I'm going to quickly show
you the differences between using the
two runtimes as you can see it supports
Linux Mac OS and
windows so if you choose to use the
nodejs runtime you would simply run the
commands as you see in the documentation
npx create next app but if you're going
to use bun version you're going to have
to switch it up a bit so first of all
let me show you where does the npx come
from so if you have node installed you
also have npx installed so you can check
this as
well if you choose to install bun using
this command then you can then run bun
version as well and if you have bun
installed you also have bonex installed
so bonex is the same as npx and Bun is
the same as node of course I'm speaking
in terms of those two different run
times great so now that we know the
basics of our commands choose the run
time you want to use and let's go ahead
and set up
nextjs but before you run this command I
just quickly want to show you something
so instead of running npx create next
app since I'm using the bun runtime I
will be using bunx Create next app and I
quickly want to show you the version of
create next app that I have this is
important because I want to ensure that
anyone following this tutorial can have
the exact same version and doesn't have
to fear that anything's been deprecated
especially because there are some
breaking changes coming in the upcoming
versions of
nextjs so let's go ahead and do the
following let's run either npx or banex
create next app and instead of at latest
we're going to use 14.2.1 14 and then
let's go ahead and give our project a
name I'm going to call it jro
clone let's go ahead and select yes for
typescript yes for Sint yes for Tailwind
yes yes for Source directory yes for the
app router no for customizing the
default import Alas and let's just wait
a second for this to install since I'm
using bun you can see that I have bun
install here if you wrote npx inside of
here you would probably have the message
using node and then you would have npm
install here so those are basically the
differences just a different runtime I
prefer bun I personally experienced it
to be much faster Master great now that
we have that we can go ahead and head
inside of our new project jira clone and
from here go ahead and open this
directory inside of Visual Studio code
or any editor that you
prefer if you get a message similar to
this inside of Visual Studio code you
can simply press yes I trust the authors
like that and there we go I am now
inside of my Joc clone you can see I
have the source directory inside of here
I have the app folder I have the fonts I
have the globals files the icon the
layout the page but let's go ahead and
immediately go inside of package.json
and I want you to see whether you have
any differences in the versions so I'm
using react 18 react Dom 18 and next
14.2.1 14 and here are my Dev
dependencies these don't have to match one
one
100% but I want to show you this in case
you run into some problems or in case
you chose to use the next latest version
so you can always look at the most
important packages which I have and
which version they are so you can set
them to be exactly like this but if you
used bonex command like we did right
here bonex create next app at 14.2.1 14
I think you will have all the exact same
versions that I do great let's go ahead
and let's try running the app so I'm
going to go ahead and you can either do
mpm run Dev or you can do Bun Run Dev so
the cool thing about bun is that you can
interchange it so if at any point you
want to switch back to npm you can do
that right so I can shut this down and I
can do npm run and it's working just
fine so run either one of those two
commands and head to Local Host 3000 I'm
going to copy the URL I'm going to go
inside of my browser here uh in case
you're wondering I'm using the arc
browser and there we go so this is my
nextjs homepage here uh my version is
dark because I'm on dark mode at the
moment yours might be bright great so
that is the initial setup that I want us
to have great great
job what we are going to set up next is
shaten UI and we're going to use it to
build our own component Library if you
want to you can visit the documentation
yourself and follow along or you can
just watch the commands I run and run them
them
yourself so I'm going to go ahead and
follow the documentation for
nextjs and first things first we have to
run npx shat CN in it here's the cool
thing about the documentation page if
you click on the copy button you can
choose which package manager you are
using so npm yarn pnpm or in my case pun
but before we run this command I want to
show you exactly which version of shat
ceni I'm using so I'm going to go ahead
and run bun x-- bun shat cn- Das
version there we go
2.1.0 so now I'm going to go ahead and
run bunx D- bun Shad CN at 2.1 0 in it
or if you're using node it would be npx
Shad CN at 2.1.0 in
it so I'm going to go ahead and repeat
my one more time 2.1 point zero and just
in it without any dashes or spaces here
let's go ahead and select which style we
would like to use so usually in my
projects I use the default style but
this time we're going to switch it up
and use the New York style this is kind
of important because regarding of which
style you choose you're going to have
some different packages installed
specifically the icons so make sure you
choose New
York for the color I'm going to go ahead
and select uh neutral as my color I'm
going to select yes for CSS variables
here and I'm just going to let it run
its course there we go so success
project initialization completed you may
now add components we're going to
explain what that means in a second but
let's quickly take a look at everything
that has changed inside of our project
here so as you can see first of all I
have some new packages here so this radx
UI react icons comes from the New York
style we also have a package class
variance Authority CLX Lucid react and
we also have Tailwind merge and Tailwind
CSS animate so those are all the new
packet packages which have been
installed running this shaty n version
so I would recommend that you go inside
of your package Jason and just double
check that you have all of those here as
well great but besides that I think we
have some more things here so we also
have a modified Tailwind config dods
here and this part is very very
important we're going to come back to
this uh later but as you can see not
much we can read from here just some
basic uh things set up
here and let's go ahead and finish
looking at everything that was added so
same thing in global. CSS you can see
that a bunch of new stuff was added here
uh and finally we have the utils file
located inside of source lib utils and
inside of here we have a new CN method
which we're going to use a lot in our
project so CN method will we used to
properly merge or conditionally show or
hide Tailwind classes without any
conflicts great so we have that so let's
go ahead and run our app now that we
have shat CN installed so Bun Run Dev
again at Local Host 3000 here let's go
ahead and refresh this page and there we
go so the only thing that changed for me
is the bright background at the moment
moment nothing else has changed great so
now uh let's go ahead and let's learn
how to add some components inside of
here so I'm going to go ahead and go
quickly back inside of shatan
documentation and I'm going to go ahead
and click on components here and I'm
going to find the button component
because that's going to be the one we're
going to be using a lot and inside of
here I'm simply going to copy the bun
version so now now let's go ahead and if
you want to you can shut down your app
or you can just open an alternative
terminal here and I'm going to paste the
command the only thing I'm going to
change is I'm not going to use the at
latest version I'm going to use the at
2.1.0 because I want you to be able to
see exactly which versions I have to
ensure that this project has some
longevity to it and so that no matter
when you come across this project you
can follow it exactly as I am right now
let's go Ahad and let's click enter and
there we go we now have a new component
inside of our project where can we find
that component well inside of source and
the new folder here components UI button.
button.
DSX the only differences is that we
cannot see that component at all so
let's go ahead and let's find this route
right here this nextjs with these two
buttons so that is located inside of
source app for folder page.
TSX what you can do here is you can
remove everything inside of this return
method and this image import and just
return a div hello world like this and
there we go you can see my Hello World
text right here perfect now let's go
ahead and let's import the button
component from components UI button so
this add sign is an alias which will
will take us to Source right so we don't
have to do the dot do SL do do do dot
you know all of those things we are just
using an alias here and now let's go
ahead and let's render our button
component here and let's go ahead and
me there we go we now have a button
component and we can go ahead and give
it a variant of destructive for example
or ghost like
this we can also give it a different
size like
large or small and the coolest thing
about the entire thing is that this
component is ours it's not hidden away
in node modules we can actually modify
every single thing you can imagine from
the actual variance which we just played
around for example above default I can
add I don't know a test here and I can
go ahead and give it a background green
and the text of white and I can now go
back here and change the
variant to test and there we go now it's
green so that's the cool thing that this
is what's going to allow us to create a
very specific and very custom uh
component library and we can do the same
thing with the sizes here and we can do
the same thing with the actual code
great so you now learned how to add the
components and you can see I'm having an
error here because I'm using an unknown
uh variant here perfect so besides this
I also want to test out whether our
Tailwind is actually working and just
show you something a bit important here
so I'm going to write Antonio here you
can write your name and I'm going to
give this a class name of text red 500
and let's go ahead and use Font semi
bold so as you can see my Tailwind is
working not only is it working but I
also have some cool hover features here
and I can even see this box right here
so how do I get those effects well very
simple it is an extension called Tailwind
Tailwind
CSS so you can go ahead and install my
apologies not Tailwind CSS just Tailwind
there we go Tailwind CSS intellisense
this is the one the one with 7 million
installs so make sure you have that it's
very useful uh when working with
Tailwind uh and another thing that I
want you to ensure and double check is
that inside of your tailing config.sys [Music]
here add features for example and inside
of here if I were to let's say add uh I
don't know component. DSX let's call it
component and let's return a div hello test
component let's go inside of our app
folder inside of page
DSX and let's import that test component
test there we go I have the text hello
test component and right now I believe
if I add a class name here text red
500 it is working right but I think that
this um Tailwind config is not set up
correctly right so let me just go ahead
and go back inside of here as you can
see this Tailwind config is only
covering uh components inside of pages
inside of components and inside of app
folder but it is not covering components
inside of features folder so why is it
that Tailwind seems to be working here
honestly I can't exactly tell you why
but what I can tell you is that it will
stop working at some point obviously
this has something to do with Tailwinds
you know Justus in time compiler or
something like that or maybe even hot
reloading uh but this technically should
not work so we have to ensure that we do
one important step here and that is to
copy and paste the line here and add
features inside of here so now we are
100% sure that all Tailwind we write
will be supported inside of this
features folder right now so we don't
really have anything important at the
moment here but we will have later on so
again double check inside of your
inside of source right that's one way of
doing it but just make sure that you
have the exact same Tailwind config as I
do great and now this should work
without any problem I'm even going to go
ahead and just remove my uh next cache
here and then I'm going to go ahead and
do Bun Run Dev again I'm going to
refresh this and this should still work
just fine I'm going to add one to three
here there we go hot reloading is
working I can change this to Green I
think we are now safe to say that uh our
features folder is definitely covered by Tailwind
Tailwind
config what we're going to do next is
add all the necessary components from
shaten for this project so let's head
back inside of our terminal here and
we've learned how to add individual
components but here's what we can do as well
well
using the command Buon x-- bun Shaden at
2.1.0 add instead of writing out the
button you can simply press the enter
after you write ad again if you're using
npx you just need npx shaten
2.1.0 so if you add add it's going to
ask you which ones to well add and you
can use the arrow keys to go up and down
and space to select or unselect specific
components so let's go ahead and add
everything we
need we're going to have to use Avatar
so make sure you select
Avatar badge we already have button so
no need for that select the calendar
card the
chart the
checkbox go ahead and select the
dialogue the drawer the drop- down menu
the for
popover scroll area
area
select separator sheet
sheet skeleton
skeleton
soner table tabs and text area and press
enter and now you're going to get asked
whether you want to overwrite the button
that's because some of the components we
selected have their own buttons so they
are asking whether you want to overwrite
it so just select yes here and there we
go so you should have a couple of new
project uh a couple of new files inside
of your project here so let me just go
ahead and show you inside of source
inside of components there we go you
should have all of these new projects
here I mean components not projects
great right so what I want to do next is
I want to show you how we're going to
customize this and turn it into our own
component Library so let's go inside of
the app folder inside of page. TSX we
can remove this import and instead we
can add a button component here and
let's write primary here like
this and what I want to do next is I want
want
to go ahead and let's see can I I kind
of have a gap
here Flex Gap four and I want to add
some more here so for example I want to
add secondary here and I want to give it
a variant of
secondary like this then I want to have
let's see what's next here basically I
destructive after
link after
link I'm going to add
outline and I believe that's
it there we go so all of my uh buttons
are here so what I'm going to do now is
I'm going to modify them and just make
them uh a bit more original and perhaps
it's better if I just let them all be
next to each other like this there we go
let's go ahead and let's go inside of
the button
component and specifically let's go
inside of the button variants and what I
want to do is I want to change how the
primary or the default button looks like
so let's go ahead and remove everything
inside of this default variant here I'm
just going to select everything inside
and just clear it out so right now as
you can see it's just empty so let's go
ahead and let's write background
gradient to
bottom from Blue 600 to Blue
Blue
700 and then let's go ahead and give it
foreground like this and then let's add
the hover effects so hover from
blue 700 and then another hover two blue
700 so now when you hover you can see
how it loses that gradient effect right
so that's going to be our uh default
button here now let's go ahead and let's
modify the destructive in a similar
manner so we're going to add background
gradient to bottom as
well from Amber
600 to Amber number 700 like that text
destructive
foreground so basically white and then
we're going to add some hover effects so
hover from Amber
700 and hover to
amember 700 so when you hover there we
go it gets flattened out that's the
effect we're looking for there we go so
we already have some pretty unique uh
buttons here great and now what I want
to do is I want to modify my secondary button
button
here so I'm going to go ahead and remove
everything inside of here and I'm going
to add background white text black hover
background neutral 100 there we go so my
secondary looks like that now great uh
I'm going to go ahead and modify the
ghost as well
here so my ghost will be border
transparent Shadow none power background
accent power text accent
foreground there we go so my ghost looks
pretty similar to my secondary but we
are going to use them on different
occasions let's remove the link we're
not going to need Link at all and
instead let's add a new one called muted
which will be background neutral 200
text neutral 600 cover background
neutral 200/80 so I'm going to go inside
of my page here and since I removed link
I'm going to change this to be muted and
write muted here there we go so that's
my muted button now great now I'm going
to add another button here I mean
another variant which we're going to call
call
territory so this will have a background
blue 100 text blue 600 border
transparent cover background blue 200
and Shadow
none I'm now going to go back inside of
page I will copy and paste the last one
territory and let's see if I made uh a
misspell here teritary make sure you
don't misspell ter there there we go now
we have three kind of important buttons
which we are going to use uh great
before we move forward what I want to do
is I want to rename this variant from
default to be called
primary so I'm going to go ahead and
select this and change this to primary
and then one important thing I have to
do is go inside of my default variance
and remove this to primary and then I
have to go inside of page here if I want
to now for example I can no longer use
default right I now have the available
availability to use primary and nothing
should change because that is the
default button so if you don't append
the variant it's going to fall back to
primary great so now let's go ahead and
let's change the sizes a bit so by
default I want all of my buttons to be a
bit larger so I'm going to give them a
height of 10 my small variant will not
have a text extra small inside of it
instead I'm going to add an extra small
variant here with a height of seven
rounded MD PX of two and text extra
small my large variant will be a bit
larger than this having a having a
height of 12 and my icons will be a bit
smaller having a height and width of
eight like that so if you want you can
try it out if I go ahead and give this a
variant I'm sorry a size of large there
we go it's pretty large and if I give it
an excess size it's pretty small I'm
want to go back inside of my button now
and now what I want to do is modify some
things in this default class name right
here so an important and the most
visible thing here is that I'm going to
be using a font semi bold instead of
font medium for my buttons right and
then the next thing I'm going to do is
I'm going to modify uh how my disabled
States look like so I'm going to go all
the way to the bottom here I'm sorry to
the end here and remove this default
disabled field and I'm going to create
my own so on disabled I'm going to add
background neutral 100 then I'm going to
do another disabled to be from neutral
100 another one two neutral 100 so what
this is doing is it's basically
flattening out any possible gradients
which we might have so we are covering
both cases whether our variant are using
backgrounds or if they're using
gradients by using this type of uh well
aliases right let me just find where I was
was
okay so after this now I'm going to add
a couple of more things including a text
color when we are disabled so again when
disabled let's use text neutral
300 let's also add a border around each
of our buttons border neutral 200 and
let's add a shadow small there we go so
those are going to be our buttons as you
can see white unique may I say so myself
great we now have these buttons and what
I want to do next is just modify a few
other components we're going to have so
let's go inside of components UI and you
should have the input component so
inside of here uh looks like I have some
error here I'm not exactly sure why but
uh we're going to come back to this
later and let's go ahead and change the
input height to be 12 instead of nine so
I want my inputs to be a bit larger so
if you want you can go to your Source
page here and add an input here if you
want to so you can observe that as well
even though it's not exactly
uh well too visible at the
moment and let's do the same thing
inside of our select component right
here so choose the select component
here find the select trigger and simply
increase from height 9 to height 12 like
this great so we have now customized our
components to our liking and we're now
going to use them to build our
authentication screens great great
job so before we continue forward and
build our authentication screens I
noticed we have some errors in our newly
added components from shat CN UI so if I
go instead of source components and for
example input
component I have an error here and by
looking at my older project it seems
like this only happen happens in the
newest version of
nextjs which at the time of making this
video happens to be uh where is it let
me just find it
14.2.1 14 and we know
the shaty N version here let's go ahead
is the latest shaten version is
2.1.0 so looks like that combination
causes these errors here so this is what
I want to do I'm going to go ahead and
shut down my
app and go ahead and run Bun Run build
or npm run build and inside of here you
should see all the errors which you have
inside of your project and most of them
these are just simple lint errors let's
go ahead and fix all of them one by one
so we can continue with our project
knowing that we no longer have any
errors inside of our components and in
an odd chance you don't have any errors
well then that's perfectly fine no need
to do anything then let's go ahead
inside of calendar.
DSX so looks like we have two errors
here we are spreading the props but we
are not using them anywhere so what I'm
going to do is I'm just going to remove
both of this here there we go so in
inside of my components UI calendar that
seems to be resolved now no more errors
in this file so that's for the calendar
we then have the chart component so
let's go inside of chart.
DSX looks like we have these three
imported but never used so I'm going to
remove them here let's scroll a bit down
and inside of here it seems like this is
defined but never used uh so let's see
can I maybe just
add a text I can usually the underscore
should be fine for no unused Wars but
for some reason it's not here so we have
to be careful with this we cannot just
remove this because it obviously needs
to be the second argument here so what
I'm going to do is I'm just going to go
ahead and
disable uh this line right here so you
can hover over this quick fix and click
disable unused bars for this
line you can also pause the screen and
spell this out exactly so that resolves
the chart issues we didn't have the
input and text area in errors so let's
go ahead instead of UI instead of input
and this can be fixed quite easily as
well so I don't basically what this is
erroring is that we are extending an
empty interface so so the way it
suggests fixing this is replacing empty
interface with a type Alias like this so
expert type input props you can see the
difference right we just change this to
be a type and instead of extends we
simply assign that to be the new element
so change it like this and we have to do
the same thing inside of the text area
right here so I'm going to go ahead and
use the same thing here quick fix
replace empty interface with a type
Alias like this in case you don't have
those options you can pause the screen
and write it out exactly as I did I
think text area was the last error so if
I do Bun Run build or npx run or npm run
build I think that now there we go so
make sure that when you run Bon run
build you can build your project without
any errors this is important I don't
want us to continue if we have any
errors at all looks like we are unlucky
with our versions of shaten and nextjs
but not a big problem we just resolved
all of them quite easily may I say so
great so what I want you to do now is do
RM rf.
next so I want you to remove all cache
and then just do b runev or npm runev
and let me go ahead and refresh my
browser here I just want to double check
that everything is working fine I'm
going to change this to primary one to3
there we go and no errors here perfect
great great
job let's go ahead and let's create our
authentication screens for that we first
have to know the basics of how routing
Works inside of nextjs so let's go
inside of the source inside of the app
folder and inside of here I'm going to
create a new folder called sign in and
then inside of that I'm going to create
a page.
TSX so page is a reserved file name
inside of nextjs and whenever used
inside of the app folder depending on
what folder it is in that will become a
part of the URL so let's go ahead and
learn that a bit make sure that inside
of your app folder you have created a
sign-in folder and then a page.
TSX in inside of that file what's
important is that you do default exports
so I'm going to call this signin page
like this and a div sign in
page so this expert default is very
important the name of the constant
doesn't matter it can be called anything
the only thing that matters is that you
do a default export here and now that we
know that our folder is named
signed-in we can visit that URL so I'm
going to quickly enable something here
called developer mode so that you can
see my URL at all times here so I'm
going to go to localhost
3000/- in and I don't have my app
running so let me just go ahead and do
that quickly and also reminder for you
in case you don't have your app running
there we go so now I after I visit SL
signin you can see that I'm on the
signin page so what happens if I don't
do the default export here what happens
if I just do export const without the
export default
well what will happen is that we have an
error because the default expert is not
a react component great so let's go
ahead and roll this
back and let's ensure that we have our
signin page visible so that's one thing
I want you to learn the other thing I
want you to learn is the another
reserved file just like Pages called the
layout so you already have the default
layout here in your project and ours is
going to be similar to this but also a
little bit different because this is the
root layout you are most likely never
going to change this file except adding
some providers or changing the fonts
right but other than that this will
mostly stay exactly as it is but the
convention it follows is well quite
similar right so if I go inside of the
uh app folder sign in and if I create
layout. DSX inside of this file and I do
the exact same thing so I'm going to
call this signin
layout and a div hello layout so what
will happen now interestingly on the
same route that I was before/ signin I'm
now seeing hello layout so it looks like
our layout file has overridden the page
file well that's because we are not
using the layout file properly so the
layout files are used for reusable
things so the proper way of using this
is to create an interface
sign in layout
props and Define the children as a
required prop here because every sign in
layout which is a reserved file will
always have children so you don't have
to worry about where they come from
react uh I mean nextjs will handle that
for us so now that we have the children
which are safely typed in this interface
above instead of rendering hello layout
we can render the children inside and
there we go now you can see that my page
is rendered instead sign in page so what
is the purpose of this layout file you
might ask well very simple let's go
ahead and let's add a class here Flex Flex
Flex
column and let's pretend to have a nvar
here I'm going to go ahead and give this
Novar a background red 500 and a height
of 10 and I'm going to go ahead and add
a paragraph
navbar so this is what layouts are used
for so if I for example had another
route inside of signin page for example
let's go ahead and call this test it
really doesn't matter for now I'm going
to go inside of this test and add
another page. DSX file I'm going to call
this test page and do a default xort and
test page here so how do I access this
test page well I go to slash sign in and
then SL test that's how you do nested
routes so follow along as I do go to/
signin SL test and there we go here's
the cool thing the Novar is completely
unchanged but the content of the page
has changed so that is the purpose of
layout files so you can add reusable
layouts to your pages right so for
example in our out screens I want both
my login and register routes to have
similar layouts so this layout file is
perfect for that later on the dashboard
screen I'm going to want to have a side
bar and I want all of my routes to have
a sidebar as well so I'm again going to
use a different layout for that so that
is the purpose of that uh great and now
let's go ahead and do the following
let's remove this test file we no longer
need it let's remove the layout from the
sign in as well and now I'm going to
show you uh one cool thing that you can
do let's just go back to SL sign in here
so youve learned that every single you
know you know um folder which has a
page. CSX inside will create a URL well
that's not always true because you can
create something called a route group
and I'll show you why they are powerful
so let's go ahead and create an out
group we do that by naming the folder
inside of our parentheses so I'm going
to call this out and what I'm going to
do is I'm going to drag and drop sign in
inside of the out folder and now I have
this prompt to up dat Imports you can
always say yes for this and I seem to
have no unsaved files if you do you know
you can just click here and save them so
what happens now exactly well first of
all let's check how our routes work I am
at Local Host 3000 signin and if I
refresh everything works just fine right
so you might have expected this to be
something like this maybe but no so
whenever there is a folder inside of
parenthesis that means that it part that
part is emitted from the URL so that's
the purpose of Route groups and what's
cool about this is that I can now go
ahead and copy the sign in paste it
inside of out and call this sign up for
example go inside of signup page. vsx
let's change this to be sign up
page and change this to be sign up page
so now both of my routes here both of my
you know Al routes are inside of my my
out route group here so then I can go
to/ sign up and on the URL side they
seem to be you know independent of one
another right they seem to behave like
separate routes but we know that inside
of our code base they are held together
in this route group and here's the
coolest thing about this while they are
emitted from the URL they can have their
own layouts so if you add layout
DSX inside and follow the same thing we
did earlier so this time I'm going to
layout out layout if I save this there
we go so now both my sign up route and
my signin routes have the same
overridden text coming from the layout
file so let's go ahead and do the thing
we did earlier which is proper use of
out layout so interface out layout
props children react react
node and inside of here out layout
props with the children inside and
render the children inside of the
instead of the text and there we go now
we have a shared layout file in two
different routes and we ensured that no
other routes will share that layout
because of this route group so both sign
in and sign up now share the same layout
and again you can do the uh navbar trick
to Showcase how that looks what I want
you to do next is to find a logo for
this project and here's a handy website
you can use called logo ipsum so logo
ipsum is something like a placeholder
logo collection here which you can
easily just click and you copy the SVG
so what's important is that you don't
actually use this for your business you
are free to use this uh you know for
demos for tutorials but don't use this
for your business and just refer to the
guidelines of this website but it's a
very very handy website for finding
quick nice looking logos for your
projects so here I am I'm going to find
a logo that I like for example this one
and now what we have to do is we have to
create this SVG file so I just clicked
and that copied the SVG and now we have
to actually create that so what we don't
have is the public folder so let's go
ahead and create a public folder in the
root of our application so not in the
source folder it needs to be outside
inside of the public folder let's go
ahead and let's add a logo.svg file
inside and let's paste everything inside
here there we go so I just pasted the
entire thing that I copied and now I
have logo. SVG
here and now let's go ahead and go
inside of our source inside of our app
Al layout. DSX right here and let's
simply go ahead and and render this logo
inside of a nextjs image so import image
from next image let's add a source to
logo. SVG let's give it a height of 50
and a width of 100 for example and out
of logo like this I'm now going to close
this website so again just click on the
logo and you have copied the SVG you can
also directly download it by clicking
here and there we go we now have our
logo right here what I want to do is
just change its color because we are
going to be using a specific uh blue
color throughout our project and I just
want to stay true to that color so let's
go inside of our
logo.svg and if you copy the same logo
as I did you can then go ahead and find
the fill option right here and just
change it to a new color and then when
you refresh there we go now it has a
blue logo and I like this way better
great now let's go ahead and let's add
this layout uh and let's actually give
it you know some nov bars some buttons
and other stuff so I'm going to go ahead
and replace this D with a main element
and I'm going to give it a class name of
background neutral 100 and a minimum
height of screen so it takes up the
whole uh page and I have removed the
image tag but I am going to leave the
image import because we're going to use
it in a second and now let's go ahead
and let's add a div here with the class
name m Max AO Max with screen to Excel
and padding of four and make sure you
put children inside of that
div there we go so you can see how now
this is centered and what did we achieve
with Max with screen to excel well this
is what we achieved uh at a certain
point you can see how our page stops
expanding right so when I am zoomed in
you can see it's following the edge of
the screen it's following but when it
reaches a certain point it will stop
following the screen so this means that
if your screen is too large we're not
going to you know resize all of our
elements needlessly instead we're just
going to allow you this specific box on
the screen for you to work with so
that's what that part achieved in case
you were
wondering great so we have that now and
now let's go ahead and let's create our
Navar element here so our Navar element
will have a class name of
flex justify between and items Center
inside of the nov bar we're going to
have a div and then we're going to have
our image let's give this div a flex
items Center and gap of two and the
image will have a source of logo SVG
make sure you put a forward slash in
front of the source an out of logo a
width of 152 and a height of
56 and there we go you can see that now
we have our logo once again but this
time it is properly positioned and
inside of semantic knv bar element here
and one thing that I think we actually
don't need is the wrapper around the
image so let's go ahead and remove this
div and just keep the image directly
inside of the knf bar like this now
here's another thing we're going to add
inside of the nov bar let's add a div
and this time we are actually going to
knit this so Flex item Center and gap of
two and inside of here we're going to
have a button
and let's go ahead and give this uh my
apologies let's give this button a
variant of secondary and for now let's
just go ahead and write sign up like
this and again I've made the same
mistake uh I keep planning on having two
buttons but we are not going to have two
buttons my apologies so you can remove
this div and instead of the enough bar
just have an image and the button my
apologies once again there we go so now
we have a Sleek looking knb bar here uh
which has a sign up button and a logo
right here in the uh opposite corner
great and now outside of this Novar
component let's go ahead and let's add a
div component
here and let's go ahead and give this a
class name of flex Flex column items
Center justify Center padding top of
four and on medium devices pading top of
14 there we go so now you can see that
our Tex next here is centered like this
and it also has quite some spacing here
and if I zoom in you can see that it
kind of has well you can see that
there's a jump in the padding right on a
specific break point this is going to be
more visible later when we have some
actual elements to
show before we go ahead and actually
develop our sign up and sign in Pages uh
I quickly want to go back inside of my
source app folder and then inside of my
layout. DS x file here and I actually
want to remove this local fonts and I
want to use the inter font so this is
what I'm going to do inside of my app
layout I'm going to import inter from
next font
Google I'm going to remove the local
font we are not going to need it so I'm
going to remove both of this here like
that and instead what I'm going to do is
I'm going to add const
enter to be enter which we imported above
above subsets
subsets
Latin and then I'm going to go ahead and
do the following I'm going to import CN
from atly utils if you remember when we
set up Shad CN we explored this little
CN util which will help us help us merge
this font class so let's go ahead and do
the following inside of our body element
I'm going to go ahead and remove the
entire thing I'm going to add CN enter
class name and then I'm going to add
anti alas I'm not sure how to pronounce
this I'm sorry uh and let me just
confirm that I didn't yes so this is the
proper uh command name my apologies and
after that minimum height of screen make
sure you add this as well there we go so
now we have a new font and here's
another thing you might see me do a lot
I like to order my imports in a specific
way so the Imports which are coming from
packages will always be at the top for
me after that I'm going to go ahead and
have my Alias Imports and after that I'm
going to have my relative Imports so
that's how I like to separate them you
don't have to follow that uh it's just a
habit that I have great so just make
sure that inside of your root layout
inside of the app folder you have
changed the font name to enter and you
have added a minimum height of screen
this is a new class which comes default
from I think shaten version I'm not
exactly sure what it does uh you can
explore that for
yourself all right now let's go ahead
and let's go back inside of our app
folder inside of our uh signin page.
DSX and here's how I want to do this so
I don't want to directly develop this
here I actually want to create my signin
card like this which I currently don't
have and now let's go ahead and let's
actually develop the signin card and
we're going to do that by creating our
features folder if you remember it from
uh when we you know we were discussing
our Tailwind config and we ensured that
our Tailwind config covers the features
folder where this is where that now
becomes very important so once again go
config.sys folder added
here now let's go inside of features we
can remove the test component and inside
of here we can go ahead and we can add
uh a new folder called out and very
simply inside of this folder we're going
to keep everything out related including
components and inside of the components
we're now going to create our signin
card. TSX like that let me just double
check inside of my app folder inside of
page that I'm not using you know that
deleted featured test file looks like
I'm not everything seems to be working
just fine now let's quickly go back
instead of the signin card inside of
features out components and whenever I
work with components I'm going to do uh
named exports so I'm not going to do
export default here I only do export
defaults for my pages and for my layouts
because that is what nextjs requires so
let's export cons signin card
here and let's just return a div sign in
card and now we can go back inside of
our out folder sign in page. TSX and we
can import this from features out
components signin card and then we can
go ahead and develop further right here
so what I'm going to do is I'm going to
go ahead and import everything we need
from our card if you remember we added
the card from shaten uh in a few
chapters before this one when we added
all components we are going to need so
just double check that you have ADD
components UI card inside of your
project right here and make sure you add
uh these four imports from that package
so now let's go ahead and let's actually
use that card here and let's go ahead
and give it a class name full width full height
height
andd width is going to be fixed to
486 pixels border will be none and
Shadow will be none as
well then let's go ahead and let's add a
card header component and the card title
component inside of the card title I'm
going to say welcome
back and let me just refresh my page and
let me go to slash sign in so we can
actually see that so make sure you are
at slash uh sign in and not/ sign up
because we are currently
developing our
app out signin page. DSX which has the signin card so make sure you're seeing
signin card so make sure you're seeing that
that properly now let's go ahead and give the
properly now let's go ahead and give the card title a class name of text to excel
card title a class name of text to excel like that uh and let's go ahead and give
like that uh and let's go ahead and give the card header a class name of flex
the card header a class name of flex items
items Center justify Center text Center
Center justify Center text Center padding of seven like
padding of seven like that below the card header let's go
that below the card header let's go ahead and let's add a div here and a
ahead and let's add a div here and a class name PX of 7 and now I want to
class name PX of 7 and now I want to create my own type of separator so we
create my own type of separator so we already have a separator from shat CN
already have a separator from shat CN but the problem with that
but the problem with that separator is that it only comes uh in
separator is that it only comes uh in flat lines right so I'm just going to go
flat lines right so I'm just going to go ahead and give this a mark margin bottom
ahead and give this a mark margin bottom so you can see the separator right here
so you can see the separator right here it only comes in flat lines but I want
it only comes in flat lines but I want to create my own dotted separator let's
to create my own dotted separator let's go ahead and learn how to create that
go ahead and learn how to create that funky little component so my own
funky little component so my own components are going to go inside of the
components are going to go inside of the components folder I'm not going to add
components folder I'm not going to add them inside of the UI folder I'm going
them inside of the UI folder I'm going to reserve the UI for shatan related
to reserve the UI for shatan related components so instead of my components
components so instead of my components I'm going to create dotted Dash
I'm going to create dotted Dash separator
separator DSX and let's go ahead and let's import
DSX and let's go ahead and let's import our
our CNU let's go ahead and let's prepare the
CNU let's go ahead and let's prepare the interface for dotted separator props so
interface for dotted separator props so we're going to have all of them
we're going to have all of them completely optional the class name which
completely optional the class name which is a string color height dot size gap
is a string color height dot size gap size and Direction which can be either
size and Direction which can be either horizontal or vertical then let's export
horizontal or vertical then let's export cons dotted separator
cons dotted separator here let's assign the props
and let's destructure all the props which we might need here so class name
which we might need here so class name color which by default will have a
color which by default will have a specific gray color so make sure you've
specific gray color so make sure you've added this then we're going to have
added this then we're going to have height which by default will be two
height which by default will be two pixels then we're going to have dot size
pixels then we're going to have dot size which will also be 2 pixels then we're
which will also be 2 pixels then we're going to have gap size which will be six
going to have gap size which will be six pixels and then we're going to have the
pixels and then we're going to have the direction which by default if not set
direction which by default if not set will be horizontal like that let's go
will be horizontal like that let's go ahead and let's create a Boolean
ahead and let's create a Boolean variable here is
variable here is horizontal so that we don't have to do
horizontal so that we don't have to do you know the direction check every time
you know the direction check every time we can just do it once here and then we
we can just do it once here and then we can do ifs later on let's go ahead and
can do ifs later on let's go ahead and let's return a div
let's return a div here and let's create a class name with
here and let's create a class name with a
a CN if it is horizontal we're going to
CN if it is horizontal we're going to render full width flex and items
render full width flex and items Center otherwise we're going to render
Center otherwise we're going to render full height Flex Flex column and items
full height Flex Flex column and items Center and then we're going to add a
Center and then we're going to add a comma at the end of here and we're going
comma at the end of here and we're going to pass in class name meaning that user
to pass in class name meaning that user can extend this however they want from
can extend this however they want from the outside of the component as well so
the outside of the component as well so what's important is this um territory
what's important is this um territory CLA right
CLA right here the then we're going to go ahead
here the then we're going to go ahead and add another div inside and this will
and add another div inside and this will actually be a self-closing div and it's
actually be a self-closing div and it's going to be the last element we add here
going to be the last element we add here and this one will have a bit more Styles
and this one will have a bit more Styles so first of all our class name again
so first of all our class name again will check uh inside of well we don't
will check uh inside of well we don't have to use CN here because we don't
have to use CN here because we don't have to worry about merging so just is
have to worry about merging so just is if is horizontal use flex grow otherwise
if is horizontal use flex grow otherwise Flex grow
Flex grow zero and now we have to do the rest in
zero and now we have to do the rest in Styles because
Styles because Tailwind will not exactly support
Tailwind will not exactly support Tailwinds Justus in time compiler will
Tailwinds Justus in time compiler will not exactly support this kind of dynamic
not exactly support this kind of dynamic variables which we need so let's go
variables which we need so let's go ahead and Define the width the if is
ahead and Define the width the if is horizontal 100% otherwise it's going to
horizontal 100% otherwise it's going to be the height variable which we have
be the height variable which we have then for the height again if is
then for the height again if is horizontal we're going to use the height
horizontal we're going to use the height variable or a
variable or a 100% for the background
100% for the background image we're going to go ahead and open
image we're going to go ahead and open back there X and then we're going to go
back there X and then we're going to go ahead and write radial gradient and make
ahead and write radial gradient and make sure that whenever you're writing this
sure that whenever you're writing this you uh do it exactly like this right so
you uh do it exactly like this right so make sure there are no typos here it's
make sure there are no typos here it's going to be a
going to be a circle we're going to add a color
circle we're going to add a color variable here so this is the kind of
variable here so this is the kind of things we are not able to do dynamically
things we are not able to do dynamically in Tailwind so that's why we're using
in Tailwind so that's why we're using the style prop here so
the style prop here so color uh without a comma so just a space
color uh without a comma so just a space 25% and then a comma transparent
25% and then a comma transparent and then a space 25%
and then a space 25% again great after that let's add a
again great after that let's add a background
background size which is going to check if is
size which is going to check if is horizontal in that case we're going to
horizontal in that case we're going to open back next and we're going to parse
open back next and we're going to parse integer dot size plus parse integer gap
integer dot size plus parse integer gap size and add pixels at the end here and
size and add pixels at the end here and then after that simply add a height
then after that simply add a height otherwise we're going to open back this
otherwise we're going to open back this again and we're going to use the height
again and we're going to use the height and then parse in do size plus Barse in
and then parse in do size plus Barse in gap
gap size in pixels like
size in pixels like that after background size we're going
that after background size we're going to have background repeat
to have background repeat option if is horizontal it's going to be
option if is horizontal it's going to be repeat X otherwise repeat Y and last one
repeat X otherwise repeat Y and last one background position
background position Center so you just created your own
Center so you just created your own separator component here and it's also
separator component here and it's also heavily customizable let's try it out
heavily customizable let's try it out I'm now going to go back inside of my
I'm now going to go back inside of my features Al components sign in card and
features Al components sign in card and I'm going to use the dotted separated
I'm going to use the dotted separated here like this and let's go ahead and
here like this and let's go ahead and see whether we did something incorrectly
see whether we did something incorrectly here because looks like our dotted
here because looks like our dotted separator at the moment is not appearing
separator at the moment is not appearing so I'm just going to go ahead uh and
so I'm just going to go ahead uh and debug what went
debug what went wrong all the issues seems to be in my
wrong all the issues seems to be in my typo so go back inside of the dotted
typo so go back inside of the dotted separator and I misspelled Circle this
separator and I misspelled Circle this is ccle so Circle and there we go we now
is ccle so Circle and there we go we now have a nice dotted separator which we
have a nice dotted separator which we can use either vertically or
can use either vertically or horizontally depending on our needs
horizontally depending on our needs great we can now continue developing our
great we can now continue developing our signin card so I'm just going to leave
signin card so I'm just going to leave this a px of s here like that below that
this a px of s here like that below that I'm going to go ahead and add a card
I'm going to go ahead and add a card content here I'm going to give the card
content here I'm going to give the card content a class name space y4 like this
content a class name space y4 like this my apologies I'm going to give it a
my apologies I'm going to give it a padding of seven actually and then I'm
padding of seven actually and then I'm going to add a form
going to add a form element which will have a class name
element which will have a class name space
space y4 like this and now let's go ahead and
y4 like this and now let's go ahead and let's add uh our input component from
let's add uh our input component from components UI input let's go ahead and
components UI input let's go ahead and make it required let's give it a type of
make it required let's give it a type of email let's go ahead and give it a value
email let's go ahead and give it a value of email let's go ahead and give it an
of email let's go ahead and give it an unchange for now to just be an empty
unchange for now to just be an empty Arrow function and value here can also
Arrow function and value here can also be empty because we don't have the email
be empty because we don't have the email constant just yet let's give it a
constant just yet let's give it a placeholder enter email
address and let's give it a disabled prop of false
prop of false now make sure you have added the input
now make sure you have added the input import here you can remove the separator
import here you can remove the separator import and one thing that I want you to
import and one thing that I want you to add uh is use client
add uh is use client here once you add use client you will
here once you add use client you will get rid of that error so what happened
get rid of that error so what happened with that error in nextjs every
with that error in nextjs every component in filed which is used inside
component in filed which is used inside of the app folder is considered to be a
of the app folder is considered to be a server
server component so in order to well it would
component so in order to well it would be incorrect to say to convert a
be incorrect to say to convert a component to use client the more correct
component to use client the more correct term is to open a boundary between
term is to open a boundary between server and client why am I using that
server and client why am I using that terminology specifically well you can
terminology specifically well you can see that adding Ed client here fixes our
see that adding Ed client here fixes our issue but we can also do this we can go
issue but we can also do this we can go instead of the out folder and we can go
instead of the out folder and we can go instead of a sign in and we can mark
instead of a sign in and we can mark this entire thing as use
this entire thing as use client you can see that that resolves
client you can see that that resolves the issue as well right so that's why
the issue as well right so that's why the terminology convert a server
the terminology convert a server component to a client component should
component to a client component should be avoided right it's incorrect the
be avoided right it's incorrect the correct version is you've opened a
correct version is you've opened a boundary between server and client
boundary between server and client because once again you can also resolve
because once again you can also resolve it by going in layout for example if I
it by going in layout for example if I go instead of the out layout and Mark
go instead of the out layout and Mark this as Ed client
this as Ed client let's go ahead and refresh and see looks
let's go ahead and refresh and see looks like uh it doesn't work on the layout
like uh it doesn't work on the layout all right I I was incorrect my apologies
all right I I was incorrect my apologies yes um so why does it not work here
yes um so why does it not work here that's because we are passing our
that's because we are passing our children through the children prop so
children through the children prop so you can actually combine client and
you can actually combine client and server components if you're going to
server components if you're going to pass them through children but that is
pass them through children but that is you know a topic we can discuss later I
you know a topic we can discuss later I don't want you to confuse you too much
don't want you to confuse you too much what I want you to do now is going set
what I want you to do now is going set the signin page and mark the page as
the signin page and mark the page as used client
used client instead this way you don't have the
instead this way you don't have the errors anymore in the signin card and
errors anymore in the signin card and you don't have to open the boundary that
you don't have to open the boundary that low you can open the boundary a bit
low you can open the boundary a bit higher because we are going to use some
higher because we are going to use some client stuff in this page here as well
client stuff in this page here as well so let's focus on the signin card for
so let's focus on the signin card for now great we have added this input here
now great we have added this input here let's go ahead and copy and paste this
let's go ahead and copy and paste this input
now and I'm going to go ahead and give this a type of password and this will be
this a type of password and this will be enter
enter password let's go ahead and give it the
password let's go ahead and give it the minimum value of eight and the maximum
minimum value of eight and the maximum value of
value of 256 and let's give it the uh disabled is
256 and let's give it the uh disabled is already false great so we now have enter
already false great so we now have enter email and we have enter password and
email and we have enter password and below that I want to add a button
below that I want to add a button component from component UI button so
component from component UI button so make sure you have added this as well
make sure you have added this as well and our button component will simply say
and our button component will simply say log in let's go ahead and give this a
log in let's go ahead and give this a disabled of false let's give it a size
disabled of false let's give it a size of large and a class name of full width
of large and a class name of full width like this there we go you can see that
like this there we go you can see that we now have uh a very nice looking uh
we now have uh a very nice looking uh sign in component here let's go ahead
sign in component here let's go ahead and develop it a bit further so what I'm
and develop it a bit further so what I'm going to do now is I'm going to go
going to do now is I'm going to go outside of the card content here and I'm
outside of the card content here and I'm going to go ahead and add another div
going to go ahead and add another div with a class name X of s and I'm going
with a class name X of s and I'm going to add a dotted separator here again and
to add a dotted separator here again and then I'm going to add a new card content
then I'm going to add a new card content here with a class name of padding S Flex
here with a class name of padding S Flex Flex column and GAP y of
Flex column and GAP y of four and then inside of here I'm going
four and then inside of here I'm going to add another button which will say
to add another button which will say login with
login with Google and I'm going to give this a
Google and I'm going to give this a variant of
variant of secondary
secondary size of large a class name of pool width
size of large a class name of pool width and I'm also going to give it an
and I'm also going to give it an explicit disabled FS so the reason I'm
explicit disabled FS so the reason I'm adding those are because I just want to
adding those are because I just want to remember later on to disable them great
remember later on to disable them great so we have this let's copy and paste
so we have this let's copy and paste this and let's change this to log in
this and let's change this to log in with GitHub for example or you know you
with GitHub for example or you know you you'll see later when we add ight uh it
you'll see later when we add ight uh it will be quite easy to add any other
will be quite easy to add any other providers you might want
providers you might want to great so I kind of want to improve
to great so I kind of want to improve this a bit by adding a package so either
this a bit by adding a package so either bun add or mpm install depending on what
bun add or mpm install depending on what you use uh react icons so that's the
you use uh react icons so that's the package we are going to need and now
package we are going to need and now let's go ahead and let's add uh two of
let's go ahead and let's add uh two of which we're going to use so that's going
which we're going to use so that's going to be FC Google from react icons
to be FC Google from react icons FC and we're going to need uh I think fa
FC and we're going to need uh I think fa GitHub from react icons
GitHub from react icons fa I hope I'm correct with this Imports
fa I hope I'm correct with this Imports so my login with Google will have FC
so my login with Google will have FC Google here with a class name Mr off two
Google here with a class name Mr off two and size of five there we go and my
and size of five there we go and my login with GitHub will have an fa GitHub
login with GitHub will have an fa GitHub with a class name Mr of Two and a size
with a class name Mr of Two and a size of five as well there we go so a very
of five as well there we go so a very nice component for us to have here uh
nice component for us to have here uh for signing in and now what I want to do
for signing in and now what I want to do is I want want to copy this entire
is I want want to copy this entire component
component here uh called sign in
here uh called sign in card and let's call this one sign up
card and let's call this one sign up card like this let's go ahead inside of
card like this let's go ahead inside of the sign up card uh and let's just
the sign up card uh and let's just briefly modified so it's going to be
briefly modified so it's going to be pretty similar let's immediately rename
pretty similar let's immediately rename it so we don't forget sign up
it so we don't forget sign up card so the difference is going to be
card so the difference is going to be that the title will say sign up here
that the title will say sign up here and we are also going to have a card
and we are also going to have a card description which you can import right
description which you can import right here from components UI card and before
here from components UI card and before we develop anything further I just want
we develop anything further I just want to go inside of my source app out sign
to go inside of my source app out sign up page I want to mark this page as use
up page I want to mark this page as use client and I want to return the sign up
client and I want to return the sign up card
whoops from features out components sign up card and now we can go to localhost
up card and now we can go to localhost 3000 signup here and there we go now it
3000 signup here and there we go now it says sign up now let's develop that card
says sign up now let's develop that card directly so we uh left off at the card
directly so we uh left off at the card description so let's go ahead and add
description so let's go ahead and add you know a description which you would
you know a description which you would most likely have on your sign up form by
most likely have on your sign up form by signing
signing up you agree to our let's go ahead and
up you agree to our let's go ahead and add a manual space like this and then
add a manual space like this and then then let's add a link
then let's add a link component you can import link from next
component you can import link from next link like I'm going to do
here and inside let's go ahead and add a span privacy
policy and this can simply go to SL privacy even though we're not going to
privacy even though we're not going to develop this page this will you know
develop this page this will you know simply render your privacy policy let's
simply render your privacy policy let's go ahead and give this a class name of
go ahead and give this a class name of text blue 700
text blue 700 there we go you agree to our privacy
there we go you agree to our privacy policy or privacy policy and then let's
policy or privacy policy and then let's go ahead and add another space here and
go ahead and add another space here and let's go ahead and copy and paste this
let's go ahead and copy and paste this link like that we can remove this one at
link like that we can remove this one at the end and this will go to slash terms
the end and this will go to slash terms and we can go ahead and do the following
and we can go ahead and do the following and and then another space here terms of
and and then another space here terms of service there we go so now our uh sign
service there we go so now our uh sign up form has you know by signing up you
up form has you know by signing up you agree to our privacy policy in terms of
agree to our privacy policy in terms of service great now besides the inputs for
service great now besides the inputs for email and password the sign up form will
email and password the sign up form will also have a type text here and enter
also have a type text here and enter name like that or more personally enter
name like that or more personally enter your name like this so when we you know
your name like this so when we you know when we sign up we're also going to have
when we sign up we're also going to have enter your name and enter alongside
enter your name and enter alongside these other Fe features
these other Fe features here before we wrap up our sign up cards
here before we wrap up our sign up cards let's go ahead and let's actually add
let's go ahead and let's actually add the functionality to control these
the functionality to control these values now since these are very simple
values now since these are very simple forms there is actually nothing stopping
forms there is actually nothing stopping us from you know just adding a set State
us from you know just adding a set State here if that's what you want but if you
here if that's what you want but if you remember in the beginning when we set up
remember in the beginning when we set up shaten and added all the components we
shaten and added all the components we need we also installed uh react hook
need we also installed uh react hook form so just double check that you have
form so just double check that you have this installed inside of your package
this installed inside of your package Json here if you don't go ahead and
Json here if you don't go ahead and install it and same is true for Zod you
install it and same is true for Zod you need to have this as well and I think
need to have this as well and I think there is also one more uh maybe
there is also one more uh maybe not all right we're going to go ahead
not all right we're going to go ahead and develop and see whether it's missing
and develop and see whether it's missing or not so let's go inside of our source
or not so let's go inside of our source inside of features let's go inside of
inside of features let's go inside of signin card and inside of here I'm going
signin card and inside of here I'm going to import use form from react hook form
to import use form from react hook form and then I'm going to create my form
and then I'm going to create my form schema here from which I will use Z from
schema here from which I will use Z from Zod so let me just
Zod so let me just import Z from Zod
import Z from Zod here like that so Z doob here and I'm
here like that so Z doob here and I'm going to have my email which will be a
going to have my email which will be a z.
z. email uh let's go ahead and z. string.
email uh let's go ahead and z. string. email
email like this and we're going to have a
like this and we're going to have a password which will be a string as
password which will be a string as well great now inside of here let's add
well great now inside of here let's add our form to be used form and let's go
our form to be used form and let's go ahead and add default values here email
ahead and add default values here email empt and
empt and password empty like
password empty like this and now we need to add our Zod
this and now we need to add our Zod resolver to connect it with the form
resolver to connect it with the form schema so let's see whether we have this
schema so let's see whether we have this package or not so from Hook form we do
package or not so from Hook form we do hook form resolvers Zod that's the one
hook form resolvers Zod that's the one we need and inside of here we need our
we need and inside of here we need our Zod resolver so let's head back inside
Zod resolver so let's head back inside of package
of package Json we do have hook for resolvers right
Json we do have hook for resolvers right here just double check you have that as
here just double check you have that as well you should have all of this uh
well you should have all of this uh because we have added the form component
because we have added the form component from
from shaten which automatically installed
shaten which automatically installed everything for us here great and we can
everything for us here great and we can now add a resolver here so resolver Zod
now add a resolver here so resolver Zod resolver and simply passing create my
resolver and simply passing create my apologies form schema
apologies form schema here and for the use form you can also
here and for the use form you can also add z.
add z. infer type of form schema like this
infer type of form schema like this there we go now our form is strictly
there we go now our form is strictly typed right here great so now what I
typed right here great so now what I want to do is I want to import some
want to do is I want to import some other things from form which we're going
other things from form which we're going to need so let's go ahead and prepare
to need so let's go ahead and prepare that so these are all the possible
that so these are all the possible things you can import from form so we're
things you can import from form so we're going to need the form form control form
going to need the form form control form field form item but we're not going to
field form item but we're not going to need form label we're not going to use
need form label we're not going to use that so just form form control form
that so just form form control form field form item and form a message here
field form item and form a message here great now let's go ahead and let's wrap
great now let's go ahead and let's wrap our form here inside of this form
our form here inside of this form component from our uh UI components form
component from our uh UI components form and let's just indent the entire thing
and let's just indent the entire thing and inside of here you would simply
and inside of here you would simply spread the form like this and then let's
spread the form like this and then let's go ahead and modify this to be our uh
go ahead and modify this to be our uh form
form field and it will use the render and get
field and it will use the render and get the
the field and then inside of
field and then inside of here we're going to go ahead and add our
here we're going to go ahead and add our input inside of that render so that's
input inside of that render so that's how you use react hook form now let's go
how you use react hook form now let's go ahead and add the name which this form
ahead and add the name which this form field controls which is uh email like
field controls which is uh email like this and we also have to pass in the
this and we also have to pass in the control to be form. control and let's go
control to be form. control and let's go ahead and wrap this input inside of
ahead and wrap this input inside of render instead of a form item component
render instead of a form item component like
like this and what we're going to do now uh
this and what we're going to do now uh is we're going to remove
is we're going to remove the value we're going to remove the
the value we're going to remove the unchange we're going to remove the
unchange we're going to remove the required we're just going to leave the
required we're just going to leave the placeholder disabled and type actually
placeholder disabled and type actually we don't even have to hold the disabled
we don't even have to hold the disabled what we can just do is spread the
what we can just do is spread the field so that's the cool thing about
field so that's the cool thing about react hook form this field uh will have
react hook form this field uh will have all the controllers right so on blur on
all the controllers right so on blur on change value disabled name so we don't
change value disabled name so we don't have to pass those manually we just
have to pass those manually we just spread the field and it reads the
spread the field and it reads the properties from these uh schemas right
properties from these uh schemas right here that's how it knows what type is it
here that's how it knows what type is it and everything else great so I think
and everything else great so I think that if I now visit my sign uh in
that if I now visit my sign uh in component there we go I can already
component there we go I can already control this and if I uh go ahead and
control this and if I uh go ahead and add let's go ahead and let's do this
add let's go ahead and let's do this let's also add do
let's also add do trim and minimum here or maybe we don't
trim and minimum here or maybe we don't need to add that minimum if we have
need to add that minimum if we have email let's see if I click log okay I
email let's see if I click log okay I think I still do so a minimum
think I still do so a minimum one and let's add the message
one and let's add the message required if I submit now okay uh let's
required if I submit now okay uh let's see inside of the form item I think I
see inside of the form item I think I forgot to add uh another thing here
forgot to add uh another thing here which is form control around my
which is form control around my input and in outside of the form control
input and in outside of the form control I need form message
I need form message like this and still okay not exactly
like this and still okay not exactly working as I expected but I think that's
working as I expected but I think that's because of this field right here so I'm
because of this field right here so I'm just going to remove it for
just going to remove it for now
now and okay so now I know what's going on
and okay so now I know what's going on well we have to modify this form to
well we have to modify this form to actually handle our onsubmit so let's do
actually handle our onsubmit so let's do this let's do const onsubmit vales z.
this let's do const onsubmit vales z. infer type of form schema so the same as
infer type of form schema so the same as our Zod resolver above this is a very
our Zod resolver above this is a very simple arrow function which is simply
simple arrow function which is simply going to consol log the
going to consol log the values and now let's go ahead and modify
values and now let's go ahead and modify the native form element here and
the native form element here and override it on submit to be form handle
override it on submit to be form handle submit and then pass in your onsubmit
submit and then pass in your onsubmit method here so now I think if you click
method here so now I think if you click log in there we go you get back an error
log in there we go you get back an error which says required and let's see can I
which says required and let's see can I now maybe remove this minimum value here
now maybe remove this minimum value here and can it simply use the email address
and can it simply use the email address there we go now it works I don't even
there we go now it works I don't even need the trim I think there we go
need the trim I think there we go because it is an invalid email perfect
because it is an invalid email perfect and for the password we are going to
and for the password we are going to need to Define some things so it's going
need to Define some things so it's going to have to have a minimum of eight
to have to have a minimum of eight characters and let's add the message for
characters and let's add the message for that as
well like that and then we can just copy this entire form field
this entire form field here change this one to the one we just
here change this one to the one we just copied to control the password give it a
copied to control the password give it a type of password and enter password
type of password and enter password Here There we go you can see that now we
Here There we go you can see that now we have those controls here uh but here's a
have those controls here uh but here's a tip for you whenever you're developing a
tip for you whenever you're developing a signin component don't Define the
signin component don't Define the minimum value of characters well you
minimum value of characters well you know make it at least
know make it at least one like this and just make it a re
one like this and just make it a re ired so the reason I'm telling you this
ired so the reason I'm telling you this is let's say you initially allow your
is let's say you initially allow your users to create passwords with six
users to create passwords with six characters right and then you have
characters right and then you have accounts in your database which have
accounts in your database which have created their account with six
created their account with six characters and then a year later you
characters and then a year later you modify it to be a minimum of eight
modify it to be a minimum of eight characters and then you go to your login
characters and then you go to your login screen and you limit your users to a
screen and you limit your users to a minimum of eight characters what happens
minimum of eight characters what happens with all of those accounts who a year
with all of those accounts who a year ago created their account with six
ago created their account with six characters so that's why I want you to
characters so that's why I want you to create a minimum requirement on the sign
create a minimum requirement on the sign up screen where user is registering so
up screen where user is registering so there it's fine but when logging in no
there it's fine but when logging in no need for
need for that great so we now modified our signin
that great so we now modified our signin card to actually use uh the uh react
card to actually use uh the uh react hook form like this and if I go ahead
hook form like this and if I go ahead now and open this I'm I'm going to zoom
now and open this I'm I'm going to zoom in here and I type test test this is
in here and I type test test this is still invalid email
still invalid email and and click now there we go you can
and and click now there we go you can now see the values and you can see my
now see the values and you can see my email and my password right here great
email and my password right here great so before we wrap up the chapter let's
so before we wrap up the chapter let's do this exact thing uh inside of our
do this exact thing uh inside of our sign up card so I'm going to go ahead
sign up card so I'm going to go ahead and I'm going to copy this Imports let's
and I'm going to copy this Imports let's go inside of the sign up card let's add
go inside of the sign up card let's add all those Imports here great and let's
all those Imports here great and let's copy the hook form use
theod like this then let's go ahead and let's
this then let's go ahead and let's define the form schema here let's paste
define the form schema here let's paste it inside of the sign up card and in
it inside of the sign up card and in here you can add a minimum of eight
here you can add a minimum of eight characters so minimum
of required like that and then let's go ahead and let's add
and then let's go ahead and let's add the constant form and the onsubmit and
the constant form and the onsubmit and let's copy it inside of the sign up card
let's copy it inside of the sign up card here and this will also have a
here and this will also have a name and let's also add a name inside of
name and let's also add a name inside of the form schema here so I'm going to go
the form schema here so I'm going to go ahead and make it at least one character
ahead and make it at least one character you never know uh how many characters
you never know uh how many characters other people's names can have so I don't
other people's names can have so I don't like to restrict this as well so I will
like to restrict this as well so I will just say required for this like this and
just say required for this like this and let's also ensure that you cannot just
let's also ensure that you cannot just enter you know whes space here um for
enter you know whes space here um for the password I'm not going to care if
the password I'm not going to care if they enter Whit space or
they enter Whit space or not great we have that now and now let's
not great we have that now and now let's go ahead and modify this as well so I'm
go ahead and modify this as well so I'm just going to copy my form
just going to copy my form here and I'm going to wrap my native
here and I'm going to wrap my native form around that so now it looks exactly
form around that so now it looks exactly the same uh as
the same uh as my as my sign in card then let's copy
my as my sign in card then let's copy the onsubmit here from the signin card
the onsubmit here from the signin card and paste it in the sign up card to the
and paste it in the sign up card to the native form element and let's go ahead
native form element and let's go ahead and copy one form field here which is a
and copy one form field here which is a self-closing tag and let's replace this
self-closing tag and let's replace this email here there we go so let's go ahead
email here there we go so let's go ahead and write enter your email so this is
and write enter your email so this is sign up so enter email address yeah
sign up so enter email address yeah that's fine okay and now I'm just going
that's fine okay and now I'm just going to copy this you know replace it and
to copy this you know replace it and replace your name as well so this will
replace your name as well so this will control the name field type will be text
control the name field type will be text enter your name like
enter your name like this let me just indent this and I will
this let me just indent this and I will copy this and I will replace the
copy this and I will replace the password input with that as well so this
password input with that as well so this will control the password the type will
will control the password the type will be the password and this will be enter
be the password and this will be enter your password and all of them have four
your password and all of them have four messages here and I think this should
messages here and I think this should now work just fine so if I go inside of
now work just fine so if I go inside of my uh sign up method now and if I click
my uh sign up method now and if I click login there we go and I should be able
login there we go and I should be able to fulfill all of this so Antonio Anton
to fulfill all of this so Antonio Anton mail.com 1 2 3 4 5 6 7 8 login values
mail.com 1 2 3 4 5 6 7 8 login values right here an email name and password
right here an email name and password perfect so you've just added react hook
perfect so you've just added react hook form to both of your sign up and sign in
form to both of your sign up and sign in cards so you have a more seamless
cards so you have a more seamless control over your forms here and then
control over your forms here and then you can safely submit them to where we
you can safely submit them to where we are going to use them uh next uh great
are going to use them uh next uh great so just one more thing I want to do
so just one more thing I want to do which is inside of the layout in our Al
which is inside of the layout in our Al layout right here so inside of the app
layout right here so inside of the app folder Al group go inside of the layout
folder Al group go inside of the layout here and what I want to do now uh is I
here and what I want to do now uh is I want to mark this as use
want to mark this as use client because from here I'm actually
client because from here I'm actually going to go ahead and I'm going to
going to go ahead and I'm going to render the path name so const path name
render the path name so const path name will be used path name here
will be used path name here uh use path
uh use path name like this and then uh we have to
name like this and then uh we have to import use path name from next
import use path name from next navigation so let me just move these
navigation so let me just move these Imports in the order I like them like
Imports in the order I like them like that and then depending on where we
that and then depending on where we currently are we are going to display
currently are we are going to display either sign up or login so I'm going to
either sign up or login so I'm going to go ahead and do this if path name is
go ahead and do this if path name is slash sign in in that case show sign up
slash sign in in that case show sign up as the button otherwise show login as
as the button otherwise show login as the button great and one more thing I
the button great and one more thing I want to do is I want to import link from
want to do is I want to import link from next
next link let's just not do the wrong import
link let's just not do the wrong import here and we're going to go ahead and add
here and we're going to go ahead and add a link inside of the button component
a link inside of the button component and it's not a good practice to add
and it's not a good practice to add anchor elements inside of button
anchor elements inside of button elements so what we can do is we can use
elements so what we can do is we can use the as child prop so the button becomes
the as child prop so the button becomes the anchor element and now let's go
the anchor element and now let's go ahead and add Dynamic hre here so if
ahead and add Dynamic hre here so if path name here is currently SL sign in
path name here is currently SL sign in we are going to redirect the/ sign up
we are going to redirect the/ sign up otherwise slash sign in like that if you
otherwise slash sign in like that if you want to save some time you can go ahead
want to save some time you can go ahead and Define const uh is on sign up page
and Define const uh is on sign up page or let's go ahead and just call it is
or let's go ahead and just call it is sign in let's use be simple so path name
sign in let's use be simple so path name here will be SL sign
here will be SL sign in uh after we Define the path name and
in uh after we Define the path name and then you can replace these two with that
then you can replace these two with that is sign in there we go make sure you
is sign in there we go make sure you don't misspell sign up and sign in here
don't misspell sign up and sign in here and I think that right now if I go ahead
and I think that right now if I go ahead and click on login there we go it
and click on login there we go it redirects me to login if I go ahead and
redirects me to login if I go ahead and click here it redirects me to sign up
click here it redirects me to sign up amazing and one last thing just to wrap
amazing and one last thing just to wrap it up is I also want to add these two
it up is I also want to add these two functions right here in the bottom of
functions right here in the bottom of these cards because that's usually where
these cards because that's usually where your users will expect them to appear so
your users will expect them to appear so let's go ahead inside of my signin
let's go ahead inside of my signin card right here let's go all the way to
card right here let's go all the way to the bottom where we end our card content
the bottom where we end our card content let's add a
let's add a div let's go ahead and render
div let's go ahead and render a dotted separator
here let's go ahead and give this a class name of
class name of px7 below that let's add add another
px7 below that let's add add another card content with a class name of
card content with a class name of padding seven Flex items Center and
padding seven Flex items Center and justify
justify Center and inside of the card content
Center and inside of the card content here we're going to add a paragraph
here we're going to add a paragraph already have an
already have an account if you do we're going to add a
account if you do we're going to add a link which you have to import from next
link which you have to import from next link I'm just going to go ahead and add
link I'm just going to go ahead and add it here there we
it here there we go the link will simply go to hr/ sign
go the link will simply go to hr/ sign in uh well we are in the signin card so
in uh well we are in the signin card so this will go to SL sign up so uh this
this will go to SL sign up so uh this will be the opposite message instead
will be the opposite message instead this will not be uh already have an
this will not be uh already have an account instead this will be don't have
account instead this will be don't have an account so don't have an account like
an account so don't have an account like this and if you want you can replace
this and if you want you can replace this with uh and appus like this in case
this with uh and appus like this in case you're getting errors in your IDE
you're getting errors in your IDE because this apostrophes uh can
because this apostrophes uh can sometimes you know cause
errors there we go so if you don't have an account we're going to redirect you
an account we're going to redirect you to/ signup and a span Here sign
to/ signup and a span Here sign up and let's go ahead and give the span
up and let's go ahead and give the span a class name text blue
a class name text blue 700 so if I go to login now uh there we
700 so if I go to login now uh there we go don't have an account uh go to sign
go don't have an account uh go to sign up and let's just go ahead and add a
up and let's just go ahead and add a little uh space here so I'm just going
little uh space here so I'm just going to add at
to add at nbsp and a semicolon at the end and this
nbsp and a semicolon at the end and this is uni code whitespace so there we go
is uni code whitespace so there we go now we have a white space here and that
now we have a white space here and that redirects to sign up so I'm going to
redirects to sign up so I'm going to copy this two so the div which holds the
copy this two so the div which holds the dotted separator and the card content
dotted separator and the card content and I'm going to go inside of the sign
and I'm going to go inside of the sign up card and I'm just going to go ahead
up card and I'm just going to go ahead all the way to the bottom after our card
all the way to the bottom after our card content ends and I'm going to paste that
content ends and I'm going to paste that here and let's see do I have link I
here and let's see do I have link I already have link imported here because
already have link imported here because we use it for privacy policy and terms
we use it for privacy policy and terms and let's just reverse the logic so this
and let's just reverse the logic so this is going to ask already have an account
is going to ask already have an account and it's going to redirect to sign in
and it's going to redirect to sign in and this will say sign in so let's go
and this will say sign in so let's go ahead and try it now if you click on
ahead and try it now if you click on sign in there we go don't have an
sign in there we go don't have an account sign up already have an account
account sign up already have an account sign in and you can also change this up
sign in and you can also change this up here amazing what we have to do next is
here amazing what we have to do next is set up app right so we can finally
set up app right so we can finally create some users great great
create some users great great job now let's go ahead and let's learn
job now let's go ahead and let's learn how to create API routes inside of
how to create API routes inside of nextjs
nextjs so you've learned that whenever you
so you've learned that whenever you create a new folder inside of nextjs app
create a new folder inside of nextjs app folder and you put a reserved file name
folder and you put a reserved file name page. DSX inside of it that will
page. DSX inside of it that will transform into a URL route so that's
transform into a URL route so that's exactly how you build API routes the
exactly how you build API routes the only difference is in the reserved
only difference is in the reserved keyword so let's go inside of our app
keyword so let's go inside of our app folder and for example let's create a
folder and for example let's create a new folder called test and inside
new folder called test and inside instead of a page. DSX let's add a
instead of a page. DSX let's add a route. DS so a different type of
route. DS so a different type of reserved file name the only thing that's
reserved file name the only thing that's important is that you created this
important is that you created this inside of the app folder now inside of
inside of the app folder now inside of here let's create a simple get function
here let's create a simple get function so export function get make sure it's
so export function get make sure it's all in caps like this and simply return
all in caps like this and simply return a response Json and a simple hello world
a response Json and a simple hello world like this and then you can simply go to
like this and then you can simply go to Local Host 3000 slash test so I'm going
Local Host 3000 slash test so I'm going to go ahead and go to slash test and
to go ahead and go to slash test and there we go hello world one practice
there we go hello world one practice that you might be used to seeing is
that you might be used to seeing is keeping API routes inside of an API
keeping API routes inside of an API folder so if you want to and what we are
folder so if you want to and what we are going to do is we're going to have an
going to do is we're going to have an API folder like this and then our API
API folder like this and then our API routes are going to be moved inside I'm
routes are going to be moved inside I'm just going to update the Imports looks
just going to update the Imports looks like I have an unsaved file here this
like I have an unsaved file here this can happen some times and if you're
can happen some times and if you're wondering what that is if this is your
wondering what that is if this is your first time using nextjs this is
first time using nextjs this is basically the next cache so you can see
basically the next cache so you can see the file route right here it's pointing
the file route right here it's pointing to this next folder you don't have to
to this next folder you don't have to worry about this file but I don't like
worry about this file but I don't like having unsaved files so what I'm going
having unsaved files so what I'm going to do is just go inside of that file and
to do is just go inside of that file and save it and then I'm going to close it
save it and then I'm going to close it just like that so I've moved my test
just like that so I've moved my test folder inste of my API folder if I now
folder inste of my API folder if I now refresh here I'm going to get a404 but
refresh here I'm going to get a404 but if I go to localhost 3000 API test there
if I go to localhost 3000 API test there we go so this is how you build API
we go so this is how you build API routes inside of
routes inside of nextjs that's fine it's great it's built
nextjs that's fine it's great it's built in uh but I have a few problems with it
in uh but I have a few problems with it primarily I don't like that I am losing
primarily I don't like that I am losing any type interference what do I mean by
any type interference what do I mean by that well let's say you want to add uh a
that well let's say you want to add uh a parameter inside of your API route which
parameter inside of your API route which is something that you will most likely
is something that you will most likely have so let's say inside of this API
have so let's say inside of this API folder I create a users folder right and
folder I create a users folder right and then I want to create a specific user ID
then I want to create a specific user ID folder so this is how I would do that
folder so this is how I would do that this is how you create uh parameters
this is how you create uh parameters inside of URL and then inside of that
inside of URL and then inside of that you would add a route. DS and then you
you would add a route. DS and then you would get this route and from this
would get this route and from this request and response inside of here you
request and response inside of here you would D structure the user
would D structure the user ID so for example inside of here we
ID so for example inside of here we would have you know request as the first
would have you know request as the first argument which can simply be a type of
argument which can simply be a type of request and then inside of here we would
request and then inside of here we would have an object which holds
have an object which holds params and we would have that params and
params and we would have that params and we would manually have to define the
we would manually have to define the user
user ID and make it a string and then I can
ID and make it a string and then I can go ahead and say user ID is for. user ID
go ahead and say user ID is for. user ID like this so inside of my API folder
like this so inside of my API folder users user ID route. DS so now if I go
users user ID route. DS so now if I go to/ API users sl12 34 there we go user
to/ API users sl12 34 there we go user ID is 1 2 3 4 there are a couple of
ID is 1 2 3 4 there are a couple of things I don't like here primarily I can
things I don't like here primarily I can already see how this will get you know
already see how this will get you know too confusing to work with uh because
too confusing to work with uh because the way routing Works inside of nextjs
the way routing Works inside of nextjs is using folder or file based routing
is using folder or file based routing what I prefer for my back end is code
what I prefer for my back end is code based routing and I have this great
based routing and I have this great example if it's still not clear to you
example if it's still not clear to you what that means this is from 10stack
what that means this is from 10stack router we're not going to be using T
router we're not going to be using T stack router I just found this random
stack router I just found this random Post in their documentation and I think
Post in their documentation and I think it's a great example so this is file
it's a great example so this is file based routing it's basically what we're
based routing it's basically what we're doing right instead of routes folder we
doing right instead of routes folder we have an API folder and then we have you
have an API folder and then we have you know reserved file names inside of that
know reserved file names inside of that so that's how you create you know API
so that's how you create you know API routes inside of nextjs but I prefer
routes inside of nextjs but I prefer this for my back end you know I'd rather
this for my back end you know I'd rather just simply Define what I want to uh the
just simply Define what I want to uh the file of my route and then specifically
file of my route and then specifically Define the path how to access that route
Define the path how to access that route for example posts post ID edit this
for example posts post ID edit this makes more sense to me and kind of
makes more sense to me and kind of easier to work with especially on the
easier to work with especially on the back end side so on the front end I'm
back end side so on the front end I'm okay with file based routing but on the
okay with file based routing but on the back end I highly prefer this way of
back end I highly prefer this way of working so I'm going to close this for
working so I'm going to close this for now that's the first issue that I have
now that's the first issue that I have with this the second issue that I have
with this the second issue that I have is param interference so as you can see
is param interference so as you can see nothing is telling me that user ID will
nothing is telling me that user ID will be a part of this route so I know that
be a part of this route so I know that I'm looking for user ID here because I
I'm looking for user ID here because I named this folder user ID but there is
named this folder user ID but there is so much room for error here for example
so much room for error here for example let's say I forgot to capitalize ID and
let's say I forgot to capitalize ID and I search for this now let's see what
I search for this now let's see what happens
happens as you can see my user id no longer
as you can see my user id no longer exists right so that's what I'm trying
exists right so that's what I'm trying to Showcase to you um it's not strictly
to Showcase to you um it's not strictly typed and this is a very big issue for
typed and this is a very big issue for me when creating something that's
me when creating something that's supposed to be you know a scalable uh
supposed to be you know a scalable uh API route and the reason that's also the
API route and the reason that's also the reason why instead of using this native
reason why instead of using this native API routes from
API routes from nextjs we are going to override that
nextjs we are going to override that system using
system using hono and if you're wondering what is
hono and if you're wondering what is hono well hono is a web application
hono well hono is a web application framework and the easiest way to explain
framework and the easiest way to explain what it is and what it does is Express
what it is and what it does is Express right so if you've ever worked with
right so if you've ever worked with Express you can imagine hono uh as
Express you can imagine hono uh as Express I personally prefer hono I think
Express I personally prefer hono I think it's it's definitely a new technology
it's it's definitely a new technology it's still to be you know tested by
it's still to be you know tested by these huge uh companies Express is
these huge uh companies Express is definitely battle tested and used by
definitely battle tested and used by millions but hono is new and definitely
millions but hono is new and definitely growing uh it has a great uh uh althor
growing uh it has a great uh uh althor behind it and it's also supported as at
behind it and it's also supported as at least from my understanding from the
least from my understanding from the team from cloud flare so definitely
team from cloud flare so definitely something to look out for and I use hono
something to look out for and I use hono personally in my production products so
personally in my production products so I'm pretty confident to teach you about
I'm pretty confident to teach you about hono so great thing about Hana is that
hono so great thing about Hana is that you can use it Standalone in a bunch of
you can use it Standalone in a bunch of different uh frame works so you can use
different uh frame works so you can use it uh let's go ahead and zoom in a bit
it uh let's go ahead and zoom in a bit so you can see you can use it on
so you can see you can use it on cloudware fastly Doo bun AWS or node.js
cloudware fastly Doo bun AWS or node.js or versel so you can deploy it on all of
or versel so you can deploy it on all of those things and the coolest thing if
those things and the coolest thing if you ask me is that you can easily uh
you ask me is that you can easily uh include it inside of uh nextjs so I'm
include it inside of uh nextjs so I'm going to go ahead and search for verel
going to go ahead and search for verel here and there we go now you can see how
here and there we go now you can see how we can and create our API routes using
we can and create our API routes using hono inside of nextjs so we're going to
hono inside of nextjs so we're going to skip this part because we already have
skip this part because we already have our app running so what we're going to
our app running so what we're going to do is we're going to visit our terminal
do is we're going to visit our terminal here and I'm just going to do bun ad or
here and I'm just going to do bun ad or in your case if you're using npm npm
in your case if you're using npm npm install not verel but hono and let's see
install not verel but hono and let's see if we need anything else uh I'm not sure
if we need anything else uh I'm not sure if I need hono SL forell I think this
if I need hono SL forell I think this comes from hono package so now we're
comes from hono package so now we're going to create this API rout in the app
going to create this API rout in the app folder API and then we're going to
folder API and then we're going to create this special kind of looking
create this special kind of looking folder so let's see what that is and
folder so let's see what that is and here's another cool thing about this
here's another cool thing about this this whole you know system that we're
this whole you know system that we're doing you can have both native routes in
doing you can have both native routes in nextjs and API routes from hono so you
nextjs and API routes from hono so you don't have to choose between one or
don't have to choose between one or another you can have both of them if for
another you can have both of them if for any reason hono doesn't support some
any reason hono doesn't support some very specific thing that you need in
very specific thing that you need in nextjs completely fine you can create
nextjs completely fine you can create you can still create normal API routes
you can still create normal API routes but let's go ahead and let's do this
but let's go ahead and let's do this inside of our API folder we're going to
inside of our API folder we're going to go ahead and create a catch all folder
go ahead and create a catch all folder and we're going to Spread spread route
and we're going to Spread spread route inside of it so this is again a special
inside of it so this is again a special kind of folder similar to uh this which
kind of folder similar to uh this which is a constant folder and you know
is a constant folder and you know similar to our out which is a route
similar to our out which is a route group so it's important that you put
group so it's important that you put double brackets inside and it's
double brackets inside and it's important that you spread the constant
important that you spread the constant inside like this and then inside create
inside like this and then inside create a route. DS file and now let's go ahead
a route. DS file and now let's go ahead and let's import hono from hono and
and let's import hono from hono and let's import handle from hono
let's import handle from hono verell and now let's go ahead and create
verell and now let's go ahead and create a const app to be new hono basad will be
a const app to be new hono basad will be slash API because we are in the slash
slash API because we are in the slash API folder here and now let's simply do
API folder here and now let's simply do app.get /hello
app.get /hello and inside of here let's let's go ahead
and inside of here let's let's go ahead and let's return this context hello
and let's return this context hello world so pretty much the same thing we
world so pretty much the same thing we did in the beginning and what we have to
did in the beginning and what we have to do now is remember the way routes work
do now is remember the way routes work in nextjs is by having specific exports
in nextjs is by having specific exports of get post put patch so right now
of get post put patch so right now that's what we are missing here so what
that's what we are missing here so what we can do what hono allows us to do is
we can do what hono allows us to do is to export con get and we just use this
to export con get and we just use this handle wrapper around
handle wrapper around our app instance right here and that's
our app instance right here and that's it the get route is now overwritten and
it the get route is now overwritten and we'll redirect to hono so let's go ahead
we'll redirect to hono so let's go ahead and check this out now so if I go to my
and check this out now so if I go to my API SL
API SL hello there we go hello world and the
hello there we go hello world and the coolest thing about this is that I can
coolest thing about this is that I can now do code based routing so I can for
now do code based routing so I can for example do let's say project SL Pro ID
example do let's say project SL Pro ID like
like this and I can return c.
Json project and let's for now just write project ID like this so now I can
write project ID like this so now I can go to SL API SL
go to SL API SL pro123 and there we go now it says
pro123 and there we go now it says project project ID and now let's
project project ID and now let's actually destructure the project
actually destructure the project ID so destructuring the Pam is a simple
ID so destructuring the Pam is a simple as forams or project ID to be c. foram
as forams or project ID to be c. foram uh my apology
uh my apology is c. request. peram and this is the
is c. request. peram and this is the coolest thing it actually infer the
coolest thing it actually infer the project ID from my URL which I've
project ID from my URL which I've written here so if I uh you remember
written here so if I uh you remember that little problem we had let let's
that little problem we had let let's first just confirm this works right so
first just confirm this works right so I'm just going to pass this project ID
I'm just going to pass this project ID here I'm going to refresh and there we
here I'm going to refresh and there we go you can see that now it says project
go you can see that now it says project ID 1 to3 here and the cool thing is if I
ID 1 to3 here and the cool thing is if I make a mistake for example and make this
make a mistake for example and make this a
a lowercase uh and let's go ahead and see
lowercase uh and let's go ahead and see why isn't it throwing uh an error it
why isn't it throwing uh an error it should be throwing an error I just think
should be throwing an error I just think I'm using a different way uh for example
I'm using a different way uh for example I think that I can
do let me just find a different way I can do it which I just want to showcase
can do it which I just want to showcase that little problem that we had it can
that little problem that we had it can actually be fixed with
actually be fixed with hono it can be using C request peram
hono it can be using C request peram like this and inside of here
like this and inside of here there we go so if I were to make this a
there we go so if I were to make this a capital there we go now we get the error
capital there we go now we get the error so yeah if you use the first way which
so yeah if you use the first way which we did uh you won't exactly get the
we did uh you won't exactly get the error why won't you get the error well
error why won't you get the error well because you are specifically looking for
because you are specifically looking for this right it doesn't matter that it
this right it doesn't matter that it doesn't exist but if you don't specify
doesn't exist but if you don't specify which program you're looking for and
which program you're looking for and instead you destructure the project ID
instead you destructure the project ID you can see that it can only be exactly
you can see that it can only be exactly what's written in the URL so that's what
what's written in the URL so that's what I wanted to Showcase to you and this is
I wanted to Showcase to you and this is how I want to build my API routes so
how I want to build my API routes so that's why we added hono I think it's
that's why we added hono I think it's absolutely great and it supports nextjs
absolutely great and it supports nextjs so let's clean this up a bit let's
so let's clean this up a bit let's remove the test folder from here let's
remove the test folder from here let's remove the users from here and let's
remove the users from here and let's simply continue to work inside of API
simply continue to work inside of API route uh route. DS right
route uh route. DS right here so for this chapter I want to stop
here so for this chapter I want to stop here and I want you to maybe play around
here and I want you to maybe play around and explore hono a bit for yourself
and explore hono a bit for yourself right uh because what we're going to do
right uh because what we're going to do next is we're going to go ahead and add
next is we're going to go ahead and add app right so I want to give you a chance
app right so I want to give you a chance to play around with hono uh go ahead
to play around with hono uh go ahead through the documentation page uh learn
through the documentation page uh learn about the motivation why it was made and
about the motivation why it was made and learn about uh how this whole amazing
learn about uh how this whole amazing you know project ID param interference
you know project ID param interference actually works so you can learn a lot
actually works so you can learn a lot here so I would highly suggest you go
here so I would highly suggest you go ahead and just you know take a peek at
ahead and just you know take a peek at the documentation it's very well written
the documentation it's very well written written I've learned a lot just by
written I've learned a lot just by looking at the documentation here and in
looking at the documentation here and in the next project we're going to go ahead
the next project we're going to go ahead and extend hono uh even further by
and extend hono uh even further by adding something called RPC so you can
adding something called RPC so you can read about this feature as well if
read about this feature as well if you're interested in what that is great
you're interested in what that is great great
great job now that we have hono set up let's
job now that we have hono set up let's add 10stack query or previously known as
add 10stack query or previously known as react query to our project and we're
react query to our project and we're going to use that in combination with
going to use that in combination with hono to get an endtoend type save
hono to get an endtoend type save requests you're going to see what I'm
requests you're going to see what I'm talking about uh just in a moment you
talking about uh just in a moment you already saw you know how great Pam
already saw you know how great Pam interference work with hono and now
interference work with hono and now imagine that extended all the way to the
imagine that extended all the way to the front end when you actually make that
front end when you actually make that request because usually you know when
request because usually you know when you write your uh fetch requests let me
you write your uh fetch requests let me go ahead you would do something like
go ahead you would do something like fetch SL projects and then you would add
fetch SL projects and then you would add you know a project IDE
you know a project IDE something like that but none of this
something like that but none of this would actually warn you if your project
would actually warn you if your project ID is valid or not valid or whether this
ID is valid or not valid or whether this route even exists right now imagine that
route even exists right now imagine that level of type safety from your back end
level of type safety from your back end to your front end so end to endend type
to your front end so end to endend type safety that's what we are going to
safety that's what we are going to achieve with our setup now so for that
achieve with our setup now so for that we need 10stack react query it is simply
we need 10stack react query it is simply the best in business here so this is the
the best in business here so this is the current highest version at the moment of
current highest version at the moment of me making this tutorial so 5.
me making this tutorial so 5. 59.0 so I'm going to go ahead and I'm
59.0 so I'm going to go ahead and I'm going to do bunad at
going to do bunad at tack react query at 5.
tack react query at 5. 59.0 you can use the latest if you want
59.0 you can use the latest if you want to I rarely see react query having any
to I rarely see react query having any breaking changes but you know I'm giving
breaking changes but you know I'm giving you uh this overview just so you know
you uh this overview just so you know the exact version I will be having great
the exact version I will be having great we now have this and what we have to do
we now have this and what we have to do now is follow react query docs to
now is follow react query docs to install it inside of nextjs and I would
install it inside of nextjs and I would highly suggest that you right now open t
highly suggest that you right now open t query uh make sure your framework is
query uh make sure your framework is chosen as react make sure your version
chosen as react make sure your version is chosen as uh latest right here or you
is chosen as uh latest right here or you know version five which right now is the
know version five which right now is the latest version you know we just
latest version you know we just installed uh version five right so
installed uh version five right so inside of the documentation page here I
inside of the documentation page here I want you to go into search and I want
want you to go into search and I want you to search
you to search Advanced server and you should get
Advanced server and you should get Advanced server rendering topic right
Advanced server rendering topic right here go ahead and click on that and now
here go ahead and click on that and now you have a file that you have to copy
you have a file that you have to copy and add in your project let's go ahead
and add in your project let's go ahead and let's copy this
and let's copy this file and let's go inside of source
file and let's go inside of source inside of components and let's create a
inside of components and let's create a query Das
query Das provider. DSX
provider. DSX and let's paste everything inside we
and let's paste everything inside we have a couple of Errors we're going to
have a couple of Errors we're going to resolve all of that so if you want to
resolve all of that so if you want to you can leave the comments so you
you can leave the comments so you understand what's going on here just
understand what's going on here just make sure you don't have the errors here
make sure you don't have the errors here you won't have any errors if you have
you won't have any errors if you have the package installed you don't have to
the package installed you don't have to modify these options the only thing
modify these options the only thing we're going to change is the following
we're going to change is the following we're going to create an interface
we're going to create an interface called query provider props and it's
called query provider props and it's going to have children which are a type
going to have children which are a type of react react
of react react node and we are not going to export
node and we are not going to export default instead we're going to do const
default instead we're going to do const query provider and we're going to turn
query provider and we're going to turn this into an arrow function and we're
this into an arrow function and we're going to assign this props right here
going to assign this props right here there we go like that you don't have to
there we go like that you don't have to modify anything further and make sure
modify anything further and make sure you do export cons yes that is very
you do export cons yes that is very important so make sure you do export
important so make sure you do export const query provider great and now let's
const query provider great and now let's go ahead and let's go inside a inside of
go ahead and let's go inside a inside of layout. DSX and now we're going to add
layout. DSX and now we're going to add that right here so I'm going to go ahead
that right here so I'm going to go ahead here and import our query Provider from
here and import our query Provider from components query
components query provider and very simply we're going to
provider and very simply we're going to wrap our children around that so query
wrap our children around that so query provider like
provider like this there we go we now have our react
this there we go we now have our react query ready and what I would like to do
query ready and what I would like to do is just revisit my Local Host 3000 here
is just revisit my Local Host 3000 here just ensure you know that my project is
just ensure you know that my project is still working I'm going to visit some of
still working I'm going to visit some of the routes we have like sign in there we
the routes we have like sign in there we go everything seems to be working just
go everything seems to be working just fine so what I want to build next is I
fine so what I want to build next is I want to go ahead and actually add app
want to go ahead and actually add app right I want to setup app right using
right I want to setup app right using their console and I want to create my
their console and I want to create my very first hook which will connect to
very first hook which will connect to hono RPC which we talked about uh just a
hono RPC which we talked about uh just a few seconds ago so this is what I want
few seconds ago so this is what I want to do next I want to go inside of source
to do next I want to go inside of source here and I want to go inside of features
here and I want to go inside of features inside of out and inside of here I'm
inside of out and inside of here I'm going to create a new folder called
going to create a new folder called server like this and inside of here I'm
server like this and inside of here I'm going to create
going to create route. DS and inside of here I'm going
route. DS and inside of here I'm going to import hono again from hono and I'm
to import hono again from hono and I'm very simply going to do const app to be
very simply going to do const app to be new hono here like that and Export
new hono here like that and Export default app
default app and inside of here what we can do is we
and inside of here what we can do is we can go ahead and create a for example
can go ahead and create a for example slash uh login we can do things like
slash uh login we can do things like that let's get the context and let's
that let's get the context and let's return c.
return c. Json success okay just a placeholder for
Json success okay just a placeholder for now so we now have a post login here so
now so we now have a post login here so that's the cool thing we don't have to
that's the cool thing we don't have to write all of our API routes inside of
write all of our API routes inside of here because that would be quite
here because that would be quite convoluted as well wouldn't it we can
convoluted as well wouldn't it we can separate them inside of our features
separate them inside of our features right here so now inside of our features
right here so now inside of our features out we have components and we have the
out we have components and we have the actual API routes uh for our
actual API routes uh for our authentication now this specific login
authentication now this specific login routes will also have to accept some
routes will also have to accept some Fields right and there's a cool package
Fields right and there's a cool package which can help us in hono to actually
which can help us in hono to actually have typ safe values as well that's
have typ safe values as well that's right not just the params but also
right not just the params but also values and this is again going to be end
values and this is again going to be end to end from front end to back end you
to end from front end to back end you will have full type safety knowing that
will have full type safety knowing that what you're passing from the front end
what you're passing from the front end can 100% be accepted on your back end so
can 100% be accepted on your back end so let's go inside of our terminal here and
let's go inside of our terminal here and let's do BN
let's do BN add
add oops bunad atono z-v
oops bunad atono z-v validator and I just remembered that I
validator and I just remembered that I didn't exactly show you my versions of
didn't exactly show you my versions of this but I guess you can see the version
this but I guess you can see the version every time I install something but let
every time I install something but let me just quickly revisit my package Json
me just quickly revisit my package Json for you uh two important packages which
for you uh two important packages which I've added was hono Z validator here and
I've added was hono Z validator here and hono itself so this is my hono version
hono itself so this is my hono version if you want to in case you're having
if you want to in case you're having some big problems in case hono has
some big problems in case hono has changed a lot since the time you know I
changed a lot since the time you know I made this tutorial you can just
made this tutorial you can just downgrade to this specific version and
downgrade to this specific version and you should have relatively the same EXP
you should have relatively the same EXP experiences I do and we already have Zod
experiences I do and we already have Zod installed when we developed our forms so
installed when we developed our forms so with this hono Zod validator we're going
with this hono Zod validator we're going to go ahead and create something called
to go ahead and create something called well uh a schema and a z validator here
well uh a schema and a z validator here so every time we do a post request to
so every time we do a post request to slash login we're going to go ahead and
slash login we're going to go ahead and add a middleware here so what's a
add a middleware here so what's a middleware a middleware inside of well
middleware a middleware inside of well also inside of Express but also inside
also inside of Express but also inside of hono is basically anything with which
of hono is basically anything with which returns a next method before the final
returns a next method before the final controller so you can chain as many
controller so you can chain as many middle Wares as you want before you do
middle Wares as you want before you do the final uh Arrow
the final uh Arrow function which I like to call the
function which I like to call the controller which which is called the
controller which which is called the controller and return back a value so
controller and return back a value so you can chain as many items in here that
you can chain as many items in here that you want but next is a reserved param
you want but next is a reserved param which all middle wordss have which you
which all middle wordss have which you can then return execute and then that
can then return execute and then that will fire the next middleware and the
will fire the next middleware and the next middleware and finally the
next middleware and finally the controller and middle Wares can be built
controller and middle Wares can be built by us and we are going to build some
by us and we are going to build some custom middlewares in this project but
custom middlewares in this project but we can also use some newly installed
we can also use some newly installed ones like the Z
ones like the Z validator from at hono Zod validator so
validator from at hono Zod validator so that is a type of
that is a type of middleware and inside of this middleware
middleware and inside of this middleware we Define what to validate in our case
we Define what to validate in our case we are validating our Json field and
we are validating our Json field and then in the second argument we have to
then in the second argument we have to create a schema so let's prepare our Zod
create a schema so let's prepare our Zod here and then what we would do is write
here and then what we would do is write z
z doob and let me remove the extra comma
doob and let me remove the extra comma which I have here and inside of here
which I have here and inside of here we're going to accept an email which is
we're going to accept an email which is a d. string. email and a password which
a d. string. email and a password which is a z. string like this and what I like
is a z. string like this and what I like to do is
to do is have my things ordered one beneath
have my things ordered one beneath another I just think it makes more sense
another I just think it makes more sense like this you can of course do it in
like this you can of course do it in whatever way you prefer or
whatever way you prefer or like uh great so we have this
like uh great so we have this now uh should I do it like
this there we go like that so now what we can do is if you want to you can also
we can do is if you want to you can also go to for example your sign in card and
go to for example your sign in card and inside of here you have you already have
inside of here you have you already have a form schema so if you want to what you
a form schema so if you want to what you can do is you can go inside of your
can do is you can go inside of your features Al and you can create a new
features Al and you can create a new file called schema. TS or maybe
file called schema. TS or maybe schemas because there's going to be
schemas because there's going to be multiple right and you can expert const
multiple right and you can expert const login
login schema actually let's do you know login
schema actually let's do you know login schema like this export const and let's
schema like this export const and let's import Z from Z
import Z from Z like this and what you can do now is you
like this and what you can do now is you can remove this from the signin card
can remove this from the signin card make sure you're doing this in the
make sure you're doing this in the signin card right we're doing logging in
signin card right we're doing logging in here and instead of you know writing it
here and instead of you know writing it here you can import login schema fromt
here you can import login schema fromt do/ schemas because we are in the same
do/ schemas because we are in the same folder we are in the out right here so
folder we are in the out right here so you we just go one folder outside and
you we just go one folder outside and here are our schemas right and I'm going
here are our schemas right and I'm going to replace all instances of form schema
to replace all instances of form schema with login schema there we go no more
with login schema there we go no more errors in my codee
errors in my codee here and what I can do now is also go
here and what I can do now is also go inside of my server route here and do
inside of my server route here and do the same thing here so I can just add
the same thing here so I can just add login schema from do do/ schemas and now
login schema from do do/ schemas and now they share the exact the same scheme as
they share the exact the same scheme as here so let's go ahead uh and try this
here so let's go ahead uh and try this out now so what we have to do is we have
out now so what we have to do is we have to build uh a use login method so we're
to build uh a use login method so we're going to go ahead head and do that but
going to go ahead head and do that but before we can do that we have to revisit
before we can do that we have to revisit back our uh out folder right here my
back our uh out folder right here my apologies API folder route route. DS so
apologies API folder route route. DS so here's an important thing when building
here's an important thing when building this thing called
this thing called RPC so let me go ahead and quickly show
RPC so let me go ahead and quickly show you this so the RPC feature allows
you this so the RPC feature allows sharing of the API specifications
sharing of the API specifications between the server and the client this
between the server and the client this is important for us we can export the
is important for us we can export the types of input types specified by the
types of input types specified by the validator and output type emitted by
validator and output type emitted by Json and hono client will be able to
Json and hono client will be able to import that so this is the functionality
import that so this is the functionality that I want and this is the important
that I want and this is the important thing uh of working with hono so you can
thing uh of working with hono so you can see that right here hono allows us to
see that right here hono allows us to create routes like this we can use the
create routes like this we can use the app instance a new every time but when
app instance a new every time but when working with rpcs we can't exactly do
working with rpcs we can't exactly do that what we have to do is we have to
that what we have to do is we have to chain them from one app right like this
chain them from one app right like this and I have to chain another one here so
and I have to chain another one here so that's how we build rpcs because I need
that's how we build rpcs because I need one constant to have the entire type
one constant to have the entire type specifications of everything I can have
specifications of everything I can have so I just want to bring that to your
so I just want to bring that to your attention right that's why when I
attention right that's why when I defined con app new hono I immediately
defined con app new hono I immediately changeed do Post right I didn't end this
changeed do Post right I didn't end this and then do
and then do app. poost because this doesn't transfer
app. poost because this doesn't transfer the types so it's important that you
the types so it's important that you immediately chain it here here like that
immediately chain it here here like that now let's go back inside of here and
now let's go back inside of here and let's actually uh go ahead and chain our
let's actually uh go ahead and chain our routes so I'm going to go ahead and
routes so I'm going to go ahead and leave the app as it is and I'm going to
leave the app as it is and I'm going to define the routes and that's going to be
define the routes and that's going to be app. route
app. route slout and then I'm going to import out
slout and then I'm going to import out from at slash features
from at slash features slout slash server like this and let me
slout slash server like this and let me go ahead and see what's going on here
go ahead and see what's going on here here so inside of my features out server
here so inside of my features out server route. DS uh I do export default app but
route. DS uh I do export default app but I think I have to do slash route to
I think I have to do slash route to actually get it so just ensure that you
actually get it so just ensure that you added an export default in your features
added an export default in your features out server route right here and then you
out server route right here and then you can import that out right here there we
can import that out right here there we go and you just change this to Route so
go and you just change this to Route so every subsequent you know uh route that
every subsequent you know uh route that we have like users it will go here and
we have like users it will go here and we're going to import them in the future
we're going to import them in the future from features users like this that's how
from features users like this that's how we're going to chain our routes and
we're going to chain our routes and we're going to keep them all in this
we're going to keep them all in this constant called routes so remove the
constant called routes so remove the users because we don't have them at the
users because we don't have them at the moment you can put the semicolon here at
moment you can put the semicolon here at the end if you want to for now at least
the end if you want to for now at least uh and now what we have to do is we have
uh and now what we have to do is we have to create a type so let's export type
to create a type so let's export type app
app type to be type of
type to be type of routes and now we have an app type which
routes and now we have an app type which which holds our entire API you can see
which holds our entire API you can see the input Json email password you can
the input Json email password you can see the output which is a success and
see the output which is a success and string why well because right here
string why well because right here that's what we output so if I change
that's what we output so if I change this to 1 2 3 4 and hover over app type
this to 1 2 3 4 and hover over app type now success is a number so you can see
now success is a number so you can see how useful this is for us because we're
how useful this is for us because we're going to use this for endtoend type
going to use this for endtoend type safety excellent now that we have this
safety excellent now that we have this set up let's go ahead and let's create
set up let's go ahead and let's create our AR RPC Ule so now we're going to go
our AR RPC Ule so now we're going to go inside of source inside of uh our lib
inside of source inside of uh our lib and we're going to create a new file
and we're going to create a new file called RPC
called RPC dods let's go ahead and import HC from
dods let's go ahead and import HC from honos client and let's
honos client and let's import app
import app type from at SLA API the catch all route
type from at SLA API the catch all route file and then route so that is this our
file and then route so that is this our entry point of hono and we are catching
entry point of hono and we are catching this app type right here and then let's
this app type right here and then let's export con client to be HC app type and
export con client to be HC app type and inside of here we have to add
inside of here we have to add HTTP Local Host
HTTP Local Host 3,000 like this now obviously this is
3,000 like this now obviously this is not how we should do it so let's go
not how we should do it so let's go ahead and immediately fix this first
ahead and immediately fix this first thing I want you to confirm is that your
thing I want you to confirm is that your app is actually running on Local Host
app is actually running on Local Host 3000 if it's not that's completely fine
3000 if it's not that's completely fine but you have to know which Port your app
but you have to know which Port your app is running on so I know that mine is
is running on so I know that mine is running on Local Host 3,000 so what I'm
running on Local Host 3,000 so what I'm going to do is I'm going to go inside
going to do is I'm going to go inside and create a new file called
and create a new file called environment. local make sure you add
environment. local make sure you add local because uh in nextjs G ignore uh
local because uh in nextjs G ignore uh only local is hidden so if you add it in
only local is hidden so if you add it in your normal environment it will be
your normal environment it will be pushed to GitHub which is not something
pushed to GitHub which is not something you want and and now let's add a
you want and and now let's add a nextore appor URL and let's add HTTP
nextore appor URL and let's add HTTP localhost sl3000 what's important is
localhost sl3000 what's important is that you use the proper protocol so if
that you use the proper protocol so if you know that you're using
you know that you're using https which is rare because usually you
https which is rare because usually you know if I just do Bun Run Dev you can
know if I just do Bun Run Dev you can see that it uses HTTP right so this is
see that it uses HTTP right so this is your url which you need put right here
your url which you need put right here so I'm going to remove the S and don't
so I'm going to remove the S and don't put a slash at the end so just like this
put a slash at the end so just like this the reason we are doing this is because
the reason we are doing this is because in production our app is not going to be
in production our app is not going to be running on Local Host 3000 so we need to
running on Local Host 3000 so we need to uh create this dynamically let's add
uh create this dynamically let's add process. environment next public app URL
process. environment next public app URL whenever you work with environment
whenever you work with environment variables I suggest that you copy and
variables I suggest that you copy and paste them rather than write them out
paste them rather than write them out because you can easily make a mistake
because you can easily make a mistake you can add a third P here and not
you can add a third P here and not notice so just be careful uh make sure
notice so just be careful uh make sure you copy and paste this things great we
you copy and paste this things great we now have the client and let's go ahead
now have the client and let's go ahead and let's see what the magic now so
and let's see what the magic now so we're going to go inside of source
we're going to go inside of source inside of features out and we're going
inside of features out and we're going to create a new folder called API and
to create a new folder called API and inside of here a new file called use
inside of here a new file called use login.
login. DS and inside of here let's go ahead and
DS and inside of here let's go ahead and let's import mutation from T react query
let's import mutation from T react query and let's import infer request type from
and let's import infer request type from hono and infer response type from hono
hono and infer response type from hono and let's also import client from at/ Li
and let's also import client from at/ Li RPC of course make sure you've added an
RPC of course make sure you've added an export to this client here and since we
export to this client here and since we have all the types here what we can do
have all the types here what we can do is we can prepare our response type from
is we can prepare our response type from this request which will be an infer
this request which will be an infer response type here type of
response type here type of client
client api.
api. out. login so that's the Coe thing I
out. login so that's the Coe thing I don't have to guess if my API route
don't have to guess if my API route exists I have type safety to tell me so
exists I have type safety to tell me so if I think it's sign in I get an error
if I think it's sign in I get an error because it's login and not only that but
because it's login and not only that but you have to open uh brackets for the
you have to open uh brackets for the next thing because it's in a special
next thing because it's in a special kind of syntax with a dollar sign at the
kind of syntax with a dollar sign at the front so you cannot use dot dollar sign
front so you cannot use dot dollar sign you have to use this kind of accessing
you have to use this kind of accessing of the object so select the post request
of the object so select the post request here like this and and let's also get a
here like this and and let's also get a type request type to also be infer
type request type to also be infer request type type of client API out
request type type of client API out login and again post and now inside of
login and again post and now inside of here if you want to you can already
here if you want to you can already specify Json so now we have the request
specify Json so now we have the request type which tells us that we need an
type which tells us that we need an email and password and a response type
email and password and a response type which will tell us exactly what will
which will tell us exactly what will happen so we can export const use login
happen so we can export const use login here
here we can get our mutation here from use
we can get our mutation here from use mutation and let's go ahead and assign
mutation and let's go ahead and assign it some types so let's give it a
it some types so let's give it a response type in the first argument a
response type in the first argument a native error in the second argument and
native error in the second argument and a request type in the third argument and
a request type in the third argument and then let's open an object here let's
then let's open an object here let's remove the trailing comma so we don't
remove the trailing comma so we don't have the lint error let's add a mutation
have the lint error let's add a mutation function and let's use asynchronous Json
function and let's use asynchronous Json and let's hover over Json for a second
and let's hover over Json for a second and there we go so when we use this in
and there we go so when we use this in our front end you can see that we have
our front end you can see that we have type safety for what we have to pass to
type safety for what we have to pass to our back end so that's what we just
our back end so that's what we just achieved a very very strict type safety
achieved a very very strict type safety here now let's get the response constant
here now let's get the response constant by using await client. API out login and
by using await client. API out login and in here you can use post right but when
in here you can use post right but when using it inside of type interference you
using it inside of type interference you cannot do it so you go if you want to
cannot do it so you go if you want to you can again you can be consistent you
you can again you can be consistent you know and you can just do uh Post in this
know and you can just do uh Post in this way if if you want to be consistent and
way if if you want to be consistent and just execute this and pass in the Json
just execute this and pass in the Json instead like this and let's return await
instead like this and let's return await response.
response. Json just like
Json just like this there we go and what we have to do
this there we go and what we have to do of course is we have to return the
of course is we have to return the mutation there we go so let's go ahead
mutation there we go so let's go ahead and let's add this to our sign in card
and let's add this to our sign in card right here so I'm going to go ah ahead
right here so I'm going to go ah ahead here and I'm going to add const from use
here and I'm going to add const from use login we can get that from do/ API use
login we can get that from do/ API use lose login and inside of here let's get
lose login and inside of here let's get our mutate method and then when we
our mutate method and then when we submit very simply mutate and we pass in
submit very simply mutate and we pass in the values or we can just add the values
the values or we can just add the values like
like this and there we go you can see that
this and there we go you can see that type safety matches 100% so for whatever
type safety matches 100% so for whatever reason we the different schema here it
reason we the different schema here it would warn us that something is wrong so
would warn us that something is wrong so this is what we just achieved end to end
this is what we just achieved end to end type safety so you can see from my uh
type safety so you can see from my uh sign in form I know exactly what I have
sign in form I know exactly what I have to pass and I know exactly what I will
to pass and I know exactly what I will get back so that's the power of hono and
get back so that's the power of hono and adding that to your next JS project
adding that to your next JS project that's why I like it so much and that's
that's why I like it so much and that's why I started doing this in all of my
why I started doing this in all of my courses lately I just think it's amazing
courses lately I just think it's amazing and I think it really gets you closer to
and I think it really gets you closer to you know scalable and production ready
you know scalable and production ready project uh great so we have that and
project uh great so we have that and here's another you know quick tip for
here's another you know quick tip for you so for example if you go inside of
you so for example if you go inside of your out
your out here server and let's say that our login
here server and let's say that our login for whatever reason had a user ID here
for whatever reason had a user ID here uh as well right so then how you would
uh as well right so then how you would Target that is before you select the
Target that is before you select the post request you would also have a user
post request you would also have a user idid but there is one problem with our
idid but there is one problem with our current implementation here you don't
current implementation here you don't have to type this out you can just I'm
have to type this out you can just I'm just trying to demonstrate something
just trying to demonstrate something here one problem here is that our
here one problem here is that our current request type is only passing in
current request type is only passing in Json so I just have to add user ID here
Json so I just have to add user ID here there we go this is the problem we are
there we go this is the problem we are passing Json but we are not passing uh
passing Json but we are not passing uh our params what you can do when whenever
our params what you can do when whenever you define a request type is just not be
you define a request type is just not be specific like this so now your request
specific like this so now your request type has both the Json that you need and
type has both the Json that you need and the Pam that you need and then you can
the Pam that you need and then you can inside of here immediately destructure
inside of here immediately destructure the Json and the Pam and you can then
the Json and the Pam and you can then pass the Json and a Pam here there we go
pass the Json and a Pam here there we go and now inside of your sign in card for
and now inside of your sign in card for example you would also be specific so
example you would also be specific so your Json will be the values and your
your Json will be the values and your Pam will simply be user ID uh 1 two 3 4
Pam will simply be user ID uh 1 two 3 4 there we go so that's how you would do
there we go so that's how you would do that I don't know if I just confused you
that I don't know if I just confused you but I just wanted to show you uh you
but I just wanted to show you uh you know if you already want to drop off and
know if you already want to drop off and try out this yourself and you play
try out this yourself and you play around with params that's how you can be
around with params that's how you can be more specific so I actually want to keep
more specific so I actually want to keep that practice so this is what I'm going
that practice so this is what I'm going to do I'm going to remove the user ID
to do I'm going to remove the user ID from login entirely I'm going to go back
from login entirely I'm going to go back inside of my use login method and I will
inside of my use login method and I will remove all of this in instances of user
remove all of this in instances of user ID here I'm also going to remove that
ID here I'm also going to remove that here but what I'm going to leave is my
here but what I'm going to leave is my request type so previously my request
request type so previously my request type here was specifically targeting
type here was specifically targeting Json but I don't want to do that I want
Json but I don't want to do that I want to be less specific and in the same time
to be less specific and in the same time being more explicit about what I'm
being more explicit about what I'm passing so instead of having you know
passing so instead of having you know Json as a prop here I'm going to
Json as a prop here I'm going to destructure Json like this and I don't
destructure Json like this and I don't have to pass the Pam at all because we
have to pass the Pam at all because we just removed that so there are no errors
just removed that so there are no errors right now in the use login method but
right now in the use login method but there will be in the signin card so just
there will be in the signin card so just remove this and if you previously passed
remove this and if you previously passed values like this you now have to pass
values like this you now have to pass them a bit more specifically like this I
them a bit more specifically like this I think by being uh less specific you are
think by being uh less specific you are more explicit and again I think this
more explicit and again I think this shows in a type safe way and you will
shows in a type safe way and you will make less mistakes this way
make less mistakes this way great and let's actually try this out
great and let's actually try this out now so I'm going to go inside of here
now so I'm going to go inside of here and what I'm going to do is I'm going to
and what I'm going to do is I'm going to show you how to actually get this param
show you how to actually get this param so I know that I will get my name my
so I know that I will get my name my apologies my email and my
apologies my email and my password and while I can do that you
password and while I can do that you know from c. request. Json for
know from c. request. Json for example uh let's me just see how can I
example uh let's me just see how can I use this C do request ad Json would be a
use this C do request ad Json would be a guess body and then instead of my my
guess body and then instead of my my body uh do I need to await
body uh do I need to await this uh turn this into an asynchronous
this uh turn this into an asynchronous method well we pretty much always have
method well we pretty much always have to do that uh okay still not exactly
to do that uh okay still not exactly clear never never mind because this is
clear never never mind because this is not how we're going to do it either way
not how we're going to do it either way so we would have the exact email and
so we would have the exact email and password from c. request. valid and then
password from c. request. valid and then our Json and you can see that I know
our Json and you can see that I know that email is a string here and password
that email is a string here and password is a string here if I go to login schema
is a string here if I go to login schema for example add I don't know some weird
for example add I don't know some weird code which is supposed to be a
code which is supposed to be a number inside of my route here I will
number inside of my route here I will also have that code right here so again
also have that code right here so again type safety is following us everywhere
type safety is following us everywhere you will be it will be almost impossible
you will be it will be almost impossible for you to make errors in this kind of
for you to make errors in this kind of environment right so great we can use c.
environment right so great we can use c. request. valid only when we are using
request. valid only when we are using the validator let me quickly revisit the
the validator let me quickly revisit the documentation to see how do I actually
documentation to see how do I actually access the body uh or
access the body uh or Json I'm trying to find I'm going to
Json I'm trying to find I'm going to pause and I'm going to find the exact
pause and I'm going to find the exact thing so looks like it is what we tried
thing so looks like it is what we tried in the beginning await c. request. Json
in the beginning await c. request. Json but I think you remember we saw and we
but I think you remember we saw and we couldn't find any type safety there so
couldn't find any type safety there so it's best to use this c. request valid
it's best to use this c. request valid Json and again just a quick tip if we
Json and again just a quick tip if we had a PM here and you wanted to access a
had a PM here and you wanted to access a Pam in a type safe way so you already
Pam in a type safe way so you already know that you can do the user ID from C
know that you can do the user ID from C request Pam here you can do it like that
request Pam here you can do it like that but if for whatever reason you want to
but if for whatever reason you want to Define it you know in a z validator you
Define it you know in a z validator you can do that you can do Z validator and
can do that you can do Z validator and you can add pm and you can do do I have
you can add pm and you can do do I have Z here I don't think I have Z I do okay
Z here I don't think I have Z I do okay you can do z. object here user ID and
you can do z. object here user ID and you can for example make it a number if
you can for example make it a number if for whatever reason you want to do that
for whatever reason you want to do that and then uh for example you can see
and then uh for example you can see right here this c. request. Prem still
right here this c. request. Prem still tells me that it's a string because it's
tells me that it's a string because it's reading from the the router it uses but
reading from the the router it uses but you can also
you can also do user ID from C request valid and
do user ID from C request valid and simply choose the Pam and this will
simply choose the Pam and this will infer user ID at as a number so if you
infer user ID at as a number so if you for whatever reason want to do that you
for whatever reason want to do that you can do it as well I'm now going to
can do it as well I'm now going to remove this I'm going to remove the user
remove this I'm going to remove the user ID and I'm going to remove this there we
ID and I'm going to remove this there we go and now let's go ahead and let's
go and now let's go ahead and let's create uh well we can return back the
create uh well we can return back the email and the
email and the password and let's also you know console
password and let's also you know console log this so it's easier for us to catch
log this so it's easier for us to catch in the
in the terminal there we go so I'm now going to
terminal there we go so I'm now going to go ahead and I'm going to entirely Focus
go ahead and I'm going to entirely Focus just confirm you have no errors in the
just confirm you have no errors in the route no errors in the signin card no
route no errors in the signin card no errors in the used login here let's go
errors in the used login here let's go ahead inside of our create next app I'm
ahead inside of our create next app I'm going to pass in Antonio mail.com and 1
going to pass in Antonio mail.com and 1 2 3 4 5 6 7 8 and I'm going to press
2 3 4 5 6 7 8 and I'm going to press enter and I'm going to go inside of
enter and I'm going to go inside of network here and let's see uh I think we
network here and let's see uh I think we actually might get an error here let me
actually might get an error here let me just see yes we are getting an error 404
just see yes we are getting an error 404 not found because I forgot a very
not found because I forgot a very important thing let's go ahead and see
important thing let's go ahead and see what I forgot inside of my source app
what I forgot inside of my source app apir rout route. TS can you guess what I
apir rout route. TS can you guess what I forgot my out routes are post routes but
forgot my out routes are post routes but I'm only overriding the native nextjs
I'm only overriding the native nextjs get route so I have to add expert cons
get route so I have to add expert cons post here and let it handle the app as
post here and let it handle the app as well remember you know native nextjs
well remember you know native nextjs apps uh API routes require explicit
apps uh API routes require explicit export of get post put patch delete
export of get post put patch delete options all of those routes so whenever
options all of those routes so whenever you introduce a new route to your hono
you introduce a new route to your hono just remember to go back to your route
just remember to go back to your route initialization of hono and if you want
initialization of hono and if you want you can already do all of them here
you can already do all of them here right if you want to you can add all of
right if you want to you can add all of them I'm going to go ahead and uh but I
them I'm going to go ahead and uh but I will probably forget once again and
will probably forget once again and we're going to get a 404 and we're going
we're going to get a 404 and we're going to get reminded that we have to do it
to get reminded that we have to do it again let's try this now so I'm going to
again let's try this now so I'm going to go ahead and prepare my network request
go ahead and prepare my network request again and let's click
again and let's click login let's refresh my okay it's it's
login let's refresh my okay it's it's already here let me just go ahead and
already here let me just go ahead and click this Antonio
click this Antonio mail.com uh 1 2 3 4 5 6 78 and there we
mail.com uh 1 2 3 4 5 6 78 and there we go login 200 let's see my payload my
go login 200 let's see my payload my payload is correct exactly what I wrote
payload is correct exactly what I wrote and let's see my response my response is
and let's see my response my response is exactly what I wrote and if I go inside
exactly what I wrote and if I go inside of my terminal there we go we have that
of my terminal there we go we have that exact thing written here so what I want
exact thing written here so what I want to do before we wrap up this chapter uh
to do before we wrap up this chapter uh is just create the exact same thing for
is just create the exact same thing for my sign up route and then in the next
my sign up route and then in the next chapter we're going to finally uh
chapter we're going to finally uh connect to app right and turn that into
connect to app right and turn that into a real
a real user so let's start by uh extracting the
user so let's start by uh extracting the schema so I want to go inside of source
schema so I want to go inside of source features out components sign up card and
features out components sign up card and inside of here I believe I have my form
inside of here I believe I have my form schema so I'm going to copy this and I'm
schema so I'm going to copy this and I'm going to go inside of out schemas and I
going to go inside of out schemas and I will export const register schema and I
will export const register schema and I will add it here let me just remove the
will add it here let me just remove the extra there we go like this and then
extra there we go like this and then let's go ahead and create our API route
let's go ahead and create our API route so inside of features out API my
so inside of features out API my apologies server route. DS remember we
apologies server route. DS remember we have to chain so chain SL register right
have to chain so chain SL register right here and I'm going to go ahead and do do
here and I'm going to go ahead and do do it the way I like so after register we
it the way I like so after register we add a z validator here we're going to
add a z validator here we're going to validate Json using the register schema
validate Json using the register schema from do do/ schemas alongside our login
from do do/ schemas alongside our login schema here and then we can copy and
schema here and then we can copy and paste this entire
paste this entire thing there we go but instead of having
thing there we go but instead of having just email and password we're also going
just email and password we're also going to have name so we can append this as
to have name so we can append this as well there we go and we can then go
well there we go and we can then go instead of API and we can copy use login
instead of API and we can copy use login and paste it and we can add use register
and paste it and we can add use register here let's go ahead and change this to
here let's go ahead and change this to be use register and let's change these
be use register and let's change these two to be register so both the response
two to be register so both the response type and the request type should be
type and the request type should be changed to use out. register here and
changed to use out. register here and here and inside of here we also have to
here and inside of here we also have to change it to register
change it to register and nothing else needs changing so this
and nothing else needs changing so this is all automatically inferred from the
is all automatically inferred from the back end from the API route what we have
back end from the API route what we have to do now is revisit our components sign
to do now is revisit our components sign up card remove the form
up card remove the form schema replace all instances of form
schema replace all instances of form schema with register schema which we can
schema with register schema which we can import from do/
import from do/ schemas and then we have to add our m M
schemas and then we have to add our m M tap method from use M uh from use
tap method from use M uh from use register from do/ API use register and
register from do/ API use register and the onsubmit we call the mutate method
the onsubmit we call the mutate method passing Json values right
passing Json values right here there we go let's try out that just
here there we go let's try out that just ensure you have no errors in any of
ensure you have no errors in any of those files I'm going to keep my network
those files I'm going to keep my network tab open here I'm going to go and click
tab open here I'm going to go and click sign up
sign up here I'm going to go ahead and select my
here I'm going to go ahead and select my name to be Antonio
name to be Antonio my email to be Antonio mail.com and my
my email to be Antonio mail.com and my password 1 2 3 4 5 6 78 and there we go
password 1 2 3 4 5 6 78 and there we go register and you can see that my
register and you can see that my response is exactly what I have written
response is exactly what I have written meaning that my payload has been
meaning that my payload has been properly processed great we are now
properly processed great we are now ready to add app right and to actually
ready to add app right and to actually save this information coming to our
save this information coming to our backend and to finally create some
backend and to finally create some sessions great great
sessions great great job what we have to do now is set up app
job what we have to do now is set up app right create an API key and install the
right create an API key and install the SDK inside of our project so that we can
SDK inside of our project so that we can use those information from our sign in
use those information from our sign in and sign up screens to finally create a
and sign up screens to finally create a user save them in our database and also
user save them in our database and also create a session before we can do any of
create a session before we can do any of that you have to visit
that you have to visit app.io or use the link in the
app.io or use the link in the description and click on the get started
description and click on the get started button go ahead and create your
button go ahead and create your account once you have created your
account once you have created your account you will probably see a screen
account you will probably see a screen similar to this so I'm just going to go
similar to this so I'm just going to go ahead and create my organization I'm
ahead and create my organization I'm going to call this CWA short for code
going to call this CWA short for code with Antonio and I'm going to select a
with Antonio and I'm going to select a completely free plan $0 a month and
completely free plan $0 a month and let's click get started and there we go
let's click get started and there we go now let's go ahead and let's create our
now let's go ahead and let's create our project so I'm going to go ahead and
project so I'm going to go ahead and call this a jira
call this a jira clone and let's click next here and I'm
clone and let's click next here and I'm going to select Frankfurt for the region
going to select Frankfurt for the region let's click create here as well and
let's click create here as well and there we go we now have our uh first
there we go we now have our uh first project right here so what we're going
project right here so what we're going to have to do next is set up a database
to have to do next is set up a database and also set up our uh API Keys which
and also set up our uh API Keys which I'm not sure where we can find uh but we
I'm not sure where we can find uh but we will we will set them up now following
will we will set them up now following the documentation so let's go and click
the documentation so let's go and click on support here and you can always click
on support here and you can always click on the docs and I want you to go inside
on the docs and I want you to go inside of products Al and I want you to find
of products Al and I want you to find SSR login if you want to you can explore
SSR login if you want to you can explore app right you know on your own they have
app right you know on your own they have a great documentation but we will
a great documentation but we will specifically focus on we will
specifically focus on we will specifically focus on using appes server
specifically focus on using appes server SDK so we won't be making app requests
SDK so we won't be making app requests from our browser from the client we have
from our browser from the client we have our own API and we're going to use it so
our own API and we're going to use it so every API request from the client will
every API request from the client will be going to our API first and then we're
be going to our API first and then we're going to communicate with app right so
going to communicate with app right so make sure you are on the SSR login no
make sure you are on the SSR login no big problem if you can't find this
big problem if you can't find this documentation page you don't have to do
documentation page you don't have to do this but if you want to learn you know
this but if you want to learn you know about the schematics of how this works
about the schematics of how this works you can do it that so let's go ahead and
you can do it that so let's go ahead and let's see uh what we have to do first so
let's see uh what we have to do first so first things first we need a server SDK
first things first we need a server SDK so let's go ahead and find that there we
so let's go ahead and find that there we go nodejs SDK seems to be 14.0 point0 so
go nodejs SDK seems to be 14.0 point0 so what you can do here is you can go and
what you can do here is you can go and add Bon add node app
add Bon add node app right and you can go ahead and append
right and you can go ahead and append this exact version verion so
14.0 like this there we go we now have the server as decay so I'm just going to
the server as decay so I'm just going to go back here the second thing we need is
go back here the second thing we need is to initialize an admin client and for
to initialize an admin client and for that we're going to need to generate an
that we're going to need to generate an API key so I'm going to click here and
API key so I'm going to click here and let's see where exactly uh do we do that
let's see where exactly uh do we do that so create an API key Integrations let's
so create an API key Integrations let's see I'm going to click go to console in
see I'm going to click go to console in my uh uh upper right corner here and
my uh uh upper right corner here and inside of my jir clone here uh there we
inside of my jir clone here uh there we go it says integrate with your server
go it says integrate with your server and I can add an API key here so I'm
and I can add an API key here so I'm going to go ahead and call this jir
going to go ahead and call this jir clone and I'm not going to set the
clone and I'm not going to set the exploration date I'm going to click next
exploration date I'm going to click next here and I'm going to go ahead and I'm
here and I'm going to go ahead and I'm going to select
going to select sessions. write right here and I'm going
sessions. write right here and I'm going to click create we can always go back
to click create we can always go back here so let me try and find this you
here so let me try and find this you know uh if I go back
know uh if I go back here okay so on my overview page if I
here okay so on my overview page if I scroll down I can find my API keys and I
scroll down I can find my API keys and I can click on an API key and from here
can click on an API key and from here you know we can go ahead and added some
you know we can go ahead and added some more Scopes but I think for now this
more Scopes but I think for now this might be the only thing we need I think
might be the only thing we need I think we also need
we also need users. right go ahead and select users.
users. right go ahead and select users. write as well and click update
write as well and click update so make sure your new API key has
so make sure your new API key has sessions. right and users. right great
sessions. right and users. right great and stay on this page for now and I'm
and stay on this page for now and I'm just going to go ahead and open this in
just going to go ahead and open this in a new
a new tab here are my documentations and I'm
tab here are my documentations and I'm going to go back to out and I'm going to
going to go back to out and I'm going to find the SSR login and I'm going to
find the SSR login and I'm going to click on tutorials at the bottom and I'm
click on tutorials at the bottom and I'm going to select nextjs
going to select nextjs SSR so we created our project we already
SSR so we created our project we already have this we have installed node app
have this we have installed node app right and now we have to create this but
right and now we have to create this but we're not going to create this part just
we're not going to create this part just yet so what's the difference between the
yet so what's the difference between the create session client and create admin
create session client and create admin client well create session client is
client well create session client is part of appes SDK which will be called
part of appes SDK which will be called when a user directly creates new records
when a user directly creates new records for example a user creates new task so
for example a user creates new task so for that we're going to use a session
for that we're going to use a session client but there is a specific
client but there is a specific functionality in our project where user
functionality in our project where user is not exactly responsible for creating
is not exactly responsible for creating something for example a new user a user
something for example a new user a user that is not logged in that is getting
that is not logged in that is getting registered for the first time cannot be
registered for the first time cannot be responsible for creating themselves so
responsible for creating themselves so for that we need an admin client so what
for that we need an admin client so what I'm going to focus on is just creating
I'm going to focus on is just creating this the admin client and as you can see
this the admin client and as you can see for that we're going to need a couple of
for that we're going to need a couple of environment variables here so let's set
environment variables here so let's set that app first I'm going to go inside of
that app first I'm going to go inside of my environment. looc first we have next
my environment. looc first we have next public app URL I'm going to add some
public app URL I'm going to add some spaces here and here's what we have to
spaces here and here's what we have to add next so we have to add the next
add next so we have to add the next public app right endpoint a very
public app right endpoint a very important prefix here is next public
important prefix here is next public this will allow us to read read that
this will allow us to read read that inside of client components as well now
inside of client components as well now let's go ahead and let's add
let's go ahead and let's add this and let's add this like this and
this and let's add this like this and then we're going to add next app right
then we're going to add next app right key notice how I didn't add public in
key notice how I didn't add public in front of it so why do we need public
front of it so why do we need public here well technically we don't need it
here well technically we don't need it since we're going to use node app right
since we're going to use node app right but in case you also in the future want
but in case you also in the future want to add Client app right asdc you want to
to add Client app right asdc you want to make sure that you can read using these
make sure that you can read using these three but your API key should not be
three but your API key should not be prefixed with next public uh it won't
prefixed with next public uh it won't exactly be the same as leaking your key
exactly be the same as leaking your key but adding next public will certainly
but adding next public will certainly make it easy for malicious users to grab
make it easy for malicious users to grab it all right so just ensure that you
it all right so just ensure that you have these set up make sure you don't
have these set up make sure you don't misspell any of those and now let's go
misspell any of those and now let's go ahead and let's fill our project with
ahead and let's fill our project with that so I'm going to go ahead and do the
that so I'm going to go ahead and do the following I'm going to go inside of my
following I'm going to go inside of my project here and I have my API key
project here and I have my API key secret I'm going to copy this don't
secret I'm going to copy this don't share your secret with anyone uh I
share your secret with anyone uh I created a new account for this and I'm
created a new account for this and I'm going to delete it after this and I'm
going to delete it after this and I'm also not going to add any credit cards
also not going to add any credit cards so uh that's why I can show you this of
so uh that's why I can show you this of course but you should not show your API
course but you should not show your API keys to anyone so make sure you have
keys to anyone so make sure you have copied the API secret key after after
copied the API secret key after after that let's go ahead and let's get our
that let's go ahead and let's get our end point so let me just find where can
end point so let me just find where can we find our end point not exactly sure
we find our end point not exactly sure but what I can see immediately is our
but what I can see immediately is our project key so we can copy this uh I
project key so we can copy this uh I think we can also see that in the
think we can also see that in the settings there we go you can go into
settings there we go you can go into settings and you can find Project ID and
settings and you can find Project ID and API endpoint so let's click project ID
API endpoint so let's click project ID here and then we can assign the project
here and then we can assign the project and let's copy the API endpoint then we
and let's copy the API endpoint then we can add that here this doesn't
can add that here this doesn't necessarily have to be in your
necessarily have to be in your environment variable but in case you
environment variable but in case you ever want to change it or you can also
ever want to change it or you can also self-host app right using oneclick setup
self-host app right using oneclick setup so for if you ever want to change that
so for if you ever want to change that it's good to have this as an end
it's good to have this as an end point and we can leave the database ID
point and we can leave the database ID empty just for now you know now let's go
empty just for now you know now let's go ahead and let's do the following let's
ahead and let's do the following let's go inside of source let's go inside of
go inside of source let's go inside of lib and let's create
lib and let's create aight. DS like this and now inside of
aight. DS like this and now inside of here let's go ahead uh and let's import
here let's go ahead uh and let's import from node app
from node app right the
right the following we're going to add client
following we're going to add client account storage users and
account storage users and databases and then let's export
databases and then let's export asynchronous
asynchronous function create admin
function create admin client let's create a client using new
client let's create a client using new client instance
client instance and then let's set endpoint to be
and then let's set endpoint to be process. environment and as always don't
process. environment and as always don't write it out copy it it's going to save
write it out copy it it's going to save you some headaches and put an
you some headaches and put an exclamation point at the end to
exclamation point at the end to overwrite type safety here let's add set
overwrite type safety here let's add set project and do the same thing don't
project and do the same thing don't write it out copy it process.
write it out copy it process. environment and there we go and set
environment and there we go and set key that is the next app right key so
key that is the next app right key so process.
process. environment there we go and then let's
environment there we go and then let's go ahead and let's return for example if
go ahead and let's return for example if we need account we're going to go ahead
we need account we're going to go ahead and return new account instance and pass
and return new account instance and pass in our admin
in our admin client there we go so right now we
client there we go so right now we pretty much created the same thing that
pretty much created the same thing that we have inside of here right so instead
we have inside of here right so instead of having this huge session client we
of having this huge session client we now have just this admin client and we
now have just this admin client and we just return the account and you can
just return the account and you can already kind of see the difference
already kind of see the difference between the admin client and the session
between the admin client and the session client in the session client we set the
client in the session client we set the endpoint and we set the project but we
endpoint and we set the project but we don't set the key which means that this
don't set the key which means that this session client is pretty harmless right
session client is pretty harmless right that's why we're going to use this when
that's why we're going to use this when user needs to create something and we're
user needs to create something and we're going to use this when we as an admin
going to use this when we as an admin need to create something like a new user
need to create something like a new user so I also want to show you one kind of a
so I also want to show you one kind of a important thing in nextjs so nextjs is a
important thing in nextjs so nextjs is a framework that allows you you know to
framework that allows you you know to create your front end and your back end
create your front end and your back end in the same project there is nothing
in the same project there is nothing stopping me from for example inside of
stopping me from for example inside of my page. DSX here doing
this for example create admin client from at lib app right so I just did this
from at lib app right so I just did this right so I can go ahead and for example
right so I can go ahead and for example console log create admin client here
console log create admin client here let's see what happens I'm going to go
let's see what happens I'm going to go inside of my project here and I'm just
inside of my project here and I'm just going to go to the root of my page Local
going to go to the root of my page Local Host 3000 right
Host 3000 right here I will refresh uh can I see
here I will refresh uh can I see anything let's
see uh okay I'm not sure why I can't see anything but here's the thing I don't
anything but here's the thing I don't even want to be able to do this right I
even want to be able to do this right I want to be thrown an error when I do
want to be thrown an error when I do these kind of things and especially I
these kind of things and especially I think if I add oh okay I know why I'm
think if I add oh okay I know why I'm not seeing conso
not seeing conso logs we are seeing something we are
logs we are seeing something we are seeing it here in the terminal there we
seeing it here in the terminal there we go hello it's because this is a server
go hello it's because this is a server component by default so if you go ahead
component by default so if you go ahead and you add use client here inside of
and you add use client here inside of your app folder page. DSX when we are
your app folder page. DSX when we are playing with the
playing with the buttons the there we go you can see I
buttons the there we go you can see I have the entire admin client right here
have the entire admin client right here not only that but take a look at this
not only that but take a look at this conso log I can see all of my
conso log I can see all of my environment variables right and you can
environment variables right and you can see how my set key is thankfully hidden
see how my set key is thankfully hidden right here right you can see how these
right here right you can see how these two are filled because they have the
two are filled because they have the next public prefix but my key is
next public prefix but my key is thankfully hidden but still you know I
thankfully hidden but still you know I can now kind of access this a malicious
can now kind of access this a malicious user can definitely find their way
user can definitely find their way around this and file it inside of client
around this and file it inside of client s side that is a very bad thing to
s side that is a very bad thing to happen so this is what I want you to do
happen so this is what I want you to do I want you to do B add server only or
I want you to do B add server only or npm install server only this is the
npm install server only this is the version I'm using
version I'm using 0.0.1 if you want the exact one and what
0.0.1 if you want the exact one and what I want you to do is go inside of your
I want you to do is go inside of your new lib so inside of source lib app
new lib so inside of source lib app right and at the top of your file simply
right and at the top of your file simply add import server only once you do that
add import server only once you do that you immediately get an error here you're
you immediately get an error here you're importing a component that needs server
importing a component that needs server only that only works in a server
only that only works in a server component so that's how we are
component so that's how we are protecting this little util so you don't
protecting this little util so you don't accidentally do things like this right
accidentally do things like this right you need an error to happen and right
you need an error to happen and right now if you remove use client from here
now if you remove use client from here you're not getting an error why well
you're not getting an error why well because this is a server component this
because this is a server component this is an equivalent of
is an equivalent of the environment is equivalent of an API
the environment is equivalent of an API route so it's safe to do it this way but
route so it's safe to do it this way but you never know you know especially if
you never know you know especially if you're new with nextjs you might
you're new with nextjs you might accidentally had a used client somewhere
accidentally had a used client somewhere and accidentally add a very very
and accidentally add a very very dangerous import that's why they
dangerous import that's why they developed this package server only so
developed this package server only so that you can protect yourself from that
that you can protect yourself from that this will fail the builds and obviously
this will fail the builds and obviously it will give you a big warning right
it will give you a big warning right here so you can remove you client and
here so you can remove you client and you can remove this as well for now
you can remove this as well for now there we go I just wanted you to do that
there we go I just wanted you to do that great so now let's go ahead and let's
great so now let's go ahead and let's see what does it say next instead of the
see what does it say next instead of the SSR
SSR initialization the first one we need to
initialization the first one we need to handle is the register route right so we
handle is the register route right so we already did this we set the environment
already did this we set the environment variables here we created our first
variables here we created our first project and you can see that they say
project and you can see that they say that for this tutorial we are only going
that for this tutorial we are only going to need sessions sessions
to need sessions sessions right so yeah if you want to you can go
right so yeah if you want to you can go back to your app right settings find
back to your app right settings find your uh API Keys
your uh API Keys here where where are my API Keys why are
here where where are my API Keys why are there not loading at the moment let me
there not loading at the moment let me just find my uh Joc clone right here in
just find my uh Joc clone right here in the
the overview uh just a second it appears
overview uh just a second it appears like I have lost my API keys right
like I have lost my API keys right now uh
now uh let me try and find them
here API key there okay it didn't load now it loaded so Joc clone inside of out
now it loaded so Joc clone inside of out I'm going to remove users. WR because
I'm going to remove users. WR because you know the admin SDK is quite powerful
you know the admin SDK is quite powerful and you want to be very specific with
and you want to be very specific with what it can do and what it can't do uh
what it can do and what it can't do uh and since we we will not be creating
and since we we will not be creating users we'll be creating accounts right
users we'll be creating accounts right so you're going to see what we're doing
so you're going to see what we're doing in a second so make sure you have
in a second so make sure you have sessions. right and click update here
sessions. right and click update here you don't need anything else for this
you don't need anything else for this API key No Big E if you left the users
API key No Big E if you left the users read and write on let's go back inside
read and write on let's go back inside uh our app right here let's see what
uh our app right here let's see what does it ask us to do next so it shows
does it ask us to do next so it shows you how to get the logged in user that's
you how to get the logged in user that's fine I want to do the sign up page first
fine I want to do the sign up page first so we have our inputs and now this is
so we have our inputs and now this is what we have to do here so we have to
what we have to do here so we have to initialize the create admin client and
initialize the create admin client and we have to destructure the account from
we have to destructure the account from that that's all good we have exactly
that that's all good we have exactly that here right instead of our lib app
that here right instead of our lib app right we have account and we can call
right we have account and we can call this create admin client in an API route
this create admin client in an API route even though we have this server only and
even though we have this server only and then we have to create a new account
then we have to create a new account with ID unique which comes from node app
with ID unique which comes from node app package and we have to pass in the email
package and we have to pass in the email password and the name from the register
password and the name from the register form and then we have to create a
form and then we have to create a session and we have to store that
session and we have to store that session in a cookie let's go ahead and
session in a cookie let's go ahead and let's do that but just a little bit
let's do that but just a little bit differently because they are using
differently because they are using server actions or as they are renamed
server actions or as they are renamed now in New react uh server functions so
now in New react uh server functions so we're not going to do that we're going
we're not going to do that we're going to use good old apis honestly I prefer
to use good old apis honestly I prefer them over server functions and I just
them over server functions and I just add rpcs because server functions
add rpcs because server functions technically are rpcs in a way let's go
technically are rpcs in a way let's go ahead and let's do that inside of API
ahead and let's do that inside of API inside of route. DS uh my apologies
inside of route. DS uh my apologies inside of features out server route so
inside of features out server route so the the server out here in the out let's
the the server out here in the out let's find the register we have name email and
find the register we have name email and password make sure this is an
password make sure this is an asynchronous controller don't forget
asynchronous controller don't forget that and now what we're going to do is
that and now what we're going to do is we're going to get the
we're going to get the account from await create admin client
account from await create admin client from lib app so make sure you've added
from lib app so make sure you've added the lib app right import here
the lib app right import here and then what we're going to do uh is
and then what we're going to do uh is we're going to do const user to be await
we're going to do const user to be await account. create and passing ID from node
account. create and passing ID from node app
right do unique and then an email then a password
unique and then an email then a password and then a name so you can hover over
and then a name so you can hover over the create method and you can see the
the create method and you can see the exact order first we need a user ID then
exact order first we need a user ID then we need an email then we need a password
we need an email then we need a password and then we need an name I don't like
and then we need an name I don't like when they do this usually I like when
when they do this usually I like when it's an object so you know I would have
it's an object so you know I would have the pass ID to be this I would have to
the pass ID to be this I would have to pass email to be this this way you know
pass email to be this this way you know you always have to double check your
you always have to double check your methods and check if the order is
methods and check if the order is correct so ID email password and then
correct so ID email password and then name in that exact
name in that exact order and then let's create our session
order and then let's create our session here to be await account create email
here to be await account create email password session add the email and the
password session add the email and the password that the user just created and
password that the user just created and now we're going to go ahead and instead
now we're going to go ahead and instead of using the cookies from next headers
of using the cookies from next headers we're going to use cookies from hono so
we're going to use cookies from hono so let's go ahead and add set cookie like
let's go ahead and add set cookie like this whoops and we can import set cookie
this whoops and we can import set cookie from hono cookie so import set cookie
from hono cookie so import set cookie from hono cookie
and then inside of here we're going to go ahead and pass in the context in the
go ahead and pass in the context in the first argument and what I like to do is
first argument and what I like to do is I like to create a constant for my out
I like to create a constant for my out cookie so let's do that inside of the
cookie so let's do that inside of the out folder in the features create a new
out folder in the features create a new file called constants dots and Export
file called constants dots and Export const out
const out cookie and let's give it a name uh I
cookie and let's give it a name uh I like to always use a prefix for my
like to always use a prefix for my cookies for example CWA for code with
cookies for example CWA for code with anonio dj- clone D session like that so
anonio dj- clone D session like that so just make sure you have the out cookie
just make sure you have the out cookie here and then you can instead of using
here and then you can instead of using strings every time just pass the out
strings every time just pass the out cookie from your dot do/ constants
cookie from your dot do/ constants because it's in the same folder right uh
because it's in the same folder right uh and what we have to pass next is from
and what we have to pass next is from this session the secret so session do
this session the secret so session do Secret like that and then some
Secret like that and then some additional safety options here so path
additional safety options here so path will be a forward slash HTTP only will
will be a forward slash HTTP only will be true secure will be true same site
be true secure will be true same site will be
will be strict and then we're going to do a max
strict and then we're going to do a max age 60 * 60 * 24 * 30 like that and
age 60 * 60 * 24 * 30 like that and we're going to go ahead and we can
we're going to go ahead and we can simply
simply return success true like this and looks
return success true like this and looks like I don't need uh the user
like I don't need uh the user here I think I don't need the user
here I think I don't need the user actually what we can do is we can return
actually what we can do is we can return back the user so data user we can do
back the user so data user we can do that there we
that there we go great let's go ahead and let's try
go great let's go ahead and let's try this out and let's see if anything
this out and let's see if anything important here happens so I'm going to
important here happens so I'm going to go ahead and I'm going to go to
go ahead and I'm going to go to localhost 3000 SL sign up so it's
localhost 3000 SL sign up so it's important important we are working in
important important we are working in the register form now and I want to show
the register form now and I want to show you inside of my application here I
you inside of my application here I think I will have some cookies because
think I will have some cookies because this is a local host I don't have any
this is a local host I don't have any cookies great no cookies for me perfect
cookies great no cookies for me perfect so what I'm going to do is I'm going to
so what I'm going to do is I'm going to create a new account now I'm going to
create a new account now I'm going to call myself Antonio I'm going to give
call myself Antonio I'm going to give myself an email of Antonio mail.com and
myself an email of Antonio mail.com and 1 2 3 4 5 6 actually let's do this I
1 2 3 4 5 6 actually let's do this I think app right has some uh specific
think app right has some uh specific password requirements so what I'm going
password requirements so what I'm going to do I'm just going to change this type
to do I'm just going to change this type to text so you can see my password so
to text so you can see my password so I'm going to call my password Antonio 1
I'm going to call my password Antonio 1 2 3 4 5 6 7
2 3 4 5 6 7 8 let me show
8 let me show you Antonio 1 2 3 45678 so that's going
you Antonio 1 2 3 45678 so that's going to be my password I'm going to click uh
to be my password I'm going to click uh instead of login this should obviously
instead of login this should obviously be registered we're going to change that
be registered we're going to change that and let's see what happens so it appears
and let's see what happens so it appears that I got 200 let's see my response
that I got 200 let's see my response there we go so I have uh returned back
there we go so I have uh returned back an entire user here uh not exactly sure
an entire user here uh not exactly sure if this is the smartest thing to do but
if this is the smartest thing to do but I don't think there are any secrets here
I don't think there are any secrets here so perhaps it's good yeah looks like
so perhaps it's good yeah looks like just dates I don't see any uh Secrets
just dates I don't see any uh Secrets here so that should be okay but
here so that should be okay but nevertheless we can change what we
nevertheless we can change what we return what I'm interested in is inside
return what I'm interested in is inside of my application there we go instead of
of my application there we go instead of my cookies local host I have my CWA jir
my cookies local host I have my CWA jir clone and I have my cookie right here
clone and I have my cookie right here there we go so we are now logged in so
there we go so we are now logged in so what I want to do now is I want to
what I want to do now is I want to develop the rest of the methods
develop the rest of the methods including log in and including log out
including log in and including log out so we already have login so let's just
so we already have login so let's just continue working with login here we're
continue working with login here we're going to have the email and the password
going to have the email and the password Here let's go ahead and let's do again
Here let's go ahead and let's do again account from await create admin
account from await create admin client let's go ahead and do con session
client let's go ahead and do con session to be await account create email
to be await account create email password
password session let's get the email and the
session let's get the email and the password here so pretty much the same
password here so pretty much the same thing as we did here you can even copy
thing as we did here you can even copy it if you want to and then what we're
it if you want to and then what we're going to do is well set the cookie again
going to do is well set the cookie again we can copy the exact same
we can copy the exact same cookie uh make sure you're doing it in
cookie uh make sure you're doing it in the login route we have the session here
the login route we have the session here we go session. secret set to out cookie
we go session. secret set to out cookie you can see how handy it is that we use
you can see how handy it is that we use the constant otherwise when you use it
the constant otherwise when you use it in many places you will probably
in many places you will probably misspell it at least once and that's
misspell it at least once and that's quite dangerous to do and we can go back
quite dangerous to do and we can go back and we can return data uh we can just
and we can return data uh we can just return Success Through and we most
return Success Through and we most likely won't need the use user in the
likely won't need the use user in the register method as well immediately so
register method as well immediately so you can return success true here as
you can return success true here as well and you can not assign this you
well and you can not assign this you don't have to assign this in the
don't have to assign this in the register you don't have to assign this
register you don't have to assign this to the user you can just await right
to the user you can just await right there we go great so we now have our
there we go great so we now have our login here and we can't really try it
login here and we can't really try it out because we already have a cookie but
out because we already have a cookie but what we can do is this uh we can go
what we can do is this uh we can go ahead and the following go inside of
ahead and the following go inside of your application find the cookie and
your application find the cookie and remove the cookie like
remove the cookie like this but I don't think that this is
this but I don't think that this is enough let me just see if I refresh okay
enough let me just see if I refresh okay I think the cookie is definitely gone
I think the cookie is definitely gone okay that's good let's just quickly
okay that's good let's just quickly visit our app right because I think I
visit our app right because I think I completely forgot to check do we have a
completely forgot to check do we have a user we do have a user here it is right
user we do have a user here it is right here we have a user and in app right you
here we have a user and in app right you can actually control it uh you can also
can actually control it uh you can also verify the account you can go ahead and
verify the account you can go ahead and uh create different targets you can go
uh create different targets you can go ahead and close the user session you can
ahead and close the user session you can see uh their activity and everything
see uh their activity and everything else here uh great so now that I
else here uh great so now that I confirmed that this user was actually
confirmed that this user was actually created here let's try logging in this
created here let's try logging in this time so remember I just removed my
time so remember I just removed my cookie from my application
cookie from my application here I'm now going to go ahead inside of
here I'm now going to go ahead inside of sign
sign in and I'm going to go ahead and log in
in and I'm going to go ahead and log in so Antonio mail.com and I'm going to use
so Antonio mail.com and I'm going to use that password Antonio 1 2 3 4 5 6 7 8
that password Antonio 1 2 3 4 5 6 7 8 and login is 200 success is true and
and login is 200 success is true and application new cookie is set again
application new cookie is set again excellent and now we have to do the
excellent and now we have to do the easiest route of them all uh which is
easiest route of them all uh which is the log out method so I'm going to go
the log out method so I'm going to go ahead and use PST for the log out I'm
ahead and use PST for the log out I'm not sure if maybe this should be uh a um
not sure if maybe this should be uh a um if this should
if this should be what exactly am I trying to say I'm
be what exactly am I trying to say I'm not sure if this should be uh a delete
not sure if this should be uh a delete method but I think post can work pretty
method but I think post can work pretty well so for now I'm just going to go
well so for now I'm just going to go ahead and do this I'm going to add a
ahead and do this I'm going to add a context here and inside of here I'm
context here and inside of here I'm going to do delete cookie from hono
going to do delete cookie from hono cookie and I'm going to pass in the
cookie and I'm going to pass in the context and out cookie here
context and out cookie here and I'm going to return c.
Json success
true like this so for now I'm just deleting the
this so for now I'm just deleting the cook but there is one more thing I have
cook but there is one more thing I have to do here but before we can do that we
to do here but before we can do that we need to create a session
need to create a session middleware uh so let's go ahead and do
middleware uh so let's go ahead and do the following well we can't really try
the following well we can't really try it out in any way now so so what we're
it out in any way now so so what we're going to have to do next is we are
going to have to do next is we are actually going to have to create uh our
actually going to have to create uh our session middleware and we're going to
session middleware and we're going to have to create another route which we're
have to create another route which we're going to use to get the currently logged
going to use to get the currently logged in user and depending on the result of
in user and depending on the result of that route we're going to redirect the
that route we're going to redirect the user away from this screen or if they
user away from this screen or if they try to go you know to dashboard we're
try to go you know to dashboard we're going to direct them back to the login
going to direct them back to the login screen but I want to end this chapter
screen but I want to end this chapter for now so what would did is we set up
for now so what would did is we set up app right we handled the login and the
app right we handled the login and the register routes to set our cookies and
register routes to set our cookies and to create users in the aite database we
to create users in the aite database we learned about the server only import
learned about the server only import which is currently protecting our create
which is currently protecting our create admin client and you've kind of
admin client and you've kind of understood what the admin client does so
understood what the admin client does so it's used for things that users
it's used for things that users themselves cannot create right uh so
themselves cannot create right uh so what we're going to do
what we're going to do later is of course we're going to use
later is of course we're going to use the storage the users and databases here
the storage the users and databases here but what we have to do next is we have
but what we have to do next is we have to create the session client and we're
to create the session client and we're going to use it as a middleware so we're
going to use it as a middleware so we're not going to use it like this instead
not going to use it like this instead we're going to use it let's say this
we're going to use it let's say this logout route should only be accessible
logout route should only be accessible to logged in users so we're going to use
to logged in users so we're going to use a custom middleware called session
a custom middleware called session middleware
middleware here and session middleware will
here and session middleware will redirect the user away if they are not
redirect the user away if they are not logged in but if they are logged in
logged in but if they are logged in they're going to provide the controller
they're going to provide the controller with the currently logged in user as DEC
with the currently logged in user as DEC so then we're going to create like a
so then we're going to create like a little client but without the key and
little client but without the key and with just the uh right read uh access
with just the uh right read uh access that the currently logged in user has so
that the currently logged in user has so that's how we're going to handle that
that's how we're going to handle that great great
great great job now let's go ahead and let's create
job now let's go ahead and let's create the session middleware which we're going
the session middleware which we're going to use to authenticate requests which
to use to authenticate requests which should only be accepted accessible to
should only be accepted accessible to log in
log in users so I'm going to go inside of
users so I'm going to go inside of source lib and inside of here I'm going
source lib and inside of here I'm going to create a session middleware do
to create a session middleware do DS we can also Mark this as server
DS we can also Mark this as server only and then let's go ahead and let's
only and then let's go ahead and let's import get cookie from hono cookie let's
import get cookie from hono cookie let's go ahead and import create middleware
go ahead and import create middleware from hono
from hono Factory let's import the constant out
Factory let's import the constant out cookie from features
cookie from features constants and let's import
account client databases models
databases models storage type account as account
storage type account as account type type databases as databases
type type databases as databases type and a lowercase
type and a lowercase as types storage as storage
as types storage as storage type and then we're going to
type and then we're going to have users as users
have users as users type from node app
type from node app right and now let's go ahead and let's
right and now let's go ahead and let's export HST session
export HST session middleware let's use the create
middleware let's use the create middleware from hono Factory and inside
middleware from hono Factory and inside of here an asynchronous method which G
of here an asynchronous method which G gives us the context and the next method
gives us the context and the next method which I briefly demonstrated in a first
which I briefly demonstrated in a first few chapters of our hono
few chapters of our hono exploration now inside of here I'm going
exploration now inside of here I'm going to create a new client and I'm going to
to create a new client and I'm going to give it an endpoint using process.
give it an endpoint using process. environment and as always I will copy it
environment and as always I will copy it from my environment. Lo and I will add
from my environment. Lo and I will add an exclamation point at the end I'm then
an exclamation point at the end I'm then going to set the project as well
going to set the project as well process. environment and again I'm going
process. environment and again I'm going to copy it from
to copy it from here like this there we go after I have
here like this there we go after I have set my client I'm going to go ahead and
set my client I'm going to go ahead and obtain the cookie using get
obtain the cookie using get cookie context and out cookie so in
cookie context and out cookie so in register and login we set the cookie in
register and login we set the cookie in our session middleware we read that
our session middleware we read that cookie and in case that cookie is
cookie and in case that cookie is missing
missing we are simply going to return context.
we are simply going to return context. Json error
Json error unauthorized and an error code of
unauthorized and an error code of 401 otherwise if we have the session
401 otherwise if we have the session we're simply going to append the session
we're simply going to append the session to the client so client set session and
to the client so client set session and pass in our session here so this set
pass in our session here so this set session will
session will serve as set key which we have inside
serve as set key which we have inside our admin client but our session client
our admin client but our session client here does not need an admin key it's not
here does not need an admin key it's not supposed to be that powerful it's only
supposed to be that powerful it's only supposed to be as powerful as the
supposed to be as powerful as the currently logged in user that's why we
currently logged in user that's why we are using session based SDK inside of
are using session based SDK inside of this
this middleware and then I can initialize an
middleware and then I can initialize an account to be new account and passing
account to be new account and passing the client and I can do the same thing
the client and I can do the same thing for a couple of other things for examp
for a couple of other things for examp example
example databases new
databases new databases and storage new
storage and I can also immediately obtain the currently logged in user
obtain the currently logged in user using await account. getet the reason
using await account. getet the reason I'm doing
I'm doing this is because we will never as as as a
this is because we will never as as as a as a as a as a logged in user we are
as a as a as a logged in user we are never going to use use the account in a
never going to use use the account in a way that we are using it from the admin
way that we are using it from the admin client so in here uh we use the account
client so in here uh we use the account right and if you're wondering why do I
right and if you're wondering why do I use get account here and I just do this
use get account here and I just do this here it's the same thing you can do you
here it's the same thing you can do you can do this if you want to const account
can do this if you want to const account and then just return account I'm just
and then just return account I'm just following the documentation in case
following the documentation in case you're confused you don't have to do it
you're confused you don't have to do it in this way uh so as a logged in user
in this way uh so as a logged in user the only reason I will be using the
the only reason I will be using the account as DEC is is to get the current
account as DEC is is to get the current user so that's why I just want to do
user so that's why I just want to do that immediately in the middleware so I
that immediately in the middleware so I don't have to repeat this piece of code
don't have to repeat this piece of code everywhere right so we set the user here
everywhere right so we set the user here and then what I can do now thanks to the
and then what I can do now thanks to the hono factory middleware is append all of
hono factory middleware is append all of this to the context so c. set account
this to the context so c. set account will be account just in case I need it C
will be account just in case I need it C do set databases will be databases
do set databases will be databases c. set storage will be
c. set storage will be storage c. set user will be user so what
storage c. set user will be user so what did I do by that well let's check it out
did I do by that well let's check it out what's important to end this middleware
what's important to end this middleware is await
is await next like this so now let's go inside of
next like this so now let's go inside of our
our features server features out server
features server features out server route. DS
route. DS and for example let's find our logout
and for example let's find our logout route this post logout route and let's
route this post logout route and let's add the session middleware here make
add the session middleware here make sure you add a comma after the session
middleware once you've added the session middleware what will happen is that if
middleware what will happen is that if an unauthorized user tries to log out
an unauthorized user tries to log out they will be stopped right here because
they will be stopped right here because we cannot find a cookie for the user
we cannot find a cookie for the user that's trying to log out
that's trying to log out meaning that they shouldn't even attempt
meaning that they shouldn't even attempt to do this and we're going to throw back
to do this and we're going to throw back a
a 401 otherwise if they pass this we're
401 otherwise if they pass this we're going to append all of these sdks and
going to append all of these sdks and their session and pass them to the
their session and pass them to the context which means that for example in
context which means that for example in if I want to log
if I want to log out I also need to clear my account
out I also need to clear my account session so how can I get my current
session so how can I get my current account well since we used c. set
account well since we used c. set account we can now access it right here
account we can now access it right here so const account will be C doget account
so const account will be C doget account like this but as you can see there is an
like this but as you can see there is an issue this is not type save this Con
issue this is not type save this Con context right here has no idea what the
context right here has no idea what the account is and since we were already
account is and since we were already that careful with type safety using the
that careful with type safety using the RPC using react query using Zod
RPC using react query using Zod validator and all of those things
validator and all of those things let's continue with that tape safety and
let's continue with that tape safety and thankfully hono allows us to do that as
thankfully hono allows us to do that as well so go back inside of your session
well so go back inside of your session middleware here and what we have to do
middleware here and what we have to do now is create the type which this
now is create the type which this middleware will return so type
middleware will return so type additional context or whatever you want
additional context or whatever you want to call it I think this is an
to call it I think this is an appropriate name and you have to have a
appropriate name and you have to have a variables here and then inside of here
variables here and then inside of here you have account to be account type
you have account to be account type then you have databases to be databases
then you have databases to be databases type after that
type after that Storage storage
Storage storage type users users
type and user is going to be model do
model do models.
models. user models. preferences
user models. preferences and let me just see uh okay we imported
and let me just see uh okay we imported models right here I wasn't sure if we
models right here I wasn't sure if we have the models so yes this is obviously
have the models so yes this is obviously different from the ones above because
different from the ones above because these are sdks but user is a result from
these are sdks but user is a result from this method this is an a fetch request
this method this is an a fetch request right so that's why we have a very
right so that's why we have a very specific uh uh type for that models user
specific uh uh type for that models user with models preferences how do I know
with models preferences how do I know this is the type these are fairly
this is the type these are fairly obvious right well I know because I
obvious right well I know because I hovered over here and saw that so I just
hovered over here and saw that so I just appended it to be here and then we are
appended it to be here and then we are simply going to use the additional
simply going to use the additional context again make sure you have
context again make sure you have variables here don't misspell it and use
variables here don't misspell it and use the capital V Here and Now use the
the capital V Here and Now use the additional context and simply add it in
additional context and simply add it in here let's see if there are any errors
here let's see if there are any errors looks like there are some errors here so
looks like there are some errors here so let's see did they do something wrong
let's see did they do something wrong databases type type databases as
databases type type databases as databases
databases type uh I'm not exactly sure what is
type uh I'm not exactly sure what is incorrect here databases maybe I
incorrect here databases maybe I misspelled it yes datab bases I have a
misspelled it yes datab bases I have a typo inside of my additional context now
typo inside of my additional context now there are no errors here and if I go
there are no errors here and if I go back to my route guess what my account
back to my route guess what my account is now typ safe no more errors so we
is now typ safe no more errors so we successfully inferred from this
successfully inferred from this middleware exactly what is extended in
middleware exactly what is extended in this context right here so you just
this context right here so you just learned how to create a custom
learned how to create a custom middleware for app right so even even if
middleware for app right so even even if there was no third- party Library we
there was no third- party Library we created our own why am I emphasizing on
created our own why am I emphasizing on that well if you go to hono if you go
that well if you go to hono if you go inside of their middlewares here you can
inside of their middlewares here you can see they have a bunch of built-in
see they have a bunch of built-in middlewares but they also have third
middlewares but they also have third party middlewares for next out for clerk
party middlewares for next out for clerk for Firebase right for all of these
for Firebase right for all of these things which you know you might want to
things which you know you might want to use but they don't have one for app
use but they don't have one for app right and the reason I want to tell you
right and the reason I want to tell you this is you don't have to be afraid of
this is you don't have to be afraid of building your own middlewares you are
building your own middlewares you are not stuck if there is a thirdparty
not stuck if there is a thirdparty middleware or Library that's missing you
middleware or Library that's missing you can always create your own and that's
can always create your own and that's what we just did right if there was an
what we just did right if there was an app right middleware listed here I would
app right middleware listed here I would definitely use it you know it's probably
definitely use it you know it's probably made from the app authors themselves
made from the app authors themselves and it's probably the best practice
and it's probably the best practice right but no biggie we can create our
right but no biggie we can create our own and now we're going to see whether
own and now we're going to see whether it works or not so we just obtained the
it works or not so we just obtained the session middleware here we have the
session middleware here we have the account and what we have to do for a
account and what we have to do for a proper log out is after we delete the
proper log out is after we delete the cookie await account delete
cookie await account delete session and delete the current session
session and delete the current session and since we use O8 this needs to be
and since we use O8 this needs to be asynchronous there we go we can't test
asynchronous there we go we can't test this out yet because we don't have any
this out yet because we don't have any screen for the currently logged in user
screen for the currently logged in user and if we want to have a screen for the
and if we want to have a screen for the currently logged in user we also need to
currently logged in user we also need to have a route which will fetch the
have a route which will fetch the currently logged in user so in the top
currently logged in user so in the top of this uh server route I'm going to add
of this uh server route I'm going to add a
a get current and we're going to use the
get current and we're going to use the session middle so I get the currently
session middle so I get the currently log in user I'm going to add uh well
log in user I'm going to add uh well just a context
just a context here and
here and uh session middleware what exactly
uh session middleware what exactly happens to be the error here I think
happens to be the error here I think it's because I'm missing a return here
it's because I'm missing a return here so let's just get the user using C doget
so let's just get the user using C doget user and let's return c.
user and let's return c. Json data and user whoops there we go
Json data and user whoops there we go once I added the return the error was
once I added the return the error was gone uh and what I'm going to do is I'm
gone uh and what I'm going to do is I'm just going to order this how I like it
just going to order this how I like it like
like this and I'm going to show you just one
this and I'm going to show you just one Quirk
Quirk here let me just indent this and this so
here let me just indent this and this so I think that for some reason if you add
I think that for some reason if you add a comma here you will get an error right
a comma here you will get an error right I don't know why but if you add comma at
I don't know why but if you add comma at the end of your uh controller here you
the end of your uh controller here you get an error like it's missing inference
get an error like it's missing inference from session middleware and I think I
from session middleware and I think I technically know why if you add a comma
technically know why if you add a comma this get route probably thinks that this
this get route probably thinks that this is a middleware because this is supposed
is a middleware because this is supposed to be the last controller which returns
to be the last controller which returns something and it's not supposed to have
something and it's not supposed to have anything after it so if you add a comma
anything after it so if you add a comma the type safety of hono probably thinks
the type safety of hono probably thinks that this is still a middleware and it
that this is still a middleware and it did not infer the types to the last
did not infer the types to the last controller at least that's my theory so
controller at least that's my theory so I just want to give that to your
I just want to give that to your attention in case you had a a comma and
attention in case you had a a comma and you're wondering why does the why does
you're wondering why does the why does Antonio have types and I don't maybe
Antonio have types and I don't maybe it's just a comma uh you can see the
it's just a comma uh you can see the same thing in the log out if I add a
same thing in the log out if I add a comma here I think there we go same
comma here I think there we go same thing happens right so just remove the
thing happens right so just remove the comma because this is supposed to be the
comma because this is supposed to be the last function uh in this array of right
last function uh in this array of right middlewares and routes and everything
middlewares and routes and everything all right so we now have the current
all right so we now have the current route here and we have to now create
route here and we have to now create create a react query hook to fetch the
create a react query hook to fetch the current user so let's do that inside of
current user so let's do that inside of features Al API and let's add use
features Al API and let's add use current.
current. DS so creating queries is even easier
DS so creating queries is even easier than creating mutations let's get used
than creating mutations let's get used query from 10stack react query let's
query from 10stack react query let's import our
import our client from our RPC library and we don't
client from our RPC library and we don't have to define the request or respon on
have to define the request or respon on sty so we just have to write one use
sty so we just have to write one use current Define the query to be used
current Define the query to be used query query key here is going to be
query query key here is going to be always written inside of an array and
always written inside of an array and this will just be current and let's add
this will just be current and let's add a query function here to be an
a query function here to be an asynchronous
asynchronous method and inside of here let's get the
method and inside of here let's get the response from a weight client API
response from a weight client API out current and we simply f the getap
out current and we simply f the getap method like this if response is not okay
method like this if response is not okay we're going to return back now we are
we're going to return back now we are not going to throw an error because we
not going to throw an error because we will always attempt to fetch this and if
will always attempt to fetch this and if you're wondering why do I have to
you're wondering why do I have to explicitly check for response okay why
explicitly check for response okay why can't I just wrap it in a try and catch
can't I just wrap it in a try and catch block that is because this is using
block that is because this is using fetch it's not using axas axus is uh a
fetch it's not using axas axus is uh a library which when you wrap try and
library which when you wrap try and catch a around it you will receive
catch a around it you will receive errors in the catch method fetch does
errors in the catch method fetch does not work that way so for fetch
not work that way so for fetch regardless if you wrap this in try and
regardless if you wrap this in try and catch it will not throw the errors in
catch it will not throw the errors in the catch method you have to handle
the catch method you have to handle errors like
errors like this otherwise if we are okay we're
this otherwise if we are okay we're going to go ahead and get this from
going to go ahead and get this from await response Json like this and we can
await response Json like this and we can simply return the data and remember to
simply return the data and remember to return back the query
return back the query there we go so what I want to do now is
there we go so what I want to do now is I want to check if I can actually you
I want to check if I can actually you know load my currently logged in user so
know load my currently logged in user so I'm going to go instead of App instead
I'm going to go instead of App instead of page. DSX and I'm going to clear all
of page. DSX and I'm going to clear all of my buttons and inputs here I'm going
of my buttons and inputs here I'm going to mark this as use client and inside of
to mark this as use client and inside of here what I'm going to do is I'm going
here what I'm going to do is I'm going to get data from use current
to get data from use current like this and I'm going to do something
like this and I'm going to do something here inside of my use effect not the
here inside of my use effect not the previous solution I
previous solution I know but if I don't have
know but if I don't have data and if we are not
loading I'm going to go ahead and redirect the user so const router from
redirect the user so const router from use router here
let's go ahead and do the following I want to do router.
push SL sign in and I'm going to call this only
in and I'm going to call this only visible to authorized
visible to authorized users and we can remove this as well
users and we can remove this as well make sure you have marked this component
make sure you have marked this component as use client because you're using hooks
as use client because you're using hooks here so let's see if this works or not
here so let's see if this works or not let's see what happens I'm going to go
let's see what happens I'm going to go instead of create next app here I'm
instead of create next app here I'm going to refresh this and I'm going to
going to refresh this and I'm going to go and open my network
go and open my network Tab and I'm going to go to Local Host
3000 and it says only visible to authorized users and if I click on
authorized users and if I click on current here you can see the currently
current here you can see the currently authorized user that is great uh you
authorized user that is great uh you might get redirected and I think that I
might get redirected and I think that I might get redirected if I actually
might get redirected if I actually remove this cookie so if I do this and I
remove this cookie so if I do this and I refresh there we go I'm redirected to
refresh there we go I'm redirected to sign in screen so if I go back to Local
sign in screen so if I go back to Local Host there we go you can see that I
Host there we go you can see that I briefly saw the screen which we can
briefly saw the screen which we can easily hide with you know if is loading
easily hide with you know if is loading just show the loader instead and you can
just show the loader instead and you can see I'm getting 401 in my network tab
see I'm getting 401 in my network tab the moment I removed that cookie so now
the moment I removed that cookie so now what I want to do is I want to add a log
what I want to do is I want to add a log out button in case I am uh logged in so
out button in case I am uh logged in so let's go ahead and create uh a mutation
let's go ahead and create uh a mutation for that so I'm going to go inside of my
for that so I'm going to go inside of my features Al API and I'm going to copy
features Al API and I'm going to copy use login and I'm going to rename it to
use login and I'm going to rename it to use log out like this and I'm going to
use log out like this and I'm going to change these two to be log out and log
change these two to be log out and log out is also using a post method so we
out is also using a post method so we don't have to change that I'm going to
don't have to change that I'm going to rename this mutation to use log out and
rename this mutation to use log out and I don't have to pass any Json at all and
I don't have to pass any Json at all and I will change this to log out there we
I will change this to log out there we go I just have to executed that's
go I just have to executed that's it and now let's go inside of our page.
it and now let's go inside of our page. CSX where we call the use
CSX where we call the use current and let's add the mutate use log
current and let's add the mutate use log out here from features out API use log
out here from features out API use log out and I'm going to go ahead and add a
out and I'm going to go ahead and add a button here from components UI button
button here from components UI button log out and on click we'll simply call
log out and on click we'll simply call mutate and looks like it's expecting
mutate and looks like it's expecting some arguments here uh uh use log out
some arguments here uh uh use log out mutation function request
mutation function request type uh I think what we can do here is
type uh I think what we can do here is just add undefined or nothing we can
just add undefined or nothing we can just do nothing here and we can remove
just do nothing here and we can remove the request type from here and yeah then
the request type from here and yeah then you can just call mutate directly I
you can just call mutate directly I think you can even do mutate no okay it
think you can even do mutate no okay it needs to be because this is an onclick
needs to be because this is an onclick Handler right let's try this now let's
Handler right let's try this now let's see what happens now and what I want to
see what happens now and what I want to do instead of the use log out is one
do instead of the use log out is one more
more thing so once I log out I'm going to go
thing so once I log out I'm going to go ahead and I want to get const query
ahead and I want to get const query client use Query
client use Query client from 10stack react query
client from 10stack react query here and I'm going to call on success
here and I'm going to call on success here and I'm going to call query client
here and I'm going to call query client inv validate
queries and I'm going to invalidate uh I think maybe this is an
invalidate uh I think maybe this is an object where key yes current so when I
object where key yes current so when I log out I'm going to invalidate the
log out I'm going to invalidate the query current which we use in use
query current which we use in use current right here so make sure you
current right here so make sure you didn't misspell the query key right here
didn't misspell the query key right here so once the user log outs we are going
so once the user log outs we are going to force a refetch of the current user
to force a refetch of the current user and then it will be undefined meaning
and then it will be undefined meaning this data will be deleted and then we're
this data will be deleted and then we're going to redirect the user to sign in
going to redirect the user to sign in let's see if our logic is working I'm
let's see if our logic is working I'm going to go ahead now and I will log in
going to go ahead now and I will log in using Antonio mail.com Antonio 1 2 3 4 5
using Antonio mail.com Antonio 1 2 3 4 5 6 7 8 I will log in um nothing has
6 7 8 I will log in um nothing has happened yet because we didn't create
happened yet because we didn't create redirects from this screen but I will
redirects from this screen but I will manually go to Local Host 3000 there we
manually go to Local Host 3000 there we go I'm not redirected and it says only
go I'm not redirected and it says only visible to authorized users I'm going to
visible to authorized users I'm going to open my network tab here and I will
open my network tab here and I will confirm so my application has cookies
confirm so my application has cookies there we go I'm going to keep the
there we go I'm going to keep the network Tab and I will click log
network Tab and I will click log out and there we go I was redirected and
out and there we go I was redirected and uh my cookie here is deleted and you
uh my cookie here is deleted and you already saw the network tab I got the
already saw the network tab I got the log out success true and then I refetch
log out success true and then I refetch the current user and I got back the
the current user and I got back the error unauthorized and since we catch
error unauthorized and since we catch the error inside of our use current so
the error inside of our use current so if we get the error we simply set the
if we get the error we simply set the current user to be null which triggers a
current user to be null which triggers a final redirect in this use effect right
final redirect in this use effect right here amazing amazing
here amazing amazing job so what I want to do in this chapter
job so what I want to do in this chapter is the following so I'm going to leave
is the following so I'm going to leave it at this for our login options so I'm
it at this for our login options so I'm not going to implement the Google and
not going to implement the Google and GitHub login right now I'm going to do
GitHub login right now I'm going to do that later if for any reason you IM
that later if for any reason you IM immediately want to explore this you
immediately want to explore this you absolutely can uh and in this
absolutely can uh and in this documentation that we were looking at
documentation that we were looking at which is basically out and then uh we
which is basically out and then uh we choose SSR login we go to the bottom we
choose SSR login we go to the bottom we choose the tutorial for
choose the tutorial for nextjs inside of here you have step
nextjs inside of here you have step number seven which says all out
number seven which says all out authentication with SSR so it's
authentication with SSR so it's definitely possible and we will do it
definitely possible and we will do it later but if for any reason you want to
later but if for any reason you want to explore it right now we can go ahead and
explore it right now we can go ahead and do it otherwise you can go ahead and
do it otherwise you can go ahead and follow what I want to do next so I want
follow what I want to do next so I want to implement a user button and besides
to implement a user button and besides user button I want to implement a
user button I want to implement a different solution for redirecting my
different solution for redirecting my user from an unauthorized page because I
user from an unauthorized page because I just don't like using use effects so
just don't like using use effects so let's go ahead and do the user button
let's go ahead and do the user button thing first I'm going to go ahead and
thing first I'm going to go ahead and login so Antonio
login so Antonio mail.com and I'm going to go ahead and
mail.com and I'm going to go ahead and enter my Antonio 1 2 3 4 5 678 password
enter my Antonio 1 2 3 4 5 678 password and I'm going to go back to Local Host
and I'm going to go back to Local Host 3000 and there we go only visible to
3000 and there we go only visible to authorized users meaning I am logged in
authorized users meaning I am logged in and I don't want to show a button like
and I don't want to show a button like this instead I want to create a
this instead I want to create a component called user button so let's go
component called user button so let's go inside of source let's go inside of
inside of source let's go inside of features inside of out components and
features inside of out components and let's create a user button.
let's create a user button. DSX let's go ahead and mark the user
DSX let's go ahead and mark the user Button as a use client component and
Button as a use client component and inside of here let's go ahead and let's
inside of here let's go ahead and let's import everything we need from the
import everything we need from the Avatar component so that's Avatar Avatar
Avatar component so that's Avatar Avatar fullback and Avatar
fullback and Avatar image after that let's import everything
image after that let's import everything we need from the dropdown menu component
we need from the dropdown menu component so that's drop down menu drop down menu
so that's drop down menu drop down menu content drop down menu item and drop
content drop down menu item and drop down menu
down menu trigger while we are here in the Imports
trigger while we are here in the Imports let's also import the dotted
let's also import the dotted separator and finally let's also import
separator and finally let's also import our use logout and a and use well not
our use logout and a and use well not use get user uh use current like this
use get user uh use current like this from use
from use current like this so that is our API
current like this so that is our API which we've just created recently which
which we've just created recently which fetches the current user from out end
fetches the current user from out end point there we go now let's export const
point there we go now let's export const user button
user button here and inside of the user button we're
here and inside of the user button we're going to go ahead and do the
going to go ahead and do the following we are first going to fetch
following we are first going to fetch the
the user using Ed
user using Ed current and then we're going to do if is
current and then we're going to do if is loading we're going to go ahead and
loading we're going to go ahead and return a specific view so inside of here
return a specific view so inside of here I'm going to go ahead and return a div
I'm going to go ahead and return a div with a class name size 10 rounded full
with a class name size 10 rounded full Flex items Center justify center
Flex items Center justify center background neutral 200 border and Border
background neutral 200 border and Border neutral
neutral 300 so basically I am creating a
300 so basically I am creating a placeholder for what we will have here
placeholder for what we will have here below the loader now let's add loader
below the loader now let's add loader which you can import from Lucid
react we have lucid react installed when we set up shat CN so just double check
we set up shat CN so just double check that you have lucid react
installed now that you have the loader here go ahead and give it a class name
here go ahead and give it a class name size 4 animate Spin and text muted
size 4 animate Spin and text muted foreground like this and then inside of
foreground like this and then inside of here let's go ahead and let's actually
here let's go ahead and let's actually uh return our Avatar
uh return our Avatar component which will have an avatar
component which will have an avatar fullback inside and inside of here we
fullback inside and inside of here we are very simply going to destructure the
are very simply going to destructure the name and
name and email from the user which we have loaded
email from the user which we have loaded above and let's just see so I have the
above and let's just see so I have the user here and I have name uh property
user here and I have name uh property name does not exist but it's pretty
name does not exist but it's pretty obvious that it does exist
obvious that it does exist here uh is that because I have to let's
here uh is that because I have to let's see okay not sure why let's go and leave
see okay not sure why let's go and leave it like this for now con avatar for back
it like this for now con avatar for back here will be
here will be name name character at zero to
uppercase otherwise we're going to use the email character as zero to uppercase
the email character as zero to uppercase Cas if we can't do either of those we're
Cas if we can't do either of those we're just going to default to letter U for
just going to default to letter U for you know
you know user uh all right and I think I know why
user uh all right and I think I know why this is happening so in case we cannot
this is happening so in case we cannot load the user at all we're not going to
load the user at all we're not going to load anything there we go now my linter
load anything there we go now my linter is
is fixed excellent now let's go ahead and
fixed excellent now let's go ahead and let's put the AAR fullback the inside of
let's put the AAR fullback the inside of the aat for right here and now we're
the aat for right here and now we're going to go ahead and style this so
going to go ahead and style this so first of all the Avatar itself will have
first of all the Avatar itself will have a class name of size 10 on Hover it's
a class name of size 10 on Hover it's going to have an opacity of 75 it's
going to have an opacity of 75 it's going to have some transition
going to have some transition properties a border and Border neutral
properties a border and Border neutral 300 the Avatar fullback will have a
300 the Avatar fullback will have a class name of background neutral 200
class name of background neutral 200 font medium text neutral 500
font medium text neutral 500 Plex item
Plex item Center and justify
Center and justify Center like this and
Center like this and now let's go ahead and let's go inside
now let's go ahead and let's go inside of our page. DSX instead of the app
of our page. DSX instead of the app folder and instead of rendering any of
folder and instead of rendering any of those let's just render the user button
those let's just render the user button component from features out components
component from features out components user button like this and there we go
user button like this and there we go now we have a nice us user button here
now we have a nice us user button here because we are logged in so make sure
because we are logged in so make sure that you are logged in while doing this
that you are logged in while doing this let's go back inside of our user button
let's go back inside of our user button and let's now turn this into a drop down
and let's now turn this into a drop down menu so I'm going to wrap the entire
menu so I'm going to wrap the entire thing instead of a drop down menu like
thing instead of a drop down menu like this I'm going to give this a property
this I'm going to give this a property of model pulse in case we want to use it
of model pulse in case we want to use it inside of some drop down other drop-
inside of some drop down other drop- down menu components sometimes this
down menu components sometimes this property can cause uh the body to freeze
property can cause uh the body to freeze with uh an overflow
with uh an overflow hidden now let's go ahead and let's wrap
hidden now let's go ahead and let's wrap the Avatar instead of a drop down menu
the Avatar instead of a drop down menu trigger
trigger component so the entire Avatar like this
component so the entire Avatar like this and let's give this a class name of
and let's give this a class name of outline none and
outline none and relative now let's go ahead and let's
relative now let's go ahead and let's add a drop down menu content with align
add a drop down menu content with align end side bottom class name width of 60
end side bottom class name width of 60 and the side offset of of
and the side offset of of 10 so that's our drop- down menu content
10 so that's our drop- down menu content inside of the drop- down menu content
inside of the drop- down menu content we're going to create a div with a class
we're going to create a div with a class name Flex Flex column items Center
name Flex Flex column items Center justify Center Gap to
justify Center Gap to PX 2.5 and py
PX 2.5 and py 4 inside of this div right here we're
4 inside of this div right here we're going to go ahead and just copy our
going to go ahead and just copy our avatar from here let's just copy it and
avatar from here let's just copy it and let's put it here like this and I think
let's put it here like this and I think that already we should be able to click
that already we should be able to click here and get our Avatar right here there
here and get our Avatar right here there we go let's go ahead and expand this and
we go let's go ahead and expand this and let's code this
let's code this further the only thing I want to change
further the only thing I want to change in this Avatar inside of the drop down
in this Avatar inside of the drop down menu is hover and opacity so we don't
menu is hover and opacity so we don't need this here and we can change this
need this here and we can change this size to be 52 pixels like that and we
size to be 52 pixels like that and we can go ahead and add a
can go ahead and add a text Excel here like
text Excel here like this now let's go ahead and let's go
this now let's go ahead and let's go outside of the Avatar and let's add a
outside of the Avatar and let's add a div here with a paragraph which will
div here with a paragraph which will either render the name of the user or
either render the name of the user or simply
simply user now let's go ahead and let's give
user now let's go ahead and let's give this a class
this a class name of text small font medium and text
name of text small font medium and text neutral 900
neutral 900 and let's give this one a class name of
and let's give this one a class name of flex Flex column items Center and
flex Flex column items Center and justify Center because below this
justify Center because below this paragraph we're going to have another
paragraph we're going to have another one which will render the
one which will render the email and I misspelled Center here there
email and I misspelled Center here there we go now both of them are centered and
we go now both of them are centered and let's give this give a class name of
let's give this give a class name of text extra small and text neutral
text extra small and text neutral 500 just like that
500 just like that now outside of this div still inside of
now outside of this div still inside of the drop down menu content we're going
the drop down menu content we're going to have a dotted separator here and
to have a dotted separator here and we're going to give it a class name of
we're going to give it a class name of margin bottom of
margin bottom of one I will just zoom out briefly so we
one I will just zoom out briefly so we can see this better I will then add a
can see this better I will then add a drop down menu item
drop down menu item here and I will very simply add a log
here and I will very simply add a log out from Lucid react so make sure you've
out from Lucid react so make sure you've added
added this and I'm going to give it a class
this and I'm going to give it a class name of size 4 and Mr of two it's going
name of size 4 and Mr of two it's going to be a self closing tab and add a log
to be a self closing tab and add a log out method here as well now let's style
out method here as well now let's style the drop down menu item to have a class
the drop down menu item to have a class name height of 10 Flex items Center
name height of 10 Flex items Center justify Center text Amber
justify Center text Amber 700 font
700 font medium and cursor
medium and cursor pointer like this
pointer like this and now I'm going to go ahead and I'm
and now I'm going to go ahead and I'm going to add mutate log out use log
going to add mutate log out use log out which we've added an import for
out which we've added an import for right here and now let's use this log
right here and now let's use this log out method here as well so I'm going to
out method here as well so I'm going to collapse this drop down menu class name
collapse this drop down menu class name here and I'm going to add on click here
here and I'm going to add on click here to call the log out method like this
to call the log out method like this there we go we've created a very nice
there we go we've created a very nice and re usable uh user button and we
and re usable uh user button and we don't need the a image
don't need the a image here there we go now we have the
here there we go now we have the functionality to log out from here so I
functionality to log out from here so I can now log out and I should be
can now log out and I should be redirected back so I can log in
redirected back so I can log in again and the problem is still here that
again and the problem is still here that we don't get redirected to uh the
we don't get redirected to uh the dashboard page once I log in right so
dashboard page once I log in right so let's go ahead and fix those things now
let's go ahead and fix those things now and here's the thing so I told you that
and here's the thing so I told you that I really don't like the solution where I
I really don't like the solution where I have to use use effect to redirect the
have to use use effect to redirect the user so I really really don't like this
user so I really really don't like this so let's start by kind of removing stuff
so let's start by kind of removing stuff here first of all we are no longer using
here first of all we are no longer using use logout here let's remove that the
use logout here let's remove that the second thing we're going to remove is
second thing we're going to remove is well everything we don't need any of
well everything we don't need any of this inside let's remove this let's
this inside let's remove this let's remove this and this and this and let's
remove this and this and this and let's remove use client so make sure that
remove use client so make sure that inside of your user button you have use
inside of your user button you have use client otherwise you're going to get
client otherwise you're going to get some errors
some errors here and now let's go ahead and re
here and now let's go ahead and re rethink how we can work with this so by
rethink how we can work with this so by default every page and component inside
default every page and component inside of app folder is considered a server
of app folder is considered a server component this gives us an advantage
component this gives us an advantage because technically what we can do now
because technically what we can do now is we can create a special util we can
is we can create a special util we can call only in server components which can
call only in server components which can be served as authorization check X I'm
be served as authorization check X I'm going to show you exactly what I mean so
going to show you exactly what I mean so instead of calling hook which will get
instead of calling hook which will get the current user we could for example
the current user we could for example check for a cookie and redirect the user
check for a cookie and redirect the user if they don't have a cookie but that's
if they don't have a cookie but that's not the safest thing to do let's
not the safest thing to do let's actually do this let's go ahead and
actually do this let's go ahead and visit our session middleware here so our
visit our session middleware here so our session middleware has this client it
session middleware has this client it has the session where it gets the cookie
has the session where it gets the cookie and basically this session middleware is
and basically this session middleware is optimized for hono right if uses get
optimized for hono right if uses get cookie from hono cookie so we're going
cookie from hono cookie so we're going to do a similar thing but we're not
to do a similar thing but we're not going to treat it like an API endpoint
going to treat it like an API endpoint let's go ahead and see what I mean so
let's go ahead and see what I mean so we're going to go inside of features
we're going to go inside of features inside of Al and I'm going to go ahead
inside of Al and I'm going to go ahead and I'm going to create a new file
and I'm going to create a new file called actions.
called actions. DS inside of here I'm going to mark this
DS inside of here I'm going to mark this as use
as use server and I'm going to export const get
server and I'm going to export const get current this is going to be an asynchron
current this is going to be an asynchron method and inside of here I'm going to
method and inside of here I'm going to go ahead and do the
go ahead and do the following I'm going to set up my client
following I'm going to set up my client again like
this from node app right and then I'm going to go ahead and get the session
going to go ahead and get the session not using hono cookies but by using
not using hono cookies but by using cookies from next headers like this now
cookies from next headers like this now depending on which version of no next
depending on which version of no next you are using because in next 15 you're
you are using because in next 15 you're going to have to a weight cookies so I
going to have to a weight cookies so I can write a weight cookies and I have a
can write a weight cookies and I have a little note here a weight has no effect
little note here a weight has no effect on this type of expression but if you're
on this type of expression but if you're using nextjs 15 I'm pretty sure uh that
using nextjs 15 I'm pretty sure uh that you're going to have to await the
you're going to have to await the cookies to get them to work so just
cookies to get them to work so just hover over cookies and see what you get
hover over cookies and see what you get if you get a promise around here you
if you get a promise around here you most likely have to await cookies but I
most likely have to await cookies but I think that I can await them as well so
think that I can await them as well so you can just write it like this this
you can just write it like this this will have no effect if you're using U
will have no effect if you're using U next version that I am but in case
next version that I am but in case you're using a newer version you have to
you're using a newer version you have to await a lot of stuff which you usually
await a lot of stuff which you usually didn't have towait in next
didn't have towait in next 14 so let's go ahead and get our out
14 so let's go ahead and get our out cookie here like
cookie here like this and then we're going to go ahead
this and then we're going to go ahead and do if there is no session we're
and do if there is no session we're going to go ahead and return
going to go ahead and return null and then let's go ahead and let's
null and then let's go ahead and let's define our account
define our account right
right here and we have to import account from
here and we have to import account from node app right so account and client
node app right so account and client from node app right and then we're
from node app right and then we're finally going to do account await
finally going to do account await account.
account. getet just like
getet just like this and if you want to you can also
this and if you want to you can also wrap the entire thing inside of try and
wrap the entire thing inside of try and catch and simply return null in case any
catch and simply return null in case any error happens
all right and now let's go ahead and let's do the following let's go inside
let's do the following let's go inside of our page. vsx in the homepage right
of our page. vsx in the homepage right here make sure there is no used client
here make sure there is no used client at the top and we're going to now turn
at the top and we're going to now turn this into an asynchronous Method Keep in
this into an asynchronous Method Keep in mind that you can only do this in server
mind that you can only do this in server components if you were to mark this as a
components if you were to mark this as a client component uh you will most likely
client component uh you will most likely get an error there we go I briefly got
get an error there we go I briefly got an error here I don't know why it's not
an error here I don't know why it's not appear in right now but you cannot use
appear in right now but you cannot use uh asynchronous and use client at the
uh asynchronous and use client at the same time so just make sure you remove
same time so just make sure you remove that so now that you have a synchronous
that so now that you have a synchronous here what you can do is you can get the
here what you can do is you can get the current user or you can just mark it as
current user or you can just mark it as user and you can now call the get
user and you can now call the get current method from features out actions
current method from features out actions and if there is no user you can redirect
and if there is no user you can redirect using next navigation which can be used
using next navigation which can be used in server components and simply redirect
in server components and simply redirect the user to slash sign in just like this
the user to slash sign in just like this so now I'm going to go ahead and log out
so now I'm going to go ahead and log out and I'm going to refresh and let's see
and I'm going to refresh and let's see what happens it looks like I'm still
what happens it looks like I'm still getting a user let's see if that is true
getting a user let's see if that is true so I'm going to go ahead and scroll all
so I'm going to go ahead and scroll all the way down and debug what's going on
the way down and debug what's going on so I have get current right here so I'm
so I have get current right here so I'm going to go ahead and see inside of my
going to go ahead and see inside of my page. vsx what happens
page. vsx what happens uh with my user
uh with my user here perhaps I'm still getting something
here perhaps I'm still getting something but I should not be getting that uh
but I should not be getting that uh looks like I still have something
looks like I still have something here and I think that's because I didn't
here and I think that's because I didn't await get current my apologies let's try
await get current my apologies let's try this now there we go I immediately get
this now there we go I immediately get redirected yes so we have to await
redirected yes so we have to await redirect here there we go so I'm going
redirect here there we go so I'm going to go ahead and try this again if I go
to go ahead and try this again if I go back to Local Host 3000 I am in
back to Local Host 3000 I am in immediately returned here there we go so
immediately returned here there we go so that's how we handled that if you want
that's how we handled that if you want to you know you can rename this to
to you know you can rename this to protect for example and then inside of
protect for example and then inside of here you can do redirect to something if
here you can do redirect to something if you want to depending on but if you're
you want to depending on but if you're using redirects keep in mind you cannot
using redirects keep in mind you cannot use them inside of try and catch that's
use them inside of try and catch that's one kvat with using a redirect from
one kvat with using a redirect from nextjs so I'm going to be explicit in my
nextjs so I'm going to be explicit in my uh oops I think I modified something so
uh oops I think I modified something so yeah get
yeah get current so I'm going to be explicit in
current so I'm going to be explicit in my tutorial here I'm just going to call
my tutorial here I'm just going to call the user and then I will manually
the user and then I will manually redirect here it's two lines not too
redirect here it's two lines not too much code if you ask me and now let's do
much code if you ask me and now let's do the same thing instead of our sign in
the same thing instead of our sign in and sign up Pages here so I'm going to
and sign up Pages here so I'm going to remove use client actually here which
remove use client actually here which means that I have to go instead of the
means that I have to go instead of the signin card and mark the signin card as
signin card and mark the signin card as use client instead
use client instead there we go now I no longer have the
there we go now I no longer have the error here I'm going to mark this as an
error here I'm going to mark this as an asynchronous uh function and I'm going
asynchronous uh function and I'm going to get the user using get
to get the user using get current from features out actions if I
current from features out actions if I have the user this time uh my apologies
have the user this time uh my apologies await don't forget towait if I have the
await don't forget towait if I have the user I'm going to redirect using next
user I'm going to redirect using next navigation to the root page which will
navigation to the root page which will be the dashboard just like that there we
be the dashboard just like that there we go and now I'm going to copy this and
go and now I'm going to copy this and I'm going to go inside of my sign up
I'm going to go inside of my sign up page right here I'm going to remove use
page right here I'm going to remove use client here I will go inside of sign up
client here I will go inside of sign up card and I will Mark sign up card as use
card and I will Mark sign up card as use client as well so first of all confirm
client as well so first of all confirm that that is fixed go inside of sign up
that that is fixed go inside of sign up there we go no errors on the sign up
there we go no errors on the sign up page so now I'm going to go ahead and do
page so now I'm going to go ahead and do the same thing here I will mark this as
the same thing here I will mark this as an asynchronous function and I will get
an asynchronous function and I will get current from features out options and
current from features out options and redirect whoops redirect from next
redirect whoops redirect from next navigation like this there we go and
navigation like this there we go and let's briefly go inside of signup card
let's briefly go inside of signup card here and let me just change the button
here and let me just change the button which I have here which says login to
which I have here which says login to register there we go let's go ahead and
register there we go let's go ahead and try this out now I'm going to go and
try this out now I'm going to go and sign in now and I will do Antonio
sign in now and I will do Antonio mail.com Antonio 1 2 3 4 5 6 78 let's
mail.com Antonio 1 2 3 4 5 6 78 let's log in and looks like I am still not
log in and looks like I am still not getting redirected if I refresh still
getting redirected if I refresh still okay let's see what I did wrong maybe I
okay let's see what I did wrong maybe I entered a wrong password so we also have
entered a wrong password so we also have to have handle those States as well but
to have handle those States as well but I first want to debug whether I did
I first want to debug whether I did something wrong or not uh so this is my
something wrong or not uh so this is my sign in page if I have the
sign in page if I have the user I will redirect to the root page so
user I will redirect to the root page so I will console log the user here and
I will console log the user here and let's see do I have the user or not I
let's see do I have the user or not I will refresh this page I will scroll
will refresh this page I will scroll down looks like user is null so perhaps
down looks like user is null so perhaps I have entered a wrong password let me
I have entered a wrong password let me try one more time
here nope looks like it's still not working I will go ahead and try this so
working I will go ahead and try this so my login seems to be success
my login seems to be success true and inside of my application here I
true and inside of my application here I seem to be having a cookie
seem to be having a cookie but it looks like I'm just not getting
but it looks like I'm just not getting uh redirected so I'm just going to go
uh redirected so I'm just going to go ahead and pause and debug this and tell
ahead and pause and debug this and tell you how to fix it and how I debugged
you how to fix it and how I debugged it well I think I know what is the issue
it well I think I know what is the issue so let's go back inside of our uh
so let's go back inside of our uh features out actions here and we've
features out actions here and we've wrote this incorrectly because if you
wrote this incorrectly because if you take a look inside of the session
take a look inside of the session middleware here uh we forgot to add
middleware here uh we forgot to add client set session
client set session right we don't do that here at all we
right we don't do that here at all we immediately get the account here so
immediately get the account here so let's do session Set uh which one is it
let's do session Set uh which one is it a client set
a client set session and pass in the session. value
session and pass in the session. value here like this and there we go now I am
here like this and there we go now I am immediately redirected let's go ahead
immediately redirected let's go ahead and try this again so if I go to slash
and try this again so if I go to slash sign
sign in I immediately redirected back same
in I immediately redirected back same thing for SL sign up but if I log out
thing for SL sign up but if I log out from
from here let's see if this works okay so
here let's see if this works okay so this works after refresh so let's see
this works after refresh so let's see how can we uh improve that from
how can we uh improve that from happening well here's a solution that I
happening well here's a solution that I like to do here because I I did this in
like to do here because I I did this in my original source
my original source code we're going to go inside of my uh
code we're going to go inside of my uh use log out method and this is what I'm
use log out method and this is what I'm going to do
going to do I'm just going to do window location
I'm just going to do window location reload like this this will invalidate
reload like this this will invalidate all queries without me having to do them
all queries without me having to do them it will ensure that user doesn't have
it will ensure that user doesn't have any you know Global States set and it
any you know Global States set and it will simply clear any you know Global
will simply clear any you know Global States right and it's kind of a Brute
States right and it's kind of a Brute Force way of doing it but honestly it
Force way of doing it but honestly it saves so much time and you don't have to
saves so much time and you don't have to worry about whether you refreshed or
worry about whether you refreshed or done anything wrong so let's try this
done anything wrong so let's try this again I will log
again I will log in uh okay looks like we have the same
in uh okay looks like we have the same issue for logging in all right my
issue for logging in all right my apologies looks like I didn't exactly
apologies looks like I didn't exactly calculate this as well as I thought
calculate this as well as I thought looks like they work after you refresh
looks like they work after you refresh so I think they log out now there we go
so I think they log out now there we go log out now is working because of this
log out now is working because of this so we could do
so we could do this but I think there is a proper uh
this but I think there is a proper uh react uh nextjs way of doing it so there
react uh nextjs way of doing it so there is a method called router. refresh which
is a method called router. refresh which is supposed to uh refresh all server
is supposed to uh refresh all server components which would technically uh
components which would technically uh refetch this so let's try with that
refetch this so let's try with that solution first if not we're going to go
solution first if not we're going to go ahead and trigger a window location
ahead and trigger a window location reload instead of use login as well but
reload instead of use login as well but first let's try this let's get the
first let's try this let's get the router from use router from next
router from use router from next navigation
navigation here and and inside of here I'm going to
here and and inside of here I'm going to do router
do router refresh let's do that first and we no
refresh let's do that first and we no longer need the query uh actually let's
longer need the query uh actually let's keep the query client yes and then let's
keep the query client yes and then let's do query client invalidate queries again
do query client invalidate queries again query key and let's give it current
query key and let's give it current value let's go ahead and copy this let's
value let's go ahead and copy this let's go instead of use login and let's do the
go instead of use login and let's do the same
same thing make sure you import the router
thing make sure you import the router from next nav ation don't import the
from next nav ation don't import the router from next router and inside of
router from next router and inside of here on success I'm going to go ahead
here on success I'm going to go ahead and call router
and call router refresh just like this and we can also
refresh just like this and we can also copy the invalidation for this and for
copy the invalidation for this and for that we also need a query client so con
that we also need a query client so con query client use Query client from T
query client use Query client from T stack uh react
stack uh react query let's try both of this again now
query let's try both of this again now make sure you are testing the login
make sure you are testing the login route right
route right now let's hope this works if not we'll
now let's hope this works if not we'll just fall back to window
reload and there we go so router refresh seems to uh have its magic on perfect
seems to uh have its magic on perfect thanks uh I'm I'm thankful we don't have
thanks uh I'm I'm thankful we don't have to use the window location reload
to use the window location reload because it's kind of an ugly solution it
because it's kind of an ugly solution it works it's great I used it initially in
works it's great I used it initially in my source code but I just remember that
my source code but I just remember that we have a proper way of refreshing our
we have a proper way of refreshing our server
server components great and now I have to do
components great and now I have to do the exact same thing for my use register
the exact same thing for my use register so let's do that let's add use Query
so let's do that let's add use Query client let's add use router from next
client let's add use router from next navigation I will move both of these
navigation I will move both of these here and let me just copy one from on
here and let me just copy one from on success
success here there we go perfect so what I want
here there we go perfect so what I want to do now is I want to go back inside of
to do now is I want to go back inside of my sign uh sign in cards here and I want
my sign uh sign in cards here and I want to see if it's possible for me
to see if it's possible for me to uh oh I can actually extract is
to uh oh I can actually extract is pending here and I want to disable all
pending here and I want to disable all of my
of my Fields if it's
Fields if it's pending like
pending like this actually we don't have to disable
this actually we don't have to disable all fields we just have to disable the
all fields we just have to disable the button so disabled is pending
button so disabled is pending and the same thing for the login with
and the same thing for the login with Google and login with GitHub because
Google and login with GitHub because they are a single click you know so
they are a single click you know so let's try this again maybe we're we're
let's try this again maybe we're we're going to have some better experience
now so now if I click login there we go it's disabled and then it redirects so
it's disabled and then it redirects so this is at least telling the user that
this is at least telling the user that something is happening and I now want to
something is happening and I now want to do the same thing to the sign up card
do the same thing to the sign up card here so let's this structure from use
here so let's this structure from use register is pending like this and let's
register is pending like this and let's go ahead and let's assign it to not the
go ahead and let's assign it to not the inputs but just the button here and
inputs but just the button here and these two buttons here as well uh great
these two buttons here as well uh great so I now think this will work even
so I now think this will work even without testing here perfect and we have
without testing here perfect and we have very nice uh redirects from the homepage
very nice uh redirects from the homepage and from the out
and from the out screens now at this point you might be
screens now at this point you might be wondering why did I not add a middleware
wondering why did I not add a middleware well I've been you know using middle
well I've been you know using middle words in some of my tutorials but lately
words in some of my tutorials but lately I've been trying to avoid them and
I've been trying to avoid them and there's a reason for that maybe avoid
there's a reason for that maybe avoid them is not the correct term I'm not
them is not the correct term I'm not avoiding them but I'm definitely not
avoiding them but I'm definitely not relying on them and this blog post by
relying on them and this blog post by pilcro who is the author of Lucia Al is
pilcro who is the author of Lucia Al is honestly a perfect
honestly a perfect explanation and and basically my way of
explanation and and basically my way of protecting pages from now on is simply
protecting pages from now on is simply being explicit so you're going to see me
being explicit so you're going to see me write
write authentication inside of each page and
authentication inside of each page and if you personally find it too tedious to
if you personally find it too tedious to do that in in each page just keep in
do that in in each page just keep in mind you can always create an
mind you can always create an abstraction right if if those two lines
abstraction right if if those two lines are too much you can always go ahead and
are too much you can always go ahead and add the read irect to be done directly
add the read irect to be done directly inside of the actions right here so you
inside of the actions right here so you can redirect the user here but I am no
can redirect the user here but I am no longer going to rely on the
longer going to rely on the middleware the reason being quite simple
middleware the reason being quite simple I don't want to assume my middleware is
I don't want to assume my middleware is protecting all of my routes I want to
protecting all of my routes I want to explicitly protect my routes I want to
explicitly protect my routes I want to see them being protected right uh at the
see them being protected right uh at the end of the day it doesn't really matter
end of the day it doesn't really matter it sounds funny when I say that but our
it sounds funny when I say that but our most important layer is protected that
most important layer is protected that is the server now I'm also handling the
is the server now I'm also handling the protection on the client side as well
protection on the client side as well and I'm going to do that explicitly I'm
and I'm going to do that explicitly I'm not going to do it through the
not going to do it through the middleware does that mean that you
middleware does that mean that you should not be using the middleware no
should not be using the middleware no using the middleware is great if you
using the middleware is great if you want to enhance user experience right so
want to enhance user experience right so if you want to have three layers of
if you want to have three layers of security so individual page layer a
security so individual page layer a middleware layer and API layer great the
middleware layer and API layer great the more the merrier but don't rely on the
more the merrier but don't rely on the middleware I personally regret that my
middleware I personally regret that my website personally uh runs entirely on
website personally uh runs entirely on middleware protection because honestly
middleware protection because honestly I'm unsure if everything that I want
I'm unsure if everything that I want covered is covered this would not happen
covered is covered this would not happen if I just explicitly added just like I
if I just explicitly added just like I do in my API routes you know with the
do in my API routes you know with the keep in mind that when I say middleware
keep in mind that when I say middleware I mean on the middleware in nextjs I I
I mean on the middleware in nextjs I I don't mean on the middleware concept in
don't mean on the middleware concept in Express or in uh hono right so those are
Express or in uh hono right so those are different types of middle wor also
different types of middle wor also nextest middleware lives in a different
nextest middleware lives in a different runtime than the rest of your app that's
runtime than the rest of your app that's also important thing to know there have
also important thing to know there have been reported security issues because of
been reported security issues because of the middleware itself and here's the
the middleware itself and here's the most important thing of all you might
most important thing of all you might got a brilliant idea well I could just
got a brilliant idea well I could just put it in layout right do not do that at
put it in layout right do not do that at all that is the worst of all three Ops
all that is the worst of all three Ops options and the least safe uh way of
options and the least safe uh way of doing it I highly suggest that you read
doing it I highly suggest that you read on authentication in
on authentication in nextjs and as always I think you can go
nextjs and as always I think you can go wrong with just being explicit you know
wrong with just being explicit you know if you want to protect the sign-in page
if you want to protect the sign-in page protect it if you want to protect the
protect it if you want to protect the dashboard page protect it that's why we
dashboard page protect it that's why we you know learned how to uh how to work
you know learned how to uh how to work with server components and how to Simply
with server components and how to Simply fetch the current user using a server
fetch the current user using a server action right if even though I think this
action right if even though I think this is technically not a server action this
is technically not a server action this is just you know we could we could have
is just you know we could we could have written this entire thing you know
written this entire thing you know directly here if we want to do it's just
directly here if we want to do it's just a lot of code to repeat so that's why
a lot of code to repeat so that's why we're not doing it uh and I think we
we're not doing it uh and I think we don't need use server here use server is
don't need use server here use server is used when writing server actions these
used when writing server actions these are not server actions so we don't need
are not server actions so we don't need that it's still going to work just fine
that it's still going to work just fine so that's the reason why I'm not using
so that's the reason why I'm not using the middleware so again we're going to
the middleware so again we're going to be using
be using explicit authentication for each page in
explicit authentication for each page in this tutorial if you want to you can
this tutorial if you want to you can create an abstraction around these two
create an abstraction around these two lines like something like I don't know
lines like something like I don't know server protect and then have props to be
server protect and then have props to be pass as to where to redirect if
pass as to where to redirect if authentication is missing or if it's not
authentication is missing or if it's not missing you can do that if you want to
missing you can do that if you want to uh great so we have that working uh I'm
uh great so we have that working uh I'm going to link this or you can you can
going to link this or you can you can Google this exact title if you want to
Google this exact title if you want to read this very interesting
read this very interesting article and now what we're going to do
article and now what we're going to do next is now that we are finally logged
in is we're going to start developing the sidebar great great
the sidebar great great job the first thing I want to do in
job the first thing I want to do in order to build my dashboard layout
order to build my dashboard layout including the sidebar and the navigation
including the sidebar and the navigation bar is to move this page. TSX file in
bar is to move this page. TSX file in its own route group just as we have the
its own route group just as we have the out route group
out route group so I'm going to do that by creating a
so I'm going to do that by creating a new folder
new folder dashboard and let's just not misspell
dashboard and let's just not misspell dashboard and I'm simply going to drag
dashboard and I'm simply going to drag and drop page. TSX
and drop page. TSX inside just like this and now I have
inside just like this and now I have this weird unsaved file let me just
this weird unsaved file let me just remind you that is from Cache right so
remind you that is from Cache right so you can just save this file and close it
you can just save this file and close it no biggy and now if you if you refresh
no biggy and now if you if you refresh nothing much should change everything
nothing much should change everything should still work as expected and now
should still work as expected and now inside of this dashboard I want to go
inside of this dashboard I want to go ahead and I want to create a
ahead and I want to create a layout. TSX file and inside of here I'm
layout. TSX file and inside of here I'm going to create a dashboard
layout and inside of here I'm going to get my children let's quickly create an
get my children let's quickly create an interface dashboard layout props with
interface dashboard layout props with the children here which are react react
the children here which are react react node let's go ahead and assign these
node let's go ahead and assign these props here and let's add a div and
props here and let's add a div and children inside and nothing much should
children inside and nothing much should change you should still see your user
change you should still see your user button right here great and now let's go
button right here great and now let's go ahead uh and let's actually create our
ahead uh and let's actually create our sidebar and our knf bar so I'm going to
sidebar and our knf bar so I'm going to go ahead and give my entire layout a
go ahead and give my entire layout a minimum height of screen meaning 100 uh
minimum height of screen meaning 100 uh VH as you can see here and then inside
VH as you can see here and then inside I'm going to go ahead and create a div
I'm going to go ahead and create a div here another one with a class name Flex
here another one with a class name Flex full width and full height I'm going to
full width and full height I'm going to move children
move children inside and then I'm going to wrap
inside and then I'm going to wrap children inside of a div
here and this div will have a class name on large devices it will will have a
on large devices it will will have a padding left of
padding left of 264 pixels now in order to try this out
264 pixels now in order to try this out make sure you are zoomed out and you
make sure you are zoomed out and you should see a big padding next to your
should see a big padding next to your user button here so make sure you are
user button here so make sure you are zoomed out if you are zoomed in you
zoomed out if you are zoomed in you looking at the mobile view so make sure
looking at the mobile view so make sure you always have enough space left I'm
you always have enough space left I'm now just going to collapse this so you
now just going to collapse this so you can see the
can see the code now what I'm going to do here is
code now what I'm going to do here is I'm going to prepare my to-do Navar like
I'm going to prepare my to-do Navar like this and I'm going to add a main element
this and I'm going to add a main element here around my children and I will
here around my children and I will encapsulate my Novar and my main element
encapsulate my Novar and my main element in another div which will handle how far
in another div which will handle how far these two can expand so we're going to
these two can expand so we're going to use MX AO and Max with screen to Excel
use MX AO and Max with screen to Excel and height pull and if you remember we
and height pull and if you remember we already used this trick inside of out
already used this trick inside of out layout
layout uh right here MX AO Max withth screen so
uh right here MX AO Max withth screen so let me remind you what that does so you
let me remind you what that does so you can see how this is following my screen
can see how this is following my screen expansion the user button right and you
expansion the user button right and you can see how how I zoom out it's also
can see how how I zoom out it's also following to the to the left but at one
following to the to the left but at one point uh it will stop expanding right so
point uh it will stop expanding right so that's basically what we are achieving
that's basically what we are achieving right
right here excellent so Max screen to excel MX
here excellent so Max screen to excel MX AO and height full great and let's go
AO and height full great and let's go inside of the main element here and
inside of the main element here and let's give it a class name height full
let's give it a class name height full py of 8 PX of six flex and flex column
py of 8 PX of six flex and flex column just like that great and now let's go
just like that great and now let's go ahead and let's actually create our
ahead and let's actually create our little uh nov bar here so we're going to
little uh nov bar here so we're going to do that outside of this div which pushes
do that outside of this div which pushes our uh content so we're going to create
our uh content so we're going to create a div here with a class name fixed
a div here with a class name fixed uh fixed left zero top zero hidden large
uh fixed left zero top zero hidden large block large width of 264 pixels which is
block large width of 264 pixels which is the same amount that we push our content
the same amount that we push our content for height
for height full and overflow y Auto so we can
full and overflow y Auto so we can scroll inside if it overflows and then
scroll inside if it overflows and then we're going to add a sidebar component
we're going to add a sidebar component inside now let's go ahead and let's
inside now let's go ahead and let's actually develop the sidebar component
actually develop the sidebar component so I'm going to go ahead and I'm going
so I'm going to go ahead and I'm going to go inside of features and I will
to go inside of features and I will create a new feature called uh actually
create a new feature called uh actually we can go and do this in components yes
we can go and do this in components yes perhaps this is a better solution so
perhaps this is a better solution so let's go ahead and create a sidebar uh.
let's go ahead and create a sidebar uh. TSX component here and inside of here
TSX component here and inside of here I'm going to go ahead and Export con
I'm going to go ahead and Export con sidebar I'm going to Simply uh return an
sidebar I'm going to Simply uh return an element uh called a side not div and
element uh called a side not div and let's go ahead and spell sidebar inside
let's go ahead and spell sidebar inside I'm now going to go back inside of my
I'm now going to go back inside of my app folder dashboard layout and I will
app folder dashboard layout and I will import the sidebar from components
import the sidebar from components sidebar so there we go now on large
sidebar so there we go now on large devices I see a text sidebar right here
devices I see a text sidebar right here great let's go inside of the sidebar and
great let's go inside of the sidebar and let's actually develop it so I'm going
let's actually develop it so I'm going to give this a class name height full
to give this a class name height full background neutral 100 padding four and
background neutral 100 padding four and with full and then I'm going to add a
with full and then I'm going to add a link from next link here with an hre to
link from next link here with an hre to the root of the page and I'm going to
the root of the page and I'm going to add an image from next image here and
add an image from next image here and I'm going to add our
I'm going to add our logo.svg with an ALT of logo and a width
logo.svg with an ALT of logo and a width of 164 and a height of 48 like this so
of 164 and a height of 48 like this so now there we go we have a nice logo here
now there we go we have a nice logo here and it's the same one that we had if you
and it's the same one that we had if you remember on our out
remember on our out screens great so we have that and now
screens great so we have that and now outside of this link let's add a dotted
outside of this link let's add a dotted separator
separator here let's go ahead and give it a class
here let's go ahead and give it a class name
name my4 and then in here let's add
my4 and then in here let's add navigation component so I'm going to go
navigation component so I'm going to go ahead and create that in components as
ahead and create that in components as well navigation.
well navigation. DSX like this let's go inside of the
DSX like this let's go inside of the navigation here and let's go ahead and
navigation here and let's go ahead and just just create a constant called
just just create a constant called routes like this inside of here we're
routes like this inside of here we're going to have a label home oops this is
going to have a label home oops this is supposed to be an object so the first
supposed to be an object so the first object will have a label of home as in
object will have a label of home as in homepage and hre will just be an empty
homepage and hre will just be an empty slash you're going to see why later and
slash you're going to see why later and then we're going to have two types of
then we're going to have two types of icons one will be uh the outline icon
icons one will be uh the outline icon and then we're going to have an active
and then we're going to have an active icon so for the first first icon I'm
icon so for the first first icon I'm going to use go home and then I'm going
going to use go home and then I'm going to use go home fill we can get both of
to use go home fill we can get both of this from our react icons so go home and
this from our react icons so go home and go home fill from react icons
go and I think we installed react icons I don't know where but just make sure
I don't know where but just make sure you have react icons installed
you have react icons installed great uh so we have that perfect and now
great uh so we have that perfect and now let's go ahead and let's copy and paste
let's go ahead and let's copy and paste this let's add an h/ tasks here to be my
this let's add an h/ tasks here to be my tasks like this and let's go ahead and
tasks like this and let's go ahead and change this to be go check
change this to be go check Circle and this one will be go check
Circle and this one will be go check Circle fill make sure you you're adding
Circle fill make sure you you're adding these Imports here after that we're
these Imports here after that we're going to have
going to have settings which will go to/ settings and
settings which will go to/ settings and they can use use the settings icon from
they can use use the settings icon from Lucid
Lucid react and it can be the same for the
react and it can be the same for the active icon because we're going to
active icon because we're going to redirect to a different type of layout
redirect to a different type of layout you're going to see that later and
you're going to see that later and lastly we're going to have
lastly we're going to have members and for this we're going to have
members and for this we're going to have the users icon also from Lucid react and
the users icon also from Lucid react and you can repeat the same one for the
you can repeat the same one for the active icon make sure you have added all
active icon make sure you have added all of this
of this Imports and now let's
Imports and now let's export con navigation
export con navigation and let's actually uh render this so
and let's actually uh render this so we're going to create a div with a class
we're going to create a div with a class name Flex Flex call or actually we could
name Flex Flex call or actually we could maybe use an unordered list let's do
maybe use an unordered list let's do that and then inside of here let's use
that and then inside of here let's use routes. map and let's get the individual
routes. map and let's get the individual item
item here and we're going to go ahead and do
here and we're going to go ahead and do this we're going to open like this and
this we're going to open like this and then we're going to return the reason is
then we're going to return the reason is because later I'm going to create some
because later I'm going to create some constants here like uh is active right
constants here like uh is active right so for example let's just set this to
so for example let's just set this to false and inside of here I'm going to
false and inside of here I'm going to use the link from next link so make sure
use the link from next link so make sure you've added an import for that as well
you've added an import for that as well there we go inside of here we're going
there we go inside of here we're going to go ahead and give it a key of item
to go ahead and give it a key of item hre and an hre will for now be item H
hre and an hre will for now be item H we're going to change that later let's
we're going to change that later let's add a div here which will very simp
add a div here which will very simp simply have our icon which will be
simply have our icon which will be defined here so make sure you use icon
defined here so make sure you use icon with a capital I so it can be used as an
with a capital I so it can be used as an jsx element and this will simply be
jsx element and this will simply be item. icon or we can already do it so if
item. icon or we can already do it so if is
is active it will use item icon active icon
active it will use item icon active icon otherwise item icon and then inside of
otherwise item icon and then inside of here we can render that icon like this
here we can render that icon like this and below that item. Lael let's give
and below that item. Lael let's give this icon a class name of size 5 and
this icon a class name of size 5 and text neutral
text neutral 500 and let's go ahead and give uh the
500 and let's go ahead and give uh the div here class name using CN Library so
div here class name using CN Library so make sure you import CN from
utils first let's add the default classes which will be Flex items Center
classes which will be Flex items Center Gap 2.5 padding 2.5 around it
Gap 2.5 padding 2.5 around it medium font medium hover text
medium font medium hover text primary let's add a
primary let's add a transition and text neutral
transition and text neutral 500 so these are our class names for the
500 so these are our class names for the first div and then let's add a comma at
first div and then let's add a comma at the end of the first class and let's
the end of the first class and let's simply do if is
simply do if is active background white shadow small
active background white shadow small cover opacity 100 text
cover opacity 100 text primary like
primary like this uh great so let's add that now
this uh great so let's add that now let's go inside of sidebar and let's
let's go inside of sidebar and let's import the navigation from do/
import the navigation from do/ navigation like this and I'm going to
navigation like this and I'm going to expand and there we go we now have a
expand and there we go we now have a nice sidebar here on our hands of course
nice sidebar here on our hands of course if you click on any of these you will
if you click on any of these you will most likely get a 404 error so make sure
most likely get a 404 error so make sure uh that uh I mean don't be surprised if
uh that uh I mean don't be surprised if you get an error we're going to create
you get an error we're going to create those pages uh
those pages uh later uh great so we have that uh let's
later uh great so we have that uh let's see is there anything else we can do uh
see is there anything else we can do uh I think we can leave it as as this we're
I think we can leave it as as this we're going to come back to this
going to come back to this later now I want to go back inside of my
later now I want to go back inside of my dashboard layout and I want to build uh
dashboard layout and I want to build uh this Novar component
this Novar component now so let's go ahead and let's remove
now so let's go ahead and let's remove this to-do comment with an actual nvar
this to-do comment with an actual nvar component like that and let's go inside
component like that and let's go inside of our components and let's create a
of our components and let's create a navb bar.
navb bar. DSX I'm going to export con
DSX I'm going to export con Novar like
Novar like this and instead of using div I will be
this and instead of using div I will be using the nov
using the nov element and before we start styling this
element and before we start styling this I want to go back to my layout just save
I want to go back to my layout just save this file go back to the layout and and
this file go back to the layout and and import the Navar from components Navar
import the Navar from components Navar so you can actually see what you're
so you can actually see what you're developing now I'm going to go ahead and
developing now I'm going to go ahead and give this na a class name padding top 4
give this na a class name padding top 4 BX of six Flex items Center justify
BX of six Flex items Center justify between I'm then going to go ahead and
between I'm then going to go ahead and create a div with a class
create a div with a class name Flex Flex column hidden Buton large
name Flex Flex column hidden Buton large Flex so we just need Flex go my
Flex so we just need Flex go my apologies like this after that add an H1
apologies like this after that add an H1 element which will render for example
element which will render for example home and a class
home and a class name text of 22 pixels or we can simply
name text of 22 pixels or we can simply use extra large let's see how much is
use extra large let's see how much is that that's 20 pixels uh 2 Excel is 24
that that's 20 pixels uh 2 Excel is 24 let's use that and let's also give it a
let's use that and let's also give it a font semi
font semi bold and below that a paragraph for
bold and below that a paragraph for example monitor all of your projects and
example monitor all of your projects and tasks here and let's give this paragraph
tasks here and let's give this paragraph a class name of text muted
a class name of text muted foreground if you want to see this just
foreground if you want to see this just make sure you are zoomed out right so
make sure you are zoomed out right so I'm in Mobile mode so I can't see it but
I'm in Mobile mode so I can't see it but there we go home monitor all of your
there we go home monitor all of your projects and the tasks here great and
projects and the tasks here great and now what I want to do is I want to add a
now what I want to do is I want to add a user button
user button here and let's import the user button
here and let's import the user button like
like this and then let's go ahead and let's
this and then let's go ahead and let's go inside of our page.
go inside of our page. DSX where our dashboard is and instead
DSX where our dashboard is and instead of here rendering the user button we
of here rendering the user button we were just going to say this is a
were just going to say this is a homepage and let's remove the import for
homepage and let's remove the import for the user button so there we go now we
the user button so there we go now we have uh a better experience here but
have uh a better experience here but this is supposed to be all the way here
this is supposed to be all the way here why is it not let's go ahead and fix
why is it not let's go ahead and fix that I'm going to go inside of my navbar
that I'm going to go inside of my navbar here and I want to see whether we are
here and I want to see whether we are missing something so we have Flex we
missing something so we have Flex we have justify between here let's see if
have justify between here let's see if our layout uh seems to be missing
our layout uh seems to be missing something and I think that we have to
something and I think that we have to add uh with p did this LG right here
add uh with p did this LG right here inside of our dashboard layout so just
inside of our dashboard layout so just add with full here and let's see if that
add with full here and let's see if that fixes it there we go so now as you can
fixes it there we go so now as you can see in this corner I have my user button
see in this corner I have my user button and here I kind of have my title and
and here I kind of have my title and description for the current page of
description for the current page of course we're going to make this Dynamic
course we're going to make this Dynamic later and if I expand you know uh at a
later and if I expand you know uh at a certain point you can see that it stops
certain point you can see that it stops expanding as close to the sidebar as
expanding as close to the sidebar as possible so you know on certain break
possible so you know on certain break points it follows but then when it gets
points it follows but then when it gets you know too wide it will no longer uh
you know too wide it will no longer uh expand unreasonably much because design
expand unreasonably much because design simply can't look good except you know
simply can't look good except you know if you have a designer who can of course
if you have a designer who can of course create widescreen uh Solutions and while
create widescreen uh Solutions and while we are here I want to create a solution
we are here I want to create a solution for the mobile sidebar because in Mobile
for the mobile sidebar because in Mobile the user button is moved here but we can
the user button is moved here but we can fix this quite easily so let's go ahead
fix this quite easily so let's go ahead and create the uh mobile
and create the uh mobile sidebar so let's go inside of our source
sidebar so let's go inside of our source components and let's create a mobile Das
components and let's create a mobile Das sidebar. TSX component and let's export
sidebar. TSX component and let's export const mobile
sidebar like this and I'm going to mark this as use
this as use client let's go ahead and let's add a
client let's go ahead and let's add a sheet component from
sheet component from /ui sheet and we're also going to need
/ui sheet and we're also going to need two more components from here so content
two more components from here so content and Trigger and inside of here let's go
and Trigger and inside of here let's go ahead and give it a model pulse here
ahead and give it a model pulse here because this can cause some overflow
because this can cause some overflow issues and let's go ahead and add the
issues and let's go ahead and add the trigger component here and let's add it
trigger component here and let's add it around a button component from /ui
around a button component from /ui button let's go ahead and add as child
button let's go ahead and add as child here so this trigger becomes the
here so this trigger becomes the button because the trigger component is
button because the trigger component is already a button so you would have a
already a button so you would have a button inside of a button and this will
button inside of a button and this will cause a hydration error so just add as
cause a hydration error so just add as child prop there let's go ahead and give
child prop there let's go ahead and give the button a variant secondary and let's
the button a variant secondary and let's give it a class name LG hidden so it's
give it a class name LG hidden so it's only visible on mobile and let's add a
only visible on mobile and let's add a menu
menu icon from Lucid react here I'm going to
icon from Lucid react here I'm going to separate this content there we
separate this content there we go and while we are here let's go
go and while we are here let's go quickly back to the nav bar and above
quickly back to the nav bar and above the user button let's add mobile sidebar
the user button let's add mobile sidebar from do/ mobile
sidebar there we go you can now see this button right
button right here uh and let's go ahead and let's
here uh and let's go ahead and let's give this button an a size icon while
give this button an a size icon while we're here there we go and this menu
we're here there we go and this menu icon will have a class name of si uh of
icon will have a class name of si uh of size four and text neutral
size four and text neutral 500 there we go like this
500 there we go like this and I'm going to do this I'm going to
and I'm going to do this I'm going to give this button a class name size 10 or
give this button a class name size 10 or maybe size
maybe size eight oh it already has a class name
eight oh it already has a class name let's see so can I do size eight
here okay I'm going to do this I'm just going to roll back and give it the size
going to roll back and give it the size of Icon here like
of Icon here like this and now let's add a sheet
this and now let's add a sheet content and let's simply render the
content and let's simply render the sidebar from do/ sidebar inside just
sidebar from do/ sidebar inside just like
like this and I'm going to go ahead and give
this and I'm going to go ahead and give the sheet content a side of left and a
the sheet content a side of left and a class name of padding zero like this so
class name of padding zero like this so now on a mobile when you click here
now on a mobile when you click here there we go it opens a nice uh sidebar
there we go it opens a nice uh sidebar here great so let let's go ahead and
here great so let let's go ahead and let's just uh
let's just uh see if I go into actual mobile mode I
see if I go into actual mobile mode I think this button might be a bit small
think this button might be a bit small so I'm going to go ahead and do this I'm
so I'm going to go ahead and do this I'm going to just going to remove size icon
going to just going to remove size icon I think it looks okay and let's give
I think it looks okay and let's give this a size five okay that just enlarges
this a size five okay that just enlarges the button so let's keep it a size four
the button so let's keep it a size four okay uh great so now I think yeah now
okay uh great so now I think yeah now it's it's much easier to click on mobile
it's it's much easier to click on mobile uh great we have some errors here we can
uh great we have some errors here we can resolve those later they're just
resolve those later they're just semantic issues I
semantic issues I believe great and now that we have that
believe great and now that we have that uh I think we are ready to close this
uh I think we are ready to close this chapter and I just want to add one more
chapter and I just want to add one more thing here so const is open and set is
thing here so const is open and set is open use State react and let's give it
open use State react and let's give it false by default let me just move this
false by default let me just move this here and I'm going to add path name from
here and I'm going to add path name from use path name so every time that we
use path name so every time that we actually change our route I will want to
actually change our route I will want to close this side bar and this is like a
close this side bar and this is like a Brute Force way of doing it so every
Brute Force way of doing it so every time we detect a change in the path name
time we detect a change in the path name we're just going to call set is open to
we're just going to call set is open to false so we're going to go ahead and
false so we're going to go ahead and extend the sheet to be open when is open
extend the sheet to be open when is open is true and on open change will simply
is true and on open change will simply control the set is open method so this
control the set is open method so this sheet trigger will uh call set is open
sheet trigger will uh call set is open and set it to true and when we click
and set it to true and when we click close somewhere it will set it to false
close somewhere it will set it to false or when use effect happens it will set
or when use effect happens it will set it to false so you can see that this
it to false so you can see that this still works just fine great so you just
still works just fine great so you just handled the mobile view and the desktop
handled the mobile view and the desktop view of the sidebar and the nbar you can
view of the sidebar and the nbar you can go ahead and log out and you can see all
go ahead and log out and you can see all of this things are working just great
of this things are working just great again don't worry about um Google and
again don't worry about um Google and GitHub login we'll do later we are right
GitHub login we'll do later we are right now focusing you know on adding as many
now focusing you know on adding as many functionalities to this project as
functionalities to this project as possible uh great so we have the uh
possible uh great so we have the uh dashboard layout ready and we are now
dashboard layout ready and we are now ready to build our first workspace and
ready to build our first workspace and create some onboarding screens great
create some onboarding screens great great
great job now let's go ahead and let's create
job now let's go ahead and let's create our workspace entity and our workspace
our workspace entity and our workspace creation form in order to do that you
creation form in order to do that you first have to go back inside a of your
first have to go back inside a of your app right console go ahead and select
app right console go ahead and select your project here and head into the
your project here and head into the databases section go ahead and create
databases section go ahead and create your first database I'm going to go
your first database I'm going to go ahead and give this a name of jira Clone
ahead and give this a name of jira Clone and I'm going to leave the custom ID
and I'm going to leave the custom ID field as empty and click
field as empty and click create after that I have my jro clone
create after that I have my jro clone database right here so once you click on
database right here so once you click on databases again you should now see your
databases again you should now see your database ID right here let's call Cy our
database ID right here let's call Cy our database ID immediately and let's go
database ID immediately and let's go inside of environment. Lo here and let's
inside of environment. Lo here and let's assign it to next public app database ID
assign it to next public app database ID like
like this now let's go inside of here and
this now let's go inside of here and let's go ahead and create a new
let's go ahead and create a new collection we're going to call this
collection we're going to call this collection
collection workspaces and let's click
workspaces and let's click create let's go ahead and uh go back
create let's go ahead and uh go back just so so we can see that we now have a
just so so we can see that we now have a new collection so we now have a database
new collection so we now have a database inside of the database we have a
inside of the database we have a collection right here and we can already
collection right here and we can already use this collection ID for the
use this collection ID for the workspaces so let's copy The Collection
workspaces so let's copy The Collection ID here and I'm going to go ahead and
ID here and I'm going to go ahead and just separate these things so let's go
just separate these things so let's go ahead and add next public
ahead and add next public app workspaces ID and let's add it right
app workspaces ID and let's add it right here like
here like that so we now have the workspaces
that so we now have the workspaces collection ID here and now we're not
collection ID here and now we're not going to create any documents but what
going to create any documents but what we are going to create are some
we are going to create are some attributes so let's go ahead and click
attributes so let's go ahead and click create an attribute and let's go ahead
create an attribute and let's go ahead and call this a string so we're going to
and call this a string so we're going to give our workspace entity an attribute
give our workspace entity an attribute called name and I'm going to limit it to
called name and I'm going to limit it to a 256 characters at most if you want to
a 256 characters at most if you want to you can limit it at 10 so I'm going to
you can limit it at 10 so I'm going to go ahead and make this a required uh
go ahead and make this a required uh attribute so make sure you have added uh
attribute so make sure you have added uh you have checked this as well and make
you have checked this as well and make sure you've named it name let's go ahead
sure you've named it name let's go ahead and click create after that let's go
and click create after that let's go ahead and let's add a user ID property
ahead and let's add a user ID property so that we can keep track which
so that we can keep track which workspace belongs to what
workspace belongs to what user and user IDs in workspace are
user and user IDs in workspace are limited to 50 so we can safely put 50
limited to 50 so we can safely put 50 here and set it as required as well in
here and set it as required as well in case you're watching this into the
case you're watching this into the future and this has changed you will get
future and this has changed you will get an appropriate error in your terminal
an appropriate error in your terminal here when you try to create a workspace
here when you try to create a workspace and you try to assign a user ID but I
and you try to assign a user ID but I think 50 should be just enough in case
think 50 should be just enough in case it's not you can go back here and
it's not you can go back here and increase it to whatever you need I'm not
increase it to whatever you need I'm not going to do that now great so we now
going to do that now great so we now have the name and the user ID for our
have the name and the user ID for our workspaces
workspaces collection what I want to do now is I
collection what I want to do now is I want to use these database ID and
want to use these database ID and workspaces ID and I want to create them
workspaces ID and I want to create them into reusable constants so that we don't
into reusable constants so that we don't have to write that all the time so let's
have to write that all the time so let's go instead of source and inside of here
go instead of source and inside of here create a config
create a config dots and let's write export cons
dots and let's write export cons databases ID and let's go ahead and give
databases ID and let's go ahead and give it a my apologies database ID because we
it a my apologies database ID because we working with one individual database and
working with one individual database and let's give it the process. environment
let's give it the process. environment and let's revisit our environment. loal
and let's revisit our environment. loal and copy the app database ID here and
and copy the app database ID here and you can put the exclamation point at the
you can put the exclamation point at the end to avoid uh so when you hover over
end to avoid uh so when you hover over here it must be a
here it must be a string and then let's go ahead and
string and then let's go ahead and Export con workspace ID like this
Export con workspace ID like this actually it's the collection workspaces
actually it's the collection workspaces ID so this will be process. environment
ID so this will be process. environment and then copy next public app right
and then copy next public app right workspaces ID and put the exclamation
workspaces ID and put the exclamation point at the end so this way we can
point at the end so this way we can easily uh just use these instead of
easily uh just use these instead of having to write this every time great
having to write this every time great now let's go ahead and let's create our
now let's go ahead and let's create our API endpoint for this so we're going to
API endpoint for this so we're going to go inside of features and we're going to
go inside of features and we're going to create a new feature called workspaces
create a new feature called workspaces here and inside of here let's go inside
here and inside of here let's go inside of features and let's create our
of features and let's create our schemas dots file
schemas dots file let's go ahead and let's
let's go ahead and let's import Z from Zod here and let's go
import Z from Zod here and let's go ahead and Export con create workspace
ahead and Export con create workspace schema to be a z.
schema to be a z. object let's give it a name of Z string
object let's give it a name of Z string let's stream it and let's give it a
let's stream it and let's give it a minimum value of one and make sure it's
minimum value of one and make sure it's required besides that uh we're also
required besides that uh we're also going to have a user ID but we are not
going to have a user ID but we are not going to pass that through any forms or
going to pass that through any forms or through any API payloads so we can just
through any API payloads so we can just leave it at name for now great we now
leave it at name for now great we now have the schema and now let's go ahead
have the schema and now let's go ahead and let's create a server folder here
and let's create a server folder here and let's create a route.
and let's create a route. DS let's import hono from hono and let's
DS let's import hono from hono and let's go ahead and create a const app new hono
go ahead and create a const app new hono and let's export default app
and let's export default app here now that we have our workspaces
here now that we have our workspaces server route we can go inside of the app
server route we can go inside of the app folder API rout route. TS and inside of
folder API rout route. TS and inside of here we can now import workspaces from
here we can now import workspaces from at/ features workspaces server
at/ features workspaces server route and then we can chain it just
route and then we can chain it just below out with a prefix of
below out with a prefix of workspaces so just be careful you know
workspaces so just be careful you know don't misspell these things even though
don't misspell these things even though it won't really matter because we are
it won't really matter because we are using types saave rpcs
using types saave rpcs now we can go back inside of our
now we can go back inside of our workspaces server and let's go ahead and
workspaces server and let's go ahead and let's create uh a very simple post route
let's create uh a very simple post route so you can just put an empty forward
so you can just put an empty forward slash because this will be slash
slash because this will be slash workspaces because our base endpoint is
workspaces because our base endpoint is slash workspaces here so you don't have
slash workspaces here so you don't have to Define workspaces again let's go
to Define workspaces again let's go ahead and let's add a z validator here
ahead and let's add a z validator here for our Json and let's go ahead and add
for our Json and let's go ahead and add our create form create workspace
our create form create workspace schema and then finally we're going to
schema and then finally we're going to have an
have an asynchronous context
asynchronous context here and now we are also uh we also need
here and now we are also uh we also need to add our session middleware here so
to add our session middleware here so let's not forget that let me just
let's not forget that let me just reorder my imports
reorder my imports here there we go and now let's go ahead
here there we go and now let's go ahead and do step bystep uh creation of the
and do step bystep uh creation of the workspace so the first thing I need to
workspace so the first thing I need to do is I need to get my databases so I
do is I need to get my databases so I can get my databases thanks to the
can get my databases thanks to the session middleware here because we
session middleware here because we create the databases with a new client
create the databases with a new client here so let's go ahead and do const
here so let's go ahead and do const databases to be C.G
databases to be C.G databases like this and let's go ahead
databases like this and let's go ahead and get the user from C.G user since we
and get the user from C.G user since we have type safety you can see that we
have type safety you can see that we have all the necessary information about
have all the necessary information about that here then let's go ahead and let's
that here then let's go ahead and let's destructure the name from C request
destructure the name from C request valid Json like
valid Json like this and then let's go ahead and
this and then let's go ahead and actually create the document so const
actually create the document so const workspace will be await databases do
workspace will be await databases do create
create document in the first argument we're
document in the first argument we're going to pass in which database to
going to pass in which database to create a document in so let's add our
create a document in so let's add our database uncore ID from our config file
database uncore ID from our config file the second argument is which collection
the second argument is which collection which is our workspace id workspaces id
which is our workspace id workspaces id again from the config
again from the config file and then finally we assign the ID
file and then finally we assign the ID of this document and we can use randomly
of this document and we can use randomly generated IDs using the ID import from
generated IDs using the ID import from node app like this so let's just add id.
node app like this so let's just add id. unique and execute that method and then
unique and execute that method and then finally we can open the body object and
finally we can open the body object and we can assign the name and since it's
we can assign the name and since it's the same name as the key we don't have
the same name as the key we don't have to write name name we can just write
to write name name we can just write name like this there we go uh great so
name like this there we go uh great so now we have our document created and we
now we have our document created and we can just do return c. Json data
can just do return c. Json data workspace just like that remember if you
workspace just like that remember if you put a comma here you're going to lose
put a comma here you're going to lose your type inference from the middleware
your type inference from the middleware so just don't put commas at the end of
so just don't put commas at the end of your middle Wares
your middle Wares great we have this ready now so what we
great we have this ready now so what we have to do now is we have to create our
have to do now is we have to create our API uh by API I mean my react query
API uh by API I mean my react query mutation so let's go inside of features
mutation so let's go inside of features and let's copy from the API folder in
and let's copy from the API folder in out let's copy the use login one let's
out let's copy the use login one let's go inside of workspaces let's create a
go inside of workspaces let's create a new folder called API and let's paste
new folder called API and let's paste use login and let's rename it to use
use login and let's rename it to use create
create workspace let's name the content inside
workspace let's name the content inside use create workspace always double check
use create workspace always double check that you have the proper file opened so
that you have the proper file opened so just close use login so you don't
just close use login so you don't accidentally modify it now we have to
accidentally modify it now we have to create the response type and the request
create the response type and the request type here so we are not going to go
type here so we are not going to go inside of api. out but we're going to go
inside of api. out but we're going to go inside of api. workspaces and then we
inside of api. workspaces and then we are directly going to go uh to the post
are directly going to go uh to the post method here so the response type there
method here so the response type there we go as you can see is is our uh
we go as you can see is is our uh created workspace and request type is
created workspace and request type is Json requesting a
Json requesting a name in the mutation function here we
name in the mutation function here we also have to modify the same thing so
also have to modify the same thing so workspaces like this and what we're
workspaces like this and what we're going to do here when we uh create
going to do here when we uh create workspace I'm going to go ahead and add
workspace I'm going to go ahead and add workspaces here basically this doesn't
workspaces here basically this doesn't yet exist but we are going to have uh a
yet exist but we are going to have uh a different use get work workspaces which
different use get work workspaces which will use the query key workspaces so
will use the query key workspaces so when we create a new one we're going to
when we create a new one we're going to make sure to refetch our workspaces so
make sure to refetch our workspaces so for now this will not do anything but
for now this will not do anything but later when we add a use get query with
later when we add a use get query with this key it will refresh and we don't
this key it will refresh and we don't need router refresh to happen here so
need router refresh to happen here so you can remove that and remove the use
you can remove that and remove the use router from next
router from next navigation great so we now have
navigation great so we now have this and now let's go ahead and let's
this and now let's go ahead and let's actually build our form so we're going
actually build our form so we're going to do that instead of source inside of
to do that instead of source inside of features inside of workspaces let's
features inside of workspaces let's create a new folder
create a new folder components and inside of here we have to
components and inside of here we have to create uh a new form so create workspace
create uh a new form so create workspace form.
TSX let's go ahead and let's create our interface here so interface create
interface here so interface create workspace form props are going to have a
workspace form props are going to have a prop called on cancel why am I adding an
prop called on cancel why am I adding an oncancel prop to a component which is
oncancel prop to a component which is supposedly just a form to create a
supposedly just a form to create a workspace well I want to reuse this form
workspace well I want to reuse this form in many different ways one including
in many different ways one including being opened in a model if it's opened
being opened in a model if it's opened in a model we have to give it some
in a model we have to give it some proper instructions on how to close that
proper instructions on how to close that model from the submitting or cancelling
model from the submitting or cancelling inside of the form right so that's the
inside of the form right so that's the only reason why I am adding that and I
only reason why I am adding that and I don't want to forget it later so that's
don't want to forget it later so that's why I immediately added it let's go
why I immediately added it let's go ahead and add create workspace form here
ahead and add create workspace form here and let's destructure our own
and let's destructure our own cancel create workspace
cancel create workspace form great now let's go ahead and let's
form great now let's go ahead and let's define the form to be use form from
define the form to be use form from react hook form inside of here we're
react hook form inside of here we're going to get go ahead and create a
going to get go ahead and create a resolver to be Zod resolver from Hook
resolver to be Zod resolver from Hook form resolvers Zod and we're simply
form resolvers Zod and we're simply going to use create workspace schema
going to use create workspace schema fromt do/ schemas the default values
fromt do/ schemas the default values will be a name with an empty value
will be a name with an empty value here just like
here just like that I'm now going to go ahead and give
that I'm now going to go ahead and give it a type so Z from Zod make sure you've
it a type so Z from Zod make sure you've added an import for
added an import for Zod do infer type of create workspace
Zod do infer type of create workspace schema uh did I do this correctly no not
schema uh did I do this correctly no not here my apologies not in the Zod
here my apologies not in the Zod resolver in use
resolver in use form there we go now let's go ahead and
form there we go now let's go ahead and let's create an onsubmit method with the
let's create an onsubmit method with the values which have the exact same Z infer
values which have the exact same Z infer here if you want to you can set that
here if you want to you can set that aside in a reusable type if you don't
aside in a reusable type if you don't want to type it two
want to type it two times and we're just going to do a
times and we're just going to do a console log of the values inside of this
console log of the values inside of this onsubmit method
onsubmit method here now let's go ahead and let's create
here now let's go ahead and let's create our layout for this so we're going to
our layout for this so we're going to return a card element from components UI
return a card element from components UI card so make sure you add Imports for
card so make sure you add Imports for this and while we are here let's also
this and while we are here let's also add the rest of the Imports which we
add the rest of the Imports which we will need card content card header and
will need card content card header and card title like this and we are also
card title like this and we are also going to need all the items from our
going to need all the items from our form so import form form control form
form so import form form control form field form item form label and form
field form item form label and form message from at/ compon components SL
message from at/ compon components SL ui/ form like that and do I have a
ui/ form like that and do I have a mistake here I do create workspace form
mistake here I do create workspace form props my apologies for that let's go
props my apologies for that let's go back inside of our card here let's give
back inside of our card here let's give the card a class name full width full
the card a class name full width full height border none and
height border none and Shadow
Shadow none inside let's create a card header
none inside let's create a card header let's give it a class name flex and
let's give it a class name flex and padding of seven
padding of seven let's create a card title
let's create a card title inside let's give the card title a class
inside let's give the card title a class name text extra large and font bold and
name text extra large and font bold and let's write create a new
let's write create a new workspace like
workspace like this below this let's add a div which is
this below this let's add a div which is going to encapsulate our dotted
going to encapsulate our dotted separator alongside with a vertical or
separator alongside with a vertical or horizontal padding s dotted
horizontal padding s dotted separator let me just move it the along
separator let me just move it the along side here there we go doted separator
side here there we go doted separator and then card content with a class name
and then card content with a class name padding
padding 7 just like this and I want to stop here
7 just like this and I want to stop here because I want to see what I am
because I want to see what I am developing so we're going to go inside
developing so we're going to go inside of app folder inside of dashboard inside
of app folder inside of dashboard inside of page. DSX and let's go ahead and add
of page. DSX and let's go ahead and add our create workspace form here let's
our create workspace form here let's give it an unan cancel to just be an
give it an unan cancel to just be an empty Arrow function like this
empty Arrow function like this so let's go ahead and go inside of our
so let's go ahead and go inside of our Local Host 3000 let's refresh and let's
Local Host 3000 let's refresh and let's see what's going on so I believe the
see what's going on so I believe the issue is that my create workspace form
issue is that my create workspace form uh is a client component and this is a
uh is a client component and this is a server component so what we have to do
server component so what we have to do is go inside of our create workspace
is go inside of our create workspace form and Mark it as use client and now
form and Mark it as use client and now the error should go away uh oh but we
the error should go away uh oh but we are passing this on cancel all right
are passing this on cancel all right this is what we're going to do for now
this is what we're going to do for now we're just going to go ahead and make
we're just going to go ahead and make this an optional prop like this so put a
this an optional prop like this so put a question mark here then we don't have to
question mark here then we don't have to pass this and then there we go we can
pass this and then there we go we can now just see create a new workspace here
now just see create a new workspace here and if you want to you can give this
and if you want to you can give this entire Diva class name of background uh
entire Diva class name of background uh muted or maybe background
muted or maybe background neutral
neutral 500 and just give it a bit of a padding
500 and just give it a bit of a padding padding four and a height full Maybe
padding four and a height full Maybe maybe there we go just something so you
maybe there we go just something so you can see you know our actual card
can see you know our actual card obviously it's not going to look like
obviously it's not going to look like that but I just want to give you an idea
that but I just want to give you an idea of how it's going to look great so we
of how it's going to look great so we now have our card content and now inside
now have our card content and now inside of here we are going to establish our
of here we are going to establish our form let's go ahead and spread the form
form let's go ahead and spread the form element inside let's define the native
element inside let's define the native form inside with a class name my
form inside with a class name my apologies with an onsubmit form handle
apologies with an onsubmit form handle submit and pass in our onsubmit which we
submit and pass in our onsubmit which we have the find right here basically the
have the find right here basically the same thing we did in the sign up and
same thing we did in the sign up and sign in cards now let's add a form field
sign in cards now let's add a form field which is a self closing tag which will
which is a self closing tag which will have a control or form. control it will
have a control or form. control it will have a name of name and a render which
have a name of name and a render which is going to immediately destructure
is going to immediately destructure field in its method here and return a
field in its method here and return a form
form item the form item will include a form
item the form item will include a form label this time with a text workspace
label this time with a text workspace name
name and Below form label we're going to have
and Below form label we're going to have form control which will include an input
form control which will include an input from components UI input so just make
from components UI input so just make sure you have included this in your
sure you have included this in your inputs as well like this and let's go
inputs as well like this and let's go ahead now and give the input a
ahead now and give the input a placeholder enter workspace name and
placeholder enter workspace name and make sure that you are doing the spread
make sure that you are doing the spread of field like right here and outside
of field like right here and outside form control don't forget form message
form control don't forget form message so we can display the errors right here
so we can display the errors right here so now let's go ahead and let's add our
so now let's go ahead and let's add our uh submit button here so this is how
uh submit button here so this is how we're actually uh going to do that we're
we're actually uh going to do that we're going to go ahead inside of this form
going to go ahead inside of this form and let's add a div
and let's add a div here encapsulating this single form
here encapsulating this single form field and let's give it a class name
field and let's give it a class name Flex Flex call and GAP y of four and
Flex Flex call and GAP y of four and inside of this div at the bottom
inside of this div at the bottom we're going to add uh well let's do it
we're going to add uh well let's do it like this we're first going to add a
like this we're first going to add a dotted
separator like this and let's go ahead and add it outside of this div which is
and add it outside of this div which is encapsulating this form field so you
encapsulating this form field so you might be wondering why did I even add
might be wondering why did I even add this div well because later we're going
this div well because later we're going to have some more fields for the
to have some more fields for the workspace so make sure you have
workspace so make sure you have encapsulated your form field in this div
encapsulated your form field in this div and then you've added a dotted separ
and then you've added a dotted separ here with a class name of
py7 and then add a div with a class name Flex items Center and justify
Flex items Center and justify between now let's go ahead and let's add
between now let's go ahead and let's add two buttons here again confirm you have
two buttons here again confirm you have the button import ready uh do I have my
the button import ready uh do I have my button where is my button imported it's
button where is my button imported it's not okay make sure you've imported
not okay make sure you've imported button from components UI button I'm
button from components UI button I'm going to go ahead and move it here
going to go ahead and move it here alongside with the input
alongside with the input and the first one in this list will be
and the first one in this list will be the cancel button which will be our type
the cancel button which will be our type button this is very important because by
button this is very important because by default it will submit the form if you
default it will submit the form if you don't explicitly give it a type of
don't explicitly give it a type of button and we're going to go ahead and
button and we're going to go ahead and give it a size of large we're going to
give it a size of large we're going to give it a variant of secondary like this
give it a variant of secondary like this and on click will be our on cancel
and on click will be our on cancel method like this and then you can copy
method like this and then you can copy and paste the button here and you can go
and paste the button here and you can go ahead and remove the type for this one
ahead and remove the type for this one or you can give it an explicit type
or you can give it an explicit type submit if you prefer it that way and the
submit if you prefer it that way and the variant here will be primary which you
variant here will be primary which you don't have to explicitly uh right and we
don't have to explicitly uh right and we won't have an on click because it will
won't have an on click because it will serve as a submit for the form and let's
serve as a submit for the form and let's go ahead and give it a label of create
go ahead and give it a label of create workpace like this there we go and now
workpace like this there we go and now let's go ahead and actually try this out
let's go ahead and actually try this out so right now if I click create workspace
so right now if I click create workspace there we go I get an error that the
there we go I get an error that the workspace name is required thanks to our
workspace name is required thanks to our schema now let's go ahead here and let's
schema now let's go ahead here and let's add our
add our mutation so
mutation so const mutate will come from use create
const mutate will come from use create workspace from do/ API use create
workspace from do/ API use create workspace so basically it's this API
workspace so basically it's this API which we've created recently which
which we've created recently which invalidates the non-existing workspaces
invalidates the non-existing workspaces query key for now in here we have mutate
query key for now in here we have mutate and let's also get is pending from here
and let's also get is pending from here and very simply we're going to go ahead
and very simply we're going to go ahead and call the mutate method here and pass
and call the mutate method here and pass the values as the Json
the values as the Json prop and now let's go ahead and disabled
prop and now let's go ahead and disabled our buttons
our buttons here if we are pending like this and
here if we are pending like this and same thing for this one let's go ahead
same thing for this one let's go ahead and try this out I'm going to go ahead
and try this out I'm going to go ahead and prepare my databases my collections
and prepare my databases my collections workspaces which currently has no
workspaces which currently has no documents inside and I'm also going to
documents inside and I'm also going to prepare my network tab here so I can see
prepare my network tab here so I can see if any errors arrive let's go ahead and
if any errors arrive let's go ahead and create a test workspace and click create
create a test workspace and click create workspace and looks like I do get a 500
workspace and looks like I do get a 500 error and it looks like my user is
error and it looks like my user is unauthorized to create this so why is
unauthorized to create this so why is that happening and how can we resolve it
that happening and how can we resolve it well that is the security that I was
well that is the security that I was talking about how we should be careful
talking about how we should be careful which client of aight we are using H and
which client of aight we are using H and we should give uh and which CL which
we should give uh and which CL which ight client should we give our user
ight client should we give our user access to so inside of our lib we have
access to so inside of our lib we have the admin client which is the allmighty
the admin client which is the allmighty all powerful client which can do
all powerful client which can do anything even though we have restricted
anything even though we have restricted the API key to only be able to create
the API key to only be able to create sessions but inside of our session
sessions but inside of our session middleware we create a different type of
middleware we create a different type of client which is only as powerful as its
client which is only as powerful as its user so for example we never gave this
user so for example we never gave this user permission to write to our database
user permission to write to our database so we have to do that let's go inside of
so we have to do that let's go inside of workspace right here let's go inside uh
workspace right here let's go inside uh of our settings make sure you are of
of our settings make sure you are of course in the workspace collection so go
course in the workspace collection so go inside of your databases find your new
inside of your databases find your new workspace collection go inside of
workspace collection go inside of settings here and inside of here you
settings here and inside of here you have permissions you can click the big
have permissions you can click the big plus and go ahead and select all users
plus and go ahead and select all users so all users can create read update and
so all users can create read update and delete this like this and go ahead and
delete this like this and go ahead and click update there we go now go back to
click update there we go now go back to documents here let's go ahead one more
documents here let's go ahead one more time I'm going to open my network Tab
time I'm going to open my network Tab and I think this time it's going to work
and I think this time it's going to work I don't even have to refresh I believe
I don't even have to refresh I believe and again looks like I'm having a 500 uh
and again looks like I'm having a 500 uh yes invalid document structure missing
yes invalid document structure missing requiring attribute user ID yes my
requiring attribute user ID yes my apologies I forgot the user ID let's go
apologies I forgot the user ID let's go ahead and do that and then let's quickly
ahead and do that and then let's quickly add some you know error feedback here to
add some you know error feedback here to the user because user has no idea what's
the user because user has no idea what's going on here and I think we also forgot
going on here and I think we also forgot to do that for login screens so I'm
to do that for login screens so I'm going to go inside of my uh server for
going to go inside of my uh server for my workspaces so inside of features
my workspaces so inside of features workspaces server route. TS and inside
workspaces server route. TS and inside of here I prepared the user but I'm not
of here I prepared the user but I'm not using it so I'm going to go ahead and
using it so I'm going to go ahead and add a user ID to be user Dot and here's
add a user ID to be user Dot and here's a quick tip inside uh app right we don't
a quick tip inside uh app right we don't use do ID we use dollar sign ID like
use do ID we use dollar sign ID like this of course if you're watching this
this of course if you're watching this far into the future and this changes you
far into the future and this changes you know just look at what you have autoc
know just look at what you have autoc completion for if you have ID you can
completion for if you have ID you can use ID and I think that now we should
use ID and I think that now we should finally be able to create this third
finally be able to create this third times the charm let's go ahead and click
times the charm let's go ahead and click create workspace there we go 200 and we
create workspace there we go 200 and we have a new workspace right here so now
have a new workspace right here so now if I go inside of my workspaces here and
if I go inside of my workspaces here and refresh there we go we have a name test
refresh there we go we have a name test workspace and my user ID right here in
workspace and my user ID right here in case you got any additional errors go
case you got any additional errors go ahead and look in the terminal and
ahead and look in the terminal and simply see what is the message just say
simply see what is the message just say if it says that the user ID is too short
if it says that the user ID is too short or too long perhaps you have to go
or too long perhaps you have to go inside of the attributes and confirm
inside of the attributes and confirm that your user ID has the proper size
that your user ID has the proper size maybe you have to increase it and update
maybe you have to increase it and update it depending on when you're watching
it depending on when you're watching this video uh great so we have that
this video uh great so we have that resolved and now what I want to do is I
resolved and now what I want to do is I want to add some uh error messages and
want to add some uh error messages and some success messages so let's go ahead
some success messages so let's go ahead and do the following we already have uh
and do the following we already have uh something called toaster I believe
something called toaster I believe instead of my package Json I already
instead of my package Json I already have soner installed because I installed
have soner installed because I installed that with shaten so if you by any chance
that with shaten so if you by any chance don't have that you would go ahead and
don't have that you would go ahead and use uh banex
use uh banex shaten uh I have no idea what is my
shaten uh I have no idea what is my version so let me just see
version so let me just see 2.1.0 so bux D- bun chaten at 2.1.0 add
2.1.0 so bux D- bun chaten at 2.1.0 add soner that's what you have to add in
soner that's what you have to add in case soner is missing from your
case soner is missing from your components here you should also have uh
components here you should also have uh a toaster I believe uh but let's go
a toaster I believe uh but let's go ahead and let's just see how we have to
ahead and let's just see how we have to implement this so we're going to go
implement this so we're going to go ahead inside of the app folder inside of
ahead inside of the app folder inside of layout. DSX right here so make sure
layout. DSX right here so make sure you're in your root layout for
you're in your root layout for now and let's go ahead and let's
now and let's go ahead and let's import toaster from components UI soner
import toaster from components UI soner so if you have components you are soner
so if you have components you are soner you have definitely installed soner
you have definitely installed soner right so you can command click or
right so you can command click or control click inside or simply find it
control click inside or simply find it in the component UI here you now that
in the component UI here you now that you have the toaster go inside of the
you have the toaster go inside of the query provider and simply add the
query provider and simply add the toaster inside and self close it that's
toaster inside and self close it that's it nothing else you have to do and now
it nothing else you have to do and now let's go ahead inside of source inside
let's go ahead inside of source inside of features inside of workspaces API use
of features inside of workspaces API use create workspace and let's go ahead and
create workspace and let's go ahead and let's import
let's import tost from sonor and in the on success
tost from sonor and in the on success let's add toast. success workspace
let's add toast. success workspace created and on error let's go ahead and
created and on error let's go ahead and add a
add a toast. error failed to create
toast. error failed to create workspace just like this so let's go
workspace just like this so let's go ahead and try this now you can see
ahead and try this now you can see nothing happens but if I write test I
nothing happens but if I write test I should get uh a nice toaster let's see
should get uh a nice toaster let's see there we go I had to refresh and now it
there we go I had to refresh and now it says workspace created here and you can
says workspace created here and you can go ahead and try with an error so if you
go ahead and try with an error so if you go inside of your server not Al but
go inside of your server not Al but inside of your uh well we can try it
inside of your uh well we can try it like this yes let's while we are here
like this yes let's while we are here let's also do this let's go inside of
let's also do this let's go inside of our out API let's go to our mutations so
our out API let's go to our mutations so use login uh use log out and use
use login uh use log out and use register let's start with use login and
register let's start with use login and let's
let's import toaster from soner my apologies
import toaster from soner my apologies toast from soner and on success post
toast from soner and on success post success logged
success logged in on
in on error post error fail to log
in and let's go ahead and do the same thing inside of our use log out and we
thing inside of our use log out and we can remove this infer request type from
can remove this infer request type from here we are not using it so import toast
here we are not using it so import toast from soner on success toast success
from soner on success toast success logged
logged out on
out on error post error fail to log
error post error fail to log out and inside of the use register as
out and inside of the use register as well let's go ahead and let's import
well let's go ahead and let's import toast from soner and on success toast
toast from soner and on success toast success
success registered on
registered on error post error fail to
error post error fail to register there we go now let's go ahead
register there we go now let's go ahead and let's log
and let's log out and if you try and type in something
out and if you try and type in something that doesn't
that doesn't exist uh let's
exist uh let's see I should be getting back an error
see I should be getting back an error but perhaps so it says 500 internal
but perhaps so it says 500 internal server error and I think yes I think I'm
server error and I think yes I think I'm not handling this error properly here I
not handling this error properly here I think I have to do if not response. okay
think I have to do if not response. okay throw new error something went or failed
throw new error something went or failed to register so I'm doing this inside of
to register so I'm doing this inside of the use register let's copy this let's
the use register let's copy this let's go instead of use log out and let's do
go instead of use log out and let's do the same thing here we have to handle
the same thing here we have to handle errors so failed to log
errors so failed to log out let's go inside of use
login failed to log in let's also go inside of my workspace
in let's also go inside of my workspace API use create workspace and do the same
API use create workspace and do the same thing
thing here so this will be failed to create
here so this will be failed to create workspace like this and now we should be
workspace like this and now we should be getting proper
getting proper errors I think I have to
refresh there we go failed to log in great so if I go ahead and write my
great so if I go ahead and write my proper email
here and my proper password Here I get logged in and if I create new works
logged in and if I create new works space workspace created great so we just
space workspace created great so we just handled that what we have to do next is
handled that what we have to do next is we have to create another entity called
we have to create another entity called members because each workspace will have
members because each workspace will have a member and we also have to implement
a member and we also have to implement uploading images to the workspace and
uploading images to the workspace and then we're going to be able to easily
then we're going to be able to easily reuse this form inside of our models
reuse this form inside of our models great great
great great job now that we have finished our
job now that we have finished our workspace form let's go ahead and let's
workspace form let's go ahead and let's extended with one more important field
extended with one more important field and let's start the same way we started
and let's start the same way we started initially from the schema so we're going
initially from the schema so we're going to go inside of source we're going to go
to go inside of source we're going to go inside of our features inside of work
inside of our features inside of work spaces schemas
spaces schemas dots and inside of here alongside name
dots and inside of here alongside name we're going to add image
we're going to add image URL like this and image URL will be the
URL like this and image URL will be the following Z do
following Z do Union and open an array
Union and open an array inside it will either be an instance of
inside it will either be an instance of file or a string but we're going to add
file or a string but we're going to add a special transform method here to
a special transform method here to accept the value and if value is equal
accept the value and if value is equal and I actually should have used the term
and I actually should have used the term identical because this is equal this is
identical because this is equal this is identical to an empty string in that
identical to an empty string in that case we are going to consider this as
case we are going to consider this as undefined otherwise as the value itself
undefined otherwise as the value itself like this and let's also chain. optional
like this and let's also chain. optional here like this and now we have to
here like this and now we have to implement actual image upload inside of
implement actual image upload inside of our
our routes and just to clarify why am I
routes and just to clarify why am I doing a union here well when I was
doing a union here well when I was building this image URL one thing that
building this image URL one thing that was actually problematic was how to pass
was actually problematic was how to pass that through the API body form and we
that through the API body form and we need to use a specific form data in
need to use a specific form data in order to pass file
order to pass file uploads uh and form data will parse
uploads uh and form data will parse undefined strings um I'm sorry undefined
undefined strings um I'm sorry undefined Fields as strings so we need to
Fields as strings so we need to additionally tell our parser if you
additionally tell our parser if you accept an empty string you should
accept an empty string you should consider that as not past and you
consider that as not past and you shouldn't consider it as a field
shouldn't consider it as a field value you're going to kind of see what I
value you're going to kind of see what I mean once we actually add our form to
mean once we actually add our form to this and it also helps with uh the
this and it also helps with uh the proper types of this and perhaps we
proper types of this and perhaps we should not have named this image URL
should not have named this image URL but it depends because later when we
but it depends because later when we create our edit or update workspace
create our edit or update workspace schema we are going to be working with
schema we are going to be working with an actual
an actual URL so yeah maybe we can just name this
URL so yeah maybe we can just name this image so we're not exactly you know too
image so we're not exactly you know too specific so let's just go ahead and
specific so let's just go ahead and specify this as an image for now so
specify this as an image for now so rename it from image URL to image and
rename it from image URL to image and now let's go back inside of workspaces
now let's go back inside of workspaces server rou.ds right here and now you
server rou.ds right here and now you should be able to extract image from
should be able to extract image from here and we are going to have to change
here and we are going to have to change this to actually use the form but we're
this to actually use the form but we're going to do that in a moment for now
going to do that in a moment for now what we have to do is well actually
what we have to do is well actually upload the image so as you can see right
upload the image so as you can see right here image will potentially be a file
here image will potentially be a file and if it is we have to handle the
and if it is we have to handle the upload so let's go ahead and prepare our
upload so let's go ahead and prepare our app Right storage for that so go back to
app Right storage for that so go back to the console where you have your uh
the console where you have your uh Collections and your uh databases and
Collections and your uh databases and this time go to storage and go ahead and
this time go to storage and go ahead and create a bucket we're going to call this
create a bucket we're going to call this bucket images and click create and there
bucket images and click create and there we go you now have the ID here you can
we go you now have the ID here you can copy it from here or go into settings I
copy it from here or go into settings I believe and well I believe you can find
believe and well I believe you can find an image here somewhere but while we are
an image here somewhere but while we are in the settings here you can also go
in the settings here you can also go ahead and play around with the the uh
ahead and play around with the the uh configuration here such as maximum files
configuration here such as maximum files size or even simply allowed file
size or even simply allowed file extensions uh and this actually will
extensions uh and this actually will kind of be important because of this so
kind of be important because of this so we don't have images so let's just copy
we don't have images so let's just copy this bucket ID and let's quickly go back
this bucket ID and let's quickly go back inside of do environment. local here and
inside of do environment. local here and let's go ahead and add next public app
let's go ahead and add next public app right and we're going to call this
right and we're going to call this images ID and I just want to add a
images ID and I just want to add a bucket
bucket ID so it's clear distinction that this
ID so it's clear distinction that this is not you know an images collection
is not you know an images collection this is a bucket and then let's go ahead
this is a bucket and then let's go ahead and let's go inside of our config file
and let's go inside of our config file where we have exported the database and
where we have exported the database and workspace ID it's in the root of our
workspace ID it's in the root of our source folder and let's go ahead and
source folder and let's go ahead and Export con images bucket ID process
Export con images bucket ID process environment next public app right images
environment next public app right images bucket ID like this all right we have
bucket ID like this all right we have this ready and now we have to go back
this ready and now we have to go back inside of our databases inside of our
inside of our databases inside of our workspaces and we have to go inside of
workspaces and we have to go inside of the attributes and we have to add a new
the attributes and we have to add a new attribute which will be a string image
attribute which will be a string image URL like this and this is where our
URL like this and this is where our maximum allowed file size comes into
maximum allowed file size comes into play there are you know different ways
play there are you know different ways we can store this another way we can do
we can store this another way we can do this is by storing image key for example
this is by storing image key for example and then we would simply store the the
and then we would simply store the the ID of that uh object in the bucket that
ID of that uh object in the bucket that would mean that every time we load this
would mean that every time we load this workspace we would have to populate that
workspace we would have to populate that image now that is obviously you know the
image now that is obviously you know the preferred way of doing it that would be
preferred way of doing it that would be the correct way of doing it but since we
the correct way of doing it but since we are working with uh a type of image that
are working with uh a type of image that we know what it will be used for it will
we know what it will be used for it will be used for mostly static avatars and we
be used for mostly static avatars and we can safely assume that this will be very
can safely assume that this will be very small
small images in that case I say it's okay to
images in that case I say it's okay to use base 64 to store in a database so
use base 64 to store in a database so for that reason and for Simplicity sake
for that reason and for Simplicity sake we're going to have an image whoops uh a
we're going to have an image whoops uh a new string attribute called image URL
new string attribute called image URL and now inside of here we're going to go
and now inside of here we're going to go ahead and Define a size maximum possible
ahead and Define a size maximum possible size for that dat datase for that data
size for that dat datase for that data or base 64 URL now they can go to very
or base 64 URL now they can go to very very big lengths if you know a file is
very big lengths if you know a file is too big but since our files will barely
too big but since our files will barely go over a megabyte I used you know Chad
go over a megabyte I used you know Chad GPT to calculate me what should be
GPT to calculate me what should be appropriately the size for a base
appropriately the size for a base 64 and that number ended up being
64 and that number ended up being 1,400,000
like this and we're not going to make this
this and we're not going to make this required and it's not going to be on
required and it's not going to be on array and let's go ahead and click
array and let's go ahead and click create and you know just double check
create and you know just double check that you entered the correct number so
that you entered the correct number so 400,000 and a million 400,000 great and
400,000 and a million 400,000 great and let's go ahead and click create and make
let's go ahead and click create and make sure it doesn't say required because
sure it doesn't say required because image URL is optional and I would also
image URL is optional and I would also suggest that while you're here you also
suggest that while you're here you also go back to your storage and click on
go back to your storage and click on images bucket and click on settings and
images bucket and click on settings and let's just modify uh the maximum file
let's just modify uh the maximum file size here and the allowed
size here and the allowed extensions so I'm going to change the
extensions so I'm going to change the maximum file size to be 1 Megabyte here
maximum file size to be 1 Megabyte here really no need for a avatar image to go
really no need for a avatar image to go above that and the allowed extensions
above that and the allowed extensions will be jpeg PNG SVG and let's also add
will be jpeg PNG SVG and let's also add jpeg with an E here right because just
jpeg with an E here right because just adding this usually I've seen you know
adding this usually I've seen you know some some um buckets handle this as the
some some um buckets handle this as the same thing but you have to explicitly
same thing but you have to explicitly add jpeg here so just make sure you add
add jpeg here so just make sure you add this and I think this will cover you
this and I think this will cover you know the most popular ones and click
know the most popular ones and click update here like that and while we are
update here like that and while we are here let's also set up the proper
here let's also set up the proper permissions so we're going to go ahead
permissions so we're going to go ahead inside of permissions here let's click
inside of permissions here let's click plus and let's first select any so we're
plus and let's first select any so we're going to allow anyone to read them
going to allow anyone to read them and let's go ahead and select all users
and let's go ahead and select all users here and let's make sure that users can
here and let's make sure that users can create read update and delete them and
create read update and delete them and well you know what actually most of this
well you know what actually most of this will never be public facing so no need
will never be public facing so no need for any role at all we can just work
for any role at all we can just work with all users role and make sure they
with all users role and make sure they have all the options and click update
have all the options and click update there we go so I have set one megabyte
there we go so I have set one megabyte here for my maximum file size I have set
here for my maximum file size I have set jpeg PNG SVG and jpeg with an E here not
jpeg PNG SVG and jpeg with an E here not sure how to how to disting between these
sure how to how to disting between these two
two pronunciations uh and that's it so make
pronunciations uh and that's it so make sure you have saved that and now we can
sure you have saved that and now we can go back inside of the code and we can do
go back inside of the code and we can do the actual upload so we're going to go
the actual upload so we're going to go back inside of features workspace server
back inside of features workspace server route. DS and now that we have the image
route. DS and now that we have the image here let's prepare a constant called
here let's prepare a constant called uploaded image URL and let's make that
uploaded image URL and let's make that either a string or
either a string or undefined and now if our image is
undefined and now if our image is instance of file in that case we're
instance of file in that case we're going to go ahead and handle the upload
going to go ahead and handle the upload so const file will
be a type of well not a type of but we now have to create the file using
now have to create the file using storage so let's prepare that con
storage so let's prepare that con storage comes from cat storage so
storage comes from cat storage so remember we have that instead of our
remember we have that instead of our session middleware we set up the storage
session middleware we set up the storage here so just make sure you do that as
here so just make sure you do that as well now that we have the storage we can
well now that we have the storage we can go ahead and do const file to be await
go ahead and do const file to be await storage create file like this let's add
storage create file like this let's add our images bucket ID from our config
our images bucket ID from our config file so just make sure you have added
file so just make sure you have added this
this import after that let's go ahead and
import after that let's go ahead and give it an id. unique we have the ID
give it an id. unique we have the ID from node app right and lastly we are
from node app right and lastly we are passing the actual image we plan to
passing the actual image we plan to upload like this so just double check
upload like this so just double check you have ID from node app right here and
you have ID from node app right here and now that you have the file this by
now that you have the file this by itself will not give you any preview URL
itself will not give you any preview URL we have to create one now if you're
we have to create one now if you're using if you ever used app right before
using if you ever used app right before you might have explored file upload
you might have explored file upload already and you might know that there is
already and you might know that there is uh a method which usually generates a
uh a method which usually generates a URL but when working with node app right
URL but when working with node app right the server SDK it works a little bit
the server SDK it works a little bit differently and we don't have that
differently and we don't have that option so we need to create an array
option so we need to create an array buffer and transform this to base 64 so
buffer and transform this to base 64 so let's create an array buffer here using
let's create an array buffer here using await
await storage get file preview pass in the
storage get file preview pass in the images bucket ID and file. ID with the
images bucket ID and file. ID with the the dollar sign here so now when I hover
the dollar sign here so now when I hover over this you can see it's a type of
over this you can see it's a type of array buffer this same method in the
array buffer this same method in the client SDK which is usually called just
client SDK which is usually called just app right usually Returns the direct URL
app right usually Returns the direct URL but the server one works differently so
but the server one works differently so now we have the array buffer double
now we have the array buffer double check that this is your type here uh in
check that this is your type here uh in case you know in the future get file
case you know in the future get file preview gets changed to return a promise
preview gets changed to return a promise and the string perhaps you already have
and the string perhaps you already have the URL so you know check that but you
the URL so you know check that but you will most likely have the array buffer
will most likely have the array buffer here as I do and now we have to assign
here as I do and now we have to assign the generated base 64 URL to our const
the generated base 64 URL to our const here so let's go ahead and open backck
here so let's go ahead and open backck and we have to go ahead and add data
and we have to go ahead and add data column image/png
column image/png semicolon base
semicolon base 64 comma and then open the template
64 comma and then open the template literals here
literals here buffer. from and go ahead and pass the
buffer. from and go ahead and pass the array buffer which we have above here
array buffer which we have above here and then chain to string base
and then chain to string base 64 like this and and now you have the
64 like this and and now you have the uploaded image URL and then you can go
uploaded image URL and then you can go ahead inside of here and add image URL
ahead inside of here and add image URL to be uploaded image URL and this size
to be uploaded image URL and this size of the buffer will vary you know from
of the buffer will vary you know from the from the size of the file which we
the from the size of the file which we just passed right and since we have
just passed right and since we have limited the size inside of our storage
limited the size inside of our storage we ensure that it most likely will never
we ensure that it most likely will never go above that if it does it will simply
go above that if it does it will simply throw an error to the user but since we
throw an error to the user but since we made it not required it won't really
made it not required it won't really break any
break any workflows uh great so we have this ready
workflows uh great so we have this ready but we are not you know finished just
but we are not you know finished just yet what we have to do next is we have
yet what we have to do next is we have to create an interface for this on the
to create an interface for this on the actual frontend side so let's go ahead
actual frontend side so let's go ahead and do that let's head inside of source
and do that let's head inside of source features workspaces components create
features workspaces components create workspace form right here
let's start by importing use ref from react here and we're going to call use
react here and we're going to call use ref by attaching it on an input field
ref by attaching it on an input field which is going to open our file explorer
which is going to open our file explorer but we're not going to you know
but we're not going to you know literally click on that input we're just
literally click on that input we're just going to use it so let's go ahead and
going to use it so let's go ahead and assign that here so const input ref will
assign that here so const input ref will be use ref with the default value of
be use ref with the default value of null but let's go ahead and give it HTML
null but let's go ahead and give it HTML input element
input element inside and now we're going to go ahead
inside and now we're going to go ahead and create another form field below this
and create another form field below this one let's go ahead and give it control
one let's go ahead and give it control form. control and the name will be image
form. control and the name will be image so that's what is going to be resolved
so that's what is going to be resolved using this form field let's go ahead and
using this form field let's go ahead and destructure the Field property
inside and now we're going to go ahead and create our form item inside of here
and create our form item inside of here uh and then well actually we don't have
uh and then well actually we don't have to open up form item right
to open up form item right now instead actually we won't have to uh
now instead actually we won't have to uh we won't have to you know follow the
we won't have to you know follow the usual semantics as we do because this is
usual semantics as we do because this is a very you know customade input that I
a very you know customade input that I will do now since it won't really follow
will do now since it won't really follow it won't use any of the normal HTML you
it won't use any of the normal HTML you know input Fields so let's scratch this
know input Fields so let's scratch this and let's just return a normal div
and we're going to give this div a class name Flex Flex column and GAP y off two
name Flex Flex column and GAP y off two inside I'm going to go ahead and create
inside I'm going to go ahead and create another div with a class name Flex item
another div with a class name Flex item Center and GAP X of five and now
Center and GAP X of five and now depending on whether we have uploaded an
depending on whether we have uploaded an image or
image or not meaning do we have field do value if
not meaning do we have field do value if we have field do value we're going to go
we have field do value we're going to go ahead and render that image here
ahead and render that image here otherwise we're going to go ahead and
otherwise we're going to go ahead and render an
render an avatar so let's import the things we
avatar so let's import the things we need first of all we need to import
need first of all we need to import image from next image and then we need
image from next image and then we need to import avatar from components UI
to import avatar from components UI Avatar and we need Avatar fallback and
Avatar and we need Avatar fallback and Avatar
Avatar image like that now let's go all the way
image like that now let's go all the way down down here and let's go ahead and
down down here and let's go ahead and give this image some properties here so
give this image some properties here so Source will check if fi. value is an
Source will check if fi. value is an instance of
file with lowercase o in that case uh instance of
o in that case uh instance of file in that case we're going to go
file in that case we're going to go ahead and do url. createobject URL
ahead and do url. createobject URL field. value
otherwise you can just use the field. value and let's fix the instance off
value and let's fix the instance off here there we go and let's see URL uh
here there we go and let's see URL uh create object url url like this so this
create object url url like this so this part needs to be Capital great so if we
part needs to be Capital great so if we receive field. value as a file which
receive field. value as a file which will happen if we are on this form for
will happen if we are on this form for the first time and we click you know on
the first time and we click you know on this uh uh add a file and we literally
this uh uh add a file and we literally select one from the file explorer at
select one from the file explorer at that point fill. value will be an
that point fill. value will be an instance of file and in order to display
instance of file and in order to display that to the user we have to convert it
that to the user we have to convert it to an object URL in an odd chance that
to an object URL in an odd chance that we actually receive uh the full URL in
we actually receive uh the full URL in field. value well then we can just use
field. value well then we can just use that and this will happen in the edit
that and this will happen in the edit form so this actually makes more sense
form so this actually makes more sense in the edit form perhaps we don't even
in the edit form perhaps we don't even have to check for this but since with
have to check for this but since with the find that field. value can be a
the find that field. value can be a string or file uh I think type safety
string or file uh I think type safety will definitely complain if I just
will definitely complain if I just assign field. value here as you can see
assign field. value here as you can see so let's go ahead and do this instead
so let's go ahead and do this instead let's go ahead and give it an ALT of
let's go ahead and give it an ALT of logo like that let's give it a fill and
logo like that let's give it a fill and a class name of object
a class name of object cover great so that's if we added an
cover great so that's if we added an image and if we did not add an image
image and if we did not add an image let's go ahead and give this a class
let's go ahead and give this a class name of size 72
name of size 72 pixels and let's go ahead and add an
pixels and let's go ahead and add an Avatar full back here and let's add an
Avatar full back here and let's add an image icon here from Lucid react so make
image icon here from Lucid react so make sure you have imported image uh icon
sure you have imported image uh icon from Lucid react
from Lucid react here and I'm going to remove the Avatar
here and I'm going to remove the Avatar image from components UI Avatar since we
image from components UI Avatar since we are not going to be using it let's give
are not going to be using it let's give the uh the image icon a class name of
the uh the image icon a class name of size 36 pixels
size 36 pixels and text neutral 400 like this and then
and text neutral 400 like this and then let's go ahead and give this div which
let's go ahead and give this div which encapsulates our image here which will
encapsulates our image here which will be you know the attached image from the
be you know the attached image from the file explorer a size of 72 pixels as
file explorer a size of 72 pixels as well
well relative rounded medium and overflow
relative rounded medium and overflow hidden just like that great and this
hidden just like that great and this relative is very important otherwise the
relative is very important otherwise the fill will not work properly for this
fill will not work properly for this this next image here great and now that
this next image here great and now that we have that I think we can already
we have that I think we can already revisit our Local Host here and let's
revisit our Local Host here and let's refresh and there we go so I'm going to
refresh and there we go so I'm going to refresh once again and you can see I
refresh once again and you can see I have a placeholder of my image icon here
have a placeholder of my image icon here meaning I don't have any image set at
meaning I don't have any image set at the moment here so let's go ahead now
the moment here so let's go ahead now and Below all of this let's add a new
and Below all of this let's add a new div with a class
div with a class name flex and flex column and let's add
name flex and flex column and let's add a paragraph work space
a paragraph work space icon let's give this a class name of
icon let's give this a class name of text small let's copy and paste this and
text small let's copy and paste this and this one will also have a text muted
this one will also have a text muted foreground as well and it will simply
foreground as well and it will simply say
say either jpeg BNG
either jpeg BNG SVG or
SVG or JPEG and Max 1
JPEG and Max 1 Megabyte now let's also go ahead and add
Megabyte now let's also go ahead and add that hidden input so a native input
that hidden input so a native input here let's give it a class name of w fit
here let's give it a class name of w fit my no hidden and let's go ahead and give
my no hidden and let's go ahead and give it an accept here of jpeg pg.
JPEG and SVG let's give it a type of
SVG let's give it a type of file and and let's go ahead and give it
file and and let's go ahead and give it a ref of input ref and let's give it a
a ref of input ref and let's give it a disabled of is
pending and now we also have to create an onchange method here so let's give it
an onchange method here so let's give it a unchange to be handle image
a unchange to be handle image change so we're going to go ahead and do
change so we're going to go ahead and do the following we're going to create a
the following we're going to create a new method here con handle image change
new method here con handle image change we're going to have an event which is a
we're going to have an event which is a typee of react change event HTML input
typee of react change event HTML input element and then we're going to get the
element and then we're going to get the file from event. target. files and then
file from event. target. files and then we're going to optionally get the first
we're going to optionally get the first in the array and if we manage to get
in the array and if we manage to get that file we're going to use form set
that file we're going to use form set value image
value image file just like that there we go and I
file just like that there we go and I think that already this should be
think that already this should be working let's go ahead and oh well we we
working let's go ahead and oh well we we didn't actually add a button which will
didn't actually add a button which will call this hidden input here so let's
call this hidden input here so let's just quickly uh go do that as well so
just quickly uh go do that as well so just below this input here we're going
just below this input here we're going to have a
to have a button which will say upload
button which will say upload image this button here will have a type
image this button here will have a type of button which is important it will
of button which is important it will have a disabled if is pending it will
have a disabled if is pending it will have a variant of territory it will have
have a variant of territory it will have a size of extra small a class name of w
a size of extra small a class name of w fit and margin top of two and on click
fit and margin top of two and on click is going to be interesting so on click
is going to be interesting so on click we're going to call the input ref.
we're going to call the input ref. current question mark click like this so
current question mark click like this so that's how we're going to trigger that
that's how we're going to trigger that hidden input so now I'm going to go
hidden input so now I'm going to go ahead and select an image here
ahead and select an image here and there we go you can now see my image
and there we go you can now see my image was selected right here excellent so
was selected right here excellent so just you know double check that you
just you know double check that you didn't accidentally uh write the
didn't accidentally uh write the incorrect accept here if you want to you
incorrect accept here if you want to you know you can remove the entire accept
know you can remove the entire accept because we do have validation on back
because we do have validation on back end which will throw errors but you know
end which will throw errors but you know for user experience it's also a good
for user experience it's also a good practice to Simply allow specific images
practice to Simply allow specific images as well so this query right here is is
as well so this query right here is is working for me to add the images I want
working for me to add the images I want to add great and what we have to do now
to add great and what we have to do now is well handle the upload so let's see
is well handle the upload so let's see if that will be working or not and I'll
if that will be working or not and I'll give you a little hint it's not going to
give you a little hint it's not going to work so if I now go right here and write
work so if I now go right here and write test and click create workspace there we
test and click create workspace there we go we get 400 here and here's an
go we get 400 here and here's an interesting thing our image is just a
interesting thing our image is just a plain object that's because we cannot
plain object that's because we cannot send an image through Json we have to
send an image through Json we have to use form data so that's the last thing
use form data so that's the last thing we have to do to enable file upload from
we have to do to enable file upload from this form so let's go ahead back inside
this form so let's go ahead back inside of our route for workspaces our post
of our route for workspaces our post method here and we are simply going to
method here and we are simply going to change our Z validator here to not
change our Z validator here to not validate Json but instead to validate
validate Json but instead to validate the form and then valid form here then
the form and then valid form here then we also have to revisit our workspaces
we also have to revisit our workspaces API using create workspace and instead
API using create workspace and instead of sending Json we will now be sending
of sending Json we will now be sending form and that should get rid of all the
form and that should get rid of all the errors inside of here and now we have to
errors inside of here and now we have to go back inside of the create workspace
go back inside of the create workspace form specifically in the onsubmit method
form specifically in the onsubmit method and we just have to do some
and we just have to do some modifications here so let's go ahead and
modifications here so let's go ahead and Define a constant final values to be a
Define a constant final values to be a spread of existing values and then get
spread of existing values and then get the image if values. image exists and it
the image if values. image exists and it is an instance of file in that case
is an instance of file in that case we're going to submit that otherwise
we're going to submit that otherwise we're just going to send an empty string
we're just going to send an empty string and this will be okay for our uh Zod
and this will be okay for our uh Zod schema here so if I send final values
schema here so if I send final values you can see no errors at all because of
you can see no errors at all because of this but we know that our create
this but we know that our create workspace schema will treat an empty
workspace schema will treat an empty string as undefined so that's why we
string as undefined so that's why we have to add that and I think that now
have to add that and I think that now already this should work just fine I
already this should work just fine I will add test let's
will add test let's try and there we go workspace created
try and there we go workspace created let's go ahead and revisit my bucket
let's go ahead and revisit my bucket here I'm going to refresh my images and
here I'm going to refresh my images and there we go you can see my image from
there we go you can see my image from unsplash right here 10 kilobytes and it
unsplash right here 10 kilobytes and it is a JPEG image there we go and if I go
is a JPEG image there we go and if I go inside of my
inside of my databases inside of workspaces I should
databases inside of workspaces I should find I think this is the newest one and
find I think this is the newest one and you can see the image URL and there we
you can see the image URL and there we go you can see the exact uh where is my
go you can see the exact uh where is my data image and I'm going to go ahead and
data image and I'm going to go ahead and paste that here and there we go so the
paste that here and there we go so the actual uploaded image is definitely here
actual uploaded image is definitely here and it fit in our F field with a,
and it fit in our F field with a, 400,000 values uh great that works just
400,000 values uh great that works just fine what I want to do to wrap this up
fine what I want to do to wrap this up is just add an on success field here
and let's do form reset so if we create a new form like
reset so if we create a new form like this I just want to clear the entire
this I just want to clear the entire thing the next time there we go that's
thing the next time there we go that's it and I'm going to go ahead and just
it and I'm going to go ahead and just add to do
add to do redirect to new
redirect to new workspace once we have that available as
workspace once we have that available as well great you've just implemented file
well great you've just implemented file upload in your create a new workspace
upload in your create a new workspace form uh and now now what we're going to
form uh and now now what we're going to be able to do is actually use the
be able to do is actually use the created workspaces to create our
created workspaces to create our workspace switcher component to modify
workspace switcher component to modify you know how some specific logic is
you know how some specific logic is going to work and we're going to go
going to work and we're going to go ahead and find a way to move this form
ahead and find a way to move this form to its proper place and also to enable
to its proper place and also to enable it in models and then we're going to
it in models and then we're going to create an almost exact form for our
create an almost exact form for our projects and then we can create our
projects and then we can create our first tasks great great
first tasks great great job now let's go ahead and let's create
job now let's go ahead and let's create the get workspaces endpoint so we can
the get workspaces endpoint so we can fetch all those new workspaces which we
fetch all those new workspaces which we just created so let's go inside of
just created so let's go inside of source features workspaces server route.
source features workspaces server route. DS and I'm going to go ahead and add a
DS and I'm going to go ahead and add a as a very first route here above the
as a very first route here above the post a get route and right now I'm only
post a get route and right now I'm only going to add the session middleware here
going to add the session middleware here and an asynchronous controller just like
and an asynchronous controller just like this and inside of here I'm just going
this and inside of here I'm just going to get my
to get my databases from C.G get
databases from C.G get databases and then I'm very simply going
databases and then I'm very simply going to go ahead and do const
to go ahead and do const workspaces to be await
databases list documents database ID which I think we
documents database ID which I think we already have imported and then workspace
already have imported and then workspace workspaces ID just like like
workspaces ID just like like this and then we're just going to return
this and then we're just going to return c. Json data
c. Json data workspaces let me scroll for you so you
workspaces let me scroll for you so you can see this is the entire controller
can see this is the entire controller for
for now and now let's go ahead ins set of
now and now let's go ahead ins set of API and let's do use getet
API and let's do use getet workspaces do DS and let's copy from out
workspaces do DS and let's copy from out API we have used current so let's just
API we have used current so let's just copy you know the contents in side and
copy you know the contents in side and paste them instead of the new use get
paste them instead of the new use get workspaces I'm going to rename this to
workspaces I'm going to rename this to use get workspaces and I will refetch
use get workspaces and I will refetch this query key to be
workspaces and then I'm going to change this to Simply Be API workspaces
this to Simply Be API workspaces doget and inside of here I'm going to go
doget and inside of here I'm going to go ahead and remove this but perhaps we can
ahead and remove this but perhaps we can just go ahead well we can return null as
just go ahead well we can return null as well here
well here or maybe we can just do this let's see
or maybe we can just do this let's see um just a second let's see what's the
um just a second let's see what's the best way of handling
best way of handling this well we can throw an error in this
this well we can throw an error in this case so throw new error failed to fetch
case so throw new error failed to fetch workspaces and then we can safely return
workspaces and then we can safely return data afterwards great now that we have
data afterwards great now that we have used get workspaces let's do the
used get workspaces let's do the following let's go ahead and simply try
following let's go ahead and simply try this out uh inside inside of our sidebar
this out uh inside inside of our sidebar here so I'm going to go inside of my
here so I'm going to go inside of my components and I'm going to go inside of
components and I'm going to go inside of my side bar right here and what I'm
my side bar right here and what I'm going to do is I'm going to create a new
going to do is I'm going to create a new component called workspace switcher so
component called workspace switcher so I'm going to copy this doted separator
I'm going to copy this doted separator and I'm going to add a workspace
and I'm going to add a workspace switcher component like this and then
switcher component like this and then I'm going to go ahead inside of my
I'm going to go ahead inside of my components and create the new workspace
components and create the new workspace Das
Das switcher do
switcher do DSX and I'm going to mark this entire
DSX and I'm going to mark this entire thing as use client and I will do export
thing as use client and I will do export con workspace switcher here and return a
con workspace switcher here and return a div workspace
div workspace switcher let's quickly go back inside of
switcher let's quickly go back inside of the sidebar component and let's import
the sidebar component and let's import the workspace switcher from workspace
the workspace switcher from workspace switcher and now in your workspace you
switcher and now in your workspace you should see a place which says workspace
should see a place which says workspace Switcher and what we're going to do now
Switcher and what we're going to do now since we marked this as use client is go
since we marked this as use client is go ahead and fetch use get
ahead and fetch use get workspaces and we're going to
workspaces and we're going to destructure the data here and what I'm
destructure the data here and what I'm going to do is very simply Json
going to do is very simply Json stringify the data like this and there
stringify the data like this and there we go a bunch of documents appearing
we go a bunch of documents appearing right here and if you want to you can
right here and if you want to you can also do this data. total and this will
also do this data. total and this will tell you the number and let's go ahead
tell you the number and let's go ahead and put an exclamation point question
and put an exclamation point question mark here there we go so you can do this
mark here there we go so you can do this so you can it will just tell you the
so you can it will just tell you the number of workspaces that currently
number of workspaces that currently exist and let's try something out so
exist and let's try something out so remember in our use create workspace
remember in our use create workspace hook I told you to add the query client
hook I told you to add the query client invalidate queries with a query key of
invalidate queries with a query key of workspaces at the time this query key
workspaces at the time this query key did not exist but now inside of our used
did not exist but now inside of our used get workspaces it exists right here so
get workspaces it exists right here so technically if everything is set up
technically if everything is set up correctly right now it says six but if I
correctly right now it says six but if I go ahead and write test one to three and
go ahead and write test one to three and create a new workspace without
create a new workspace without refreshing now it says seven exactly
refreshing now it says seven exactly what we wanted great so we now have that
what we wanted great so we now have that inside of our workspace switcher so
inside of our workspace switcher so let's go ahead and we'll develop this
let's go ahead and we'll develop this further I'm going to go ahead and give
further I'm going to go ahead and give this div a class name of flex Flex
this div a class name of flex Flex column Gap y of two like that I'm going
column Gap y of two like that I'm going to remove everything inside for now and
to remove everything inside for now and I will add a new div here with a class
I will add a new div here with a class named Flex items Center and justify
named Flex items Center and justify between and this will be position for
between and this will be position for our label workspaces and on the other
our label workspaces and on the other side we're going to have an
side we're going to have an R add Circle fill icon and you can
R add Circle fill icon and you can import this
import this icon from react icons are R I like this
icon from react icons are R I like this now let's go ahead and style this two so
now let's go ahead and style this two so I'm going to give this paragraph a class
I'm going to give this paragraph a class name of text extra small
name of text extra small uppercase text
uppercase text neutral 500 like this and I'm going to
neutral 500 like this and I'm going to give the r i Circle fill a class name of
give the r i Circle fill a class name of size five text
size five text neutral 400 my apologies let's add 500
neutral 400 my apologies let's add 500 here cursor pointer hover
here cursor pointer hover opacity 75 and
opacity 75 and transition and I think that already we
transition and I think that already we should have this visible on our sidebar
should have this visible on our sidebar here there we go it says workspace
here there we go it says workspace workspaces right here and we have a
workspaces right here and we have a little plus icon here so now let's go
little plus icon here so now let's go ahead and just change this uh just a bit
ahead and just change this uh just a bit I kind of don't like how this uh looks
I kind of don't like how this uh looks at the moment perhaps I to add a bold
at the moment perhaps I to add a bold text here um but let's leave it like
text here um but let's leave it like this for now we're going to you know
this for now we're going to you know come back to this a bit later so let's
come back to this a bit later so let's go ahead now and go outside of this div
go ahead now and go outside of this div and let's import everything we need from
and let's import everything we need from our select component so select select
our select component so select select content select item trigger and value
content select item trigger and value from add components UI select we've
from add components UI select we've added this component in the beginning
added this component in the beginning from shaten UI quick reminder you can
from shaten UI quick reminder you can use bonex chaty in I think the version
use bonex chaty in I think the version is 2 I don't know
is 2 I don't know 1.0 add select right so that's how you
1.0 add select right so that's how you would do that just you know go back and
would do that just you know go back and at least look at uh what's the latest
at least look at uh what's the latest version so let's see it's 2.1.0 okay so
version so let's see it's 2.1.0 okay so that's how you add components just a
that's how you add components just a quick reminder even though you should
quick reminder even though you should definitely have this right and then
definitely have this right and then let's go ahead and add the select here
let's go ahead and add the select here and let's go ahead and add a select
trigger let's go ahead and give the select trigger a class name of full
select trigger a class name of full width background neutral 200 font medium
width background neutral 200 font medium and padding one and inside of the select
and padding one and inside of the select trigger we're going to add a select
trigger we're going to add a select value here with a place holder of no
value here with a place holder of no workspace selected
workspace selected and then we're going to go ahead and
and then we're going to go ahead and create a select content here inside of
create a select content here inside of the select content we're going to query
the select content we're going to query over our data right here which we can
over our data right here which we can remap the workspaces for clarity so
remap the workspaces for clarity so let's add
let's add workspaces question mark documents. map
workspaces question mark documents. map so we are not directly working you know
so we are not directly working you know with data we have to access it through
with data we have to access it through documents because that's what app uh
documents because that's what app uh list documents returns so this list
list documents returns so this list documents here as you can see Returns
documents here as you can see Returns the model document list which consists
the model document list which consists of documents which is an array of items
of documents which is an array of items but also stuff like total which will
but also stuff like total which will tell you how many total uh items are
tell you how many total uh items are there so let's get this individual
there so let's get this individual workspace here and let's return a select
workspace here and let's return a select item
item inside and inside of here we can simply
inside and inside of here we can simply add a workspace do name and let's give
add a workspace do name and let's give the select item a key of workspace
the select item a key of workspace ID individual workspace ID and oops this
ID individual workspace ID and oops this should be
should be workspace workspace like this so
workspace workspace like this so workspace and workspace and let's also
workspace and workspace and let's also give it a value of the same workspace do
give it a value of the same workspace do ID with a dollar sign like that so I
ID with a dollar sign like that so I think that now we should be able to
think that now we should be able to click here and I should be able to look
click here and I should be able to look at all the available workspaces to click
at all the available workspaces to click on right right here great so now I want
on right right here great so now I want to make this a bit prettier and I want
to make this a bit prettier and I want to display avatars of each of those
to display avatars of each of those workspaces let's go inside of our
workspaces let's go inside of our workspaces features inside of components
workspaces features inside of components and let's create a new file called
and let's create a new file called workspace
workspace avatar.
avatar. DSX let's create an interface workspace
DSX let's create an interface workspace Avatar props and inside let's add an
Avatar props and inside let's add an image to be an optional string a name to
image to be an optional string a name to be a string and class name to be an
be a string and class name to be an optional string as well while we are
optional string as well while we are here let's go ahead and import a couple
here let's go ahead and import a couple of packages starting with CN
of packages starting with CN utils image from next image and Avatar
utils image from next image and Avatar and Avatar
and Avatar fullback from at SL components UI Avatar
then let's go ahead and Export const workspace
Avatar inside of here let's go ahead and let's D structure the image name and the
let's D structure the image name and the class name and let's assign them to be
class name and let's assign them to be infered inferred from the workspace
infered inferred from the workspace Avatar props and then inside of here if
Avatar props and then inside of here if we have an image we're going to Simply
we have an image we're going to Simply return a div with an image which uses
return a div with an image which uses that image as a source
we're going to give this image an ALT of name of the
name of the workspace Bill property and class name
workspace Bill property and class name object cover we're going to give
object cover we're going to give the wrapping div of that image element a
the wrapping div of that image element a class name which will be used inside of
class name which will be used inside of CN U so the default classes will be size
CN U so the default classes will be size 10 relative rounded medium
10 relative rounded medium overflow
overflow hidden and then we're going to pass
hidden and then we're going to pass class name as an optional second
class name as an optional second argument if the user wants to extend
argument if the user wants to extend this look otherwise if we don't have
this look otherwise if we don't have this we're going to go ahead and add an
this we're going to go ahead and add an avatar component with an avatar fallback
avatar component with an avatar fallback and in case you're wondering why just
and in case you're wondering why just not add Avatar image here we have that
not add Avatar image here we have that as well and it accepts The Source
as well and it accepts The Source because inside of Select component which
because inside of Select component which we are using right here here it causes a
we are using right here here it causes a very bad flickering effect it just it's
very bad flickering effect it just it's too noticeable for me to left it inside
too noticeable for me to left it inside so that's why I chose uh that's why I
so that's why I chose uh that's why I opted for this option so let's go ahead
opted for this option so let's go ahead and go inside of the Avatar fullback
and go inside of the Avatar fullback it's not going to be a self closing tag
it's not going to be a self closing tag so Avatar fullback and inside simply
so Avatar fullback and inside simply render the first letter of the name and
render the first letter of the name and let's go ahead and give this a class
let's go ahead and give this a class name of text white background blue
name of text white background blue 600 font semi bold text large and
uppercase and then let's go ahead and give our avator here a class
give our avator here a class name
name CN size 10 and class
name there we go and now let's go ahead back inside of our workspace switcher
back inside of our workspace switcher and now let's utilize our new workspace
and now let's utilize our new workspace Avatar component so instead of our
Avatar component so instead of our select item we're going to go ahead and
select item we're going to go ahead and create a diff with a class name Flex
create a diff with a class name Flex justify
justify start items Center Gap three and font
start items Center Gap three and font medium inside of that div we're going to
medium inside of that div we're going to add our workspace Avatar component which
add our workspace Avatar component which you can import from features workspaces
you can import from features workspaces components workspace
components workspace Avatar and we're going to go ahead and
Avatar and we're going to go ahead and give the workspace Avatar here a name of
give the workspace Avatar here a name of workspace do name we're going to give it
workspace do name we're going to give it an image of workspace image
URL and here's like an important uh thing to tell you so yeah this workspace
thing to tell you so yeah this workspace which we have here instead of these
which we have here instead of these workspaces you can see that one problem
workspaces you can see that one problem with it is that it doesn't have you can
with it is that it doesn't have you can see that it has this property X is a
see that it has this property X is a string and a type of any
string and a type of any basically what they've done is alongside
basically what they've done is alongside the basic elements which app documents
the basic elements which app documents need to have they also allowed for any
need to have they also allowed for any custom field because it's not strictly
custom field because it's not strictly typed so you have to be a bit careful
typed so you have to be a bit careful with this make sure you don't misspell
with this make sure you don't misspell name and make sure you don't misspell
name and make sure you don't misspell image URL but there is a fix we can do
image URL but there is a fix we can do to improve type safety on that end and
to improve type safety on that end and make sure when using IDs you're using
make sure when using IDs you're using ones with the dollar
ones with the dollar sign great so we now have the workspace
sign great so we now have the workspace Avatar here set up up and below that
Avatar here set up up and below that let's add a span element workspace do
let's add a span element workspace do name and let's go ahead and give the
name and let's go ahead and give the span a class name of truncate in case
span a class name of truncate in case it's too long and there we go you can
it's too long and there we go you can already see how better this looks right
already see how better this looks right now we can now handle our uh workspace
now we can now handle our uh workspace switcher components in a nicer way the
switcher components in a nicer way the only problem is some are rounded some
only problem is some are rounded some are not rounded so I'm going to go ahead
are not rounded so I'm going to go ahead inside of my workspace Avatar and and
inside of my workspace Avatar and and I'm going to go ahead and give the
I'm going to go ahead and give the Avatar here a rounded MD and looks like
Avatar here a rounded MD and looks like that doesn't fix it so let's also add it
that doesn't fix it so let's also add it to the Avatar fullback here and there we
to the Avatar fullback here and there we go great so we have that what I want to
go great so we have that what I want to resolve now is this outline which
resolve now is this outline which happens inside of this select component
happens inside of this select component so let's go inside of workspace switcher
so let's go inside of workspace switcher here and let's go inside of the select
here and let's go inside of the select component here and let's go ahead and
component here and let's go ahead and explore which one causes that outline
explore which one causes that outline right here so we have the content I
right here so we have the content I don't think it's that one uh I think it
don't think it's that one uh I think it might be the trigger component right
might be the trigger component right here I'm going to go ahead and
here I'm going to go ahead and explore so let's try by giving this
explore so let's try by giving this select trigger a focus outline none
select trigger a focus outline none let's see will that do something
let's see will that do something and oh do we already have that property
and oh do we already have that property okay so we already have this property so
okay so we already have this property so okay no need to write it twice but one
okay no need to write it twice but one of these causes this
of these causes this really
really ugly uh outline here it might be let's
ugly uh outline here it might be let's see is it ring perhaps so what if I say
see is it ring perhaps so what if I say offset transparent
offset transparent here it still appears right here uh ring
here it still appears right here uh ring one ring
one ring transparent maybe this one there we go
transparent maybe this one there we go so change the focus ring to transparent
so change the focus ring to transparent let me just find where it is focus ring
let me just find where it is focus ring and change it to transparent if you want
and change it to transparent if you want to and then that will as you can see
to and then that will as you can see remove that uh I have to call it ugly
remove that uh I have to call it ugly outline uh great we now have an amazing
outline uh great we now have an amazing workpace switcher here and what we have
workpace switcher here and what we have to do next is fix a little problem that
to do next is fix a little problem that we have and that problem is the
we have and that problem is the following so right now I have this user
following so right now I have this user right which is my Antonio user and in
right which is my Antonio user and in here I can see all of this amazing
here I can see all of this amazing workspaces but here's the problem if I
workspaces but here's the problem if I log out and if I go ahead and create a
log out and if I go ahead and create a new account here John and John
new account here John and John mail.com and john2 3 4 5 6
78 and log in I can see the exact same workspaces well
I can see the exact same workspaces well that's not exactly ideal isn't it so
that's not exactly ideal isn't it so what we have to do in the next chapter
what we have to do in the next chapter is implement the members functionality
is implement the members functionality so we're going to have to invent
so we're going to have to invent something called an invite code for each
something called an invite code for each of our workspace and we're going to have
of our workspace and we're going to have to create a members collection so that
to create a members collection so that we only load workspaces that we are a
we only load workspaces that we are a member of great great
member of great great job now let's go ahead and let's create
job now let's go ahead and let's create the members collection inside of
the members collection inside of app so I'm going to go back inside of my
app so I'm going to go back inside of my database here so outside of my
database here so outside of my workspaces collection right here where
workspaces collection right here where we have database ID and in here I have
we have database ID and in here I have my workspaces collection so I'm going to
my workspaces collection so I'm going to find the button create collection and
find the button create collection and I'm going to call this members I'm going
I'm going to call this members I'm going to go ahead and create this and I'm
to go ahead and create this and I'm going to give it some attributes the
going to give it some attributes the first attribute is going to be the user
first attribute is going to be the user ID which will have a size of 50 and it's
ID which will have a size of 50 and it's going to be
going to be required the second attribute is going
required the second attribute is going to be workspace ID which will also have
to be workspace ID which will also have a size of 50 and it will also be
a size of 50 and it will also be required there we go now let's go inside
required there we go now let's go inside of settings here let's go inside of
of settings here let's go inside of permissions all users create read update
permissions all users create read update and delete and save that great now let's
and delete and save that great now let's go ahead and let's go back inside of our
go ahead and let's go back inside of our app here my apologies let's first copy
app here my apologies let's first copy the new collection ID for our members so
the new collection ID for our members so you can copy it from here you can go
you can copy it from here you can go inside of settings and I'm pretty sure
inside of settings and I'm pretty sure you can find it somewhere else but just
you can find it somewhere else but just copy it here for now go inside of your
copy it here for now go inside of your environment.
environment. loc and go ahead and create an
loc and go ahead and create an environment key similar to workspaces ID
environment key similar to workspaces ID but this will be members ID and paste it
but this will be members ID and paste it here and then go inside of your config
here and then go inside of your config and just as you've added workspaces ID
and just as you've added workspaces ID we now have the members ID and I'm going
we now have the members ID and I'm going to go ahead and paste my next public app
to go ahead and paste my next public app right members ID just like that and now
right members ID just like that and now we have to go ahead and do the following
we have to go ahead and do the following so every time that we create a new
so every time that we create a new workspace we're going to go ahead and
workspace we're going to go ahead and create a new member as well and I forgot
create a new member as well and I forgot one thing so instead of the members
one thing so instead of the members collection go inside of attributes and
collection go inside of attributes and go ahead and create a new
go ahead and create a new attribute which will be a type of enum
attribute which will be a type of enum right here let's go ahead and give it a
right here let's go ahead and give it a key of rooll and let's go ahead and give
key of rooll and let's go ahead and give it some elements so by default you can
it some elements so by default you can either be an admin or you can be a
either be an admin or you can be a member so those two and make it required
member so those two and make it required and let's go ahead and click create
and let's go ahead and click create there we
there we go so now let's go ahead back inside of
go so now let's go ahead back inside of our features workspace spes API my
our features workspace spes API my apologies server so workspaces server
apologies server so workspaces server route. DS and when we create a new
route. DS and when we create a new workspace we're automatically going to
workspace we're automatically going to create a new member so let's go ahead
create a new member so let's go ahead and do await
and do await databases. create
databases. create document and inside of here I'm going to
document and inside of here I'm going to pass my database
pass my database ID which I already have imported and
ID which I already have imported and then I'm going to add my members ID
then I'm going to add my members ID which is the new uh constant I have
which is the new uh constant I have added in my config file here so now I
added in my config file here so now I have my members ID I'm going to sign a
have my members ID I'm going to sign a unique ID and the body will be as
unique ID and the body will be as follows user ID will come from the
follows user ID will come from the current user which we have extracted
current user which we have extracted here from the session middleware so
here from the session middleware so that's the current user and then we're
that's the current user and then we're going to combine that with our newly
going to combine that with our newly created
created workspace do
workspace do ID like that there we go and we also
ID like that there we go and we also have to pass in a required role admin
have to pass in a required role admin but let's go ahead and improve this a
but let's go ahead and improve this a bit so I'm going to go ahead and create
bit so I'm going to go ahead and create a new feature folder called members and
a new feature folder called members and inside of here I'm going to go ahead and
inside of here I'm going to go ahead and create types.
create types. DS and I'm going to
DS and I'm going to export enum member role it will be admin
export enum member role it will be admin and map that to admin member and map
and map that to admin member and map that to
that to member and now let's go back inside of
member and now let's go back inside of the route and let's add Our member role
the route and let's add Our member role which you can import from features
which you can import from features members
members types and go ahead and assign a member
types and go ahead and assign a member role admin so the person that creates
role admin so the person that creates this is an admin we could also use you
this is an admin we could also use you know the user ID in the workspace field
know the user ID in the workspace field but it's easier to control it from a
but it's easier to control it from a separate collection like this great so
separate collection like this great so we now have uh the actual member to be
we now have uh the actual member to be joined every time that we create a new
joined every time that we create a new workspace so this is what I'm going to
workspace so this is what I'm going to do for now I'm going to go ahead inside
do for now I'm going to go ahead inside of my uh Collections workspaces and I'm
of my uh Collections workspaces and I'm going to select all documents here and
going to select all documents here and I'm going to delete them like this be
I'm going to delete them like this be careful don't select workspaces like
careful don't select workspaces like this and delete it because then you're
this and delete it because then you're going to have to create a new collection
going to have to create a new collection and you're going to have to set up all
and you're going to have to set up all the attributes again you're going to
the attributes again you're going to have to set up all permissions again in
have to set up all permissions again in case you're wondering can the
case you're wondering can the collections and you know the storage and
collections and you know the storage and the databases can they be
the databases can they be programmatically created yes yes they
programmatically created yes yes they can if you want to you can go ahead and
can if you want to you can go ahead and go inside of the docs here I'm not
go inside of the docs here I'm not entirely sure how to where it's located
entirely sure how to where it's located so I'm just going to pause the video and
so I'm just going to pause the video and show you that part of the documentation
show you that part of the documentation so basically you have to go to API
so basically you have to go to API reference here and for example click on
reference here and for example click on databases here to see all apis and you
databases here to see all apis and you can here in the upper right corner
can here in the upper right corner select your platform under server
select your platform under server nodejs and in here you can for example
nodejs and in here you can for example create a database programmatically
create a database programmatically giving it the database ID and the
giving it the database ID and the database name so if you want to use this
database name so if you want to use this for your seed file you can do that you
for your seed file you can do that you can also update it you can delete it you
can also update it you can delete it you can also create new collections you can
can also create new collections you can give your collection a name you can give
give your collection a name you can give them permissions and you can also create
them permissions and you can also create some attributes here so if you are if
some attributes here so if you are if you were wondering can we do those
you were wondering can we do those things programmatically yes you can but
things programmatically yes you can but again be careful you know don't select
again be careful you know don't select this for databases don't select this for
this for databases don't select this for collections you know it's just going to
collections you know it's just going to be a problem because you're going to
be a problem because you're going to have to create a new collection and then
have to create a new collection and then you're going to have to assign all the
you're going to have to assign all the new permissions and all the attributes
new permissions and all the attributes here and you have to remember you know
here and you have to remember you know the size and everything so just be
the size and everything so just be careful with that great so right now I
careful with that great so right now I have no workspaces and I have no members
have no workspaces and I have no members so let's see that now I'm going to
so let's see that now I'm going to refresh here there we go no workspace
refresh here there we go no workspace selected because nothing here works so
selected because nothing here works so I'm going to go ahead and write test and
I'm going to go ahead and write test and create a workspace and it says workspace
create a workspace and it says workspace created so there we go now I have my
created so there we go now I have my workspace immediately here because of
workspace immediately here because of our reinv validated queries and let's
our reinv validated queries and let's see do I have a new member I do with my
see do I have a new member I do with my current user ID and my workspace ID and
current user ID and my workspace ID and my role is admin right here perfect and
my role is admin right here perfect and this matches exactly great so what we
this matches exactly great so what we can do now is create a couple of this so
can do now is create a couple of this so I'm going to create test two and test
I'm going to create test two and test three here so I have test test two and
three here so I have test test two and test three here and what I want to do
test three here and what I want to do now is modify inside of my workpace
now is modify inside of my workpace workspaces server the GU method right
workspaces server the GU method right here right now it just loads every
here right now it just loads every single document that exists so now let's
single document that exists so now let's modify that to only load workspaces
modify that to only load workspaces which we are a member of so for that
which we are a member of so for that we're going to have to extract the
we're going to have to extract the current user from the extended context
current user from the extended context here and let's go ahead and do cons
here and let's go ahead and do cons members await
members await databases list
databases list documents database ID members
ID and let's go ahead and add a query from node app right so alongside ID we
from node app right so alongside ID we now have a query do equal the user ID
now have a query do equal the user ID property matches the currently logged in
property matches the currently logged in user do dollar sign ID like this and now
user do dollar sign ID like this and now we have all members that this user is a
we have all members that this user is a part of and then we can immediately uh
part of and then we can immediately uh make our lives Easier by checking if
make our lives Easier by checking if members if not members. documents.
members if not members. documents. length or you can for example do if
length or you can for example do if members. total is zero you can also do
members. total is zero you can also do that you can immediately break this
that you can immediately break this method and early return data documents
method and early return data documents empty array with total zero so we are
empty array with total zero so we are simulating app right response here we
simulating app right response here we already know if you cannot find any
already know if you cannot find any memberships no point in loading any
memberships no point in loading any workspaces for this person otherwise we
workspaces for this person otherwise we can go ahead and create workspace IDs
can go ahead and create workspace IDs array which are member members.
array which are member members. documents so for each member that we
documents so for each member that we find we are going to map over that
find we are going to map over that member and simply return member
member and simply return member workspace ID again make sure you don't
workspace ID again make sure you don't misspell this because none of these are
misspell this because none of these are strictly type at the moment so just make
strictly type at the moment so just make sure you didn't misspell workspace ID
sure you didn't misspell workspace ID here and you should have an array of
here and you should have an array of workspace IDs and then we can expand our
workspace IDs and then we can expand our workspaces here with a query
workspaces here with a query contains dollar sign ID and workspace
contains dollar sign ID and workspace IDs like this and we can also do query
IDs like this and we can also do query order descending dollar sign created
at and now if I go ahead and refresh this let's see I'm still getting test
this let's see I'm still getting test three test two and test because I
three test two and test because I created all of those but if I now log
created all of those but if I now log out and if I log in with my other
out and if I log in with my other account
here if I've done everything correctly I should not be seeing this but looks like
should not be seeing this but looks like I am still oh after I refreshed uh I
I am still oh after I refreshed uh I cannot see them so why is this happen
cannot see them so why is this happen happening well it looks like that inside
happening well it looks like that inside of my use logout I don't reinv validate
of my use logout I don't reinv validate those so let's do query client
those so let's do query client invalidate
invalidate queries query
queries query key and let's just do workspaces
key and let's just do workspaces here so now when person logs out we are
here so now when person logs out we are going to clear all existing workspaces
going to clear all existing workspaces for that person so there we go now it
for that person so there we go now it works as expected as you can see my
works as expected as you can see my Antonio us us has no workspaces here but
Antonio us us has no workspaces here but I'm going to create Antonio's
I'm going to create Antonio's workspace and now it appears here but if
workspace and now it appears here but if I log out now and if I go into my John I
I log out now and if I go into my John I believe and enter my account there we go
believe and enter my account there we go I don't have Antonio's workspace I only
I don't have Antonio's workspace I only have these three excellent so you've
have these three excellent so you've just created the member the members
just created the member the members functionality here let me just move this
functionality here let me just move this where it
where it belongs all right so a couple of more
belongs all right so a couple of more things I want to do before we wrap up
things I want to do before we wrap up this uh chapter here so I want to go
this uh chapter here so I want to go back inside of my app right Cloud here
back inside of my app right Cloud here and I want to go inside of my
and I want to go inside of my workspaces there we go let's go inside
workspaces there we go let's go inside of attributes and let's add a new
of attributes and let's add a new attribute which is a string and let's
attribute which is a string and let's call and let's call it invite code I'm
call and let's call it invite code I'm going to give it a size of 10 you can
going to give it a size of 10 you can choose whatever size you want for your
choose whatever size you want for your invite codes and we're going to make
invite codes and we're going to make this a required field so let's create
this a required field so let's create that and now we can go ahead and go
that and now we can go ahead and go inside of documents and we can remove
inside of documents and we can remove all workspaces so be careful make sure
all workspaces so be careful make sure you didn't accidentally select
you didn't accidentally select Collections and let's do the same thing
Collections and let's do the same thing instead of members so I'm going to
instead of members so I'm going to remove all
remove all members and now let's go ahead and let's
members and now let's go ahead and let's create a little util for creating a 10
create a little util for creating a 10 digigit uh invite codes here so we're
digigit uh invite codes here so we're going to go ahead inside of our lib
going to go ahead inside of our lib utils right here and let's export
utils right here and let's export function generate invite code which will
function generate invite code which will accept the length which can be a number
accept the length which can be a number and first of all let's define the
and first of all let's define the characters which we accept for our code
characters which we accept for our code so this is what I'm going to do I'm
so this is what I'm going to do I'm going to do the entire alphabet here
going to do the entire alphabet here like this then I'm going to repeat the
like this then I'm going to repeat the entire alphabet but in lower case and
entire alphabet but in lower case and then I'm simply going to add all single
then I'm simply going to add all single digit numbers like this so you don't
digit numbers like this so you don't have to do it exactly like this you can
have to do it exactly like this you can you know just type in the alphabet or
you know just type in the alphabet or just type in any random set of
just type in any random set of characters here or just numbers if
characters here or just numbers if that's what you prefer and now what
that's what you prefer and now what we're going to do is we are going to go
we're going to do is we are going to go ahead and prepare the result here and do
ahead and prepare the result here and do a very simple for Loop so for let I
a very simple for Loop so for let I being zero I is lower than uh the length
being zero I is lower than uh the length we're going to go ahead and simply do an
we're going to go ahead and simply do an increase of the I and very simply from
increase of the I and very simply from characters do character at math floor
characters do character at math floor math random times
math random times characters do
characters do length like this and and return the
length like this and and return the result just like
result just like that and now now that we have the
that and now now that we have the generate invite code we can specify the
generate invite code we can specify the length here let's go ahead and let's go
length here let's go ahead and let's go back inside of features
back inside of features workspaces server route in the post
workspaces server route in the post method here and let's simply generate a
method here and let's simply generate a new invite code here we don't even have
new invite code here we don't even have to do it in a variable we can just add
to do it in a variable we can just add invite
invite code and let's go ahead and make it
code and let's go ahead and make it gener at invite code 10 or you know four
gener at invite code 10 or you know four five six it doesn't matter but the
five six it doesn't matter but the maximum is 10 so if you want to you can
maximum is 10 so if you want to you can add just six here you can of course use
add just six here you can of course use any other library for generating invite
any other library for generating invite codes if you prefer you know you don't
codes if you prefer you know you don't have to build your own but you know it's
have to build your own but you know it's very simple to build one just give it a
very simple to build one just give it a set of characters to work with and
set of characters to work with and iterate over the length you've specified
iterate over the length you've specified and use some math utils to create a
and use some math utils to create a unique code great let's try it out now
unique code great let's try it out now so I'm going to go inside of here I will
so I'm going to go inside of here I will refresh and I will create a test
refresh and I will create a test workspace there we go no errors if I
workspace there we go no errors if I refresh I should have a new member and
refresh I should have a new member and if I go inside of my workspaces there we
if I go inside of my workspaces there we go image URL is null but my invite code
go image URL is null but my invite code is right here and I believe it has 1 two
is right here and I believe it has 1 two 3 four five six characters exactly as we
3 four five six characters exactly as we have specified right here great so we
have specified right here great so we have all of that finished finished and
have all of that finished finished and what I want to build next is the actual
what I want to build next is the actual functionality to click on a workspace
functionality to click on a workspace and change the actual URL and basically
and change the actual URL and basically enable it so that we always have a
enable it so that we always have a specific workspace selected so that then
specific workspace selected so that then when we click on tasks or settings or
when we click on tasks or settings or members we are always uh having the
members we are always uh having the context of the current workpace and then
context of the current workpace and then we will be able to wrap up our
we will be able to wrap up our workspaces by adding the proper
workspaces by adding the proper onboarding by adding a model when you
onboarding by adding a model when you click here and settings and members
click here and settings and members great great
great great job now let's go ahead and let's create
job now let's go ahead and let's create the individual workspace ID page so I'm
the individual workspace ID page so I'm going to go ahead and go inside of my
going to go ahead and go inside of my source folder here inside of my app
source folder here inside of my app folder and inside of the dashboard route
folder and inside of the dashboard route group and then I'm going to create a new
group and then I'm going to create a new folder called workspaces
folder called workspaces and then inside of that a constant
and then inside of that a constant throughoute workspace ID like this and
throughoute workspace ID like this and I'm going to create a new page. TSX file
I'm going to create a new page. TSX file inside and let's go ahead and create a
inside and let's go ahead and create a simple workspace ID page here with a div
simple workspace ID page here with a div workspace
workspace ID like that and then let's go ahead and
ID like that and then let's go ahead and try it out immediately so I'm go to
try it out immediately so I'm go to Local Host 3000 SL workspaces SL1 23
Local Host 3000 SL workspaces SL1 23 like this and there we go we have
like this and there we go we have workspace ID here so now I want to go
workspace ID here so now I want to go inside of my workspace switcher
inside of my workspace switcher component and I want it so that every
component and I want it so that every time I click on a specific item here we
time I click on a specific item here we get redirected to that uh to that
get redirected to that uh to that workspace so let's go ahead and add on
workspace so let's go ahead and add on this select item
this select item here let's go ahead not on select item
here let's go ahead not on select item my apologies on the select we're going
my apologies on the select we're going to do an on value
to do an on value change on select and let's define on
change on select and let's define on select
select here to give us an ID which the user
here to give us an ID which the user clicked
clicked on and then we'll have to add our router
on and then we'll have to add our router from use router coming from next
from use router coming from next navigation and we're simply going to
navigation and we're simply going to redirect the
redirect the user to slash workspaces slash ID that
user to slash workspaces slash ID that the user just selected like this and I
the user just selected like this and I also uh want to do one more thing which
also uh want to do one more thing which we're going to do in a second here so
we're going to do in a second here so right now when I click on test there we
right now when I click on test there we go inside of my URL I am redirected uh
go inside of my URL I am redirected uh to that workspace as you can see the
to that workspace as you can see the problem is my workspace switcher
problem is my workspace switcher component is not controlled at the
component is not controlled at the moment so let's see how we can improve
moment so let's see how we can improve that I'm going to go inside of my source
that I'm going to go inside of my source features
features workspaces and let's create a new folder
workspaces and let's create a new folder called Hooks and inside of here I'm
called Hooks and inside of here I'm going to create use workspace id.
going to create use workspace id. DS let's go ahead and let's import use
DS let's go ahead and let's import use parms from next navigation
parms from next navigation and let's export const use workspace ID
and let's export const use workspace ID here let's define the params from use
here let's define the params from use params and let's return Rams workspace
params and let's return Rams workspace ID as string like this and then you can
ID as string like this and then you can go back inside of your workspace
go back inside of your workspace switcher and you can get the current
switcher and you can get the current workspace ID using the used workspace ID
workspace ID using the used workspace ID method here I'm going to move this
method here I'm going to move this alongside here and the reason this will
alongside here and the reason this will work work is because uh well it won't
work work is because uh well it won't work because we have to search for pam.
work because we have to search for pam. workpace ID it needs to be exactly the
workpace ID it needs to be exactly the same as your app folder dashboard
same as your app folder dashboard workspaces workspace ID it needs to
workspaces workspace ID it needs to match the capitalization so be careful
match the capitalization so be careful and then we're going to go ahead and do
and then we're going to go ahead and do the following we're simply going to give
the following we're simply going to give this a value of workspace ID which is
this a value of workspace ID which is controlled from the params so there we
controlled from the params so there we go now you can see this says test right
go now you can see this says test right here so I'm going to go back to my root
here so I'm going to go back to my root page just so I have this form I'm going
page just so I have this form I'm going to create a couple of this test one two
to create a couple of this test one two three something one two three and just a
three something one two three and just a random name here so now you can see when
random name here so now you can see when I click on one they stay selected
I click on one they stay selected because they are controlled by the URL
because they are controlled by the URL inside uh by the PM called workspace ID
inside uh by the PM called workspace ID inside of the urls so they stay selected
inside of the urls so they stay selected great so now what we have to do is
great so now what we have to do is create a redirect every time user visits
create a redirect every time user visits the root page so I don't want the user
the root page so I don't want the user to be able to visit this kind of page
to be able to visit this kind of page this should almost never exist what
this should almost never exist what should happen is that the user gets
should happen is that the user gets immediately redirected to the first IND
immediately redirected to the first IND the array or we can store the last one
the array or we can store the last one the user used in local storage or you
the user used in local storage or you can store it in the database so let's go
can store it in the database so let's go ahead and see how we can do that so I'm
ahead and see how we can do that so I'm going to go inside of app folder inside
going to go inside of app folder inside of dashboard inside of page.
of dashboard inside of page. TSX and for now uh just go ahead and
TSX and for now uh just go ahead and have a couple of workspaces created
have a couple of workspaces created because we're going to remove this form
because we're going to remove this form we're going to bring it back later but
we're going to bring it back later but for now we don't really need it right so
for now we don't really need it right so we are trying to finish this you know
we are trying to finish this you know homepage here so remove this remove this
homepage here so remove this remove this you can even remove you know the div
you can even remove you know the div let's just write
let's just write homepage and just use this user redirect
homepage and just use this user redirect here so what I want to do is I want to
here so what I want to do is I want to leverage the fact that this is a server
leverage the fact that this is a server component and the same way I've created
component and the same way I've created this get current action well it's not an
this get current action well it's not an action I should I really should not be
action I should I really should not be calling it that it's just a database
calling it that it's just a database query in a server component that's what
query in a server component that's what it is so yeah this actions is kind of
it is so yeah this actions is kind of misleading but I also like the name but
misleading but I also like the name but you should know these are not server
you should know these are not server actions right you can rename these two
actions right you can rename these two queries if you want to so this is what
queries if you want to so this is what I'm going to do I'm going to abstract
I'm going to do I'm going to abstract this client right here uh or maybe we
this client right here uh or maybe we can copy let's see let's go ahead and do
can copy let's see let's go ahead and do this let's copy the entire actions from
this let's copy the entire actions from here and let's put it inside of
here and let's put it inside of workspaces so workspaces should now have
workspaces so workspaces should now have its own actions
its own actions here and this is what we're going to do
here and this is what we're going to do we're going to go ahead and get the out
we're going to go ahead and get the out cookie from its actual place so
cookie from its actual place so out features out constants from here and
out features out constants from here and we're going to call this get
we're going to call this get workspaces and we're going to initialize
workspaces and we're going to initialize the client in exactly the same way we
the client in exactly the same way we are going to get the cookie because we
are going to get the cookie because we need the cookie but we are going to go
need the cookie but we are going to go ahead and do just some things
ahead and do just some things differently so if there's no session
differently so if there's no session we're going to return back total uh
we're going to return back total uh actually no we can return back zero now
actually no we can return back zero now yes let's do that and we're not going to
yes let's do that and we're not going to go and use the account but we're going
go and use the account but we're going to do
to do databases so new
databases so new databases and then inside of here what
databases and then inside of here what we're going to do uh oh I didn't import
we're going to do uh oh I didn't import databases so instead of account I'm now
databases so instead of account I'm now importing databases here and instead of
importing databases here and instead of running this query instead of our get
running this query instead of our get workspaces action we're going to go
workspaces action we're going to go ahead and copy the one from here from
ahead and copy the one from here from our
our API this one so we get the
API this one so we get the members and again we get the workspace
members and again we get the workspace IDs so let's copy from the members this
IDs so let's copy from the members this members total check this iteration of
members total check this iteration of workspace IDs and finally the workspaces
workspace IDs and finally the workspaces copy all of that and put it
copy all of that and put it inside now let's fix the errors so first
inside now let's fix the errors so first of all I'm going to go ahead and REM and
of all I'm going to go ahead and REM and import the database ID from the config
import the database ID from the config here and then I'm going to ahead and
here and then I'm going to ahead and import the members ID from the config as
import the members ID from the config as well and I will then import query from
well and I will then import query from node app right uh we have it right here
node app right uh we have it right here we need to get the user so we're going
we need to get the user so we're going to have to use our account after all so
to have to use our account after all so new account from node app right and pass
new account from node app right and pass the client make sure you have brought
the client make sure you have brought back the account here and then we would
back the account here and then we would simply get the user from uh await
simply get the user from uh await account
account doget and then this is the tricky part
doget and then this is the tricky part so instead of returning c. Json we would
so instead of returning c. Json we would just return this
just return this object we don't even have to do it
object we don't even have to do it inside of data
inside of data we can just immediately return what
we can just immediately return what usually app right would return now let's
usually app right would return now let's also add the workspaces ID from the
also add the workspaces ID from the config
config here and let's go ahead now and let's
here and let's go ahead now and let's finally return workspaces here like this
finally return workspaces here like this one thing that's worrying me are this
one thing that's worrying me are this the return nulls I think that we can
the return nulls I think that we can safely just return you know
safely just return you know these empty States instead of no so this
these empty States instead of no so this and in in case we get any errors you
and in in case we get any errors you know just return the exact same thing
know just return the exact same thing empty doc documents and empty count
empty doc documents and empty count because we are going to use this after
because we are going to use this after our get current so we don't have to
our get current so we don't have to handle any you know missing session
handle any you know missing session errors inside of here we're just going
errors inside of here we're just going to go ahead and you know return empty
to go ahead and you know return empty lists if we notice this is a logged out
lists if we notice this is a logged out user
user and now let's go ahead and let's go
and now let's go ahead and let's go ahead and can I do this after this so
ahead and can I do this after this so can I keep these together and then get
can I keep these together and then get my workspaces here a wait get
my workspaces here a wait get workspaces I think I can and what I want
workspaces I think I can and what I want to do here is if my workspaces do total
to do here is if my workspaces do total is zero in that case we should
is zero in that case we should redirect to whoops
redirect to whoops to slash workspaces slash
to slash workspaces slash create else we should
create else we should redirect to open back deex slw
redirect to open back deex slw workspaces SL workspaces do documents
workspaces SL workspaces do documents first in the array and get the ID of
first in the array and get the ID of that item like this and then let's go
that item like this and then let's go ahead and give this uh so let's see it
ahead and give this uh so let's see it looks like this return will always be
looks like this return will always be unreachable yes this return will always
unreachable yes this return will always be
be unreachable it seems so perhaps I don't
unreachable it seems so perhaps I don't have to return anything here and there
have to return anything here and there we go you can see that now if I try and
we go you can see that now if I try and manually go to Local Host
manually go to Local Host 3000 I am immediately redirected to one
3000 I am immediately redirected to one of the IDS here but if I happen to not
of the IDS here but if I happen to not have any workspaces I will be redirected
have any workspaces I will be redirected to workspaces
to workspaces create so let's see how can we handle
create so let's see how can we handle this in the best way so I think that
this in the best way so I think that there is one thing I can do to improve
there is one thing I can do to improve this and it's to add a loading file
this and it's to add a loading file because as you just saw if I I want I
because as you just saw if I I want I planned on doing like a little spinner
planned on doing like a little spinner here in the return the problem is this
here in the return the problem is this is unreachable
is unreachable code so let's go ahead and add a
code so let's go ahead and add a loading. DSX file which is a reserved
loading. DSX file which is a reserved file name you can already see the error
file name you can already see the error here and this loading will fire during
here and this loading will fire during these
these awaits so let's go inside of loading
awaits so let's go inside of loading here and let's go ahead and Export uh
here and let's go ahead and Export uh dashboard
dashboard loading and inside of here I'm going to
loading and inside of here I'm going to go ahead and create a div with a class
go ahead and create a div with a class name well let's do minimum height of
name well let's do minimum height of screen Flex item Center justify
screen Flex item Center justify Center and inside of here let's add a
Center and inside of here let's add a loader from locid react and let's give
loader from locid react and let's give it the class name size six animate Spin
it the class name size six animate Spin and text muted
foreground like this so now I don't know if you will be able to see this
if you will be able to see this especially if you have you know any
especially if you have you know any cache but I think you just saw for a
cache but I think you just saw for a brief second before I
brief second before I redirected I have a little spinner here
redirected I have a little spinner here so that's what I wanted to achieve and I
so that's what I wanted to achieve and I think that we can also use height full
think that we can also use height full instead of minimum height screen I think
instead of minimum height screen I think this will take actually take up the
this will take actually take up the correct
space that puts it here okay that's that's also fine uh great so we now have
that's also fine uh great so we now have that resolved so what I want to do next
that resolved so what I want to do next is when I click on this button I want to
is when I click on this button I want to have my model
have my model opened so just before we go there I
opened so just before we go there I still just can't stop thinking about my
still just can't stop thinking about my implementation of these actions here I
implementation of these actions here I kept thinking that this is just not the
kept thinking that this is just not the correct way of doing it and even you
correct way of doing it and even you know doing the try
know doing the try catch okay let's just leave it like this
catch okay let's just leave it like this for now when I say not correct I don't
for now when I say not correct I don't mean like a security issue I just mean I
mean like a security issue I just mean I kind of don't like it but okay let's not
kind of don't like it but okay let's not dwell too much into this we are using
dwell too much into this we are using this purely to improve user experience
this purely to improve user experience right we are just using this to redirect
right we are just using this to redirect the user from the homepage to a randomly
the user from the homepage to a randomly assigned well uh the the latest
assigned well uh the the latest workspace they've created so let's go
workspace they've created so let's go ahead and do the following we now have
ahead and do the following we now have to create uh a model which will be
to create uh a model which will be opened every time we click on this handy
opened every time we click on this handy little plus icon here so let's go ahead
little plus icon here so let's go ahead and do that and here's the cool thing
and do that and here's the cool thing we're going to do something called a
we're going to do something called a responsive model so let's go ahead and
responsive model so let's go ahead and do that first so we're going to go
do that first so we're going to go inside of components and create a new
inside of components and create a new file called
file called responsive model.
responsive model. TSX and let's go ahead and import use
TSX and let's go ahead and import use media from react use which is a package
media from react use which is a package we are going to have to install so let
we are going to have to install so let me go ahead and check what version I
me go ahead and check what version I have so bun add react use you're going
have so bun add react use you're going to see the version in a second so bun
to see the version in a second so bun add react use at 17.5 point1 so if you
add react use at 17.5 point1 so if you want to you can run it like this Bon add
want to you can run it like this Bon add and then use this specific version great
and then use this specific version great so we now have react use here and now
so we now have react use here and now let's go ahead and import everything we
let's go ahead and import everything we need from components UI dialogue which
need from components UI dialogue which comes from shat CN UI so that is the
comes from shat CN UI so that is the dialogue and the dialogue content and
dialogue and the dialogue content and then let's also import everything we
then let's also import everything we need from the
need from the drawer this is actually the first time
drawer this is actually the first time I'm doing uh something like this in my
I'm doing uh something like this in my tutorials so you are probably uh not
tutorials so you are probably uh not used to seeing this component but it's
used to seeing this component but it's really cool I promise let's create an
really cool I promise let's create an interface responsive model props
interface responsive model props children react react node and I have to
children react react node and I have to use brackets here so
use brackets here so children open which is a Boolean and
children open which is a Boolean and onopen change which accepts the open
onopen change which accepts the open value which is a Boolean and it returns
value which is a Boolean and it returns a void and let's export
a void and let's export const responsive model
here whoops okay let's assign responsive model
model props right here and let's destructure
props right here and let's destructure the
the children the open and unopen
children the open and unopen change and I forgot to put the equals
change and I forgot to put the equals sign here and then let's go ahead and
sign here and then let's go ahead and Define our we on desktop so if is
Define our we on desktop so if is desktop use media we'll go ahead and in
desktop use media we'll go ahead and in Brackets do minimum width of
Brackets do minimum width of 1,24 pixels and by default we're going
1,24 pixels and by default we're going to consider this to be true if we are on
to consider this to be true if we are on desktop we're going to go ahead and we
desktop we're going to go ahead and we are going to use a model or a dialogue
are going to use a model or a dialogue right and we're going to pass in the
right and we're going to pass in the open props and the onopen change props
open props and the onopen change props like this
like this inside we're going to use a dialogue
inside we're going to use a dialogue content we're going to give it a class
content we're going to give it a class name of full width SM maximum width of
name of full width SM maximum width of large padding of zero border of none
large padding of zero border of none overflow y AO hide scroll bar which is
overflow y AO hide scroll bar which is something we have to implement ourselves
something we have to implement ourselves this does not exist in
this does not exist in tailwind and maximum height of
tailwind and maximum height of 85 uh VH
85 uh VH so let's go ahead and quickly Implement
so let's go ahead and quickly Implement our hide scroll Bar Method here so let's
our hide scroll Bar Method here so let's go inside of our
go inside of our global. CSS located inside of the app
global. CSS located inside of the app folder and I want to do this all the way
folder and I want to do this all the way at the bottom here so hide scroll bar
at the bottom here so hide scroll bar will'll have- ms- overflow D style to be
will'll have- ms- overflow D style to be none so this is for uh Internet Explorer
none so this is for uh Internet Explorer and
and Edge then we're going to have normal
Edge then we're going to have normal scroll
scroll bar with to be none this is for Firefox
bar with to be none this is for Firefox and then hide scroll bar again this time
and then hide scroll bar again this time with a pseudo webkit D- scroll bar my
with a pseudo webkit D- scroll bar my apologies just a single Dash here
apologies just a single Dash here display
display none like that so now we have a working
none like that so now we have a working a hide scroll hide the scroll bar class
a hide scroll hide the scroll bar class here and inside of the dialogue uh
here and inside of the dialogue uh inside of the dialogue content we're
inside of the dialogue content we're going to render our
going to render our children like that and now let's do a
children like that and now let's do a very similar thing outside of the is
very similar thing outside of the is desktop but instead of using the
desktop but instead of using the dialogue it's going to be using the
dialogue it's going to be using the drawer
drawer component which handily accept the exact
component which handily accept the exact same props so no need to change much
same props so no need to change much here and now we're going to add drawer
here and now we're going to add drawer content
content here and we're going to go ahead and
here and we're going to go ahead and replace this with a div so don't put
replace this with a div so don't put these class names on the drawer content
these class names on the drawer content and we're just going to modify it a bit
and we're just going to modify it a bit so I'm just going to leave the uh
so I'm just going to leave the uh overflow y AO the height scroll bar and
overflow y AO the height scroll bar and the maximum height and then I'm going to
the maximum height and then I'm going to render the children inside there we go
render the children inside there we go so we now have a very useful responsive
so we now have a very useful responsive model which on desktop is going to
model which on desktop is going to display a dialogue but on mobile it's
display a dialogue but on mobile it's going to display a nice native looking
going to display a nice native looking drawer you're going to see it in a
drawer you're going to see it in a second I promise it's a really nice
second I promise it's a really nice implementation and I'm probably going to
implementation and I'm probably going to start doing this in every tutorial from
start doing this in every tutorial from now on uh great so now let's go ahead
now on uh great so now let's go ahead let's go inside of our source features
let's go inside of our source features workspaces components and since we
workspaces components and since we already have the create workspace form
already have the create workspace form we can reuse it so let's create a new
we can reuse it so let's create a new create workspace model
create workspace model TSX let's import
TSX let's import responsive model from components
responsive model from components responsive model let's import create
responsive model let's import create workspace form from /c create workspace
workspace form from /c create workspace form like this and let's export const
form like this and let's export const create workspace
create workspace model and let's go ahead and return
model and let's go ahead and return responsive model and passing the create
responsive model and passing the create workspace form inside and I'm just going
workspace form inside and I'm just going to hard code open to be true and onop
to hard code open to be true and onop change is just going to be an empty
change is just going to be an empty Arrow function for now so I want to
Arrow function for now so I want to hardcode this to be opened so that we
hardcode this to be opened so that we can see how this
can see how this looks so let's add this create workspace
looks so let's add this create workspace model to our dashboard layout because
model to our dashboard layout because it's only going to be used inside of
it's only going to be used inside of that layout and nowhere else so inside
that layout and nowhere else so inside of dashboard inside of layout here we're
of dashboard inside of layout here we're going to go ahead and inside of this
going to go ahead and inside of this first div simply add the create
first div simply add the create workspace model and self close it so
workspace model and self close it so make sure you've added the import create
make sure you've added the import create workspace model
workspace model here like that and now we have a problem
here like that and now we have a problem here because this is this layout by
here because this is this layout by default is a server component so let's
default is a server component so let's go ahead and go inside of the create
go ahead and go inside of the create workspace model and let's turn this into
workspace model and let's turn this into use client like this
use client like this and there we go you can see how cool
and there we go you can see how cool this looks on my mobile so on mobile
this looks on my mobile so on mobile this is a drawer but when I expand on
this is a drawer but when I expand on desktop or zoom out it becomes a
desktop or zoom out it becomes a dialogue so that's what I wanted to show
dialogue so that's what I wanted to show you I think this is a really really cool
you I think this is a really really cool feature and now we have to create the
feature and now we have to create the actual controls which will open or close
actual controls which will open or close this dialogue usually in my tutorials I
this dialogue usually in my tutorials I use global State Management like tand or
use global State Management like tand or any Yi you know context any any Global
any Yi you know context any any Global Management and recently I've come across
Management and recently I've come across the idea to create a tutorial and H have
the idea to create a tutorial and H have all of my Global states which hold not
all of my Global states which hold not so so crucial things so like opening
so so crucial things so like opening models inside of my URL and this is
models inside of my URL and this is actually cool because I can there I can
actually cool because I can there I can then share a URL and open a model for
then share a URL and open a model for someone so I found the perfect package
someone so I found the perfect package for that uh which is called Nu so let's
for that uh which is called Nu so let's go ahead and add that again I'm going to
go ahead and add that again I'm going to show you exact version that I'm using
show you exact version that I'm using and I highly recommend actually I'm not
and I highly recommend actually I'm not even going to show you any other version
even going to show you any other version this is exactly how you have to install
this is exactly how you have to install KN so because a new version of KN is
KN so because a new version of KN is coming out and it's going to be a
coming out and it's going to be a breaking change so if you want to use
breaking change so if you want to use the code exactly as I do make sure you
the code exactly as I do make sure you are using this version so KN at
are using this version so KN at 1.19
1.19 .1 and this is very very important
.1 and this is very very important because KNX will be updated to work with
because KNX will be updated to work with next 15 and next 15 includes a handful
next 15 and next 15 includes a handful of breaking changes and same is true for
of breaking changes and same is true for n that being said if you want to explore
n that being said if you want to explore you can explore the new no version so
you can explore the new no version so simply Google it and visit the
simply Google it and visit the documentation and I believe the new
documentation and I believe the new major version will be well two and you
major version will be well two and you can see all the changes from there but
can see all the changes from there but in this tutorial this is the version I
in this tutorial this is the version I will be using I highly recommend that
will be using I highly recommend that you do the exact same things or mpm
you do the exact same things or mpm install iox with that exact
install iox with that exact version great now let's go ahead and
version great now let's go ahead and let's go inside of our workspaces
let's go inside of our workspaces feature folder and what I want to do is
feature folder and what I want to do is I want to go inside of my
I want to go inside of my Hooks and I want to create use create
Hooks and I want to create use create workspace model. ts. TS so basically a
workspace model. ts. TS so basically a hook that will control whether this
hook that will control whether this model is opened or not and let's go
model is opened or not and let's go ahead and go import from KN use Query
ahead and go import from KN use Query State and parse as
State and parse as Boolean let's expert cons to use create
Boolean let's expert cons to use create workpace
workpace model let's go ahead and Define is open
model let's go ahead and Define is open and set is
and set is open to come from use Query State whoops
open to come from use Query State whoops use Query state in the first argument
use Query state in the first argument we're going to Define what query
we're going to Define what query parameter in the URL is going to control
parameter in the URL is going to control this and I'm going to call it create Das
this and I'm going to call it create Das workspace and then I'm going to go ahead
workspace and then I'm going to go ahead and added some options so parse as
and added some options so parse as Boolean with default option of false and
Boolean with default option of false and with some other options clear on default
with some other options clear on default to be true this clear on default will
to be true this clear on default will basically mean that once I close the
basically mean that once I close the model or once this set is open is
model or once this set is open is changed back to false I don't want my
changed back to false I don't want my URL to be Local Host
URL to be Local Host 3000 create workspace false I don't want
3000 create workspace false I don't want that so I only want this to appear if
that so I only want this to appear if it's true so in case set is open gets uh
it's true so in case set is open gets uh with a false value it will simply clear
with a false value it will simply clear it like this that's what this option
it like this that's what this option does that's why we are assigning that
does that's why we are assigning that and this is I also now explained how
and this is I also now explained how this works so you can see you get a
this works so you can see you get a syntax very similar to your usual is
syntax very similar to your usual is open set is open from react use state
open set is open from react use state right you give it a default value the
right you give it a default value the only extension here is is that it will
only extension here is is that it will be completely synchronized with the URL
be completely synchronized with the URL so a very powerful package and now let's
so a very powerful package and now let's simply return a couple of things here so
simply return a couple of things here so if you want you know we could have
if you want you know we could have returned just this directly it can also
returned just this directly it can also work with that but I kind of want to
work with that but I kind of want to make it more convenient for us so let's
make it more convenient for us so let's return is open which will simply be our
return is open which will simply be our is open here and since we do pars as
is open here and since we do pars as Boolean and with default false uh this
Boolean and with default false uh this is actually parsed as a Boolean as you
is actually parsed as a Boolean as you can see usually you know technically all
can see usually you know technically all of these queries are strings but we are
of these queries are strings but we are parsing them as a Boolean right so if
parsing them as a Boolean right so if you run into any errors here what you
you run into any errors here what you can do what I did ini was this I turned
can do what I did ini was this I turned it manually into a Boolean but in case
it manually into a Boolean but in case you hover and it says Boolean you're
you hover and it says Boolean you're safe to do
safe to do this and then let's go ahead and let's
this and then let's go ahead and let's create two handle little utils con open
create two handle little utils con open is simply going to be set is open to
is simply going to be set is open to true con close will be set is open to
true con close will be set is open to false so just a few hand little utilus
false so just a few hand little utilus here so we don't you know just return
here so we don't you know just return this exactly let's at least make it
this exactly let's at least make it easier for us but I also want to return
easier for us but I also want to return set is open just in case we need it in
set is open just in case we need it in this format right we are most likely
this format right we are most likely going to work with these two handlers
going to work with these two handlers helpers here but just in case we need
helpers here but just in case we need the direct access to this it's good to
the direct access to this it's good to have it great so we now have this and
have it great so we now have this and now let's go inside of our create
now let's go inside of our create workspace model and let's go ahead and
workspace model and let's go ahead and add it here so use
add it here so use uh is it use create workspace model yes
uh is it use create workspace model yes use create workspace model from do/
use create workspace model from do/ hooks use create workspace
hooks use create workspace model execute the hook we're going to
model execute the hook we're going to get the open is open and let's go ahead
get the open is open and let's go ahead and get set is open here so I'm just
and get set is open here so I'm just going to go ahead and pass set is open
going to go ahead and pass set is open inside and this will be controlled with
inside and this will be controlled with this there we go so now we're going to
this there we go so now we're going to do this we're going to go inside of our
do this we're going to go inside of our workspace switcher component so make
workspace switcher component so make sure you inside of here it's located
sure you inside of here it's located inside of our Global components for now
inside of our Global components for now and inside of here I'm going to add that
and inside of here I'm going to add that so use create workpace model from
so use create workpace model from features workspaces I'm just going to
features workspaces I'm just going to move it as side with all these other
move it as side with all these other Imports for
Imports for workspaces and I'm just going to call
workspaces and I'm just going to call our helper open like this and very
our helper open like this and very simply I'm going to add to this R I add
simply I'm going to add to this R I add Circle fill on on
Circle fill on on click open just like this let's go ahead
click open just like this let's go ahead and try it out now so I want you to pay
and try it out now so I want you to pay attention to my URL right now nothing
attention to my URL right now nothing happens but when I click plus there we
happens but when I click plus there we go you can see that I have appended a
go you can see that I have appended a new create workspace to be true right
new create workspace to be true right here and it opens right here and if I
here and it opens right here and if I change this to false you can see that
change this to false you can see that it's not open if I manually change it to
it's not open if I manually change it to true it's opened and this is really cool
true it's opened and this is really cool because I can share this state in a form
because I can share this state in a form of URL with someone else so if you're
of URL with someone else so if you're new employee needs help you can just
new employee needs help you can just copy this URL and give it to them and it
copy this URL and give it to them and it will open a model on creating a new
will open a model on creating a new workspace right here uh great so we now
workspace right here uh great so we now have that and I can you know let's go
have that and I can you know let's go ahead and try out
ahead and try out this it works just well one thing is
this it works just well one thing is missing though so inside of my uh where
missing though so inside of my uh where is my create workspace model I'm going
is my create workspace model I'm going to go ahead and also get the close
to go ahead and also get the close Helper and I'm simply going to pass the
Helper and I'm simply going to pass the on cancel here to be
on cancel here to be close just like this so now if you click
close just like this so now if you click cancel it actually closes the model
cancel it actually closes the model isn't that a cool feature and I think we
isn't that a cool feature and I think we can do the same thing uh on mutate here
can do the same thing uh on mutate here so on success you can also kind of do on
so on success you can also kind of do on cancel if you want to but since we will
cancel if you want to but since we will be handling the
be handling the redirect that will in itself clear the
redirect that will in itself clear the URL of any queries so you know however
URL of any queries so you know however you want to do it let's let's do it you
you want to do it let's let's do it you know let's let's add on cancel here as
know let's let's add on cancel here as well so every time oh cannot invoke an
well so every time oh cannot invoke an object which is possibly undefined yes
object which is possibly undefined yes so we cannot invoke it directly we have
so we cannot invoke it directly we have to chain the execution with an optional
to chain the execution with an optional parameter like this so let's try it
parameter like this so let's try it again I'm going to go ahead and do this
again I'm going to go ahead and do this la la and once we created it actually
la la and once we created it actually closes and we can actually now do this
closes and we can actually now do this redirect I completely forgot about that
redirect I completely forgot about that so let's go ahead and instead of create
so let's go ahead and instead of create workspace form go ahead and add a router
workspace form go ahead and add a router use router from next
use router from next navigation in case you're lost where we
navigation in case you're lost where we are this is create workspace form inside
are this is create workspace form inside of features workspaces components so the
of features workspaces components so the very first form besides out forms which
very first form besides out forms which we've created and now inside of here on
we've created and now inside of here on success I'm going to destructure the
success I'm going to destructure the data how do I know that I have to
data how do I know that I have to destructure it well because I am using
destructure it well because I am using the hook called use create workspace
the hook called use create workspace which uses the workspaces post so if I
which uses the workspaces post so if I go s a workspaces server and find my
go s a workspaces server and find my post method here you can see that I know
post method here you can see that I know that I will return a newly created
that I will return a newly created workspace inside of my data object right
workspace inside of my data object right here and thanks to hono we have end to
here and thanks to hono we have end to end type safety here so when I hover
end type safety here so when I hover over data I know that I have the ID of
over data I know that I have the ID of the new workspace here so what I can do
the new workspace here so what I can do now is router. push
now is router. push open backck SL workspaces and then data
open backck SL workspaces and then data and get the
and get the ID just like that let's go ahead and try
ID just like that let's go ahead and try this out let's see if this oncancel will
this out let's see if this oncancel will maybe mess some things up I'm going to
maybe mess some things up I'm going to go ahead and do new
go ahead and do new workspace and let's see looks like I was
workspace and let's see looks like I was not redirected and I think that that
not redirected and I think that that actually might be because of this on
actually might be because of this on cancel so I'm going to go ahead and
cancel so I'm going to go ahead and comment it out and see
comment it out and see whether uh it will be resolved now so
whether uh it will be resolved now so let's try another try
let's try another try here and there we go now it works with
here and there we go now it works with another try it works so I'm going to
another try it works so I'm going to remove the unan looks like by the time
remove the unan looks like by the time this changes the URL this gets
this changes the URL this gets overridden so I'm not going to call on
overridden so I'm not going to call on cancel here because the router will
cancel here because the router will clear up the URL and since we are using
clear up the URL and since we are using URL as our uh Model Management this is a
URL as our uh Model Management this is a good enough solution great so we have
good enough solution great so we have finished that so what I want to do in
finished that so what I want to do in the next chapter is build the individual
the next chapter is build the individual create page so what am I talking about
create page so what am I talking about I'm talking about this random page which
I'm talking about this random page which I've only wrote the URL of and haven't
I've only wrote the URL of and haven't really spoken of more than that so
really spoken of more than that so inside of my dashboard workspaces
inside of my dashboard workspaces page if we have workspaces we redirect
page if we have workspaces we redirect to the first in the array but if we
to the first in the array but if we don't we have to handle this route right
don't we have to handle this route right here that's what we're going to do next
here that's what we're going to do next great great
great great job now let's go ahead and let's create
job now let's go ahead and let's create the individual Standalone create route
the individual Standalone create route for a missing workspace so we're going
for a missing workspace so we're going to go ahead and do the following so far
to go ahead and do the following so far I've thought you that you can create
I've thought you that you can create route groups and put specific routes
route groups and put specific routes inside but I bet you didn't know that
inside but I bet you didn't know that you can create an additional route group
you can create an additional route group and put almost the same type of routes
and put almost the same type of routes inside and I kind of butchered that
inside and I kind of butchered that explanation let me show you what I mean
explanation let me show you what I mean so let's create a new route group called
so let's create a new route group called Stand Alone stand alone purely referring
Stand Alone stand alone purely referring to the type of layout that this is going
to the type of layout that this is going to be uh I'm talking about the design
to be uh I'm talking about the design right so let's go ahead inside of here
right so let's go ahead inside of here and let's create a new folder called
and let's create a new folder called workspaces again and this time
workspaces again and this time specifically create and then inside a
specifically create and then inside a page. DSX let's go ahead and create the
page. DSX let's go ahead and create the workspace create page and a div
workspace create page and a div workspace create a page like this so now
workspace create a page like this so now at the moment you can see that my
at the moment you can see that my workspace ID page is in this layout with
workspace ID page is in this layout with a sidebar but if I remove the ID from my
a sidebar but if I remove the ID from my URL and replace it with Slash create I
URL and replace it with Slash create I am redirected to a page which definitely
am redirected to a page which definitely exists but it uses a completely
exists but it uses a completely different layout so you can do that as
different layout so you can do that as well and that's exactly what we are
well and that's exactly what we are going to do so now let's go inside of
going to do so now let's go inside of the Standalone route group and let's
the Standalone route group and let's create a new file called layout. bsx
create a new file called layout. bsx make sure to put the layout file inside
make sure to put the layout file inside of the Standalone route group and not
of the Standalone route group and not inside of
inside of workspaces and let's go ahead and do the
workspaces and let's go ahead and do the Standalone layout here let's create the
Standalone layout here let's create the interface stand alone layout props let's
interface stand alone layout props let's get the children which are react react
get the children which are react react node let's go ahead and extract the
node let's go ahead and extract the children from the
children from the props and now let's go ahead and let's
props and now let's go ahead and let's style this so I'm going to go ahead and
style this so I'm going to go ahead and give this a main element and put the
give this a main element and put the children inside I'm then going to go
children inside I'm then going to go ahead and wrap my children around a div
ahead and wrap my children around a div with a class name Flex Flex column items
with a class name Flex Flex column items Center justify Center and
py4 I'm then going to go ahead and give this main div a background neutral 100
this main div a background neutral 100 and a minimum height of
and a minimum height of screen then I'm going to go ahead and
screen then I'm going to go ahead and create another div element here which
create another div element here which will
will encapsulate my children and the div
encapsulate my children and the div currently encapsulating my children and
currently encapsulating my children and I'm simply going to give it a width
I'm simply going to give it a width restriction here so MX is going to be
restriction here so MX is going to be Auto MX is going to be with screen to
Auto MX is going to be with screen to Excel and the padding of four like this
Excel and the padding of four like this and I just had to refresh my page to
and I just had to refresh my page to finally see those changes uh great and
finally see those changes uh great and now Above This div which is
now Above This div which is encapsulating my children I'm going to
encapsulating my children I'm going to create a nov bar with a class name Flex
create a nov bar with a class name Flex justify between items Center and the
justify between items Center and the height of 73 pixels and inside I'm going
height of 73 pixels and inside I'm going to add a link from next SL link with an
to add a link from next SL link with an HRA to the root of our app which will
HRA to the root of our app which will automatically uh redirect the user to
automatically uh redirect the user to the first workspace in the array or back
the first workspace in the array or back to this page if no workspace
to this page if no workspace exists and then let's add a next image
exists and then let's add a next image where we are simply again going to
where we are simply again going to render our logo. SVG let's give it an Al
render our logo. SVG let's give it an Al of logo a height of 56 and a width of
152 and besides that a user button from features out components user button like
features out components user button like this and there we go you can see that
this and there we go you can see that now we have kind of a standalone layout
now we have kind of a standalone layout uh the only issue I seem to be having uh
uh the only issue I seem to be having uh is that it expands too much I want it to
is that it expands too much I want it to stop expanding at some point and I think
stop expanding at some point and I think that I made a mistake here instead of MX
that I made a mistake here instead of MX this is supposed to be Max
this is supposed to be Max screen so now let's try this again there
screen so now let's try this again there we go you can see that it stops
we go you can see that it stops expanding at a certain point and the
expanding at a certain point and the rest is actually pretty easy so now that
rest is actually pretty easy so now that we have the Standalone layout we can go
we have the Standalone layout we can go inside of workspaces create page. DSX
inside of workspaces create page. DSX right here and what we can do is give
right here and what we can do is give this Diva class name of full width and
this Diva class name of full width and on large uh Max with
on large uh Max with Excel and simply render the create
Excel and simply render the create workspace form
workspace form inside just like this and there we go we
inside just like this and there we go we now have our individual Standalone
now have our individual Standalone workspace here so now let's see how this
workspace here so now let's see how this will work so you can go ahead and you
will work so you can go ahead and you know create something from here and you
know create something from here and you will get redirected to that new page but
will get redirected to that new page but the real purpose of this is onboarding
the real purpose of this is onboarding because user will probably never visit
because user will probably never visit this again unless they delete or leave
this again unless they delete or leave all of their workspaces so right now
all of their workspaces so right now this is how we create new workspaces but
this is how we create new workspaces but let's say you know we have a completely
let's say you know we have a completely new account here looks like I'm logged
new account here looks like I'm logged out but I seem to not be redirected away
out but I seem to not be redirected away from here and I know why because we
from here and I know why because we forgot to add the uh redirection
forgot to add the uh redirection controls in this page so let's just
controls in this page so let's just manually go to Local Host 3000
manually go to Local Host 3000 here and now I'm redir Ed so I'm going
here and now I'm redir Ed so I'm going to go ahead and click on sign up and I
to go ahead and click on sign up and I will create a new uh a new account here
will create a new uh a new account here so let me go ahead and call myself I
so let me go ahead and call myself I don't know
don't know do do
do do mail.com do 1 2 3 4 5 6 7
mail.com do 1 2 3 4 5 6 7 8 and now since you can see that since I
8 and now since you can see that since I had no available workspace that I'm a
had no available workspace that I'm a member of I get redirected to this page
member of I get redirected to this page so that is the purpose of this page so
so that is the purpose of this page so let's go Ahad ahead and add a couple of
let's go Ahad ahead and add a couple of improvements so first things first once
improvements so first things first once we click cancel I do want to uh do
we click cancel I do want to uh do something I kind of want to go back uh
something I kind of want to go back uh to my uh route but we can actually fix
to my uh route but we can actually fix this in a different way so I'm going to
this in a different way so I'm going to go ahead instead of create workspace
go ahead instead of create workspace form and if we don't have
form and if we don't have cancel I can actually hide the cancel
cancel I can actually hide the cancel button so I'm going to go ahead and do
button so I'm going to go ahead and do this I'm going to give this a class name
this I'm going to give this a class name using the CN Li
using the CN Li library from lib utils so just make sure
library from lib utils so just make sure you have added
you have added this so I'm doing this in create
this so I'm doing this in create workpace form in workspaces feature
workpace form in workspaces feature folder components and I'm going to find
folder components and I'm going to find the cancel button again and very simply
the cancel button again and very simply I'm going to do this if we have on
I'm going to do this if we have on cancel this will be block otherwise it
cancel this will be block otherwise it will be hidden or maybe I'm looking for
will be hidden or maybe I'm looking for invisible there we go and this will be
invisible there we go and this will be visible or we can just do
visible or we can just do this we can do this safely if we use CN
this we can do this safely if we use CN because CN will not you know turn this
because CN will not you know turn this into uh turn this into all Z it it will
into uh turn this into all Z it it will not like usually if you do this in a
not like usually if you do this in a Turner if you open the inspect element
Turner if you open the inspect element you would see a class name called false
you would see a class name called false or true right and I think that maybe I
or true right and I think that maybe I have to turn this into a Boolean let's
have to turn this into a Boolean let's see uh visibility hidden if on cancel
see uh visibility hidden if on cancel exist if on cancel does not exist there
exist if on cancel does not exist there we go so if not on cancel in that case
we go so if not on cancel in that case this is invisible like this so right now
this is invisible like this so right now if I click
if I click here uh okay let's go ahead and just
here uh okay let's go ahead and just create our first workspace here but here
create our first workspace here but here cancel exists right because we are
cancel exists right because we are passing on cancel in that uh model right
passing on cancel in that uh model right and it's just to close the model like
and it's just to close the model like this so if we are manually stumbl on
this so if we are manually stumbl on create the cancel does not exist because
create the cancel does not exist because this is supposed to serve as an
this is supposed to serve as an onboarding screen either
onboarding screen either way great now that we are already here
way great now that we are already here let's go ahead and let's kind of see
let's go ahead and let's kind of see where where are all the places where we
where where are all the places where we forgot to put our uh authentication
forgot to put our uh authentication right so let's go inside of dashboard
right so let's go inside of dashboard inside of this page we are handling this
inside of this page we are handling this so I will just copy this and prepare it
so I will just copy this and prepare it let's go inside of workspaces workspace
let's go inside of workspaces workspace ID here and let's do the same thing so
ID here and let's do the same thing so this will be an
this will be an asynchronous uh
asynchronous uh server server component here and I will
server server component here and I will simply do get current from features out
simply do get current from features out actions and redirect from next
actions and redirect from next navigation like this so if user logs out
navigation like this so if user logs out from the workspace ID page they will get
from the workspace ID page they will get redirected away and let's do the same
redirected away and let's do the same thing inside of Standalone workspace as
thing inside of Standalone workspace as create so again asynchronous
and let's import get current and let's import redirect from next
import redirect from next navigation there we go so now all of our
navigation there we go so now all of our routes that exist at the moment are
routes that exist at the moment are protected great so if I were to log out
protected great so if I were to log out from here I am redirected exactly as we
from here I am redirected exactly as we expected great so you have now developed
expected great so you have now developed the individual workspace create page so
the individual workspace create page so what I want to develop next is the
what I want to develop next is the setting space page for my workspace so
setting space page for my workspace so I'm going to go ahead and go inside of
I'm going to go ahead and go inside of my Antonio account here uh looks like I
my Antonio account here uh looks like I don't have any uh workspaces to Antonio
don't have any uh workspaces to Antonio and I think that's because we cleared up
and I think that's because we cleared up the workspaces a while ago so I'm going
the workspaces a while ago so I'm going to go ahead and create my first
to go ahead and create my first workspace in my Antonio account there we
workspace in my Antonio account there we go and now when I click on the settings
go and now when I click on the settings I want to see settings in my Standalone
I want to see settings in my Standalone layout so that's why I want to do the
layout so that's why I want to do the settings next and then I'm going to do
settings next and then I'm going to do the members because they both use the
the members because they both use the new Standalone layout which we have just
new Standalone layout which we have just created and once we finish up everything
created and once we finish up everything regarding the workspaces we can go ahead
regarding the workspaces we can go ahead and create our first project great great
and create our first project great great job now let's go ahead and let's create
job now let's go ahead and let's create the functionality to update our
the functionality to update our workspaces we're going to start with the
workspaces we're going to start with the schema so let's head inside of the
schema so let's head inside of the source features workspaces schema schas
source features workspaces schema schas and we can copy the create workspace
and we can copy the create workspace schema like this and this time let's go
schema like this and this time let's go ahead and let's make the
ahead and let's make the update workspace schema the only
update workspace schema the only difference being that the name can be
difference being that the name can be optional meaning that it makes sense to
optional meaning that it makes sense to change this message to genuinely be must
change this message to genuinely be must be one or more characters because that's
be one or more characters because that's the correct message if you are about to
the correct message if you are about to change the workspace name we require you
change the workspace name we require you to add at least one character but if you
to add at least one character but if you don't submit a name we're going to
don't submit a name we're going to consider that you want to skip updating
consider that you want to skip updating the name so the image can stay exactly
the name so the image can stay exactly the same nothing needs changing here and
the same nothing needs changing here and it's optional great so now let's go
it's optional great so now let's go inside of server inside of route. DS and
inside of server inside of route. DS and let's go ahead and let's create create a
let's go ahead and let's create create a patch method so at the bottom I'm going
patch method so at the bottom I'm going to add a patch to slash workpace ID
to add a patch to slash workpace ID here's a quick tip don't do this be
here's a quick tip don't do this be explicit with your ID parameters is
explicit with your ID parameters is going to help you and save you some
going to help you and save you some headaches trust me let's add a session
headaches trust me let's add a session middleware let's add a z validator here
middleware let's add a z validator here for the form and let's add the update
for the form and let's add the update workspace schema so make sure you have
workspace schema so make sure you have added an import for your new update
added an import for your new update workspace schema
workspace schema here and then add an asynchronous
here and then add an asynchronous controller here let's get the databases
controller here let's get the databases here from C get
here from C get databases let's get the storage from C.G
databases let's get the storage from C.G storage let's get the current user from
storage let's get the current user from C.G user just like this and let's go
C.G user just like this and let's go ahead and let's
ahead and let's destructure the param so
destructure the param so C request
C request foram will be workspace
foram will be workspace ID and let's go ahead and let's
ID and let's go ahead and let's destructure the name and image URL from
destructure the name and image URL from C request valid form and not image URL
C request valid form and not image URL it's just going to be image what we have
it's just going to be image what we have to do now is check whether the person
to do now is check whether the person who is trying to access uh this patch
who is trying to access uh this patch route even though they are logged in can
route even though they are logged in can they should they be allowed to do this
they should they be allowed to do this so we have to see whether this person is
so we have to see whether this person is a member of this workspace so let's go
a member of this workspace so let's go ahead and do const member and let's go
ahead and do const member and let's go ahead and create a little helper called
ahead and create a little helper called get member so let's go ahead and do that
get member so let's go ahead and do that I'm going to go ahead and do you know
I'm going to go ahead and do you know for now just null and let's go inside of
for now just null and let's go inside of our feature members feature folder
our feature members feature folder create a new file called
.ts go ahead and import query from node app right and type databases from node
app right and type databases from node app right import the database ID and the
app right import the database ID and the members ID from the config and Export
members ID from the config and Export con get
con get member to be an asynchronous
member to be an asynchronous method let's go ahead and create an
method let's go ahead and create an interface get member props to have
interface get member props to have databases a type of
databases a type of databases workspace ID to be a type of
databases workspace ID to be a type of string and user ID to be a type of
string and user ID to be a type of string so we now have to assign this
string so we now have to assign this props right
props right here databases workspace ID and user ID
here databases workspace ID and user ID so we are getting the three minimum
so we are getting the three minimum values we need to fetch the the current
values we need to fetch the the current member and then let's do const members
member and then let's do const members await databases list
await databases list documents passing the database ID
documents passing the database ID passing the members ID and finally add a
passing the members ID and finally add a query confirm that the workspace ID
query confirm that the workspace ID equals the past prop workspace ID and we
equals the past prop workspace ID and we can collapse these two in an array
can collapse these two in an array the second
the second query should match the user
ID like this and return members documents first and only in the
documents first and only in the array well nothing is really you know
array well nothing is really you know nothing on the orm wise is protecting
nothing on the orm wise is protecting this to be the only member but we know
this to be the only member but we know thanks to our implementation and how we
thanks to our implementation and how we are working with the members collection
are working with the members collection that with our with our implementation
that with our with our implementation how how these are created only one of
how how these are created only one of this exact combination should exist if
this exact combination should exist if two of them exist we have a problem all
two of them exist we have a problem all right and now let's go and by the way
right and now let's go and by the way yes you can create indexes in app right
yes you can create indexes in app right so we can come back to this later and
so we can come back to this later and explore if we can perhaps have this be
explore if we can perhaps have this be an index and also give it a unique
an index and also give it a unique property so maybe perhaps that can throw
property so maybe perhaps that can throw an error and if you want to protect it
an error and if you want to protect it in another way you can do it instead of
in another way you can do it instead of workspaces server route. vs when you
workspaces server route. vs when you create one you can check for this new
create one you can check for this new member which you've created and you can
member which you've created and you can then query and if if you can find
then query and if if you can find another member with the same user ID and
another member with the same user ID and the same workspace ID you have a problem
the same workspace ID you have a problem and you would have to do the same thing
and you would have to do the same thing in the
in the upcoming uh join route I'm going to you
upcoming uh join route I'm going to you know repeat that once we get to that for
know repeat that once we get to that for now let's just focus on this so now we
now let's just focus on this so now we have the get member method which will be
have the get member method which will be our helper whenever we need to quickly
our helper whenever we need to quickly access a
access a member so now let's go ahead and let's
member so now let's go ahead and let's see can this member actually update this
see can this member actually update this workspace so a wait get
workspace so a wait get member passing the
member passing the databases passing the workspace ID and
databases passing the workspace ID and passing the user ID which is user.
passing the user ID which is user. ID and we have to import get member and
ID and we have to import get member and we have to await get member make sure
we have to await get member make sure you have imported this from features
you have imported this from features members
members utils and then if there is no member to
utils and then if there is no member to be found or if member. roll is not
be found or if member. roll is not member roll.
member roll. admin in that case we're going to return
admin in that case we're going to return c. Json
c. Json error unauthorized
like this and let's go ahead and also assign a
assign a 401 great and to answer your question
401 great and to answer your question yes you could explore and see can you do
yes you could explore and see can you do this in the session middleware I've
this in the session middleware I've attempted to do it a few times but it
attempted to do it a few times but it just became too big of an abstraction to
just became too big of an abstraction to keep care of right now it seems very
keep care of right now it seems very easy to do the problem is we are not
easy to do the problem is we are not always going to have the workspace ID
always going to have the workspace ID inside of our API URL that's the biggest
inside of our API URL that's the biggest issue so if you were to rework when you
issue so if you were to rework when you finish the entire project if you were to
finish the entire project if you were to rework everything to have nested API
rework everything to have nested API routes so every single API route will
routes so every single API route will have the workspace ID in the context
have the workspace ID in the context then yes you could probably extend the
then yes you could probably extend the session middleware to accept some
session middleware to accept some options like uh admin true and then you
options like uh admin true and then you would not have to do this every time but
would not have to do this every time but for now I think this is just
for now I think this is just fine and now let's go ahead and
fine and now let's go ahead and basically what we have to do is well
basically what we have to do is well repeat everything that we did initially
repeat everything that we did initially right here so let's prepare the uploaded
right here so let's prepare the uploaded image URL and this if Clause deciding
image URL and this if Clause deciding whether the file should be uploaded or
whether the file should be uploaded or not so I'm going to copy this exact
not so I'm going to copy this exact block from my post method and I'm going
block from my post method and I'm going to paste it in my new patch method after
to paste it in my new patch method after my authorization here there we go and
my authorization here there we go and then what I'm going to do is const the
then what I'm going to do is const the workspace to be await
workspace to be await databases. update document I'm going to
databases. update document I'm going to pass in database uncore id workspaces
pass in database uncore id workspaces id workspace ID since I have it it's
id workspace ID since I have it it's inside of my uh request Pam right here
inside of my uh request Pam right here and then I'm going to go ahead and pass
and then I'm going to go ahead and pass in the name and the image which will be
in the name and the image which will be my uploaded image URL and be careful
my uploaded image URL and be careful it's image URL when storing it into the
it's image URL when storing it into the database that is the very important part
database that is the very important part we can confirm that by going inside of
we can confirm that by going inside of our post method you can see it's image
our post method you can see it's image URL that is being accepted so to repeat
URL that is being accepted so to repeat we are calling it image inside of our
we are calling it image inside of our form data or our request body because
form data or our request body because technically especially in this case of
technically especially in this case of patch it can be a string it can already
patch it can be a string it can already be a base 64 URL so if I were to call
be a base 64 URL so if I were to call this image URL it wouldn't make sense
this image URL it wouldn't make sense when user sends a file and if I were to
when user sends a file and if I were to just call it a file it wouldn't make
just call it a file it wouldn't make sense when user sends a URL so that's
sense when user sends a URL so that's why I've just named it image and that's
why I've just named it image and that's why we are doing the instance of check
why we are doing the instance of check here to see what do we have to do with
here to see what do we have to do with this because if we just receive an image
this because if we just receive an image uh we can just go ahead you know and
uh we can just go ahead you know and leave it as it is so no point in even
leave it as it is so no point in even updating it but here's the thing uh one
updating it but here's the thing uh one thing that is kind of bothering me is
thing that is kind of bothering me is that this uploaded image URL should by
that this uploaded image URL should by default uh
default uh be it should be exactly what uh we have
be it should be exactly what uh we have passed right I think I'm kind of seeing
passed right I think I'm kind of seeing a potential bug
a potential bug here else if
here else if image is a type of
image is a type of string or I can do just else if uploaded
string or I can do just else if uploaded image URL to be my image can I just do
that else whoops just else and then uploaded image is passed here okay I
uploaded image is passed here okay I think that we can do that to kind of
think that we can do that to kind of preserve our uploaded image and then
preserve our uploaded image and then what we're going to do in the end here
what we're going to do in the end here is just return c. Json data workspace so
is just return c. Json data workspace so the updated
the updated workspace great so we have created our
workspace great so we have created our update method now so let me close
update method now so let me close everything and let's go inside of our
everything and let's go inside of our API let's copy use create workspace and
API let's copy use create workspace and let's do use update workspace
let's do use update workspace here let's go ahead and change the
here let's go ahead and change the constant name to use update workspace
constant name to use update workspace again and be careful you're in the
again and be careful you're in the correct file and we are now going to
correct file and we are now going to change both of the response and request
change both of the response and request types to first get the colum workspace
types to first get the colum workspace ID and then get the patch
ID and then get the patch method so these are the inferences which
method so these are the inferences which we need and then we have to change that
we need and then we have to change that here as well so we are now getting the
here as well so we are now getting the workspace ID patch and we also need to
workspace ID patch and we also need to destructure the parameter this
destructure the parameter this time and we also have to pass the param
time and we also have to pass the param this time and this will be failed to
this time and this will be failed to update workspace and this will be
update workspace and this will be workspace updated and inside of here on
workspace updated and inside of here on success we can destructure the updated
success we can destructure the updated data I believe uh data does not exist on
data I believe uh data does not exist on response type okay let's see what that
response type okay let's see what that is about so if I go back inside of my
is about so if I go back inside of my patch method server route my patch here
return c. Json data workspace so this definitely should exist so I'm not
definitely should exist so I'm not entirely
entirely sure uh why it's throwing an error
sure uh why it's throwing an error here response type oh yes so we have to
here response type oh yes so we have to be explicit with our response types
be explicit with our response types perhaps this is something that we should
perhaps this is something that we should revisit our response types can by
revisit our response types can by default will give us an error or the
default will give us an error or the data so
data so I want to give this a 200 meaning that I
I want to give this a 200 meaning that I only want the success version of the
only want the success version of the response type because I'm handling the
response type because I'm handling the error here so now you can see that it
error here so now you can see that it works here now I can safely use the UN
works here now I can safely use the UN success method here and then what I can
success method here and then what I can do is I can invalidate queries
do is I can invalidate queries individual workspace and
individual workspace and data ID this does not exist yet but it
data ID this does not exist yet but it will exist in the future and we
will exist in the future and we technically don't have well yes it's
technically don't have well yes it's better that we update this as well in
better that we update this as well in case you know we rename one from our
case you know we rename one from our list we need to get that updated in
list we need to get that updated in workpace switcher as well so yes inside
workpace switcher as well so yes inside of your API inside of your mutations if
of your API inside of your mutations if you want to do something in the on
you want to do something in the on success like data here uh looks like
success like data here uh looks like here it's working okay I think it
here it's working okay I think it depends on the type of API route that we
depends on the type of API route that we use right so I guess that here somewhere
use right so I guess that here somewhere we are throwing errors yes in this one
we are throwing errors yes in this one we are throwing errors so that's why we
we are throwing errors so that's why we have to specify that instead of our
have to specify that instead of our request response type here we only want
request response type here we only want to focus on the positive uh optimistic
to focus on the positive uh optimistic route right because we are going to
route right because we are going to handle the error here all right now that
handle the error here all right now that we have this ready we have to go ahead
we have this ready we have to go ahead and create our uh edit
and create our uh edit workspace form so let's go ahead and do
workspace form so let's go ahead and do that I'm going to go ahead and do go
that I'm going to go ahead and do go inside of my Source instead of my
inside of my Source instead of my features workspaces components I'm going
features workspaces components I'm going to copy create workspace form and I'm
to copy create workspace form and I'm going to paste it and I'm going to turn
going to paste it and I'm going to turn it into edit workspace form double check
it into edit workspace form double check that you are inside of the newly copied
that you are inside of the newly copied edit workspace form and let's go ahead
edit workspace form and let's go ahead and create and remove these three
and create and remove these three instances to edit workspace form like
instances to edit workspace form like this besides on cancel this will also
this besides on cancel this will also have the initial values the problem is
have the initial values the problem is we don't have the type so let's go ahead
we don't have the type so let's go ahead and let's create the workspace type I'm
and let's create the workspace type I'm going to go ahead inside of my
going to go ahead inside of my workspaces feature folder and I will
workspaces feature folder and I will create a new file called types. DS
create a new file called types. DS inside of here I'm going to import
inside of here I'm going to import models from node app right and I will
models from node app right and I will export type
export type workspace and I'm going to make it be
workspace and I'm going to make it be models. document which means this will
models. document which means this will hold all the ID collection ID database
hold all the ID collection ID database is and also this ambiguous any but this
is and also this ambiguous any but this time we're going to extend it with the
time we're going to extend it with the fields we know are there like name image
fields we know are there like name image URL invite
URL invite code and user ID so we know that these
code and user ID so we know that these things are what exist in the workspace
things are what exist in the workspace so I'm going to go inside of my edit
so I'm going to go inside of my edit workspace form and I'm going to go ahead
workspace form and I'm going to go ahead and pass my workspace from dot do/ types
and pass my workspace from dot do/ types to be here like that now we have the
to be here like that now we have the initial values inside of here alongside
initial values inside of here alongside on
on cancel great so now let's go ahead and
cancel great so now let's go ahead and let's modify our hook form here to no
let's modify our hook form here to no longer use the create workspace schema
longer use the create workspace schema so we can remove one two three four
so we can remove one two three four instances of create workspace schema
instances of create workspace schema with edit workspace
with edit workspace schema did I call it edit workspace
schema did I call it edit workspace schema uh I didn't I called it update
schema uh I didn't I called it update workspace SEMA okay so let me just fix
workspace SEMA okay so let me just fix this so all these four instances in the
this so all these four instances in the onsubmit method in the Zod resolver in
onsubmit method in the Zod resolver in the infer of the form constant and in
the infer of the form constant and in the import change all of them to update
the import change all of them to update workspace
workspace schema all right and now let's go ahead
schema all right and now let's go ahead and do the following I'm going to go
and do the following I'm going to go ahead and change the default values to
ahead and change the default values to spread initial values here and then
spread initial values here and then image will be initial values image URL
image will be initial values image URL or just an empty string that's how we're
or just an empty string that's how we're going to initialize our values and we
going to initialize our values and we need some special uh care for the image
need some special uh care for the image here great and now inside of the
here great and now inside of the onsubmit we have to modify it uh just a
onsubmit we have to modify it uh just a bit here so we are passing the
bit here so we are passing the form but we also have to pass in the
form but we also have to pass in the Pam which will be workspace ID and it
Pam which will be workspace ID and it has to be the initial values do ID
has to be the initial values do ID like this there we go and inside of here
like this there we go and inside of here let's change this to be
let's change this to be undefined and that did not fix this
undefined and that did not fix this issue so let me see uh the property name
issue so let me see uh the property name are
are incompatible okay so I'm going to have
incompatible okay so I'm going to have to uh improve this but okay let's leave
to uh improve this but okay let's leave it like this for now let's for now just
it like this for now let's for now just change the text here so I'm going to go
change the text here so I'm going to go ahead and change this card title to
ahead and change this card title to Simply say initial values. name here for
Simply say initial values. name here for now okay let's leave it as this as this
now okay let's leave it as this as this yeah and now I'm going to close
yeah and now I'm going to close everything and I'm going to go inside of
everything and I'm going to go inside of my source instead of my features my
my source instead of my features my apologies in my app folder Standalone
apologies in my app folder Standalone workspaces and I'm going to create a new
workspaces and I'm going to create a new folder workspace ID so exactly the same
folder workspace ID so exactly the same as they have in my dashboard but then
as they have in my dashboard but then I'm going to do an additional folder
I'm going to do an additional folder called called settings so if you access
called called settings so if you access the settings you will have a standalone
the settings you will have a standalone layout rather than the dashboard one
layout rather than the dashboard one because uh well it just suits more now
because uh well it just suits more now let's go ahead and give this a page. DSX
let's go ahead and give this a page. DSX let's go ahead and turn this into a
let's go ahead and turn this into a workspace ID settings page I like to be
workspace ID settings page I like to be explicit with my page names
explicit with my page names here ID settings
here ID settings page like this and let's turn this into
page like this and let's turn this into an a synchronous method let's get the
an a synchronous method let's get the user using a wait get
user using a wait get current if there is no user we are going
current if there is no user we are going to redirect the user to slash sign in so
to redirect the user to slash sign in so let's immediately take care of that so
let's immediately take care of that so we don't forget and now what we have to
we don't forget and now what we have to do is we have to extract the workspace
do is we have to extract the workspace ID from our uh from our uh uh server
ID from our uh from our uh uh server component because we can do that if you
component because we can do that if you have the workspace ID from here the same
have the workspace ID from here the same way we used the hook use workspace ID to
way we used the hook use workspace ID to uh infert the pams we can actually get
uh infert the pams we can actually get some default props that every server
some default props that every server component has to D structure the ID
component has to D structure the ID inside of a server
inside of a server component so let's go ahead and create
component so let's go ahead and create an interface workspace ID settings page
an interface workspace ID settings page props and let's go ahead and give it
props and let's go ahead and give it Rams SL
Rams SL SL string not slug my apologies uh
SL string not slug my apologies uh workspace ID string so now you can go
workspace ID string so now you can go ahead and assign those
ahead and assign those here and you can D structure the
here and you can D structure the params and you should now be able to get
params and you should now be able to get rams. workspace ID inside of them so
rams. workspace ID inside of them so let's go ahead and let's I am now on my
let's go ahead and let's I am now on my test workspace here so if I were to pend
test workspace here so if I were to pend after my workspace id/
after my workspace id/ settings I should get redirected to the
settings I should get redirected to the Standalone layout and I can read my
Standalone layout and I can read my workspace ID from my URL inside of a
workspace ID from my URL inside of a server component because remember
server component because remember instead of server components I cannot
instead of server components I cannot use hooks like this that's only for
use hooks like this that's only for client components great so now that we
client components great so now that we have this ready uh one thing that I want
have this ready uh one thing that I want to do before we even go here is enable
to do before we even go here is enable the actual settings button here because
the actual settings button here because right now clicking on it leads us to
right now clicking on it leads us to nowhere so let's first fix that and then
nowhere so let's first fix that and then we're going to go ahead and finish the
we're going to go ahead and finish the settings
settings form so in order to fix that we have to
form so in order to fix that we have to go back inside of our source
go back inside of our source components navigation component where we
components navigation component where we have our settings actually defined and
have our settings actually defined and inside of here I want to import use
inside of here I want to import use workspace ID from features workspaces
workspace ID from features workspaces hooks use workspace ID uh and I also
hooks use workspace ID uh and I also want to
want to import use path name from next
import use path name from next navigation so inside of here I think
navigation so inside of here I think that the nav the nav bar my apologies
that the nav the nav bar my apologies the
the sidebar is is it a client component I'm
sidebar is is it a client component I'm not sure the workspace switcher okay so
not sure the workspace switcher okay so the workspace switcher needed its own
the workspace switcher needed its own news client so the navigation will also
news client so the navigation will also need its own use client so make sure
need its own use client so make sure that inside of the navigation. DSX where
that inside of the navigation. DSX where you have the routes constant you have
you have the routes constant you have added use client at the top great now
added use client at the top great now inside of the navigation here we're
inside of the navigation here we're going to Define our workpace ID to be
going to Define our workpace ID to be used workspace ID and then our path name
used workspace ID and then our path name will be our used path name
will be our used path name here and then we're going to go ahead
here and then we're going to go ahead and inside of uh routes. map we're going
and inside of uh routes. map we're going to Define the full hre to where we are
to Define the full hre to where we are going to have to redirect because we
going to have to redirect because we can't just redirect the SL settings we
can't just redirect the SL settings we have to redirect the current route and
have to redirect the current route and then slash settings so I want to go
then slash settings so I want to go ahead and do this I want to go ahead and
ahead and do this I want to go ahead and create the full hre to be slash
create the full hre to be slash workspaces and then slash workspace ID
workspaces and then slash workspace ID and
and then item. hre we don't have to add a
then item. hre we don't have to add a slash here because each of these has a
slash here because each of these has a slash except the homepage which
slash except the homepage which basically
basically is just this that's why it's okay to put
is just this that's why it's okay to put an empty string in this case and let's
an empty string in this case and let's go ahead and check if path name is equal
go ahead and check if path name is equal to full hre so only then is is active
to full hre so only then is is active actually going to be true and let's
actually going to be true and let's change the HRA here to be our full HRA
change the HRA here to be our full HRA and now there we go the home is now
and now there we go the home is now selected and if I click on settings I am
selected and if I click on settings I am redirected to the standalone settings
redirected to the standalone settings page and if I go back you can see how
page and if I go back you can see how home gets selected perfect so now we can
home gets selected perfect so now we can work on the settings page
work on the settings page here so the first thing that we need to
here so the first thing that we need to do is make a way for This Server
do is make a way for This Server component inside of our features
component inside of our features workspaces here where is our uh my
workspaces here where is our uh my apologies inside of our app folder
apologies inside of our app folder Standalone workspaces workspace ID
Standalone workspaces workspace ID settings page we somehow need to pass
settings page we somehow need to pass our edit uh workspace form which we just
our edit uh workspace form which we just edited and copied you know from the
edited and copied you know from the create workspace form the problem is it
create workspace form the problem is it is missing initial values right you can
is missing initial values right you can see that a lot of issues here are
see that a lot of issues here are appearing so we need to find a way
appearing so we need to find a way inside of our server component here to
inside of our server component here to get those values of course we don't have
get those values of course we don't have to do that you know we could just create
to do that you know we could just create a wrapper and just use uh use get
a wrapper and just use uh use get workspace if that's what we prefer but
workspace if that's what we prefer but you know let's explore our options uh
you know let's explore our options uh let's see if maybe this would be uh
let's see if maybe this would be uh simpler to do so I'm going to go ahead
simpler to do so I'm going to go ahead and I'm going to go inside of my app
and I'm going to go inside of my app folder my apologies have of my features
folder my apologies have of my features workspaces actions and I'm going to go
workspaces actions and I'm going to go ahead and copy get
workspaces and I will paste it here and at this point I don't like how many
at this point I don't like how many times we have repeated this client so
times we have repeated this client so I'm going to look into ways to abstract
I'm going to look into ways to abstract this somewhere so I'm going to call this
this somewhere so I'm going to call this get workspace and I will accept a
get workspace and I will accept a workspace
ID which will be a string and I just want to be consistent so I'm going to
want to be consistent so I'm going to separate this into an interface get
separate this into an interface get workspace props workspace ID which is a
workspace props workspace ID which is a string like
string like this so let me just assign this here
this so let me just assign this here there we go workspace ID so everything
there we go workspace ID so everything here can stay the same except this this
here can stay the same except this this can be null because this time we are not
can be null because this time we are not going to list anything so the members
going to list anything so the members are not something we need here at
are not something we need here at all uh we're just going to
all uh we're just going to need we're just going to need the
need we're just going to need the current workspace so let me go ahead and
current workspace so let me go ahead and do this the individual workspace get
do this the individual workspace get uh yes we can do get document
uh yes we can do get document here and we can simply pass in the
here and we can simply pass in the workspace
workspace ID and in the case of catch we can just
ID and in the case of catch we can just return null and we can let's leave the
return null and we can let's leave the user here for now because I think there
user here for now because I think there is one thing that we can already do here
is one thing that we can already do here is check should this user be able to
is check should this user be able to access this workspace so yeah we can do
access this workspace so yeah we can do that
that member will be a way to get member from
member will be a way to get member from do/ uh members
do/ uh members utils like this or you can change it to
utils like this or you can change it to slash you know features I think this
slash you know features I think this makes more sense I want to see where it
makes more sense I want to see where it comes from like this so wait to get
comes from like this so wait to get member pass in the databases pass in the
member pass in the databases pass in the user ID which is my user. ID and passing
user ID which is my user. ID and passing the workspace ID which I'm getting from
the workspace ID which I'm getting from my props here and then inside of here if
my props here and then inside of here if I'm not a member of this work base I can
I'm not a member of this work base I can just return back no I don't even have to
just return back no I don't even have to throw any errors here so let's return
throw any errors here so let's return null like
null like this and then let's go inside of page
this and then let's go inside of page and let's go ahead and after this
and let's go ahead and after this redirect let's get the initial values to
redirect let's get the initial values to be
be await uh get
workspace and I've imported that from workspace actions here and I'm going to
workspace actions here and I'm going to pass in the workspace ID to be par Ram's
pass in the workspace ID to be par Ram's workspace
workspace ID if there are no initial values I'm
ID if there are no initial values I'm just going to go return now or I can
just going to go return now or I can return or I can redirect
return or I can redirect here to forward
here to forward slash or maybe just forward
slash or maybe just forward slash workspace
ID arams workspace ID in case I cannot load the initial values I will just
load the initial values I will just redirect the user away from the set page
redirect the user away from the set page and if I do have them I think I will be
and if I do have them I think I will be able to send them now all right so
able to send them now all right so document is not
document is not assignable uh to workspace okay and we
assignable uh to workspace okay and we can actually fix that so let's go setad
can actually fix that so let's go setad of get
of get workspace and inside of here when we get
workspace and inside of here when we get the document let's go ahead and give it
the document let's go ahead and give it a specific type here of workspace from
a specific type here of workspace from dot SL types and I can already see the
dot SL types and I can already see the error in my other tab is
error in my other tab is gone so that's how you you can you know
gone so that's how you you can you know modify these queries from app right to
modify these queries from app right to actually give you uh the proper types
actually give you uh the proper types and there we go now you can see it's
and there we go now you can see it's gone and you can also see my settings
gone and you can also see my settings right here excellent so what we have to
right here excellent so what we have to do now uh is we have to uh well first
do now uh is we have to uh well first change the text here and we also have to
change the text here and we also have to change the methods we are calling so you
change the methods we are calling so you can see that now if I go ahead and I
can see that now if I go ahead and I create a new workspace here workspace to
create a new workspace here workspace to to edit and create
to edit and create it let me zoom out and then click on the
it let me zoom out and then click on the settings there we go it says workspace
settings there we go it says workspace to edit and let's try can we load the
to edit and let's try can we load the image so I'm going to go ahead and call
image so I'm going to go ahead and call this test image and I will upload an
this test image and I will upload an image so let me just find the one uh
image so let me just find the one uh that I use all the time let's go ahead
that I use all the time let's go ahead and click create
workspace there we go this is working and if I click on settings it works as
and if I click on settings it works as well well great so we can definitely
well well great so we can definitely load this now let's go ahead and let's
load this now let's go ahead and let's see inside of our edit workspace form
see inside of our edit workspace form what are all the changes we need to do
what are all the changes we need to do uh to make this actually
uh to make this actually work let's start with changing the
work let's start with changing the design a bit so I'm going to go back
design a bit so I'm going to go back inside of my edit workspace form I have
inside of my edit workspace form I have loaded the initial values and let me now
loaded the initial values and let me now modify the card header a bit so I'm
modify the card header a bit so I'm going to go ahead inside of the card
going to go ahead inside of the card header and I'm going to give it a flex
header and I'm going to give it a flex Flex row items Center Gap X4 padding of
Flex row items Center Gap X4 padding of seven and space y zero so I override the
seven and space y zero so I override the existing space
existing space Y and then what I'm going to do is I'm
Y and then what I'm going to do is I'm going to add a button before the card
going to add a button before the card title and this button will simply say
title and this button will simply say back this button will also have a size
back this button will also have a size of small it will have a variant of
of small it will have a variant of secondary and it will have an onclick to
secondary and it will have an onclick to be on
be on cancel and then let's also go ahead and
cancel and then let's also go ahead and give it an arrow left icon from Lucid
react like this
oops there we go uh well where did I move my arrow left icon let's keep it
move my arrow left icon let's keep it inside of the button and let's give it a
inside of the button and let's give it a class name of size four and it should
class name of size four and it should be before the text like this uh
be before the text like this uh and the this is two uh joined together
and the this is two uh joined together so let's give it an MR of two as well so
so let's give it an MR of two as well so it looks a bit better great uh and now
it looks a bit better great uh and now in the card Title Here we have our test
in the card Title Here we have our test image okay so that looks fine great and
image okay so that looks fine great and now let's go ahead and modify the text
now let's go ahead and modify the text here from create workspace to to become
here from create workspace to to become uh save
uh save changes like this and one more thing
changes like this and one more thing we're going to do is we we have our
we're going to do is we we have our router here so for this button here
router here so for this button here we're going to do if we have on cancel
we're going to do if we have on cancel we're going to call on cancel otherwise
we're going to call on cancel otherwise we're going to go ahead and do the an
we're going to go ahead and do the an narrow function router. push and simply
narrow function router. push and simply go back to
go back to workspaces and then initial value Val
workspaces and then initial value Val use do
use do ID let me expand this so you can see
ID let me expand this so you can see what I did so in this new button which
what I did so in this new button which we have added in our card header I have
we have added in our card header I have added an if we have an uncan call the
added an if we have an uncan call the uncan otherwise go ahead and create an
uncan otherwise go ahead and create an arrow function which will simply
arrow function which will simply redirect the user back to the workspace
redirect the user back to the workspace where they come from because in a server
where they come from because in a server component we're not going to pass the UN
component we're not going to pass the UN cancel this on cancel is only you know
cancel this on cancel is only you know for models and stuff so right and when I
for models and stuff so right and when I click back there we go I am redirected
click back there we go I am redirected back to the workspace where I come from
back to the workspace where I come from now I'm noticing that this just looks a
now I'm noticing that this just looks a little small so let's see why is that
little small so let's see why is that happening I assume it's something inside
happening I assume it's something inside of the uh app folder
of the uh app folder Standalone lay out here uh let's see did
Standalone lay out here uh let's see did I
I forget uh to do something here so I
forget uh to do something here so I think this is just fine mxw screen 2
think this is just fine mxw screen 2 Excel that's okay let's take a look in
Excel that's okay let's take a look in the workspaces workspace ID
the workspaces workspace ID page I think I know uh what I
page I think I know uh what I have uh done so let's go ahead and give
have uh done so let's go ahead and give this div a class name of full width and
this div a class name of full width and on large maximum width of excel so I
on large maximum width of excel so I think that now there we go now we get
think that now there we go now we get some serious space here great and let's
some serious space here great and let's go ahead and now and actually call the
go ahead and now and actually call the proper function in the edit workspace
proper function in the edit workspace form because so so far I think we've
form because so so far I think we've been working with use create workspace
been working with use create workspace so now let's do use update
so now let's do use update workspace and remove use create
workspace and remove use create workspace here and you can see that the
workspace here and you can see that the moment I replaced with that this new
moment I replaced with that this new mutate no longer gives us any errors so
mutate no longer gives us any errors so let's see if this actually works uh so
let's see if this actually works uh so my current workspace name is test image
my current workspace name is test image I'm going to click settings and call
I'm going to click settings and call this test rename and click save change
this test rename and click save change es and okay I get an error let's see
es and okay I get an error let's see what's going on and I get a 405 can you
what's going on and I get a 405 can you guess why did I get a 405 so that means
guess why did I get a 405 so that means method not found uh let's go ahead and
method not found uh let's go ahead and go inside of source app folder API rout
go inside of source app folder API rout route. DS I forgot we have a new patch
route. DS I forgot we have a new patch so let's go ahead and handle that with
so let's go ahead and handle that with hono as well let's save it let's try
hono as well let's save it let's try again test
again test rename and all right something happened
rename and all right something happened but looks like I have made a mistake uh
but looks like I have made a mistake uh in regards of my uh image URL here so it
in regards of my uh image URL here so it looks like I have somehow accidentally
looks like I have somehow accidentally updated one of my workspaces here in
updated one of my workspaces here in this case it's this one called test
this case it's this one called test rename I have updated the image URL to
rename I have updated the image URL to literally be a string undefined and I
literally be a string undefined and I think that's because of my recent change
think that's because of my recent change I did in the edit workspace form inside
I did in the edit workspace form inside of my onsubmit method I have changed
of my onsubmit method I have changed this
this from an empty string to undefined I will
from an empty string to undefined I will now convert it back to an empty string
now convert it back to an empty string so now it is exactly the same as the
so now it is exactly the same as the create workspace form so if I go inside
create workspace form so if I go inside of my own submit here there we go it's
of my own submit here there we go it's exactly the same now so this is what I'm
exactly the same now so this is what I'm going to do if you cannot find if this
going to do if you cannot find if this happened to you and you cannot find
happened to you and you cannot find which one it is what you can do is you
which one it is what you can do is you know just go inside of your workspaces
know just go inside of your workspaces be careful that you're not selecting
be careful that you're not selecting collections select all workspaces and
collections select all workspaces and just delete all
just delete all workspaces sometimes you might have two
workspaces sometimes you might have two pages I'm now going to go inside of my
pages I'm now going to go inside of my members and I'm going to delete all
members and I'm going to delete all members
members here let's try this thing again I'm
here let's try this thing again I'm going to go ahead and I'm going to go
going to go ahead and I'm going to go inside of Local Host 3000 here and I
inside of Local Host 3000 here and I will create a new you know test with
will create a new you know test with image and I'm going to go ahead and
image and I'm going to go ahead and select uh a random image that I have uh
select uh a random image that I have uh from unsplash okay
from unsplash okay let's create a workspace
let's create a workspace here there we go this works just fine
here there we go this works just fine I'm going to go inside of settings and I
I'm going to go inside of settings and I will change this to test rename and
will change this to test rename and click save
click save changes and there we go I successfully
changes and there we go I successfully renamed this workspace great and I'm now
renamed this workspace great and I'm now going to go ahead and try and upload a
going to go ahead and try and upload a new
new image so let's go ahead and try this out
image so let's go ahead and try this out another image from unsplash here let's
another image from unsplash here let's see if this time a new image will be
see if this time a new image will be appended and there we go a new image was
appended and there we go a new image was added great before I wrap up the chapter
added great before I wrap up the chapter there's just one more thing I want to do
there's just one more thing I want to do and it's in regards to well both the
and it's in regards to well both the create and the update form but let's do
create and the update form but let's do it in the create form first so have your
it in the create form first so have your create form opened and go inside of the
create form opened and go inside of the create workspace form right here one
create workspace form right here one thing that we forgot to do is that when
thing that we forgot to do is that when I add
I add image I have no way of removing that
image I have no way of removing that image right so let's go ahead and just
image right so let's go ahead and just create a quick uh function for that so
create a quick uh function for that so this is what we're going to do we're
this is what we're going to do we're going to go ahead and find our button
going to go ahead and find our button for upload image here inside of the
for upload image here inside of the create workspace form and I'm going to
create workspace form and I'm going to wrap it inside of a ternary conditional
wrap it inside of a ternary conditional if we have field. value in that case I'm
if we have field. value in that case I'm going to render one button otherwise I'm
going to render one button otherwise I'm going to render another button so I will
going to render another button so I will just copy this button and put it here
just copy this button and put it here like
like this and now I'm going to change uh what
this and now I'm going to change uh what these buttons actually do so if we have
these buttons actually do so if we have a image then the button will say remove
a image then the button will say remove image its variant will be
image its variant will be destructive and it's on click will do
destructive and it's on click will do the
the following it will first do field on
following it will first do field on change to be null and then it will do if
change to be null and then it will do if input ref. current it will do input ref.
input ref. current it will do input ref. current. value is an empty string and
current. value is an empty string and there we go you can see that now uh if I
there we go you can see that now uh if I do test here and if I upload an image I
do test here and if I upload an image I can click remove image and the image
can click remove image and the image gets removed great and now we can do the
gets removed great and now we can do the exact same thing so I'm going to copy
exact same thing so I'm going to copy this stary and I'm going to add it
this stary and I'm going to add it inside of my edit workspace form right
inside of my edit workspace form right here so let me go ahead and find my
here so let me go ahead and find my upload image button I will select it and
upload image button I will select it and I will replace it with the these taries
I will replace it with the these taries to either remove an image or to upload
to either remove an image or to upload an image so now if I go inside of my
an image so now if I go inside of my settings page you can see that I have
settings page you can see that I have the option to remove the image here as
the option to remove the image here as well great so you've just created the
well great so you've just created the settings page uh you've handled out
settings page uh you've handled out redirects you have loaded the initial
redirects you have loaded the initial settings you've learned how to mix
settings you've learned how to mix server components with client components
server components with client components you've learned how server components can
you've learned how server components can be served as server environments one
be served as server environments one thing that's bugging me that's probably
thing that's bugging me that's probably bugging you as well is this inside of my
bugging you as well is this inside of my actions here in the workspaces I am
actions here in the workspaces I am repeating a lot of code especially you
repeating a lot of code especially you know this client it's mostly because you
know this client it's mostly because you know of this I kind of want to handle
know of this I kind of want to handle this in a different way every time but I
this in a different way every time but I think what I can do is I can just throw
think what I can do is I can just throw error if there's no session and then I
error if there's no session and then I can just decide how my catch will react
can just decide how my catch will react right so I think that's what we can
right so I think that's what we can focus on in the next chapter abstracting
focus on in the next chapter abstracting this client right here and then we're
this client right here and then we're going to go ahead and do the settings uh
going to go ahead and do the settings uh do the members for a workspace great
do the members for a workspace great great
great job now let's go ahead and let's
job now let's go ahead and let's refactor our server side queries
refactor our server side queries especially that repeating uh client
especially that repeating uh client initialization code so let's find the
initialization code so let's find the first one I want to fix the first one I
first one I want to fix the first one I want to fix is inside of the in is the
want to fix is inside of the in is the basically get
basically get current so here here's what I want to
current so here here's what I want to change first instead of calling this
change first instead of calling this actions I want to call this
actions I want to call this queries because they're not actions so
queries because they're not actions so now we have to find all files which have
now we have to find all files which have used get
used get current so let's go ahead and do that
current so let's go ahead and do that and let's refactor them I'm going to go
and let's refactor them I'm going to go ahead and expand this as much as I can
ahead and expand this as much as I can so first one is insource app folder out
so first one is insource app folder out route group signin file and it looks for
route group signin file and it looks for action
action so we're going to go ahead and change
so we're going to go ahead and change this to queries or we can do this in a
this to queries or we can do this in a faster way
faster way perhaps if you're using visual studio
perhaps if you're using visual studio code you can search for all instances of
code you can search for all instances of actions and then you can select all of
actions and then you can select all of them and replace them with
them and replace them with queries just make sure you don't
queries just make sure you don't misspell them and
misspell them and replace and I think that now there we go
replace and I think that now there we go I have eight refactored files and all of
I have eight refactored files and all of them have the same change simply
them have the same change simply changing features out queries uh
changing features out queries uh features out actions to queries so you
features out actions to queries so you can do
can do that great that's the first thing I
that great that's the first thing I wanted to do because they're not actions
wanted to do because they're not actions so let's now go back inside of features
so let's now go back inside of features Al
Al queries and now uh let's resolve this so
queries and now uh let's resolve this so we're going to go ahead inside of our
we're going to go ahead inside of our lib app right and we're actually going
lib app right and we're actually going to do
to do uh what was here in the first place so
uh what was here in the first place so export
export asynchronous function create session
asynchronous function create session client which I so proudly told you that
client which I so proudly told you that we're not going to do because we're
we're not going to do because we're going to use the middleware well we are
going to use the middleware well we are still going to use it in the middle work
still going to use it in the middle work separately so this will be for Server
separately so this will be for Server components and let's copy the client
components and let's copy the client from get current for
from get current for example like
example like this and then let's go ahead and let's
this and then let's go ahead and let's get the
session so we need to import cookies from next headers and we need to import
from next headers and we need to import Al cookie from features Al constants
Al cookie from features Al constants like this and make sure to also have
like this and make sure to also have server only here at the Top If there is
server only here at the Top If there is no session or if there is no session.
no session or if there is no session. value we're going to throw new
value we're going to throw new error unauthorized
error unauthorized here otherwise we're going to go ahead
here otherwise we're going to go ahead and do this well first we're going to do
and do this well first we're going to do client set
client set session session.
value and then we're going to go ahead and do this so get
and do this so get account get
databases let's go ahead and do return new databases
new databases client and I think for now we only need
client and I think for now we only need account and that I don't think I've used
account and that I don't think I've used anything else and I just want to bring
anything else and I just want to bring something to your attention so you can
something to your attention so you can see how here I'm using session and
see how here I'm using session and session. value but you probably notice
session. value but you probably notice that inside of my session middleware I
that inside of my session middleware I only do if not session that's because
only do if not session that's because inside inside of the session middleware
inside inside of the session middleware we're using get cookie from hono cookie
we're using get cookie from hono cookie so inside of here session is directly
so inside of here session is directly the value but inside of the next cookie
the value but inside of the next cookie inste of here it's a object which has
inste of here it's a object which has the value so that's why I'm doing that
the value so that's why I'm doing that and since we are not going to use this
and since we are not going to use this inside of any routes honor routes we're
inside of any routes honor routes we're going to use them inside of server
going to use them inside of server components directly so this is why I
components directly so this is why I have to handle using next headers all
have to handle using next headers all right so now I have uh this create
right so now I have uh this create session client set up so let me go back
session client set up so let me go back and set of my out queries. DS and let me
and set of my out queries. DS and let me remove this entire thing
remove this entire thing now so I'm just going to do const create
now so I'm just going to do const create session client
session client here and can I the structure
here and can I the structure account from here oh
account from here oh wait and I have to import create SE
wait and I have to import create SE client from Li app right can I destruct
client from Li app right can I destruct your account I can yes this is exactly
your account I can yes this is exactly what I do and I'm going to wrap it
what I do and I'm going to wrap it inside of try and catch because I'm
inside of try and catch because I'm going to get an error thrown here if I'm
going to get an error thrown here if I'm unauthorized there we go I just reduced
unauthorized there we go I just reduced a bunch of code here let's just see if
a bunch of code here let's just see if it's working so I'm in a random
it's working so I'm in a random workspace ID here if I log out this
workspace ID here if I log out this should still read irect me and it does
should still read irect me and it does and if I go to local close
and if I go to local close 3000 it redirects me back if I go to
3000 it redirects me back if I go to local 3000 workspaces one through three
local 3000 workspaces one through three settings it redirects me back
settings it redirects me back immediately excellent now I'm going to
immediately excellent now I'm going to go back inside of my uh user
go back inside of my uh user here and I'm going to try the opposite
here and I'm going to try the opposite I'm now logged in and I'm going to try
I'm now logged in and I'm going to try and go to slash sign
and go to slash sign in and I get r directed to here
in and I get r directed to here excellent I think we have we established
excellent I think we have we established that this is working just fine so now we
that this is working just fine so now we resolved the out queries let's go ahead
resolved the out queries let's go ahead and go inside of the workspaces again we
and go inside of the workspaces again we have workspaces actions here let's go
have workspaces actions here let's go ahead and first do the refactoring of
ahead and first do the refactoring of renaming the actions to queries because
renaming the actions to queries because they're not
they're not actions
actions queries like this so now I'm going to go
queries like this so now I'm going to go ahead and find everywhere where we use
ahead and find everywhere where we use featur features workspaces actions okay
featur features workspaces actions okay lucky for us it's only two places in the
lucky for us it's only two places in the dashboard uh page right here let me show
dashboard uh page right here let me show you where it's located so instead of
you where it's located so instead of source app dashboard rout group page.
source app dashboard rout group page. TSX change this to queries that's the
TSX change this to queries that's the first thing we're going to do and one
first thing we're going to do and one more place is here let me show
more place is here let me show you again instead of source app folder
you again instead of source app folder uh this time in Stand Alone workspaces
uh this time in Stand Alone workspaces workspace ID settings page. DSX so
workspace ID settings page. DSX so change these to queries basically find
change these to queries basically find all instances of features workspaces
all instances of features workspaces actions and they now must be
actions and they now must be queries there we go first part of
queries there we go first part of refactoring done now let's go inside of
refactoring done now let's go inside of features workspaces here inside of the
features workspaces here inside of the queries and let's go ahead and remove
queries and let's go ahead and remove this now so we no longer need any of
this now so we no longer need any of this here
this here the only thing we're going to need now
the only thing we're going to need now are the databases and the account so I'm
are the databases and the account so I'm going to do const databases and account
going to do const databases and account from async create session client not
from async create session client not async a
async a wait create session client from lib app
wait create session client from lib app right
right and I think this will work just fine
and I think this will work just fine it's inside of try cat and inside of cat
it's inside of try cat and inside of cat we decide that this is what we return so
we decide that this is what we return so so in case we get an error here it will
so in case we get an error here it will fall back to this which was exactly what
fall back to this which was exactly what it was before great so we have resolved
it was before great so we have resolved this and now let's do the exact same
this and now let's do the exact same thing instead of get workspace so we
thing instead of get workspace so we remove the entire client thing here and
remove the entire client thing here and all of
all of these I just add this and there we go so
these I just add this and there we go so what are we supposed to test now well
what are we supposed to test now well first let's remove the unused out cookie
first let's remove the unused out cookie let's remove the unused Imports
so we are supposed to test these things we're supposed to test whether we
things we're supposed to test whether we get redirected the to the first
get redirected the to the first available workspace ID when we hit Local
available workspace ID when we hit Local Host 3000 let's see if that
Host 3000 let's see if that works it works we get redirected to the
works it works we get redirected to the first ID great the second thing we have
first ID great the second thing we have to check is the settings page so let's
to check is the settings page so let's see if that works if it does we have
see if that works if it does we have successfully
successfully refactored there we go amazing amazing
refactored there we go amazing amazing job you've just done a refactor so you
job you've just done a refactor so you don't write as many code and you can now
don't write as many code and you can now safely you know uh use this inside of
safely you know uh use this inside of your server components every time you
your server components every time you need uh great so does this mean that uh
need uh great so does this mean that uh We've wrote some API routes which are
We've wrote some API routes which are useless no it's just that if we have a
useless no it's just that if we have a server component it's a shame not to use
server component it's a shame not to use it right uh but don't worry we're later
it right uh but don't worry we're later going to have some models
going to have some models which are
which are impossible uh to you know prefill as a
impossible uh to you know prefill as a server component so we are going to have
server component so we are going to have to use those API routes we have which we
to use those API routes we have which we have just created but you know uh if
have just created but you know uh if possible I like to use server components
possible I like to use server components uh to fetch the data and especially to
uh to fetch the data and especially to redirect and protect because it's just
redirect and protect because it's just more convenient than to use use effect
more convenient than to use use effect great so you've handled that refactor
great so you've handled that refactor and what we're going to do next is
and what we're going to do next is create the members page great great
create the members page great great job before we can move on onto the
job before we can move on onto the members list there is a couple of things
members list there is a couple of things I forgot to do in the settings page so
I forgot to do in the settings page so we're going to go back to our edit
we're going to go back to our edit workspace form but before we can even do
workspace form but before we can even do that we have to create some new API
that we have to create some new API routes so let's go inside of features
routes so let's go inside of features workspaces server route. TS and inside
workspaces server route. TS and inside of here at the bottom I'm going to add a
of here at the bottom I'm going to add a delete workspace ID so we're going to
delete workspace ID so we're going to create the delete method now I'm going
create the delete method now I'm going to add the session middleware and I'm
to add the session middleware and I'm going to add uh an asynchronous
going to add uh an asynchronous controller right here let's go ahead and
controller right here let's go ahead and let's get the databases from CET
let's get the databases from CET databases let's get the user from CET
databases let's get the user from CET user and let's get the current member
user and let's get the current member from await get member
from await get member and let's also destructure the workspace
and let's also destructure the workspace ID from C request parm like this now we
ID from C request parm like this now we can pass in the databases we can pass in
can pass in the databases we can pass in the workspace ID and the user ID coming
the workspace ID and the user ID coming from user. dollar sign ID if there is no
from user. dollar sign ID if there is no member found or if that member. roll is
member found or if that member. roll is not member ro. admin in any of those
not member ro. admin in any of those cases
cases we're going to go ahead and break this
we're going to go ahead and break this method by returning back an error
unauthorized and the 401 code otherwise let's go ahead and let's
code otherwise let's go ahead and let's delete the database document so await
delete the database document so await databases. delete
databases. delete document database underscore ID
document database underscore ID workspaces underscore ID and passing the
workspaces underscore ID and passing the workspace ID right here and we're going
workspace ID right here and we're going to return back the ID which we deleted
to return back the ID which we deleted so I'm going to do c.
so I'm going to do c. Json data and I'm going to simulate as
Json data and I'm going to simulate as this was coming from app right since
this was coming from app right since since we've learned that our data will
since we've learned that our data will almost always have dollar sign ID I
almost always have dollar sign ID I don't want to change that now by adding
don't want to change that now by adding my ID so I'm going to keep the dollar
my ID so I'm going to keep the dollar sign ID and I'm going to add
sign ID and I'm going to add uh the workspace ID which I had from my
uh the workspace ID which I had from my param like this there we go so that is
param like this there we go so that is my delete document and I'm going to add
my delete document and I'm going to add a to-do list here to-do delete
a to-do list here to-do delete members
members projects and
projects and tasks because we have to delete them
tasks because we have to delete them manually if we are deleting a
manually if we are deleting a workspace all right now let's go ahead
workspace all right now let's go ahead instead of API and let's copy use create
instead of API and let's copy use create workspace and let's rename it to use
workspace and let's rename it to use delete
delete workspace let's go inside of the newly
workspace let's go inside of the newly created use delete workspace and let's
created use delete workspace and let's go ahead and rename some things so use
go ahead and rename some things so use delete
delete workspace and let's go ahead and change
workspace and let's go ahead and change this to be
this to be workspaces workspace ID and change the
workspaces workspace ID and change the method to be delete there we go response
method to be delete there we go response type and request type let's go ahead and
type and request type let's go ahead and do the same thing inside of the mutation
do the same thing inside of the mutation function itself so workspace ID and then
function itself so workspace ID and then the delete method and besides this time
the delete method and besides this time we're not going to have form we're
we're not going to have form we're actually only going to have
actually only going to have Pam there we go no errors and we're
Pam there we go no errors and we're going to change the error here to be
going to change the error here to be failed to delete workspace this will be
failed to delete workspace this will be workspace deleted and this will be
workspace deleted and this will be failed to delete workspace once again
failed to delete workspace once again we're going to invalidate the queries
we're going to invalidate the queries workspaces and the individual workspace
workspaces and the individual workspace with our
data uh let's see also we have to change the response type here to only be the
the response type here to only be the positive one because we are protecting
positive one because we are protecting that API route so it can have an error
that API route so it can have an error coming back so inside of here we're
coming back so inside of here we're going to also get data ID this does not
going to also get data ID this does not exist yet same thing as our update
exist yet same thing as our update workspace method great so we now have
workspace method great so we now have our used delete workspace here so let's
our used delete workspace here so let's go ahead and let's revisit our
go ahead and let's revisit our workspaces feature for and go inside of
workspaces feature for and go inside of components edit form inside of the edit
components edit form inside of the edit uh workspace form here let's go ahead
uh workspace form here let's go ahead and let's wrap this entire thing uh
and let's wrap this entire thing uh inside of a div so before the first card
inside of a div so before the first card even appears let's add a div because
even appears let's add a div because we're going to have multiple cards
we're going to have multiple cards inside like this so I'm going to go
inside like this so I'm going to go ahead and indent this entire card here
ahead and indent this entire card here and I'm going to give this div a class
and I'm going to give this div a class name of flex Flex column and Gap y of
name of flex Flex column and Gap y of four so now I'm going to go ahead and go
four so now I'm going to go ahead and go to the end here and add a new
to the end here and add a new card and I'm going to go ahead and give
card and I'm going to go ahead and give this card a class
this card a class name full width full height border none
name full width full height border none and Shadow none basically exactly as we
and Shadow none basically exactly as we had above then inside of here I'm going
had above then inside of here I'm going to go ahead and add oops card
to go ahead and add oops card content in which I'm going to give a
content in which I'm going to give a class name name padding
seven and I'm going to add a div here with a class
with a class name Flex Flex
name Flex Flex column and then inside of here I'm going
column and then inside of here I'm going to add an H3 element danger zone I'm
to add an H3 element danger zone I'm going to go ahead and give the danger
going to go ahead and give the danger zone a class name of font
zone a class name of font bold and then below that a paragraph
bold and then below that a paragraph explaining what's happening so I'm going
explaining what's happening so I'm going to give this a text small and text muted
to give this a text small and text muted for
ground inside of here we're going to say deleting a workspace is a
deleting a workspace is a irreversible and will remove all
irreversible and will remove all Associated data uh so is irreversible
Associated data uh so is irreversible let's just fix the grammar deleting a
let's just fix the grammar deleting a workspace is irreversible and will
workspace is irreversible and will remove all uh Associated data okay I I
remove all uh Associated data okay I I hope I wrote that correctly great and
hope I wrote that correctly great and outside of this paragraph we're going to
outside of this paragraph we're going to go ahead and add a button and this
go ahead and add a button and this button will say delete
button will say delete workspace let's go ahead and let's give
workspace let's go ahead and let's give this button a class name of margin top
this button a class name of margin top six with fit ml Auto like this and let's
six with fit ml Auto like this and let's go ahead and let's give it a size small
go ahead and let's give it a size small let's give it a variant of
destructive and let's go ahead and give it a type of
button and now let's give it a disabled of is
of is pending and let's go ahead and give it
pending and let's go ahead and give it an on click for now to just be an empty
an on click for now to just be an empty Arrow function so there we go uh we now
Arrow function so there we go uh we now have uh a danger zone right here where
have uh a danger zone right here where we can delete our workspace but before
we can delete our workspace but before we can do that uh I want to create
we can do that uh I want to create something called a confirmation model so
something called a confirmation model so I don't want to be able to just click
I don't want to be able to just click here and get my workspace immediately
here and get my workspace immediately removed I first want to create a use
removed I first want to create a use confirm
confirm hook let's go inside of source and let's
hook let's go inside of source and let's create the reusable hooks folder inside
create the reusable hooks folder inside of here I'm going to create the use
of here I'm going to create the use confirm. TSX so it's important that it
confirm. TSX so it's important that it is a TSX file let's go ahead and let's
is a TSX file let's go ahead and let's import use state from
import use state from react let's import button not from react
react let's import button not from react today picker but from s/ components UI
today picker but from s/ components UI button from here and alongside button I
button from here and alongside button I also want to import the type button
also want to import the type button props
props here and then let's also import a
here and then let's also import a responsive
responsive model from components responsive model
model from components responsive model and let's go ahead and let's import
and let's go ahead and let's import everything from our card so card card
everything from our card so card card content description header and title and
content description header and title and then let's export con use conf
then let's export con use conf confirm so this will be a handy little
confirm so this will be a handy little hook which will trigger a model for us
hook which will trigger a model for us every time we need
every time we need one
one so let's go ahead and let's give it a
so let's go ahead and let's give it a required title a required
required title a required message and then a variant which will be
message and then a variant which will be button props
button props variant and it will have a default value
variant and it will have a default value of primary so it's optional then let's
of primary so it's optional then let's define the promise and set promise to be
define the promise and set promise to be US state and by default it will be null
US state and by default it will be null but let's give it some proper types here
but let's give it some proper types here so it can be a type of object which has
so it can be a type of object which has resolve inside of itself with a value
resolve inside of itself with a value Boolean like this and it returns back a
Boolean like this and it returns back a void
void or it can be
or it can be null like
null like this then we're going to go ahead and
this then we're going to go ahead and create const
create const confirm which will call new promise so
confirm which will call new promise so we can aade this whenever we need it
we can aade this whenever we need it we're going to see how handy the
we're going to see how handy the functionality is in a moment once we
functionality is in a moment once we wrap it up so let's call set promise and
resolve then let's do const handle close to very simply return the promise as a
to very simply return the promise as a null which will later control the
null which will later control the dialogue which will close thanks to this
dialogue which will close thanks to this action so that's handle close then let's
action so that's handle close then let's do con handle
do con handle confirm so this handle confirm will
confirm so this handle confirm will simply call the promise and resolve it
simply call the promise and resolve it to tr and then wherever we use this
to tr and then wherever we use this we're going to consider this as
we're going to consider this as something that user pressed to
something that user pressed to confirm and we also have to do handle
confirm and we also have to do handle close after
close after this and then we also have to handle
this and then we also have to handle cancel which is basically deny so handle
cancel which is basically deny so handle cancel promise resolve false and then
cancel promise resolve false and then close and finally then we can go ahead
close and finally then we can go ahead and create our const confirmation
and create our const confirmation dialogue let's use the responsive
dialogue let's use the responsive model let's give it an open to be if
model let's give it an open to be if promise is not null and on open change
promise is not null and on open change can simply be handled
can simply be handled close inside let's add a card with a
close inside let's add a card with a class name full height B width border
class name full height B width border none and Shadow
none and Shadow none and let's add card content here
none and let's add card content here with a class name adding top of eight
with a class name adding top of eight inside of the card content let's add a
inside of the card content let's add a card
header which will hold the card title which will have the title prop which we
which will have the title prop which we are going to pass and let's give the
are going to pass and let's give the card header an override padding to
card header an override padding to padding zero let's let's add card
padding zero let's let's add card description to Simply have our message
description to Simply have our message prop here outside of the card header
prop here outside of the card header we're going to add a div with a class
we're going to add a div with a class name padding top of four full with flex
name padding top of four full with flex Flex column Gap Y 2 large Flex row Gap
Flex column Gap Y 2 large Flex row Gap X2 items Center and justify
X2 items Center and justify end and then we're going to go inside
end and then we're going to go inside and add two
and add two buttons both of these buttons are coming
buttons both of these buttons are coming from type component UI button so the
from type component UI button so the first button will have an onclick of
first button will have an onclick of handle
handle cancel variant of outline and a class
cancel variant of outline and a class name full width but on large
name full width but on large devices outo
devices outo with and inside this button will say
with and inside this button will say cancel we're then going to copy this
cancel we're then going to copy this this one will say and I'll confirm the
this one will say and I'll confirm the variant will be a prop
variant will be a prop variant and the class name can stay the
variant and the class name can stay the same and this one will say
same and this one will say confirm like this and then we just have
confirm like this and then we just have to
to return confirmation dialogue and
return confirmation dialogue and confirm like
confirm like this and let's just
this and let's just modify our expected return props here
modify our expected return props here because otherwise uh typescript is going
because otherwise uh typescript is going to give us an error so inside of the use
to give us an error so inside of the use confirm here go ahead and give it the
confirm here go ahead and give it the expected return types to be an
expected return types to be an array the first element is our confirm
array the first element is our confirm model which is a jsx do element like
model which is a jsx do element like this and the second one is a promise of
this and the second one is a promise of unknown there we go and looks like I'm
unknown there we go and looks like I'm getting uh an error here uh so let me
getting uh an error here uh so let me just confirm oh yes the confirmation
just confirm oh yes the confirmation dialogue here is supposed to be
dialogue here is supposed to be returning not inside of curly brackets
returning not inside of curly brackets inside of normal brackets my
inside of normal brackets my apologies okay that
apologies okay that works uh but looks like uh my promise
works uh but looks like uh my promise here is set incorrectly so let me just
here is set incorrectly so let me just see uh what mistake did I make
here oh I think I know why it's because I have to return the promise inside of
I have to return the promise inside of my confirm method I was looking at this
my confirm method I was looking at this sorry so return new promise instead of
sorry so return new promise instead of the confirm method and there we go now
the confirm method and there we go now this is resolve great so now let's go
this is resolve great so now let's go back inside of my features
back inside of my features workspaces inside of components edit
workspaces inside of components edit workspace form and what we can do now is
workspace form and what we can do now is a very cool thing I can go ahead and add
a very cool thing I can go ahead and add const uh Delete confirm delete dialogue
const uh Delete confirm delete dialogue let's call it like that delete dialogue
let's call it like that delete dialogue and confirm
delete from use confirm which is my you know globally
confirm which is my you know globally reusable hook I'm just going to move it
reusable hook I'm just going to move it here and I can go ahead and say exactly
here and I can go ahead and say exactly uh what I want to warn my user about so
uh what I want to warn my user about so for example when they're deleting I'm
for example when they're deleting I'm going to give them a title of delete
going to give them a title of delete workspace and I'm simply going to tell
workspace and I'm simply going to tell them this
them this action cannot be undone
action cannot be undone and I'm going to give them a destructive
and I'm going to give them a destructive variant so they know that this is a
variant so they know that this is a destructive action I'm then going to go
destructive action I'm then going to go ahead and create a method const handle
ahead and create a method const handle delete which is going to be an
delete which is going to be an asynchronous method and I will then get
asynchronous method and I will then get an okay from await confirm
an okay from await confirm delete if not okay I will break the
delete if not okay I will break the method otherwise I can cons the log
method otherwise I can cons the log deleting like this let's go ahead and
deleting like this let's go ahead and use dis handle delete and let's assign
use dis handle delete and let's assign it to our new button delete workspace
it to our new button delete workspace let's see what happens I'm going to go
let's see what happens I'm going to go ahead and open my console here and when
ahead and open my console here and when I click delete workspace let's refresh
I click delete workspace let's refresh uh first when I click delete workspace
uh first when I click delete workspace uh looks like nothing is happening and I
uh looks like nothing is happening and I think I know why because I forgot an
think I know why because I forgot an important thing I never used the delete
important thing I never used the delete dialogue we have to render that inside
dialogue we have to render that inside of our code so I'm going to put it just
of our code so I'm going to put it just after my first div here
after my first div here and it's a self closing tag let's try
and it's a self closing tag let's try this again now I'm going to refresh this
this again now I'm going to refresh this I'm going to expand my inspect element
I'm going to expand my inspect element I'm going to click delete workspace and
I'm going to click delete workspace and there we go you can see how it asks me
there we go you can see how it asks me to confirm or deny and this is mobile
to confirm or deny and this is mobile mode I believe uh is it mobile no okay
mode I believe uh is it mobile no okay so this is on mobile on desktop mode I
so this is on mobile on desktop mode I would expect it uh to be next to one
would expect it uh to be next to one another so let's go ahead and see can I
another so let's go ahead and see can I quickly resolve that in of use confirm
quickly resolve that in of use confirm here uh so it
here uh so it says Flex Flex column but in large it is
says Flex Flex column but in large it is flex row and I think that this should be
flex row and I think that this should be justified between
maybe no this still is not working uh okay let's leave this back ad
working uh okay let's leave this back ad justify end let's just leave it like
justify end let's just leave it like this for now okay and when I click
this for now okay and when I click confirm here
confirm here you can see I get the deleting console
you can see I get the deleting console inside so now I have a confirmation
inside so now I have a confirmation which will fire every time that I need
which will fire every time that I need to delete something dangerous uh great
to delete something dangerous uh great now let's go ahead and let's add another
now let's go ahead and let's add another mutation here so
mutation here so const mutate delete workspace is pending
const mutate delete workspace is pending will be is deleting
workspace and let me just fix the capitalization here from use delete
capitalization here from use delete workspace so this is a new hook which we
workspace so this is a new hook which we we've recently created API use delete
we've recently created API use delete workspace I'm just going to collapse
workspace I'm just going to collapse these two so it's more readable for
these two so it's more readable for us and now I'm going to go ahead and go
us and now I'm going to go ahead and go here and simply pass in the delete
here and simply pass in the delete workspace and I'm going to pass the PM
workspace and I'm going to pass the PM to be workspace ID initial values.
to be workspace ID initial values. ID that's it and after the on success
ID that's it and after the on success here
push to the root page there we go uh but I do want to fix
page there we go uh but I do want to fix uh this because it is bothering me uh so
uh this because it is bothering me uh so let's see what exactly uh is going wrong
let's see what exactly uh is going wrong here so I should be able to expand
here so I should be able to expand this let me zoom out as much as I can
this let me zoom out as much as I can I'm trying to keep it inside of uh
I'm trying to keep it inside of uh desktop mode so let's see what if I
desktop mode so let's see what if I remove the flex column completely
remove the flex column completely here so now it works so this LG Flex
here so now it works so this LG Flex row seems to not be
row seems to not be working that's interesting the position
working that's interesting the position does not
does not matter but I am definitely on LG value
matter but I am definitely on LG value if I move it to a lower breakpoint
if I move it to a lower breakpoint medium still the same
medium still the same thing uh okay H I'm going to pause the
thing uh okay H I'm going to pause the video and I'm going to see how I can
video and I'm going to see how I can debug
debug this I think I know why it's
this I think I know why it's happening uh and it's one of those
happening uh and it's one of those things which I warned you about in the
things which I warned you about in the beginning so it it's it's bothering me
beginning so it it's it's bothering me because the Tailwind for this is correct
because the Tailwind for this is correct it should be
it should be working let's go inside of our Tailwind
working let's go inside of our Tailwind config and let's see something our
config and let's see something our Tailwind is covered inside of pages
Tailwind is covered inside of pages inside of components inside of app and
inside of components inside of app and inside of features which we have added
inside of features which we have added and where is our source hooks use
and where is our source hooks use confirm inside of hooks which means
confirm inside of hooks which means Tailwind is not active there you can see
Tailwind is not active there you can see what kind of bugs appear and they're so
what kind of bugs appear and they're so so hard to debug because you have no
so hard to debug because you have no idea what's going on so let's add hooks
idea what's going on so let's add hooks here as well this is why I warned you in
here as well this is why I warned you in the beginning to add this to your
the beginning to add this to your features because you would be losing
features because you would be losing your mind as to why it's happening and
your mind as to why it's happening and there we go the moment I have added that
there we go the moment I have added that you can see that now I have a nice
you can see that now I have a nice mobile and desktop delete workspace and
mobile and desktop delete workspace and let's see if this is finally working so
let's see if this is finally working so inside of here I have one workspace let
inside of here I have one workspace let me create uh two workspaces
me create uh two workspaces here I'm going to go to the old one
here I'm going to go to the old one click settings delete workspace and
click settings delete workspace and confirm it says failed to delete
confirm it says failed to delete workspace and it says 40 5 you already
workspace and it says 40 5 you already know what that means it's because I
know what that means it's because I consistently refuse to write all the uh
consistently refuse to write all the uh methods inside of my initial hono here
methods inside of my initial hono here but I think this will be the last one we
but I think this will be the last one we will use so there we go delete finally
will use so there we go delete finally all right please work now confirm there
all right please work now confirm there we go workspace deleted and only the
we go workspace deleted and only the other one is available amazing so if I
other one is available amazing so if I deleted this one now I should get a rid
deleted this one now I should get a rid directed uh to my create method so this
directed uh to my create method so this is kind of a bug okay good thing that we
is kind of a bug okay good thing that we have tested this uh so yeah H that's
have tested this uh so yeah H that's interesting yeah so it redirected to
interesting yeah so it redirected to this and then I got redirected I guess
this and then I got redirected I guess to some cache I guess this is cash so
to some cache I guess this is cash so here's what you can do in order to avoid
here's what you can do in order to avoid that kind of
that kind of thing uh we can go inside of features
thing uh we can go inside of features workspaces API use delete workspace and
workspaces API use delete workspace and instead of uh okay my apologies uh use
instead of uh okay my apologies uh use delete where do I use use
delete inside of edit workspace form okay so it's what I was just had opened
okay so it's what I was just had opened here let me find the delete workspace
here let me find the delete workspace here and instead of doing router. push
here and instead of doing router. push I'm going to do
I'm going to do window.location.href
window.location.href right here this will do a hard refresh
right here this will do a hard refresh and I think it will clear any cache
and I think it will clear any cache which is there at that moment which uh
which is there at that moment which uh you know tells you that the route still
you know tells you that the route still exists so if I now create a test
exists so if I now create a test workspace
workspace here and if I go inside of settings let
here and if I go inside of settings let me just refresh just in case and click
me just refresh just in case and click delete
delete workspace there we go I'm redirected
workspace there we go I'm redirected back to the create method great so that
back to the create method great so that now works amazing and let me just use
now works amazing and let me just use this is deleting workspace uh all the
this is deleting workspace uh all the way down to this button so I'm going to
way down to this button so I'm going to disable it if it's spending in meaning
disable it if it's spending in meaning like we are saving the form above but
like we are saving the form above but also if we are deleting
also if we are deleting it uh great so this is now working we
it uh great so this is now working we now have extended our settings form here
now have extended our settings form here and there is one more thing we have to
and there is one more thing we have to extend the settings form with and that
extend the settings form with and that is the reset of the invite code but
is the reset of the invite code but we're going to stop the chapter uh so
we're going to stop the chapter uh so you can wrap up the deletion process
you can wrap up the deletion process great great
great great job so to wrap up the settings form of
job so to wrap up the settings form of the individual workspace let's create
the individual workspace let's create the last method and action which will be
the last method and action which will be here which is the action to reset the
here which is the action to reset the invite code which is a 10 six 58 digit
invite code which is a 10 six 58 digit code whatever you decide so let's go
code whatever you decide so let's go ahead inside of our features workspaces
ahead inside of our features workspaces server route. DS and I'm going to go
server route. DS and I'm going to go ahead and I will copy the delete
ahead and I will copy the delete method and just chain it below I'm going
method and just chain it below I'm going to change this to be a post method and
to change this to be a post method and it's going to be called slw workspace ID
it's going to be called slw workspace ID SL reset invite
SL reset invite code and we're going to do the exact
code and we're going to do the exact same logic here so only admins can reset
same logic here so only admins can reset invite codes here we don't have to
invite codes here we don't have to delete anything here instead the only
delete anything here instead the only thing we're going to do is update the
thing we're going to do is update the doent with a new invite
doent with a new invite code using the generate invite code
code using the generate invite code which we already have here and just pass
which we already have here and just pass you know how many numbers you want let's
you know how many numbers you want let's see I used six numbers when I created
see I used six numbers when I created the workspace in my post method so okay
the workspace in my post method so okay I'm going to use six numbers and letters
I'm going to use six numbers and letters again and since I have the entire thing
again and since I have the entire thing I can just return the workspace I think
I can just return the workspace I think const
const workspace like this
workspace like this there we go and just return back to
there we go and just return back to workspace there we go uh we have that
workspace there we go uh we have that ready and now I want to go ahead and go
ready and now I want to go ahead and go inside of my API and I'm going to go
inside of my API and I'm going to go inside of use update workspace actually
inside of use update workspace actually let's do use delete workspace it's more
let's do use delete workspace it's more similar and let's call this use reset
similar and let's call this use reset invite code so it's very descriptive
invite code so it's very descriptive hook let's go ahead and change the
hook let's go ahead and change the constant name use reset invite code
constant name use reset invite code and now let's change the response and
and now let's change the response and the request type here so it's going to
the request type here so it's going to be workspace ID but after that it's
be workspace ID but after that it's going to be reset invite code and after
going to be reset invite code and after that it's going to be a post method the
that it's going to be a post method the 200 will stay here because we are
200 will stay here because we are throwing an error in case we are not the
throwing an error in case we are not the admin and after that we have to do the
admin and after that we have to do the same change here so this is a post
same change here so this is a post method but in between these two we are
method but in between these two we are passing in reset invite code we are not
passing in reset invite code we are not passing any form data or any Json but
passing any form data or any Json but simply the Pam which is the workspace ID
simply the Pam which is the workspace ID which we need this will be failed to
which we need this will be failed to reset invite code and inside of here the
reset invite code and inside of here the only thing we are going to reset is the
only thing we are going to reset is the workspace and data ID right here and I
workspace and data ID right here and I think that what we could also do okay
think that what we could also do okay we're not going to do that here never
we're not going to do that here never mind uh I'm going to tell you what I was
mind uh I'm going to tell you what I was thinking about when we get to this and
thinking about when we get to this and I'm just going to write here uh invite
I'm just going to write here uh invite code
code reset and failed to reset invite
reset and failed to reset invite code there we go okay and now let's go
code there we go okay and now let's go ahead and let's go back inside of our
ahead and let's go back inside of our features
features workspaces API my apologies
workspaces API my apologies components edit workspace form and now
components edit workspace form and now I'm going to copy this card which I have
I'm going to copy this card which I have created for the danger zone and I'm
created for the danger zone and I'm going to paste it in between my first
going to paste it in between my first card which was my form and my danger
card which was my form and my danger zone like this so now if I save I have
zone like this so now if I save I have two danger zones so now I'm going to go
two danger zones so now I'm going to go ahead and uh modify this so this is the
ahead and uh modify this so this is the reset invite
reset invite Zone first let's go ahead and let's
Zone first let's go ahead and let's create a constant which will hold the
create a constant which will hold the full invite
full invite link because this is something that
link because this is something that users will be able to copy from here as
users will be able to copy from here as well so open backx and go ahead and give
well so open backx and go ahead and give it a window location
it a window location origin slash workspaces whoops outside
origin slash workspaces whoops outside of this slash workspaces and then slash
of this slash workspaces and then slash initial values dollar sign
initial values dollar sign ID and then slash jooin and then initial
ID and then slash jooin and then initial values
values dot invite
dot invite code just
code just like that there we go we now have the
like that there we go we now have the full invite link here and now let's
full invite link here and now let's focus on this second card which is a
focus on this second card which is a copy of our uh danger zone here so I'm
copy of our uh danger zone here so I'm going to change this to be invite
going to change this to be invite members I'm going to go ahead and change
members I'm going to go ahead and change the paragraph here to be use the
the paragraph here to be use the invite link to add members to your
invite link to add members to your workspace
and then I'm going to go ahead and do the following so I'm going to add a div
the following so I'm going to add a div here with a class
here with a class name margin top of four inside of this
name margin top of four inside of this div I'm going to add a new
div I'm going to add a new div Flex item Center and GAP X of
div Flex item Center and GAP X of two then I'm going to go ahead and add
two then I'm going to go ahead and add an input which will be disabled and have
an input which will be disabled and have a value of full link
a value of full link inside uh full invite link
inside uh full invite link yes it's full invite link and below that
yes it's full invite link and below that we're going to have a button well below
we're going to have a button well below that but actually next to that we're
that but actually next to that we're going to have a button uh with an
going to have a button uh with an onclick for now just an empty
onclick for now just an empty function and then a variant of secondary
function and then a variant of secondary and the size let's actually use class
and the size let's actually use class name size 12 like this and inside let's
name size 12 like this and inside let's add a copy icon from loose react so make
add a copy icon from loose react so make sure you've added the copy icon from
sure you've added the copy icon from Lucy react here and there we go you can
Lucy react here and there we go you can see that now you have the full invite
see that now you have the full invite link and when you click this it is
link and when you click this it is supposed to copy it uh to your clipboard
supposed to copy it uh to your clipboard so let's Implement that but let's just
so let's Implement that but let's just give the copy icon a class name of size
give the copy icon a class name of size five and now let's go ahead and let's
five and now let's go ahead and let's call this handle copy invite link like
call this handle copy invite link like this and let's develop it right here
this and let's develop it right here const handle copy invite link we'll do
const handle copy invite link we'll do Navigator do clipboard. write text full
Navigator do clipboard. write text full invite link from the constant we have
invite link from the constant we have above and then let's use a then and call
above and then let's use a then and call the toast which you can import from
the toast which you can import from soner I'm just going to move it to the
soner I'm just going to move it to the top with these packages here so toast.
top with these packages here so toast. success invite link copied to the
success invite link copied to the clipboard or no need for D just like
clipboard or no need for D just like that there we go so now when you click
that there we go so now when you click this there we go invite L link copied
this there we go invite L link copied and when I paste there we go so
and when I paste there we go so obviously this exact route does not
obviously this exact route does not exist yet but I already know that this
exist yet but I already know that this is the correct invite code it has six
is the correct invite code it has six digits and I think I saw I saw it when
digits and I think I saw I saw it when we were looking at our databases so
we were looking at our databases so we're later going to create this join uh
we're later going to create this join uh route right now it does not exist so
route right now it does not exist so this is what users will enter and then
this is what users will enter and then they will be able to become members of
they will be able to become members of the workspace but now we have to create
the workspace but now we have to create a functionality to reset the invite code
a functionality to reset the invite code so let's go ahead and finish the UI here
so let's go ahead and finish the UI here uh at the bottom so we have prepared
uh at the bottom so we have prepared this delete workspace which we just
this delete workspace which we just copied so we're going to go ahead and
copied so we're going to go ahead and call this reset invite
link like that and let's go ahead and just add a doted separator here with the
just add a doted separator here with the class name of P D
class name of P D y-7 and I think that I forgot to add the
y-7 and I think that I forgot to add the dotted separator for my danger zone so
dotted separator for my danger zone so let's just do that I want to be
let's just do that I want to be consistent so inside of here I have this
consistent so inside of here I have this button delete workspace and just above
button delete workspace and just above it I will add a dotted separator with a
it I will add a dotted separator with a class name
class name py7 there we go now all of my forms here
py7 there we go now all of my forms here have nice dotted separators here uh
have nice dotted separators here uh let's go ahead and let's actually add
let's go ahead and let's actually add the
the mutation I'm going to go ahead and just
mutation I'm going to go ahead and just cop this one and I will now rename this
cop this one and I will now rename this to use reset invite code mutate will be
to use reset invite code mutate will be reset invite code is spending will be is
reset invite code is spending will be is resetting invite code there we go so now
resetting invite code there we go so now let's go ahead and let's also copy the
let's go ahead and let's also copy the use confirm because this is also
use confirm because this is also destructive action so this will be
destructive action so this will be instead of delete dialogue this will be
instead of delete dialogue this will be reset dialogue and this will be confirm
reset dialogue and this will be confirm reset and this will be set invite
reset and this will be set invite link and we're going to go ahead and
link and we're going to go ahead and give it uh this will
current invite link just so the user knows if they have
link just so the user knows if they have shared this with anyone uh they're going
shared this with anyone uh they're going to have to share it again great let's go
to have to share it again great let's go ahead and add the reset dialogue first
ahead and add the reset dialogue first so we don't forget it so I'm just going
so we don't forget it so I'm just going to add it below the delete dialogue
to add it below the delete dialogue and then well we can go ahead and copy
and then well we can go ahead and copy the handle delete method they're both
the handle delete method they're both very
very similar and we're going to call this
similar and we're going to call this handle reset invite
handle reset invite code
code [Music]
[Music] confirm reset and instead of delete
confirm reset and instead of delete workspace we're going to do reset invite
workspace we're going to do reset invite code and the params are exactly the same
code and the params are exactly the same but the on success does not need any
but the on success does not need any redirection but it will need
redirection but it will need something so now let's append the handle
something so now let's append the handle reset invite code to our new card in the
reset invite code to our new card in the middle of the two cards the one with the
middle of the two cards the one with the invite members the one with the reset
invite members the one with the reset invite link here so on click handle
invite link here so on click handle reset invite code and is pending or is
reset invite code and is pending or is resetting invite
resetting invite code let's go ahead and try it out
code let's go ahead and try it out now so right now my invite link is am
now so right now my invite link is am d621 if I click reset invite link and
d621 if I click reset invite link and confirm my invite code is reset but this
confirm my invite code is reset but this one seems to be exactly the same except
one seems to be exactly the same except if I refresh then it's a different code
if I refresh then it's a different code why does that happen well because
why does that happen well because remember how do we get the initial data
remember how do we get the initial data for the edit workspace form well if you
for the edit workspace form well if you go inside of your Standalone here so app
go inside of your Standalone here so app Standalone workspaces workspace ID
Standalone workspaces workspace ID settings we get it through a server
settings we get it through a server component this is where we get our
component this is where we get our initial values so what we have to do is
initial values so what we have to do is we have to call our router refresh so
we have to call our router refresh so let's go inside of the edit workspace
let's go inside of the edit workspace form and let's find our not the unsubmit
form and let's find our not the unsubmit where is my uh handle reset invite code
where is my uh handle reset invite code in here we're going to call router.
in here we're going to call router. refresh this will refetch server
refresh this will refetch server components and let's try this again so
components and let's try this again so this is my uh this is my uh uh invite
this is my uh this is my uh uh invite code eh2 nt2 if I click reset
code eh2 nt2 if I click reset there we go this is the new code amazing
there we go this is the new code amazing amazing job so you just finished that so
amazing job so you just finished that so what we're going to do next is we're
what we're going to do next is we're going to wrap up this invite
going to wrap up this invite functionality because it's not that
functionality because it's not that difficult and once we've finally
difficult and once we've finally finished with everything that workspace
finished with everything that workspace can do we can go ahead and do the
can do we can go ahead and do the members which will display a list of our
members which will display a list of our members and we're going to be able to
members and we're going to be able to change their role like admin or member
change their role like admin or member or kick them or leave ourselves from a
or kick them or leave ourselves from a workspace and then we can work with
workspace and then we can work with projects and tasks amazing amazing
projects and tasks amazing amazing job now let's go ahead and let's build
job now let's go ahead and let's build the invite system so first let's build
the invite system so first let's build the actual join API post route which
the actual join API post route which will be called by the user attempting to
will be called by the user attempting to join a workspace so let's go inside of
join a workspace so let's go inside of source features workspaces server route.
source features workspaces server route. DS right here and I'm going to go ahead
DS right here and I'm going to go ahead and I'm going to to join a post slw
and I'm going to to join a post slw workpace id/ jooin and then I'm going to
workpace id/ jooin and then I'm going to go ahead and just collapse this route
go ahead and just collapse this route here and then I'm going to add a session
here and then I'm going to add a session middleware so only logged in users can
middleware so only logged in users can do this and then let's add a z
do this and then let's add a z validator to look for Json and no need
validator to look for Json and no need to Define this in a schema we can just
to Define this in a schema we can just go ahead and write a code to be a string
go ahead and write a code to be a string that's all we need and we also need to
that's all we need and we also need to add add the Zod import here so Z from
add add the Zod import here so Z from Zod now let's go ahead all the way
Zod now let's go ahead all the way down and let's go ahead and add our
down and let's go ahead and add our asynchronous controller now inside of
asynchronous controller now inside of here I'm going to go ahead and
here I'm going to go ahead and destructure the workspace ID from C
destructure the workspace ID from C request perm and I'm going to
request perm and I'm going to destructure the code from oops C request
destructure the code from oops C request valid
valid Json then we're going to go ahead head
Json then we're going to go ahead head and get the databases from the
and get the databases from the middleware context let me just fix the
middleware context let me just fix the constant typo here so that's going to be
constant typo here so that's going to be c. get databases and we're also going to
c. get databases and we're also going to need the user C.G user then let's go
need the user C.G user then let's go ahead and let's get uh the member so I'm
ahead and let's get uh the member so I'm going to do a wait get
going to do a wait get member and I'm going to pass in the
member and I'm going to pass in the workspace ID which comes from the Pam
workspace ID which comes from the Pam the user ID which comes from the current
the user ID which comes from the current user do dollar sign ID and we also need
user do dollar sign ID and we also need to pass in the databases I like to pass
to pass in the databases I like to pass that first if we have a
that first if we have a member that means no point in calling
member that means no point in calling this API route so we're going to return
this API route so we're going to return back a cjson error already a member and
back a cjson error already a member and not 500 but 400 like this then let's go
not 500 but 400 like this then let's go ahead and let's get the workspace using
ahead and let's get the workspace using the workspace ID from the param here so
the workspace ID from the param here so we're going to go ahead and fetch await
we're going to go ahead and fetch await databases get
databases get document and we're going to pass in the
document and we're going to pass in the database ID the workspaces ID and then
database ID the workspaces ID and then the workspace ID here and I want to give
the workspace ID here and I want to give it some strict types here so I'm going
it some strict types here so I'm going to go ahead and give the get document
to go ahead and give the get document the workspace type fromt do/ types let
the workspace type fromt do/ types let me show you this import right here so
me show you this import right here so this is our type which expands the model
this is our type which expands the model document oops it's located inside of
document oops it's located inside of features workspaces types so now we're
features workspaces types so now we're using it inside of this route
using it inside of this route here and we are assigning it down here
here and we are assigning it down here and the reason we are assigning it is so
and the reason we are assigning it is so that now I can safely do a comparison
that now I can safely do a comparison here if workspace do invite code is not
here if workspace do invite code is not equal to the code entered from the body
equal to the code entered from the body we're going to go ahead and return c.
we're going to go ahead and return c. Json here error in valid invite code and
Json here error in valid invite code and then 400 error like
then 400 error like this otherwise if all of this is okay
this otherwise if all of this is okay we're going to go ahead and create a new
we're going to go ahead and create a new member so databases. create
member so databases. create document database
document database ID members collection ID I mean just
ID members collection ID I mean just members ID id. unique because we
members ID id. unique because we creating a new member here and go ahead
creating a new member here and go ahead and give it a workspace ID a user ID of
and give it a workspace ID a user ID of user. dollar sign ID and the role of
user. dollar sign ID and the role of member ro. member because this user is
member ro. member because this user is joining not creating the workspace and
joining not creating the workspace and then we can go ahead and return back
then we can go ahead and return back data
data workspace all right we now have that
workspace all right we now have that ready so let's go inside of workspaces
ready so let's go inside of workspaces API uh and let's see which one we can
API uh and let's see which one we can copy the used reset in code and paste it
copy the used reset in code and paste it and let's rename this to use join
and let's rename this to use join workspace let's rename the constant to
workspace let's rename the constant to use join workspace here and let's go
use join workspace here and let's go ahead and modify these ones so uh the
ahead and modify these ones so uh the request and response here should be join
request and response here should be join I believe there we go and remember to
I believe there we go and remember to have 200
have 200 here and now let's go ahead and fix that
here and now let's go ahead and fix that here as well so just replace this with
here as well so just replace this with join and alongside Pam we also need Json
join and alongside Pam we also need Json here so go ahead and pass Json there we
here so go ahead and pass Json there we go and this will be
go and this will be failed to join
failed to join workspace on success here we're going to
workspace on success here we're going to say joined
workspace and in here we can invalidate our workspaces query and we can also
our workspaces query and we can also invalidate the individual workspace data
invalidate the individual workspace data I query and again failed to join
I query and again failed to join workspace
workspace here like this and while we are
here like this and while we are here I just want to take a look at all
here I just want to take a look at all of my response types here and I kind of
of my response types here and I kind of don't like that in some of them we are
don't like that in some of them we are not doing the whole you can see the 200
not doing the whole you can see the 200 thing but we can refactor that later but
thing but we can refactor that later but because I'm not having any errors but if
because I'm not having any errors but if you are you might need that 200 but
you are you might need that 200 but we're going to count to later no worry
we're going to count to later no worry so now that we have the API for that
so now that we have the API for that let's go ahead and let's create uh the
let's go ahead and let's create uh the actual page for that so we're going to
actual page for that so we're going to go ahead and go inside of source let's
go ahead and go inside of source let's go inside of the uh app folder my app
go inside of the uh app folder my app folder instead of Standalone workspaces
folder instead of Standalone workspaces workspace ID and let's create a new
workspace ID and let's create a new folder called join and inside of here
folder called join and inside of here let's create a page. TSX like this
let's create a page. TSX like this let's go ahead and Mark this workspace
let's go ahead and Mark this workspace id id join
id id join page and workspace ID join
page and workspace ID join page like this so now I should be able
page like this so now I should be able to go inside of my
to go inside of my settings I should be able to copy this
settings I should be able to copy this invite link and I should be able to
invite link and I should be able to paste it
paste it here uh and looks like I am getting uh
here uh and looks like I am getting uh an error here oh yes because this is not
an error here oh yes because this is not the way I wanted my apologies so we have
the way I wanted my apologies so we have workspaces workspace ID join on the same
workspaces workspace ID join on the same level as settings and then another one
level as settings and then another one invite code like this and then drag and
invite code like this and then drag and drop page
drop page inside like this and let's refresh now
inside like this and let's refresh now and there we go now our invite link
and there we go now our invite link works so let me show you the new new
works so let me show you the new new structure so workspaces works space ID
structure so workspaces works space ID join and then the invite code and that
join and then the invite code and that is equivalent to this workspaces
is equivalent to this workspaces workspace ID join invite code right so
workspace ID join invite code right so just double check inside of your
just double check inside of your settings here when you work with full
settings here when you work with full invite link where is my P invite link
invite link where is my P invite link it's inside of the edit workspace form
it's inside of the edit workspace form so window location origin workspaces
so window location origin workspaces workspace ID join invite code make sure
workspace ID join invite code make sure you don't misspell any of those great so
you don't misspell any of those great so we now have this page right here let's
we now have this page right here let's go ahead and let's protect it so I'm
go ahead and let's protect it so I'm going to turn this into an asynchronous
going to turn this into an asynchronous method and I'm going to go ahead and do
method and I'm going to go ahead and do user a way to get
user a way to get current if there is no user we can
current if there is no user we can redirect from next navigation to sign
redirect from next navigation to sign in like
in like that so now let's go ahead and let me
that so now let's go ahead and let me just quickly peek at my settings page
just quickly peek at my settings page here
here because I just want to copy this
because I just want to copy this interface so let's go back inside of the
interface so let's go back inside of the invite code
invite code here like this and in the params now we
here like this and in the params now we can structure the workspace ID and let
can structure the workspace ID and let me just rename this so workspace ID join
me just rename this so workspace ID join page
page props let's D structure that here so
props let's D structure that here so params and now what we can do is we can
params and now what we can do is we can load the workspace that we are trying to
load the workspace that we are trying to join so we can display to the user some
join so we can display to the user some information but there is one problem uh
information but there is one problem uh if you take a look at our features
if you take a look at our features workspaces queries here which we use in
workspaces queries here which we use in server components for the get workspace
server components for the get workspace we ensure that the user is a member here
we ensure that the user is a member here right so this is what we're going to do
right so this is what we're going to do we're going to copy this get workspace
we're going to copy this get workspace and we can copy the interface as well so
and we can copy the interface as well so the entire thing and let's paste it down
the entire thing and let's paste it down here and we're going to call this get
here and we're going to call this get workspace info and get workspace info
workspace info and get workspace info here so get workspace info props it will
here so get workspace info props it will be exactly the same the only difference
be exactly the same the only difference here uh will be the following and that
here uh will be the following and that is that you don't have to be a member to
is that you don't have to be a member to visit this page so I'm going to go ahead
visit this page so I'm going to go ahead and remove this part but we're going to
and remove this part but we're going to be careful we're not going to return
be careful we're not going to return back everything we're just going to
back everything we're just going to return the name so workspace do name
return the name so workspace do name because that is uh the only thing we
because that is uh the only thing we need here so get workspace info and no
need here so get workspace info and no need for account here there we go so now
need for account here there we go so now we have our server component ready to
we have our server component ready to give the user some useful information
give the user some useful information let's get this get workspace info and
let's get this get workspace info and let's see if it works so const workspace
let's see if it works so const workspace here will be get await get workspace
here will be get await get workspace info and passing the workspace ID as
info and passing the workspace ID as pams workspace ID uh and I have to
pams workspace ID uh and I have to import get workspace info from features
import get workspace info from features works spes
works spes queries and then workspace ID actually
queries and then workspace ID actually let's go ahead and add workspace ID here
let's go ahead and add workspace ID here well we can just Json stringify
well we can just Json stringify workspace let's see the entire thing
workspace let's see the entire thing which we fetch here and there we go as
which we fetch here and there we go as you can see we fetch name test and
you can see we fetch name test and here's an even more important thing to
here's an even more important thing to try out I'm going to copy my invite link
try out I'm going to copy my invite link and I'm going to go ahead and log out
and I'm going to go ahead and log out and I will now uh log in with another
and I will now uh log in with another account that I have have that is not a
account that I have have that is not a part of this workspace right so this
part of this workspace right so this user has no workspace as you can see so
user has no workspace as you can see so I'm going to paste that invite link here
I'm going to paste that invite link here and there we go that user can still see
and there we go that user can still see the information about the workspace they
the information about the workspace they are trying to join so I suggest you also
are trying to join so I suggest you also log out and go into another user and
log out and go into another user and confirm that you can see the information
confirm that you can see the information about this workspace and what I want to
about this workspace and what I want to do now is the following I want to build
do now is the following I want to build inside of my Fe features inside of
inside of my Fe features inside of workspaces let's go inside of components
workspaces let's go inside of components and let's create join workspace form.
and let's create join workspace form. DSX and this one is going to be uh quite
DSX and this one is going to be uh quite simple so first of all we're going to
simple so first of all we're going to mark this as use client we're going to
mark this as use client we're going to export con join workspace form
export con join workspace form here and let's go ahead and let's add
here and let's go ahead and let's add everything we need from the card so card
everything we need from the card so card card content description header and
card content description header and title here and we're going to go ahead
title here and we're going to go ahead and give this entire thing a div uh no
and give this entire thing a div uh no need for a div actually we can
need for a div actually we can immediately do card give the card full
immediately do card give the card full width full
width full height border none and Shadow none then
height border none and Shadow none then create a card header which will have a
create a card header which will have a class name of padding 7 and then we're
class name of padding 7 and then we're going to have a card Title Here with a
going to have a card Title Here with a class name text extra large
class name text extra large font Bold and the text join workspace
font Bold and the text join workspace below that we're going to have a card
below that we're going to have a card description
description here which will have you apostrophe have
here which will have you apostrophe have been which you can replace with appos if
been which you can replace with appos if you're getting IDE errors so you've been
you're getting IDE errors so you've been invited to join and then we're just
invited to join and then we're just going to go ahead and create an
going to go ahead and create an interface here join workspace form props
interface here join workspace form props initial values will simply accept the
initial values will simply accept the name which is a string because that's
name which is a string because that's the only thing we are allowing this user
the only thing we are allowing this user to see so from here assign the props the
to see so from here assign the props the structure the name and then uh is it not
structure the name and then uh is it not oh initial values okay so initial values
oh initial values okay so initial values so you've been invited to
so you've been invited to join initial
join initial values. name and we're going to wrap
values. name and we're going to wrap that inside of strong so it's a bit
that inside of strong so it's a bit Bolder than the other than the rest of
Bolder than the other than the rest of the text and then work space like
the text and then work space like this and let's go ahead and immediately
this and let's go ahead and immediately add this component inside of our
add this component inside of our Standalone workspaces workspace ID uh
Standalone workspaces workspace ID uh join invite code page. TSX so I'm going
join invite code page. TSX so I'm going to go ahead and create the join
to go ahead and create the join workspace form here and the initial
workspace form here and the initial values will be our workspace we can
values will be our workspace we can rename this to uh let's go ahead and do
rename this to uh let's go ahead and do this we can rename these to initial
this we can rename these to initial values
values and we can also do a redirect if this
and we can also do a redirect if this doesn't work so if there is no initial
doesn't work so if there is no initial values we can redirect the user
values we can redirect the user whoops redirect the user
whoops redirect the user to well just to the root
to well just to the root page because we can't redirect them to
page because we can't redirect them to the ID because we don't even have the ID
the ID because we don't even have the ID here uh great so redirect from next
here uh great so redirect from next navigation and there we go you can see
navigation and there we go you can see how now this says you've been invited to
how now this says you've been invited to join the test workspace and I'm just
join the test workspace and I'm just going to give div a class name of full
going to give div a class name of full width and on large devices maximum width
width and on large devices maximum width of excel so now it should look a bit
of excel so now it should look a bit better there we go now let's head back
better there we go now let's head back inside of the join workspace form here
inside of the join workspace form here and we're not going to have too much uh
and we're not going to have too much uh thing to build here there aren't going
thing to build here there aren't going to be any inputs so don't worry about
to be any inputs so don't worry about that let's go ahead and do px7 here then
that let's go ahead and do px7 here then let's add a dotted separator
let's add a dotted separator here which we can uh import from
here which we can uh import from components dotted
components dotted separator then let's add a card content
separator then let's add a card content with a class name uh padding of
with a class name uh padding of seven and inside of here we're can add a
seven and inside of here we're can add a div with a class
div with a class name Flex items Center and justify
name Flex items Center and justify between and inside of here let's add a
between and inside of here let's add a button from component UI button so this
button from component UI button so this is another component that I have added
is another component that I have added an import for above and let's go ahead
an import for above and let's go ahead and write cancel in the first one
and write cancel in the first one one and let's go ahead and write join
one and let's go ahead and write join workspace in the second one uh I think
workspace in the second one uh I think that we can maybe improve this so let's
that we can maybe improve this so let's go ahead and give it a flex column by
go ahead and give it a flex column by default but let's go ahead and give it
default but let's go ahead and give it uh a LG Flex row here and let's go ahead
uh a LG Flex row here and let's go ahead and give this a class name of w full
and give this a class name of w full button LG W
button LG W fit and the same same thing for this one
fit and the same same thing for this one so on mobile it will look like this but
so on mobile it will look like this but on desktop it will look like this there
on desktop it will look like this there we go so now let's go ahead and give
we go so now let's go ahead and give them some proper variants so this will
them some proper variants so this will be a
be a variant of secondary it will be a type
variant of secondary it will be a type of button uh and we're going to give it
of button uh and we're going to give it an as child prop because inside of it
an as child prop because inside of it we're going to use a next link so make
we're going to use a next link so make sure you've added an import for next
sure you've added an import for next link I'm going move it here and this
link I'm going move it here and this will simply redirect to the root page so
will simply redirect to the root page so that root page will then uh you know
that root page will then uh you know move the user away if needed uh and let
move the user away if needed uh and let me also just add a gap Y 2 here like
me also just add a gap Y 2 here like this or Gap X2 as well well we can just
this or Gap X2 as well well we can just do Gap two in that
do Gap two in that case great and now this one here will
case great and now this one here will have a size of large and this one here
have a size of large and this one here will also have a size of large I forgot
will also have a size of large I forgot to add that that and we're going to give
to add that that and we're going to give this one a type of uh well no need for
this one a type of uh well no need for submit it can also be a button because
submit it can also be a button because it's going to have its own on click here
it's going to have its own on click here so now let's go ahead and ensure this is
so now let's go ahead and ensure this is used client so that you can now go ahead
used client so that you can now go ahead and add the mutate to use join workspace
and add the mutate to use join workspace here so import this from API use join
here so import this from API use join workspace and now what we have to do is
workspace and now what we have to do is another thing so going inside of
another thing so going inside of workspaces inside of hooks and copy use
workspaces inside of hooks and copy use workspace ID paste it and call this one
workspace ID paste it and call this one use invite
use invite code rename this to use invite code and
code rename this to use invite code and search for param invite code like this
search for param invite code like this so you have this little reusable hook
so you have this little reusable hook whenever you need it the you could have
whenever you need it the you could have also done this in a different way so you
also done this in a different way so you could have added the invite code here
could have added the invite code here and then you can just pass that to the
and then you can just pass that to the Joint workspace form if you want to uh
Joint workspace form if you want to uh you decide how you want to do it you
you decide how you want to do it you also you know could have completely
also you know could have completely avoided the server component Fetch and
avoided the server component Fetch and you simply could have marked everything
you simply could have marked everything as use client and you could have done
as use client and you could have done the use get workspace but you know let's
the use get workspace but you know let's leverage server components since we have
leverage server components since we have them and since they are so handy to
them and since they are so handy to redirect the user away like this let's
redirect the user away like this let's go inside of the joint workspace form
go inside of the joint workspace form here and let's get our invite code so
here and let's get our invite code so now no matter where this join workspace
now no matter where this join workspace form is uh we will be able to fetch this
form is uh we will be able to fetch this from our URL using the use invite
from our URL using the use invite code and then let's go ahead and let's
code and then let's go ahead and let's actually create the onsubmit here so
actually create the onsubmit here so const
const onsubmit we'll call the mutate method
onsubmit we'll call the mutate method which will have the
which will have the Pam which will be uh the workspace ID
Pam which will be uh the workspace ID which we also need here on
workspace use workspace ID again you could have passed this as a prop from
could have passed this as a prop from the parent server component if that's
the parent server component if that's what you prefer uh at this point I'm
what you prefer uh at this point I'm almost experimenting with all the things
almost experimenting with all the things we can do here uh and alongside PM we
we can do here uh and alongside PM we also need a Json for the code to be
also need a Json for the code to be invite
invite code there we go and then on success
code there we go and then on success here we're going to go ahead and D
here we're going to go ahead and D structure the data and inside of here we
structure the data and inside of here we need the router so let's add the
need the router so let's add the router from next
router from next navigation I'm just going to move it
navigation I'm just going to move it here to the
here to the top uh and then this router here will
top uh and then this router here will have a router. push slash workspaces
have a router. push slash workspaces data. dollar sign ID which is uh
data. dollar sign ID which is uh returned from our API so let's go ahead
returned from our API so let's go ahead and assign the onsubmit here to the join
and assign the onsubmit here to the join workspace so on click here onsubmit and
workspace so on click here onsubmit and let's go ahead and give it a disabled is
let's go ahead and give it a disabled is spending which we did not destructure so
spending which we did not destructure so let's destructure this from here and
let's destructure this from here and let's do the same thing to the cancel
button let's try it out now so this user is not a part of this workspace and if I
is not a part of this workspace and if I click join workspace joined workspace
click join workspace joined workspace and there we go I am in the test
and there we go I am in the test workspace and let's go ahead and confirm
workspace and let's go ahead and confirm that by going inside of of source app
that by going inside of of source app folder dashboard workspaces workspace
folder dashboard workspaces workspace ID and let me just go ahead and I think
ID and let me just go ahead and I think I can just do this
I can just do this params params workspace ID so it's
params params workspace ID so it's completely type unsafe but you can see
completely type unsafe but you can see it appears because we know it's here
it appears because we know it's here right so I just want to confirm so it
right so I just want to confirm so it starts with 670 0 and ends with 535
starts with 670 0 and ends with 535 so I'm now logged in uh as John
so I'm now logged in uh as John 670 okay let's log out and let's log in
670 okay let's log out and let's log in with
Antonio and let's select test and there we go as you can see this is the exact
we go as you can see this is the exact same workspace so we officially have two
same workspace so we officially have two members inside of this test workspace
members inside of this test workspace right here great so we can now remove
right here great so we can now remove these params from the workspace ID page
these params from the workspace ID page and let me just show you the quick
and let me just show you the quick refactor you can do if you want to so
refactor you can do if you want to so you can go inside of your app folder
you can go inside of your app folder Standalone workspaces workspace ID join
Standalone workspaces workspace ID join invite code and if you want to uh here's
invite code and if you want to uh here's what uh we can do well when we get
what uh we can do well when we get workspace info we can also you know well
workspace info we can also you know well let's not return the ID no need for that
let's not return the ID no need for that because we have the workspace ID here
because we have the workspace ID here and we also have the invite code here so
and we also have the invite code here so we can do this this and then we can go
we can do this this and then we can go ahead and pass the following so initial
ahead and pass the following so initial values can stay the initial values and
values can stay the initial values and then we can pass in the code to be pams
then we can pass in the code to be pams invite code and the workspace ID can be
invite code and the workspace ID can be pams workspace ID so this way you would
pams workspace ID so this way you would not have to use those uh you
not have to use those uh you know you don't have to do this I'm just
know you don't have to do this I'm just demonstrating so then inside of here you
demonstrating so then inside of here you would have the code and the workspace ID
would have the code and the workspace ID and then you would not have to use these
and then you would not have to use these two right so a tip for you if you want
two right so a tip for you if you want to do that but I mean this works just
to do that but I mean this works just fine so however you prefer of whatever
fine so however you prefer of whatever way you prefer doing
way you prefer doing it uh great so I'm going to remove
it uh great so I'm going to remove invite code from here because I'm not
invite code from here because I'm not using it uh and great that's it you've
using it uh and great that's it you've implemented the invite system and now we
implemented the invite system and now we are ready uh to finally go ahead and
are ready uh to finally go ahead and implement the members page in which we
implement the members page in which we will now hopefully uh see both of these
will now hopefully uh see both of these members Antonio and join and John in
members Antonio and join and John in this test workspace right here great
this test workspace right here great great
great job in order to build our members list
job in order to build our members list first let's build the members API let's
first let's build the members API let's go inside of features members and let's
go inside of features members and let's create a new folder called server inside
create a new folder called server inside let's create a route. DS let's go ahead
let's create a route. DS let's go ahead and create a const app new hono from
and create a const app new hono from hono and let's export default
hono and let's export default app now let's go back inside of our app
app now let's go back inside of our app folder API route route. DS let's go
folder API route route. DS let's go ahead and chain slash members to our
ahead and chain slash members to our members and while we have to import that
members and while we have to import that so import members from features members
so import members from features members server
server route like that so make sure you have
route like that so make sure you have chained your new members here it doesn't
chained your new members here it doesn't really matter before or after works
really matter before or after works spaces just be mindful of semicolons
spaces just be mindful of semicolons like this let's go inside and first
like this let's go inside and first let's go ahead and let's build uh the
let's go ahead and let's build uh the get query so this is an API endpoint
get query so this is an API endpoint which will be used to obtain all members
which will be used to obtain all members in a workspace so let's go ahead and add
in a workspace so let's go ahead and add a session middleware
a session middleware here from lib session
here from lib session middleware and then let's go ahead and
middleware and then let's go ahead and let's add a z validator here for our our
let's add a z validator here for our our query so we have to import the Z
query so we have to import the Z validator from hono Zod validator and
validator from hono Zod validator and this will be a z doob workpace ID z.
this will be a z doob workpace ID z. string and then an asynchronous
string and then an asynchronous controller here let's go ahead and let's
controller here let's go ahead and let's move the Zod validator here and let's
move the Zod validator here and let's import Z from Zod like this now let's go
import Z from Zod like this now let's go ahead and let's obtain the users from
ahead and let's obtain the users from our await cre create admin client so
our await cre create admin client so this is something we have not done
this is something we have not done before we have to use the admin client
before we have to use the admin client for this one let's go quickly back
for this one let's go quickly back inside of the admin client so that's
inside of the admin client so that's located inside of your Source lib folder
located inside of your Source lib folder app. TS inside of the create admin
app. TS inside of the create admin client we don't have the users so let's
client we don't have the users so let's add get users return new
add get users return new users
users client and we should have the user users
client and we should have the user users import here so not user
import here so not user users like this node app right great so
users like this node app right great so we now have that here so we can go back
we now have that here so we can go back here and there we go we now have the
here and there we go we now have the users from our admin client let's get
users from our admin client let's get the databases from our C doget databases
the databases from our C doget databases here let's get the current user from C.G
here let's get the current user from C.G user and let's go ahead and let's obtain
user and let's go ahead and let's obtain the workspace ID from C request valid
the workspace ID from C request valid the query
the query here like that and now let's get the
here like that and now let's get the current member using a wait get member
current member using a wait get member here you can do that from do/ utils
here you can do that from do/ utils because we are in the members feature
because we are in the members feature folder and inside of here we have the
folder and inside of here we have the get
get member we're going to pass in the
member we're going to pass in the databases we're going to pass in the
databases we're going to pass in the workspace ID and the user ID to be the
workspace ID and the user ID to be the current user. dooll sign ID if there is
current user. dooll sign ID if there is no member we're going to go ahead and
no member we're going to go ahead and return back c. Json error
unauthorized with a 401 and now let's go ahead and let's get
401 and now let's go ahead and let's get all members so const members will be
all members so const members will be await
await databases list documents passing the
databases list documents passing the database ID from the config passing the
database ID from the config passing the members ID from the config so make sure
members ID from the config so make sure you have added these two here database
you have added these two here database ID and the members ID and then we're
ID and the members ID and then we're going to add a query from node app right
going to add a query from node app right so I'm going to move that here with the
so I'm going to move that here with the other Global Imports query.
other Global Imports query. equal workspace ID to the current
equal workspace ID to the current workspace ID from our query so we are
workspace ID from our query so we are going to load all members from the
going to load all members from the workspace but keep in mind Our member
workspace but keep in mind Our member collection only has has the user ID and
collection only has has the user ID and the workspace ID it has no information
the workspace ID it has no information about the name for example of that user
about the name for example of that user only the ID of the user so what we have
only the ID of the user so what we have to do now is we have to populate those
to do now is we have to populate those users and for that we're going to use
users and for that we're going to use this users from the admin client that's
this users from the admin client that's why we need that so let's write cons
why we need that so let's write cons populated members like that await
populated members like that await promise all so we're using promise all
promise all so we're using promise all so that we can do a weight inside of map
so that we can do a weight inside of map right so let's write members documents
right so let's write members documents map we're going to mark this as an
map we're going to mark this as an asynchronous method get the individual
asynchronous method get the individual member in each iteration here get the
member in each iteration here get the user from that member here so that's
user from that member here so that's going to be await users. getet member
going to be await users. getet member user
user ID and then go ahead and return the
ID and then go ahead and return the existing member but extend it by giving
existing member but extend it by giving it a name and by giving it an email
it a name and by giving it an email and there we go now you created a list
and there we go now you created a list of populated
of populated members and then go ahead and return c.
members and then go ahead and return c. Json data spread the members why are we
Json data spread the members why are we spreading the members you might ask
spreading the members you might ask because the members is a document list
because the members is a document list collection right but populated members
collection right but populated members is just an array of objects so we have
is just an array of objects so we have to preserve uh those other attributes
to preserve uh those other attributes which the list document from app right
which the list document from app right has like total so we are are preserving
has like total so we are are preserving that here and then we are simply
that here and then we are simply appending documents to be populated
appending documents to be populated members there we go but we're not done
members there we go but we're not done yet while we are here let's also
yet while we are here let's also implement the delete individual member
implement the delete individual member here so using the member ID ensuring we
here so using the member ID ensuring we have the session middleware let's go
have the session middleware let's go ahead and add a uh asynchronous
ahead and add a uh asynchronous controller
here so we're going to go ahead and first obtain the member ID from C
first obtain the member ID from C request uh
request uh Pam then we're going to get the user
Pam then we're going to get the user from C get user from the middleware then
from C get user from the middleware then we're going to get databases from c.
we're going to get databases from c. getet
getet databases and then inside of here let's
databases and then inside of here let's go ahead and add const member to
go ahead and add const member to delete will be await
delete will be await databases. getet
databases. getet document databas is ID
members collection members ID and then the member ID so this is how we load the
the member ID so this is how we load the individual member that we want to delete
individual member that we want to delete but now let's also load all members in
but now let's also load all members in the workspace so const all members in
the workspace so const all members in workspace will be await
workspace will be await databases list
databases list documents database ID members
documents database ID members ID and then query equal workspace ID
ID and then query equal workspace ID member to delete workspace
member to delete workspace ID like this so we are loading all other
ID like this so we are loading all other members which are in the same workspace
members which are in the same workspace as the member we are attempting to
as the member we are attempting to delete because we don't have the
delete because we don't have the workspace ID inside of the URL here so
workspace ID inside of the URL here so now we're going to go ahead and obtain
now we're going to go ahead and obtain ourselves so this member is us right so
ourselves so this member is us right so a wait get member we are passing in the
a wait get member we are passing in the databases we are passing in the
databases we are passing in the workspace ID to be the member to delete
workspace ID to be the member to delete workspace ID and finally we are passing
workspace ID and finally we are passing the user ID to be user. ID since the
the user ID to be user. ID since the member to delete doesn't have strict
member to delete doesn't have strict typings be careful with this don't
typings be careful with this don't misspell it don't add double s or maybe
misspell it don't add double s or maybe missing a letter be careful andure that
missing a letter be careful andure that you're using the proper workspace ID in
you're using the proper workspace ID in all places now we have ourselves here so
all places now we have ourselves here so now we can check are we allowed to
now we can check are we allowed to delete this member first of all if there
delete this member first of all if there is no member meaning we are not a part
is no member meaning we are not a part of this workspace immediately let's
of this workspace immediately let's break the method here with an error
unauthorized like this then if our member. ID is not equal to member to
member. ID is not equal to member to delete. ID and I'm using the invalid IDs
delete. ID and I'm using the invalid IDs here so we are using the dollar signs
here so we are using the dollar signs right so if we are not trying to remove
right so if we are not trying to remove ourselves we are attempting to remove
ourselves we are attempting to remove someone else and if our current role is
someone else and if our current role is not member roll. admin you can import
not member roll. admin you can import member Ro from types here so if we are
member Ro from types here so if we are trying to remove someone else and we are
trying to remove someone else and we are not an admin we are also going to return
not an admin we are also going to return back this and I'm forgotting to I forgot
back this and I'm forgotting to I forgot to add four ones here that's important
to add four ones here that's important so h knows it's an error did I forgot to
so h knows it's an error did I forgot to do that anywhere else I did not okay
do that anywhere else I did not okay like this and then let's go ahead and
like this and then let's go ahead and finally do await databases. delete
finally do await databases. delete document database ID members ID member
document database ID members ID member ID here and we are finally just going to
ID here and we are finally just going to return c. Json data dollar sign ID
return c. Json data dollar sign ID member ID which was the initial we
member ID which was the initial we wanted to delete or we can use member to
wanted to delete or we can use member to delete. ID like this there we go so
delete. ID like this there we go so that's our delete method and now let's
that's our delete method and now let's add the last one which is the patch
add the last one which is the patch method which will be used to modify the
method which will be used to modify the role of the member so member ID right
role of the member so member ID right here again session middle again and this
here again session middle again and this time we're going to have a z
time we're going to have a z validator Json Z
validator Json Z doob A rooll will be be a z. native enum
doob A rooll will be be a z. native enum and we're going to pass in the member
and we're going to pass in the member role so only either member or admin will
role so only either member or admin will be available to pass inside of this
be available to pass inside of this validation here let's go ahead and add
validation here let's go ahead and add our asynchronous controller
our asynchronous controller here and now we're going to go ahead and
here and now we're going to go ahead and do uh pretty much the same thing as we
do uh pretty much the same thing as we did here so we can copy it uh also looks
did here so we can copy it uh also looks like we're not using the all members in
like we're not using the all members in the workspace here so let me just check
the workspace here so let me just check whether I forgot to do this or did is
whether I forgot to do this or did is this just an
this just an extra it seems like I forgot to use this
extra it seems like I forgot to use this so before we allow the user to delete
so before we allow the user to delete let's also do if all members in the
let's also do if all members in the workspace do total is equal to one we
workspace do total is equal to one we are also going to throw back an error
are also going to throw back an error meaning that you cannot delete the last
meaning that you cannot delete the last member in the workspace so cannot delete
member in the workspace so cannot delete the only member and we're going to throw
the only member and we're going to throw back a 400 in that case so if you want
back a 400 in that case so if you want to remove that you going to have to
to remove that you going to have to delete the workspace in itself great so
delete the workspace in itself great so I want to go ahead and I'm going to
I want to go ahead and I'm going to actually copy the entire delete
actually copy the entire delete method uh actually let's copy the
method uh actually let's copy the controller that's maybe a smarter thing
controller that's maybe a smarter thing to do so let's copy the controller of
to do so let's copy the controller of the
the delete and then let's paste it here in
delete and then let's paste it here in the patch method and then now we're
the patch method and then now we're going to modify it here so inside of
going to modify it here so inside of here let's go ahead and let's extract
here let's go ahead and let's extract the member ID from Pam that's here we
the member ID from Pam that's here we have have the user we have the databases
have have the user we have the databases but this time this is not a member to
but this time this is not a member to delete this is a member to
delete this is a member to update I want to you know have the
update I want to you know have the proper terminology here and we are still
proper terminology here and we are still fetching all members in the workspace
fetching all members in the workspace here but using the member to update this
here but using the member to update this time then we get ourselves using member
time then we get ourselves using member to update here and then we can follow
to update here and then we can follow the same logic if we are not a member of
the same logic if we are not a member of this workspace we are unauthorized to do
this workspace we are unauthorized to do this and then the second thing we're
this and then the second thing we're going to do is if our just this if we
going to do is if our just this if we are not an admin we have no right to
are not an admin we have no right to remove anyone's role it doesn't matter
remove anyone's role it doesn't matter if we are trying to change ourselves
if we are trying to change ourselves right if we are not an admin we cannot
right if we are not an admin we cannot do this and then last thing we're going
do this and then last thing we're going to do is if all members in the workspace
to do is if all members in the workspace do total are equal to one we're going to
do total are equal to one we're going to go ahead and do this as well so cannot
go ahead and do this as well so cannot uh downgrade the only member and 400 and
uh downgrade the only member and 400 and then inside of here instead of having
then inside of here instead of having the leete document we're going to have
the leete document we're going to have update document with database ID members
update document with database ID members ID member ID and then simply we're going
ID member ID and then simply we're going to pass a roll and we have to
to pass a roll and we have to destructure the role uh from the body
destructure the role uh from the body here so let's do that const roll C
here so let's do that const roll C request valid Json and now we have the
request valid Json and now we have the role here let's see uh we are also
role here let's see uh we are also missing the member to update here there
missing the member to update here there we go so we just finished our members
we go so we just finished our members API so so now let's go ahead and let's
API so so now let's go ahead and let's create our uh API folder here inside of
create our uh API folder here inside of the members feature folder and let's
the members feature folder and let's create the hooks for each of those so
create the hooks for each of those so I'm going to close this and I'm going to
I'm going to close this and I'm going to go ahead and copy uh let's see
go ahead and copy uh let's see workspaces I think is the most similar
workspaces I think is the most similar one so we're going to copy the use
one so we're going to copy the use delete workspace let's start with that
delete workspace let's start with that I'm going to paste that inside of
I'm going to paste that inside of members and I'm going to rename this to
members and I'm going to rename this to use delete
use delete member let's go inside of use delete
member let's go inside of use delete member let's rename it to use delete
member let's rename it to use delete member let's go ahead and change the
member let's go ahead and change the response and the request types here to
response and the request types here to be
be members and member ID pointing for the
members and member ID pointing for the lead leave the 200 here same thing here
lead leave the 200 here same thing here members and member
members and member ID and then inside of here fail to
ID and then inside of here fail to delete
delete member this will be member deleted this
member this will be member deleted this will reset the member query and this
will reset the member query and this will reset the individual member data ID
will reset the individual member data ID but actually no need for that we will
but actually no need for that we will not have this at all we will not we will
not have this at all we will not we will never have a query for the individual
never have a query for the individual member so we can remove this and this
member so we can remove this and this will again be failed to the lead
will again be failed to the lead member and no need for the data
member and no need for the data destructuring inside of the UN success
destructuring inside of the UN success then at all great now let's copy this
then at all great now let's copy this and paste it and let's go ahead and add
and paste it and let's go ahead and add use update
use update member like this let's go ahead and
member like this let's go ahead and replace this member ID with patch here
replace this member ID with patch here then let's rename this to use update
then let's rename this to use update member and let's go ahead and alongside
member and let's go ahead and alongside PM also the structure the Json which we
PM also the structure the Json which we are going to have and let's change this
are going to have and let's change this to patch we now have an error because
to patch we now have an error because it's missing the Json which holds the
it's missing the Json which holds the new role for the user as you can see
new role for the user as you can see role member rooll the error is going to
role member rooll the error is going to be failed to update member member
be failed to update member member updated it's going to reinv validate
updated it's going to reinv validate members and this will be failed to
members and this will be failed to update member as
update member as well and let's go ahead now and copy the
well and let's go ahead now and copy the workspaces use get workspaces and let's
workspaces use get workspaces and let's paste it inside of our members
paste it inside of our members API so let's rename this to use get
API so let's rename this to use get members like this and let's rename this
members like this and let's rename this to use get members as well the query key
to use get members as well the query key will be members right here and let's go
will be members right here and let's go ahead and simply call
ahead and simply call members. getet but we are missing the
members. getet but we are missing the query here so let's go ahead and add
query here so let's go ahead and add that interface use get members props
that interface use get members props will require a workspace ID which will
will require a workspace ID which will be a string so inside of here let's add
be a string so inside of here let's add use get members props require the
use get members props require the workspace ID and then inside of here we
workspace ID and then inside of here we can pass in the Pam workspace
can pass in the Pam workspace ID will it work like that uh it's not PM
ID will it work like that uh it's not PM it's query I believe there we go so this
it's query I believe there we go so this will be failed to fetch
will be failed to fetch members there we go make sure this is
members there we go make sure this is called members and also make sure you
called members and also make sure you it'ss only for this workspace ID so we
it'ss only for this workspace ID so we have to do a combination of these two so
have to do a combination of these two so it can only uh so that it can refresh if
it can only uh so that it can refresh if we change a workspace otherwise it will
we change a workspace otherwise it will not fetch new members so now when we
not fetch new members so now when we change the workspace ID it will fetch it
change the workspace ID it will fetch it a new every time great so we have all of
a new every time great so we have all of that ready and now we are ready to
that ready and now we are ready to create our
create our screen so let's go inside of source app
screen so let's go inside of source app folder Standalone let's go inside of
folder Standalone let's go inside of workspaces workspace ID and and then
workspaces workspace ID and and then let's create a new folder called members
let's create a new folder called members and inside a new file page.
and inside a new file page. TSX let's go ahead and name it workspace
TSX let's go ahead and name it workspace ID members
ID members page like this and I immediately want to
page like this and I immediately want to protect this page when it's a server
protect this page when it's a server component so I'm going to mark this as
component so I'm going to mark this as an asynchronous component and I will
an asynchronous component and I will import get current from features Al
import get current from features Al queries and I will import
redirect from next navigation inside of here I will query
navigation inside of here I will query the user using a weit get current and if
the user using a weit get current and if there is no user I will redirect Away
there is no user I will redirect Away From
From Here to/ sign
Here to/ sign in and then usually we would leverage
in and then usually we would leverage This Server component you know to fetch
This Server component you know to fetch uh members as well but since uh in all
uh members as well but since uh in all the previous Pages where we used the
the previous Pages where we used the server side fetching here it was for you
server side fetching here it was for you know initial data for forms right like
know initial data for forms right like name title status things like that but
name title status things like that but this will be a list of members so I
this will be a list of members so I think it's better to fetch this client
think it's better to fetch this client side so that we can Implement you know
side so that we can Implement you know infinite loading if needed in the future
infinite loading if needed in the future so that's why I'm going to handle the
so that's why I'm going to handle the server component only for this very
server component only for this very convenient redirect without use effect
convenient redirect without use effect here and then what I'm going to do is
here and then what I'm going to do is I'm going to go inside of my features
I'm going to go inside of my features inside of my
inside of my workspaces and let's create a component
workspaces and let's create a component called members form uh members list.
called members form uh members list. DSX like this so let's export const
DSX like this so let's export const members list
members list here now will you put this you know
here now will you put this you know inside of members or workspaces it's
inside of members or workspaces it's really your choice perhaps perhaps maybe
really your choice perhaps perhaps maybe it's better to keep it in the members uh
it's better to keep it in the members uh feature folder you know we'll see let's
feature folder you know we'll see let's implement it first so I'm just going to
implement it first so I'm just going to write you know members list here first
write you know members list here first I'm going to go to this page and I will
I'm going to go to this page and I will simply give this one uh a class name of
simply give this one uh a class name of full width and on large maximum width of
full width and on large maximum width of Excel and then inside of here I'm going
Excel and then inside of here I'm going to add a members list like this from
to add a members list like this from features workspaces components members
features workspaces components members list and I think that now if I click on
list and I think that now if I click on members here there we go I have the
members here there we go I have the members list in a standalone file
members list in a standalone file perfect so just like on settings for
perfect so just like on settings for example right here excellent let's go
example right here excellent let's go back inside of our members list now and
back inside of our members list now and let's go ahead and style this so I think
let's go ahead and style this so I think we should start by marking this entire
we should start by marking this entire thing as use client and importing the
thing as use client and importing the card card content card header and card
card card content card header and card title from components UI card besides
title from components UI card besides that we're also going to need our handy
that we're also going to need our handy use workspace ID from features
use workspace ID from features workspaces hooks use workspace ID so
workspaces hooks use workspace ID so that we can obtain it from the URL so
that we can obtain it from the URL so let's go ahead uh and do that I'm going
let's go ahead uh and do that I'm going to go ahead and get the workspace ID
to go ahead and get the workspace ID from use workspace ID here and let's go
from use workspace ID here and let's go ahead and style our card here so this
ahead and style our card here so this will be a card with a class name pool
will be a card with a class name pool width pool height border none and Shadow
width pool height border none and Shadow none inside of here I'm going to add a
none inside of here I'm going to add a card header component with a class name
card header component with a class name Flex Flex row item
Center Gap X of four padding of seven and space y off zero then inside of here
and space y off zero then inside of here I'm going to add a button component so
I'm going to add a button component so let's make sure that you import the
let's make sure that you import the button component from components UI
button component from components UI button inside of this button I'm going
button inside of this button I'm going to write back so the user can go back to
to write back so the user can go back to uh the dashboard and I'm going to add an
uh the dashboard and I'm going to add an arrow left
arrow left icon I'm going to give this a class name
icon I'm going to give this a class name of size 4 and then m R of two and this
of size 4 and then m R of two and this button will have a variant of
button will have a variant of secondary and it will have a size of
secondary and it will have a size of small this button will also be a link in
itself which will have an HRA to redirect the user to go back to slash
redirect the user to go back to slash workspaces and then slash back to the
workspaces and then slash back to the workspace ID and let's go ahead and
workspace ID and let's go ahead and assign the as child here so the button
assign the as child here so the button becomes the link there we go
becomes the link there we go now outside of this button we're going
now outside of this button we're going to add a card title
to add a card title here and we are simply going to add uh
here and we are simply going to add uh members list here and let's add a class
members list here and let's add a class name here text extra large and font
bold there we go outside of the card header we're going to add a div with a
header we're going to add a div with a class name
class name px7 and we're going to go ahead and add
px7 and we're going to go ahead and add a dotted separator here now this dotted
a dotted separator here now this dotted separator comes from the components
separator comes from the components doted separator import I'm just going to
doted separator import I'm just going to go ahead and align my imports here there
go ahead and align my imports here there we go and below this we are adding a
we go and below this we are adding a card
card content and let's give this a class name
content and let's give this a class name of padding seven like this inside of
of padding seven like this inside of here let's go ahead and let's get our
here let's go ahead and let's get our used get members and let's pass in the
used get members and let's pass in the workspace ID which we have and we have
workspace ID which we have and we have imported use get members from our newly
imported use get members from our newly created features members API use get
created features members API use get members inside of here we can now
members inside of here we can now destructure the data like this and then
destructure the data like this and then we can go ahead inside and we can do
we can go ahead inside and we can do data dot
data dot documents and make sure you put the
documents and make sure you put the question mark here so data question mark
question mark here so data question mark documents do map get individual member
documents do map get individual member and index
and index here and let's add a fragment around
here and let's add a fragment around each of our uh from react around each of
each of our uh from react around each of our members here so I'm going to go
our members here so I'm going to go ahead and just add the fragment here
ahead and just add the fragment here like this and inside let's add a
div with a class name Flex item Center and GAP two and give the fragment a key
and GAP two and give the fragment a key of member dollar sign ID inside of this
of member dollar sign ID inside of this div we're going to add a member Avatar
div we're going to add a member Avatar which is a component we don't yet have
which is a component we don't yet have but luckily for us it's going to be very
but luckily for us it's going to be very simple to the one that exists called
simple to the one that exists called project uh called workspace Avatar so
project uh called workspace Avatar so let's go inside of source features
let's go inside of source features workspaces components workspace Avatar
workspaces components workspace Avatar and we can copy this and then we can go
and we can copy this and then we can go inside of the members inside of the new
inside of the members inside of the new folder we're going to create inside
folder we're going to create inside components and simply paste in uh well
components and simply paste in uh well you can just copy it like this and uh
you can just copy it like this and uh this will be called members
this will be called members avatar. DSX and paste it inside let's go
avatar. DSX and paste it inside let's go ahead and rename the three instances to
ahead and rename the three instances to members Avatar here so it will have uh
members Avatar here so it will have uh no it will not have an image actually so
no it will not have an image actually so only a name class name and let's add a
only a name class name and let's add a fullback class name here to be a string
fullback class name here to be a string as well and we're going to remove this
as well and we're going to remove this and we're going to add a fullback class
and we're going to add a fullback class name uh it will not have an image so no
name uh it will not have an image so no need for this case we are just working
need for this case we are just working with this one uh it will have a size
with this one uh it will have a size five by default uh it will be transition
five by default uh it will be transition it will be border border neutral 300 and
it will be border border neutral 300 and it will be around did
it will be around did full like this uh oh I've removed the
full like this uh oh I've removed the return there we go and our Avatar
return there we go and our Avatar fullback here uh will do the following
fullback here uh will do the following so this one let's go ahead and do this
so this one let's go ahead and do this let's remove the entire thing and let's
let's remove the entire thing and let's reuse our
reuse our CN so the default classes will be
CN so the default classes will be background neutral 200 font medium text
background neutral 200 font medium text neutral 500 100 Flex items Center and
neutral 500 100 Flex items Center and justify
justify Center and then inside of each of them
Center and then inside of each of them uh also besides this we're going to pass
uh also besides this we're going to pass in the fullback class name like this and
in the fullback class name like this and we can go ahead and do this it's a name
we can go ahead and do this it's a name do character
do character at zero and then to
uppercase like this and we can remove the image import so now we have the
the image import so now we have the members uh the members aat here uh I
members uh the members aat here uh I just want to call this member Avatar not
just want to call this member Avatar not members so member Avatar and I'm going
members so member Avatar and I'm going to rename it here as well member
to rename it here as well member Avatar great we can now go back inside
Avatar great we can now go back inside of workspaces components members list
of workspaces components members list which the more I look at it the more I
which the more I look at it the more I think it should belong to the members
think it should belong to the members feature folder uh now let's import the
feature folder uh now let's import the member avatar from features members
member avatar from features members components member Avatar right here and
components member Avatar right here and we're going to go ahead and pass in the
we're going to go ahead and pass in the f in the class name size 10 all back
f in the class name size 10 all back class name text large and name member.
class name text large and name member. name there we go uh and Let me refresh
name there we go uh and Let me refresh this to see uh is anything loading here
this to see uh is anything loading here looks like it's not loading uh we're
looks like it's not loading uh we're going to go ahead and debug I think I
going to go ahead and debug I think I maybe know why uh let's see yeah so we
maybe know why uh let's see yeah so we don't have the scope to load the users
don't have the scope to load the users so let's go ahead and fix that so I'm
so let's go ahead and fix that so I'm going to go ins set my members here my
going to go ins set my members here my apologies of my app right console and
apologies of my app right console and first let's check our members collection
first let's check our members collection specifically the settings okay we have
specifically the settings okay we have all the right permissions set so what
all the right permissions set so what the issue is instead of our admin client
the issue is instead of our admin client attempting to list the users so let's go
attempting to list the users so let's go ahead and go inside of our settings
ahead and go inside of our settings let's find
let's find our uh in the overview here maybe API
our uh in the overview here maybe API Keys here they are click on your API key
Keys here they are click on your API key here and then go ahead inside of the
here and then go ahead inside of the Scopes and now in the out click on
Scopes and now in the out click on users. read like this and click update
users. read like this and click update and I think that now if I refresh there
and I think that now if I refresh there we go now I have to users Antonio and uh
we go now I have to users Antonio and uh John right here great so our member
John right here great so our member Avatar now works no more errors inside
Avatar now works no more errors inside of our API end points and now below the
of our API end points and now below the member Avatar let's go ahead and let's
member Avatar let's go ahead and let's add a div with a class name Flex Flex
add a div with a class name Flex Flex column and let's add a paragraph which
column and let's add a paragraph which will render the member name which we
will render the member name which we have populated there we go here's the
have populated there we go here's the name and a class name text small and
name and a class name text small and font medium like this below this we're
font medium like this below this we're going to add uh a text extra small and
going to add uh a text extra small and text muted foreground and this will
text muted foreground and this will render the member
render the member email like this there we go
email like this there we go uh so I have Flex items Center here uh
uh so I have Flex items Center here uh but something here I don't like uh okay
but something here I don't like uh okay uh so what we're going to do next is
uh so what we're going to do next is outside of this div here we're going to
outside of this div here we're going to go ahead and we're going to add a button
go ahead and we're going to add a button component which will have a more
component which will have a more vertical icon from Lucid react so make
vertical icon from Lucid react so make sure you have imported the more vertical
sure you have imported the more vertical icon here now we're going to go ahead
icon here now we're going to go ahead and give this button a class name of ml
and give this button a class name of ml AO so it's on the other side a variant
AO so it's on the other side a variant of secondary and the size of Icon so
of secondary and the size of Icon so it's small and let's now give the more
it's small and let's now give the more vertical icon a class name here of size
vertical icon a class name here of size 4 and text muted foreground like
4 and text muted foreground like this uh great so we have this and then
this uh great so we have this and then outside of this div still inside of the
outside of this div still inside of the fragment let's do if index is smaller
fragment let's do if index is smaller than uh our data documents length minus
than uh our data documents length minus one in that case we're going to go ahead
one in that case we're going to go ahead and render a separator component not a
and render a separator component not a dotted separator just our separator
dotted separator just our separator component from shaten again if you don't
component from shaten again if you don't have that you can do BU x--
have that you can do BU x-- bun chaten
bun chaten 2.1.0 add
2.1.0 add separator in case you don't have the
separator in case you don't have the separator component but you should
separator component but you should because we added it in the beginning of
because we added it in the beginning of our tutorial there we go we now have the
our tutorial there we go we now have the separator and let's give it a class name
separator and let's give it a class name myy 2.5 and there we go now as you can
myy 2.5 and there we go now as you can see each of our members here has a nice
see each of our members here has a nice little separator here great so now what
little separator here great so now what we have to do is when we click on these
we have to do is when we click on these uh to show some options but I just don't
uh to show some options but I just don't like how this separator looks can I
like how this separator looks can I change this color to neutral 300 maybe
change this color to neutral 300 maybe or maybe a
background make it a bit darker okay I'm not going to uh play around with this
not going to uh play around with this too much uh let's go ahead and let's
too much uh let's go ahead and let's actually uh create this button uh into a
actually uh create this button uh into a drop- down menu so for that we need to
drop- down menu so for that we need to import everything uh used for the drop-
import everything uh used for the drop- down menu so let's import the
down menu so let's import the following drop down menu drop down menu
following drop down menu drop down menu content drop- down menu item and drop
content drop- down menu item and drop down menu trigger from components UI
down menu trigger from components UI drop down menu and then we're going to
drop down menu and then we're going to wrap this entire button here inside of
wrap this entire button here inside of that so let's grab the button inside of
that so let's grab the button inside of the drop down
the drop down menu like
menu like this let's give the uh drop down menu
this let's give the uh drop down menu itself well nothing let's just continue
itself well nothing let's just continue by with wrapping and composing so the
by with wrapping and composing so the button will go inside of the drop down
button will go inside of the drop down menu trigger and to avoid a hydration
menu trigger and to avoid a hydration error we're going to give this as child
error we're going to give this as child so the trigger becomes the button
so the trigger becomes the button because the drop- down menu trigger is a
because the drop- down menu trigger is a button element and then we would have
button element and then we would have button inside of a button that causes
button inside of a button that causes hydration errors so as child we'll fix
hydration errors so as child we'll fix that and then inside of here we're going
that and then inside of here we're going to go ahead and have a drop- down menu
to go ahead and have a drop- down menu content with the side of bottom and an
content with the side of bottom and an line of end like this and then inside of
line of end like this and then inside of here we're going to have our uh dropdown
here we're going to have our uh dropdown menu items so drop down menu item here
menu items so drop down menu item here the first one will be set as
the first one will be set as administrator and we're going to go
administrator and we're going to go ahead and give this a class name of font
ahead and give this a class name of font medium and let me just fix the
medium and let me just fix the capitalization here class
capitalization here class name then we're going to go ahead and
name then we're going to go ahead and have an on click here which for now will
have an on click here which for now will be empty and disabled which will be
be empty and disabled which will be false and then let's go ahead and let's
false and then let's go ahead and let's copy and paste this one this one will be
copy and paste this one this one will be set as
member and then the last one uh will have a class name of text Amber
have a class name of text Amber 700 and it will have a remove member.
700 and it will have a remove member. name here so now when you click here
name here so now when you click here there we go set as administrator set as
there we go set as administrator set as member and remove John or remove Antonio
member and remove John or remove Antonio now let's go ahead and let's add our
now let's go ahead and let's add our hooks for this so we are working with
hooks for this so we are working with use update member and use delete member
use update member and use delete member let me separate them so we're going to
let me separate them so we're going to add const use delete member from
add const use delete member from features member use delete member we're
features member use delete member we're going to add mutate to be delete member
going to add mutate to be delete member is spending will be is deleting
is spending will be is deleting member and then we can copy and paste
member and then we can copy and paste this and call use update member this
this and call use update member this will be update member and this will be
will be update member and this will be is updating member just make sure you
is updating member just make sure you have imported your both use delete
have imported your both use delete member and your use update member I'm
member and your use update member I'm just going to move them with the
just going to move them with the features here above there we
features here above there we go now that we have those uh let's go
go now that we have those uh let's go ahead and let's actually uh handle the
ahead and let's actually uh handle the delete member and handle the update
delete member and handle the update member so
member so const handle update
const handle update member will be an asynchronous method
member will be an asynchronous method actually no need for asynchronous it
actually no need for asynchronous it will just accept the member we are
will just accept the member we are trying to delete so member
trying to delete so member ID which is a
ID which is a string and it will accept a role which
string and it will accept a role which is a type of member role so in import
is a type of member role so in import that so make sure you have added the
that so make sure you have added the member Ro import from features members
member Ro import from features members types I'm just going to move it here
types I'm just going to move it here there we
there we go so now inside of here we're going to
go so now inside of here we're going to call the update
call the update member and we're going to pass in the
member and we're going to pass in the Json to be
Json to be rooll and we're going to pass in the Pam
rooll and we're going to pass in the Pam to be member
ID there we go no errors great now let's go ahead and let's add the confirmation
go ahead and let's add the confirmation for our uh Delete here so for that we're
for our uh Delete here so for that we're going to have to go
going to have to go here and import use confirm from hooks
here and import use confirm from hooks use
use confirm and let's go ahead and set that
confirm and let's go ahead and set that up here so this is an
up here so this is an array like this let's go ahead and ask
array like this let's go ahead and ask remove
remove member this member will be removed from
member this member will be removed from the
the workspace and
workspace and destructive we're going to have a
destructive we're going to have a confirm
confirm let's just call this a dialogue and
let's just call this a dialogue and confirm actually let's call it a confirm
confirm actually let's call it a confirm dialogue just in case we import the
dialogue just in case we import the actual dialogue anytime we can render
actual dialogue anytime we can render the confirm dialogue
here like this and then let's go ahead and let's implement the const handle
and let's implement the const handle delete member and this will be an
delete member and this will be an asynchronous method which will accept
asynchronous method which will accept the member ID to delete and then we're
the member ID to delete and then we're going to get the okay using await
going to get the okay using await confirm here if not okay we are going to
confirm here if not okay we are going to break the method immediately otherwise
break the method immediately otherwise we're going to go ahead and call the
we're going to go ahead and call the delete member member ID and in case we
delete member member ID and in case we deleted
deleted ourselves uh we're going to go ahead and
ourselves uh we're going to go ahead and pass in the unsuccess
pass in the unsuccess here window location reload like this so
here window location reload like this so this is kind of a Brute Force way of
this is kind of a Brute Force way of ensuring everything is up to date after
ensuring everything is up to date after this action and it looks like I'm
this action and it looks like I'm getting an error here I think I need to
getting an error here I think I need to pass the per
Ram there we go now let's assign the handle the update member and handle
handle the update member and handle delete member to our drop- down menu
delete member to our drop- down menu items here so the set as administrator
items here so the set as administrator will call the handle update member and
will call the handle update member and it will pass in the member do ID with
it will pass in the member do ID with the dollar sign and then member roll.
the dollar sign and then member roll. admin and it will be disabled if is
admin and it will be disabled if is updating member so I'm going to copy
updating member so I'm going to copy these two and I'm going to replace them
these two and I'm going to replace them for this one this one will set the
for this one this one will set the member Ro to
member Ro to member and we can copy this two as well
member and we can copy this two as well and replace them for the remove one here
and replace them for the remove one here this will be handle delete member and no
this will be handle delete member and no need for the second argument here and
need for the second argument here and this will be is deleting member like
this will be is deleting member like this let's go ahead and try it out so
this let's go ahead and try it out so I'm logged in as Antonio and I'm pretty
I'm logged in as Antonio and I'm pretty sure I created the test uh uh workspace
sure I created the test uh uh workspace here so I can go inside of my members
here so I can go inside of my members and I should be able to remove John
and I should be able to remove John after confirmation
after confirmation and there we go John has been removed
and there we go John has been removed from the list and if I now go ahead and
from the list and if I now go ahead and log out here and go inside of John
log out here and go inside of John mail.com John 1 2 3 4 5 6 78 log in uh
mail.com John 1 2 3 4 5 6 78 log in uh there we go John has no idea about this
there we go John has no idea about this workspace perfect so now I'm going to go
workspace perfect so now I'm going to go ahead and go back inside of my Antonio
ahead and go back inside of my Antonio here let's invite John back again so I'm
here let's invite John back again so I'm going to go inside of my settings for
going to go inside of my settings for the test workspace and I will copy the
the test workspace and I will copy the invite link
invite link I will log out I will log in as
John let's go ahead and accept the invite here join workspace and now if I
invite here join workspace and now if I go inside of settings here and if I my
go inside of settings here and if I my apologies if I go inside of members and
apologies if I go inside of members and if I attempt to remove Antonio here
if I attempt to remove Antonio here there we go fail to delete member and if
there we go fail to delete member and if I attempt to set myself as administrator
I attempt to set myself as administrator or member it does not allow me to do any
or member it does not allow me to do any of those things as you can see uh so
of those things as you can see uh so let's go ahead and quickly try one thing
let's go ahead and quickly try one thing if I am
Antonio and I go inside of members here let's wait a second for this to
here let's wait a second for this to load oh I think it did not load yeah I I
load oh I think it did not load yeah I I think I pressed on the my apologies so
think I pressed on the my apologies so what happened here is that before my
what happened here is that before my workspace loaded I pressed on members
workspace loaded I pressed on members and then that led to undefined so
and then that led to undefined so perhaps why workspace is loading while
perhaps why workspace is loading while we are getting redirected we can disable
we are getting redirected we can disable that so that doesn't happen so now you
that so that doesn't happen so now you can see it works what I want to know now
can see it works what I want to know now is can I set John as administrator here
is can I set John as administrator here member updated amazing and now if I go
member updated amazing and now if I go back inside of
back inside of John I really should have set up
John I really should have set up multiple browsers for this now John
multiple browsers for this now John should be able to downgrade Antonio
should be able to downgrade Antonio there we go member updated and that
there we go member updated and that would also mean now that
would also mean now that Antonio should no longer be able to
Antonio should no longer be able to remove
remove John let's hope this is the last time I
John let's hope this is the last time I do this so now JN if I remove
do this so now JN if I remove JN I get an error but I should be able
JN I get an error but I should be able to remove myself from the workspace and
to remove myself from the workspace and that works because I am a member uh
that works because I am a member uh great but it looks like my window reload
great but it looks like my window reload uh did not trigger here on success
uh did not trigger here on success that's odd uh but I think that after
that's odd uh but I think that after refreshing this uh yeah okay what we
refreshing this uh yeah okay what we have to do is we have to handle some
have to do is we have to handle some error States right but if the user tries
error States right but if the user tries to go back here uh they won't be able to
to go back here uh they won't be able to load anything here because we will cause
load anything here because we will cause an error if they try to load a workspace
an error if they try to load a workspace they're not a part of so you didn't have
they're not a part of so you didn't have to worry about that but the important
to worry about that but the important thing is this user now has no workspace
thing is this user now has no workspace our role base access control works
our role base access control works perfectly amazing amazing job
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.