hey there my name is Antonio and today we're building a finance manager software as a service a web app designed to give you control over your financial data we'll start on the user dashboard where you can view and manage your daily transactions income and expenses through interactive graphs watch as I switch between different chart types for transactions and categories and see just how easily you can filter data by accounts and date ranges let's dive in and see all of the amazing features this app is going to have in this segment we'll explore the transactions tab
here a detailed table displays dates categories pays amounts and accounts you'll see how to sort search and bulk delete transactions with ease I'll demonstrate adding a new transaction via a drawer that slides out with a form ready to fill notice the intuitive select components for accounts and categories simply type and hit enter to create new ones plus our smart amount component lets you toggle between income and expense types effortlessly after creating a new transaction I can quickly find it by searching our data table watch as I rename an account directly from the trans transaction updating
it instantly next I remove a category setting the transaction to be uncategorized I will also show you just how easy it is to edit and delete any transaction from the data table demonstrating the flexibility and realtime functionality of our system now let's try importing a transactions with a CSV upload I am immediately prompted to upgrade a reminder that our project is also a software as a service next I head to settings to connect our bank account using plate but I encounter another upgrade prompt clicking on the upgrade redirects me to lemon squeezy our payment provider
after completing the upgrade and returning back to the project all previously blocked features are now fully accessible it's exciting to see these powerful tools unlocked allowing us to seamlessly import transactions and connect directly to our bank accounts now let's explore one of the key features we just unlocked connecting to our bank account using plate after selecting my bank I am redirected to log in and asked which accounts I want to link once I complete this setup a success message confirms that everything is connected and and now let's take a deeper look back in our finance
manager you'll see a comprehensive update across the board new transactions have been added accounts are up toate and even the categories have been refreshed to reflect the latest financial data it is incredibly seamless providing a secure and efficient way to manage your finances now let's explore another feature we've unlocked importing transactions via CSV after uploading the file an editable data table pops up each header is a select component letting us map columns to specific Fields the continue button shows how many fields we've set and if we don't need some data we just select skip after
setting up our fields we then choose the account where we want to import the data and once the import is complete I'll showcase how the imported data is now fully integrated and displayed in the transactions tab this precise control ensures that the data fits perfectly into our financial management system and to wrap up let's check out the settings where you can disconnect your bank or cancel your subscription any time for full control this will ensure that you have full autonomy over your data and your services and now without further Ado let's get started if you're
interested in an extended version of this course where we learn how to implement a monthly subscription and a paid plan using lemon squeezy as well as how to connect to a bank account to fetch transactions using blade make sure to check out the link in the description and join code with Antonio Pro to unlock the source code and all additional content for all of my projects let's get started by configuring nextjs inside of our project in order to run nextjs you're going to have to install no JS or alternatively you can install bun which is
something I'm going to be using for this tutorial bun is a new JavaScript runtime with significant performance improvements over the existing nodejs but if for whatever reason you want to use node you can do that let's go ahead and run the automatic installation command as you can see they are using the npx command but since I just said that I'm going to be using bun I'm going to be using the alternative bun X you can choose which one you want to use let's go ahead inside of our Visual Studio code and let's open up the
terminal inside of here I'm going to run npx create next app at latest and then I'm going to give my app a name in this case I'm going to call it Finance tutorial let's press enter and let's go ahead and set up the configuration we're going to be using typescript we want Sint we are going to be using Tailwind but we are not going to be using the source directory we are going to be using the app router and we are not going to change the default import alas meaning we're going to leave it at
the at sign now let's wait a second for all of this to install great after you get the success message go ahead and open up your project I'm going to go ahead and press on finance tutorial and you should be greeted with a message similar to this at least inside of Visual Studio code and you can safely press yes for the app structure you should see something like this an app folder with a layout and Page inside public folder and some configuration files depending on whether you used bun meaning bun X or you used npx
you are or not going to see this bun lock file so I am using bun so I have this file if you don't have this file that's completely fine it just means that you're using node or npx both are completely fine for this entire project great now before we run our app I want to install our well not a component library but let's call it a command line interface which is going to help us to build our component Library so why am I explaining this in that way well that's because I don't want to call
this a component library because the moment you click in the documentation you have a sentence which says this is not a component Library instead it's a collection of reusable components that we can copy and paste into our apps so that's perfect for our use case because we want to build our own component Library let's press on the installation let's select next JS right here we've already run this command so no need to run this but now we have to run the following npx shat cnii latest in it or if you're using bun it's going to
be bun X for you so let's go ahead and copy this command let's head back inside of our terminal and I'm going to run bun X shat cnii latest my apologies for this like this so b x shat cnii latest in it or you can use npx if you're using node I'm going to be using the default style and I'm going to select the Slate color I'm going to be using CSS variables for Colors Let's Wait a moment for this to install great after you've installed this you should see some new folders such as the
components which should be completely empty and the lib which now holds the utils file inside of here here you're going to have a CN method which is going to be very useful for when we want Dynamic Tailwind classes but without conflicting the Styles now we are ready to run this project let's go ahead inside of the terminal if you're using node you're going to run mpm runev but if you're using bun you're going to run bun runev both should start the project on a local host 3000 so let's go ahead and open the project and
there we go you should be seeing the nextjs landing page now let's go ahead and let's add a button component from shaten library I'm going to go ahead and find the button component and in here you should see the installation instructions right here so whenever you click on here you can select whether you want to copy it as an mpm yarn pmpm or bun instance so since I'm using bun I'm going to be copying the bun installation instructions so let's head back inside of our terminal here I'm going to go ahead and open a new
one and I'm going to run the command you can also use a simpler version so this is what was copied from here but you can just do bunx shaten UI at latest at button this is simpler or you can use npx shatu I latest add button there we go so just ensure you have your project running here let me zoom in a bit and in here in the components you should now see the UI folder which holds the button component so this is the key difference between shatan UI and all the other uh component libraries
so shaten UI inserts the code of the button inside of your project so in here I can change whatever I want if I don't want these classes I can modify them here if I want to add another variant I can do that easily so let's go ahead and try out so just make sure you have your Local Host running and let's go inside of the app folder the app folder is where our client and server routing is going to live more specifically the page and layout files so the page is a reserved file name in
nextjs which indicates a route nextjs uses file and folder based routing so if you have a page file purely inside of the app folder which is not surrounded by any additional folders this represents the root page so that's why we can find this page right here in this code I'm going to remove this import and then I'm going to entirely clean the return method and I'm going to write a new one inside of here I'm very simply going to add our button component from component UI button and I'm going to say click me let's save
and there we go we now have a button which says click me and now we can access all the props which we just looked at a moment ago so we have class name variant size as child and some other props which are extended from the button HTML attributes itself and we can also try out these attributes here so how about we give it a variant of destructive that's going to change the button to red and here's a cool thing if you want to you can add your own variant for example blue BG blue 600 text
white now if if I go back you can see how typescript is going to automatically recommend blue to me and there we go now the button is blue you can always remove this variant and when you go back you're going to get an error this was an intro of our project and the technology that we are going to use next we're going to learn how to build our Landing login page how to set up authentication and we're going to get introduced to hono the which is going to be our library for building the API now
let's go ahead and let's prepare our authentication routes and our protected routes so I'm going to be using a concept inside of nextjs calling route groups so let's go ahead over the basics of routing let's say I want to create a new client route called test I would first create a folder inside of the app folder and inside I would create a page. DSX once missing for this to become a route is a default export so something like this and I'm going to call this test page I'm going to return a div calling text Page
like that so what's crucial here is that you do a default export if you just do an export const this is not going to register as a route so it's very important that whenever you have a page. DSX somewhere you do an export default so let's try that out on Local Host 3000 make sure you have your app running go to slash test and you should see the test page so now you know that the folder name will take part of the URL but what if you don't want uh your uh folder to be a
part of the URL you can do that by using a route group something like this let's call this out inside of parentheses and then inside of here let's create a sign in route and let's go ahead and create a page. DSX inside once again I'm going to do a signin page default export Viv signin page and how do you think I'm about to access this route I just told you that one way to not have your folder name inside of the URL is by putting it inside of parenthesis so what you can do is instead
of going to to localhost 3000 SL out/ signin this is a 404 page instead out doesn't exist in the URL so it is just slash sign in so that was a quick explanation of Route groups if you created the test route you can now remove it but go ahead and create uh your own uh well route group for out pages so we're going to create this sign in we can copy this and let's create a sign up page and let's go ahead and change this one to be a sign up page and let's rename this
to sign up now you should have a localhost 3000 signin and slash sign up there we go uh also the default export name of the function does not matter so this can be called X and it's not going to change anything at all right all that matters is that you do a default export great one more thing that we're going to do before we add our authentication library is we are going to create a catch all route so usually if I wanted to create a route which can accept a parameter like uh user ID I
would create a folder inside of parentheses user ID and then I would access that inside of my component but what if I don't know what I'm going to rece I simply want to catch every single parameter every single query whatever is possible in this route that's called a catch all route you can read more about that in nextjs documentation so what we're going to do here is create this double square parentheses and then I'm going to spread sign in and very simply I'm going to drag and drop this page inside that's going to open this
weird unsaved page. TS file so this is as you can see it triggered a next change don't worry about this here are the steps to resolve this if this happened to you first of all immediately close this nextext folder I see a lot of people accidentally continue to develop inside of here this is not your repository this is cash so simply close this folder if this page has opened for you just click save so you no longer have that c ccle here and then you can just close it if for any reason you think you've
done something wrong don't worry at all here are the next steps you can do go ahead inside of your terminal here and shut down your app then go ahead and remove the next folder entirely then simply go ahead inside the terminal and run your app again remember I'm using bun so I'm doing bun runev if you're using node it's going to be npm runev and the moment you run this you're going to see the next folder being reinitialized so you don't have to worry if you mess something up inside make sure you refresh your Local
Host every time you shut down and restart your server now let's go back inside of the app folder out inside of here we started working on this catch all route so what I've done is I've moved page. TSX from not just being inside of the sign in folder but inside of the catch all route here so what has changed absolutely nothing this is still on localhost 3000 sign up but now this is going to give our out provider the ability to catch every parameter every call back URL and everything else it needs to properly handle
redirects from this authentication page now I want to do the very same thing but for sign up so go ahead and simply spread sign up here and again drag and drop this inside and repeat the steps go ahead and immediately close the next folder go ahead and enter this unsaved file and just save it and close it make sure you are not developing inside of the next folder and again if you think you've messed it up remove the next folder and restart your app great now you should have the signin and the sign up Pages
ready to be used and now I want to go ahead and I want to create another route group called dashboard so the reason I'm using route groups is only so I can organize my files in a better way so I I could have created the sign in and sign up purely inside of the app folder but I like my files organized and this is very useful because then inside of this out folder I can also create a components folder if that's something I want so I can collocate items here great so I'm going to remove
this empty folder make sure you didn't accidentally remove this components we need that great so inside of this this dashboard what I want to do is I want to make this my root page so I want this what's on the Local Host 3000 this button I can do this very easily see since dashboard is inside of parentheses it means that it will not be a part of URL so I can just drag and drop the page. PSX inside and then again close the next folder go ahead and save this cache file and close it and
don't open next again if you accidentally messed it up you can remove the folder and just restart your server and nothing should change now so inside of your dashboard page. vs XX you should still have this button and when I refresh everything is still working fine I'm now going to remove this button and instead I'm going to render a paragraph which will say this is an authenticated route so only logged in users should be able to see this this authenticated route so we have an issue we are not logged in but we are able to
see this route that's not something I want when you user tries to access the dashboard the root page I want them to be redirected to either sign in or sign up so let's go ahead and install clerk inside of our project so head to clerk.com create an account and find a way to create a new application if you already have uh a couple of projects you're going to see a screen similar to this if it is your first time you're going to be redirected to a screen like this so I'm going to call this finance
and go ahead and select any amount of providers you want you can even select some web three options here I'm going to keep it simple with Google and email login in fact I'm just going to keep Google or basically whatever you want go ahead and create your application and then we're going to follow the steps to add this inside of nextjs so in here let's go ahead and install clerk nextjs I'm going to be using bun so I'm going to be using bun add instead instead of mpm install you can use mpm or yarn or
pnpm it really does not matter so let's go ahead inside of the terminal here I will shut down the app for now and I'm going to do BN add Clerk nextjs or you can do npm install Clerk nextjs and while this is installing let's see our next step so our next step is to set your environment variables so inside of environment. loal we need next public clerk publishable key and clerk secret key I'm going to go ahead and copy both of those I'm going to go inside of my project let's see if we already have
a environment file looks like we don't so let's create one in the root of our application create. environment. loal here's what important whenever you're working with environment files make sure that they are inside of G ignore so inside of here you can see that I have environment. loal which means that this file will not be committed to GitHub do not commit to GitHub if you haven't put that inside of your G ignore you can also directly write environment. local you don't need to know these wild cards at all so let me go ahead and do
something I'm going to press command shift p or control shift p on Windows and I will reload the window the reason I'm doing this is because I want to show you that this environment. loal has a bit of a darker or uh grayed out color as opposed to for example public folder or the file below that same as node modules so this is a visual cue for you to confirm that this file is not going to be committed and that will tell you that it's safe to put these two environment variables inside so let's just
repeat where I got those right here in step two set your environment variables I copied the two of those and I've simply pasted them inside of our newly created environment. loal file let's see what our next steps are so we are using the app router and we have app folder and layout. TSX so it's asking us to add clerk provider do our app I'm going to copy the top line here let's go inside of app folder layout and in here I'm going to go ahead and add that here and then I'm going to wrap my
entire project inside of that there we go but we are not done yet one crucial thing we are missing is a middleware so what we can do is we can copy the entire middleware from here and let's create a root file middleware dods make sure you don't misspell this file as middleware is a reserved keyword in nextjs let's paste that in here and let's go ahead and try and run our app uh actually before we do that let's just see okay they tell us to run our app so I think it should be good enough
for us to try this out so inside of terminal Bun Run Dev or npm let's visit our local host and let's see what's going on there we go so this is still telling us that this is an authenticated route so we can still visit this right so let's learn how we can protect this route so a big change has happened in clerk since my last tutorial usually the clerk middleware by default protected all routes that is no longer the case from now on we decide which routes are protected which means that all routes are public
by default so how about we explore the documentation here the guide so while we are here we can already do this we can build our sign up and our signin page because we have this exact structure already set up let's take a look at it here so I'm going to collapse this and let me expand my file browser here in the app folder we already have sign in and sign up let's go inside of this catch all page. vsx and let's go ahead and copy this and I'm copying sign up so it's a better idea
to go inside of sign up right so we can literally select everything and paste it here and make sure the path aligns so slash sign up slash up let's save this and now let's do the same thing for sign in so so step two I'm copying it and going inside of here replacing everything slash sign in so this is the correct route because remember the out is a route group and this is a catch all so none of this will inter interfere with the fact that the only route we have is sign in and sign
up great and now let's go ahead and update our environment variables so they need to match as well let's inside of environment. loal and let's add next public clerk sign in is/ signin and sign up is slash sign up perfect and now what I want to do is I want to learn how to protect our routes so as it says right here we should now be able to manually visit our routes so Local Host 3000 there we go we have our signin route here and if I go to Local Host 3000 SL sign up there
we go we have the sign up route here so both of this seem to be working just fine but we still still have this issue why can we visit Local Host 3000 so inside of this guide I'm going to go ahead and I'm going to look at clerk middleware here and inside of here we should get a quick guide on how to protect our routes so this is our current case if I'm not mistaken so if I go to my middleware there we go so just an empty clerk middleware with a matcher which catches pretty
much everything except the static assets so let's go ahead and do this let's create the is protected route using the create route matcher so I'm going to go ahead and paste that here and we can import the create route matcher from clerk nextjs server so let's just add this there we go but we don't have slash dashboard instead all we have is a slash route and make sure that you remove this wild card asteris here because what this represents is dashboard and everything after dashboard so in our case it would be slash and everything after
slash but that's not true because we there are some routes which we want to be well not public but not specifically protected by the next JS middleware and also remove this so for now just add this but we're not done yet so this just created a constant what we have to do now is we actually have to create a logic which is going to uh well work on protecting the routes so this is what I recommend that you do go ahead and open this method in here in the parameters you have access to out and
request let's also prepare by adding an import of next response from next server and if is protected route the current request in that case let's call the out do protect otherwise let's return next response. next so just like any other middleware if you've ever used Express or nodejs before it's going to return next meaning it's going to go further there we go so now if we implemented this correctly and if I try and go on Local Host 3000 there we go I am now redirected and you can see how now I have this redirect URL
parameters right here so all of that will now work flawlessly because we made sure that we added these catch all routes if you don't use this catch all routes I think clerk is actually going to throw you some errors so uh I didn't come up with the aidea of this catch all routes right uh I simply knew it because I already went over clerk documentation so that's where I found those guides great what I want to do now is I want to make our Pages just a bit prettier right so let's go inside of signin
page. TSX and let's go ahead and wrap this up like this so we have more space to work with let's open up a div here and inside of here I want to give this a class name of Minh screen so it takes up the full height and I want to work with grids so I'm going to initialize a grid c one and now this doesn't make too much sense right why am I initializing a grid if I'm only going to have one column that's because I'm working on a mobile first design so on mobiles it's
going to have one column but the moment we hit a large break point it's going to be grid called 2 and now let's go ahead and open up another div wrapping up our sign in here and inside of here I want to give this a class name of full height on large devices we're going to work with flex we're going to have a flex column items Center and justify Center and PX is going to be four like this and then inside of that div let's create another one with a class name of text Center space
y4 and padding top of 16 let's create a heading element here which says welcome back let's give this H1 element a class name of font bold text 3 Excel and let's go ahead and give this a uh muted foreground or perhaps how about a specific hex color 2 E2 a47 there we go that looks better and now below that I'm going to open up a paragraph so like this and I'm going to say login or create account to get back to your dashboard and let's give this a class name of text base and let's give
it a text again a specific hex color of 7 E8 C a0 there we go and now let's go ahead and open up a div here which is going to wrap our sign in element inside let's give it a class name Flex item Center justify Center and margin top of eight there we go so it's nicely moved and centered now and what I want to do now is I want to add a little loader here and let's expand just so you can see how it looks like so this is how it's going to look on
desktop right so our login is going to be on the left side and in here we're going to have the logo of our application but on mobile it's just going to collapse to the middle here and on tablets as well so what I want to do now is I want to make sure that when I refresh you can see how there's this uh blink right where this element doesn't exist so we can take care of that by leveraging a couple of things from clerk nextjs called clerk loaded and clerk loading and let's also import loader
two from Lucid react if you're wondering where we got this package from it's from shaten UI so the moment we run shat CN UI latest initialize and then it offered as two Styles default and New York if you chose default as I told you to do in the tutorial you're going to have lucid react inside of your package ad Json so if you go ahead here there we go you're going to have leid react if you chose a New York style you're going to have radic icons whatever you have you can M manually add this
one if you want so make sure you just add some kind of loader icon and then let's go ahead and do the following I'm going to wrap this instead of clerk loaded so this will only show when clerk has finally loaded and below that we're going to add a clerk loading and inside of here I'm going to render that loader to element and let's give it a class name of animate Spin and text muted foreground so now when I refresh there we go we have a nice loader right here perfect and now what I want
to do is I want to go just by the end of this div right here so let's go to our top div as you can see in here let me try and expand this so it's as clear as possible so our top div initializes a grid which means that this is the first column so in order to take care of the second column this is where we do that so I'm going to go ahead and give this a class name of height full BG blue 600 and then by mobile first it's going to be hidden
on mobile but on large it's going to become a flex component and then and we're going to Center all the items inside and also justify Center all the items inside as well so let's take a look if I expand now there we go you can see how this is now blue but if I go to mobile it's just a single view what we have to do now is add the logo of our application so we add assets inside of the public folder here you can use any logo you want I'm going to be using the
logo ipsum right here so I already have this but let me download the SVG again drag and drop it inside of the public folder right here I'm going to go ahead and rename this to logo.svg and since this is an SVG file we can easily find colors like this and change them to be white there we go so these two instances you don't have to do this right or you can use any logo you want let's back inside of our uh app out so whatever file you did this to it doesn't matter if you did
it in sign up or sign in because we're going to copy and paste either way so I started working here right I added BG blue and let me just restart my window because it looks like my Tailwind extension stopped working there we go uh yeah about that Tailwind extension so if you're wondering how I have this useful little snippet here and how come when I hover I get this this or how come when I type I get out the completion here that is all thanks to the Tailwind CSS extension Library there we go so just
make sure you have this installed if you're working with Tailwind it's extremely useful now let's go ahead and let's import image from next image it's actually default like this there we go so in here let's add an image and let's give it a source of/ logo SVG or whatever you put inside of your public folder so I put logo. SVG so that's what I have and let's give it a height of 100 and a width of 100 and an ALT of logo and let's try it out there we go what I want to do now
is I want to copy and paste this entire thing and I want to add it to the other sign up page so let's quickly do that I will select everything and paste and I'm simply going to change this to import sign up from Clerk nextjs and add sign up here make sure you also fix the path so this is inside of the sign up folder so sign up let's try it out now if I click on sign up here there we go so both of my routes are now working let's try this one more time
if I go to Local Host 3000 I am immediately redirected here now let's try logging in and there we go we are redirected back here which says this is an authenticated route if you want to log out here's a quick way you can do that go inside of your app inside of dashboard page. DSX and go ahead and import the user button from clerk next JS and in here just render the user button and let's add the after sign out URL to go back to the root page make sure you add this and refresh your
page I'm going to zoom in and now you're going to have an icon of the currently logged in user perfect so in here you can manage your account and you can see some stuff here or you can just sign out and there we go we are back to the root page perfect you just implemented signing in with Clerk and in here you should actually be able to see your first users and then you can manage the users you can ban them impersonate them and do a bunch of other things um perfect so in here you
also have this labor secured by clerk if for any reason you don't want to see that you can go inside of customization branding and you can turn it off there we go so now if I refresh there we go no more clerk instance es perfect you now have a fully working authentication what we're going to do next is we're going to learn how to write API routes inside of nextjs and we're going to incorporate incorporate hono as an alternative way to write API routes and I think you're going to like why we are going to
be doing that great great job so now that we've wrapped up authentication in our project it's time for us to create our first API route and to also integrate hono inside of our nextjs project so first of all how do you build API routes in nextjs without hono so we learned that page. DSX is a reserved keyword for files when representing client routes whatever folder page. TSX is in is going to become part of the URL well same is true for writing API routes so here is the structure that I prefer I always create an
API folder and then inside I create an API route for example test so this will be the equivalent of localhost 3000 API test but instead of writing page. DSX we write route. DS like this and inside of here using route Handler we Define which method is this for for example export constant get this will represent the get method so I can go ahead and write return next response which I can import from next SL server and my apologies. Json and I can write hello world just like this so now if I go ahead and write
SL API SL test there we go we get our Json object back but if I wanted to have some parameters here what I would have to do is I would have to create an additional folder for example test ID and then I would need a route. DS inside and then I would have to access this again so let me copy this and paste it here in order to access this test ID we would have have to D structure first the request and then we would get the param so let me just align this like that
so it's easier to look at first of all request would be a type of next request from next server and in here we would D structure the params and that would be a type of params test ID string so why test ID here well because that's what we gave this folder the name so then I can keep the hello world but I can extend this by also giving it a test ID to be params test ID so if I go to slash API slash test and then slash1 to3 I should get that in my Json
object so API SL test one two 3 and there we go hello world test one two 3 and if you wanted this to be a post request you would simply change it to post or patch or put so if I change this and refresh there we go I get 405 meaning that uh we don't have a get request on this route and if I bring it back it's here so this is fine we now know how to write API routes using your regular nextjs but what I want to do is I want to use hono
so hono is as it says here a web application framework it is fast lightweight and built on web standards and what's the coolest part about it is that it has support for any JavaScript runtime so in here they give examples for cloud flare fastly Dino bun AWS or nodejs so let's go ahead and see what this is all about so as you can see in here this has more of a uh structure similar to expr press or fast API something like that if you worked in your separate node.js environments and I honestly prefer this for
building API routes over folder and file based structure so folder based structure for me is fine for for front end routes so this is okay to me but API routes I'd rather have something like this so let's go ahead and see how we can add this so in this quick start they are giving you examples uh if you're building from scratch right but we are not building from scratch we already have our project so instead what we're going to do is we're simply going to install hono inside of our project so I'm going to shut
down the app and I'm going to write BN add hono as simple as this and now I'm going to try and find exactly where in the documentation they explain to us how we can add this to a next JS project so let's see in here in getting started we have options for verell and there we go so it's right here if you use app router addit app API and then create a catch all route and inside a route. DS so basically what we have to do is we have to create an endpoint that from now
on is going to go through hono instead of going through regular route handlers so we're going to reserve ve the API folder for that I don't know if I made that clear but when making route routes API routes you don't need to use the API folder so this is just my preference and obviously a preference of many since they recommend having this folder as well so we are going to reserve this folder for hono so I'm going to go ahead and remove the test folder we no longer need that and instead I'm going to go
ahead and create a catch all route like this so just spread the route inside and then I'm going to create a route. TS file so let's go ahead and do this exactly I'm going to copy this snippet here it's very short so even if you don't copy it you can pause the screen and write it yourself so there we go let's take a look at what was just written here so first of all we add the Imports uh for hono then we add an import to handle our router from hono versel and what's cool about
this is that in here you can add uh any adapter you want for for example AWS Lambda or for example bun if you're just running on bun or Cloud Fair Pages workers so a bunch of different things here but since we are working with nextjs let's keep it to versel then we can also Define the runtime to be Edge if you want to you can uh turn it off or you can also write nodejs so for now I'm going to keep it on the edge but later we're actually gonna have to turn off the edge
because of the play uh integration that we will do so plade will be used to connect to our bank accounts and play uses axios on the back end which unfortunately doesn't work well with the edge so later we're going to have to remove this but for now let's just keep it so we work with this example so in here we initialize a new hono app and we immediately chain the base path to be/ API so that works for us because we are in the API folder so that is correct and then we initialize a/o route
and we very simply return so what is a c they write C everywhere you might also see CTX somewhere like this that basically means context that's how you can read that so let's go ahead and try this out and so in here it's pretty clear what we do we Define a get route to go to hello and what we do is we return a Json object with a message Hello nextjs below that we enable the hono to work on get routes and post routes so I don't know if um you kind of noticed this already
but this get and post are actually the route handlers that we just built a moment ago but instead of doing export con get and then inside of here we return next response. Json on instead of that we tell them no from now on you're just going to handle the app and app is hono so that's how hono works we kind of overwrite existing nextjs route handlers so we don't have to do that file based routing so let's try it out uh if I did everything correctly now if I refresh this I should get a 404
but and I think I need to have my app running because I shut it down so make sure you do this as well so now it should be 404 here if I'm not mistaken there we go and then let's go ahead and let's attempt to go to slash hello my apologies API hello and there we go we have a message saying hello nextjs and here's a quick uh tip for you so let's try something else I don't know if I explain this enough but I'm logged in currently if I log out and if I attempt
to go to slash API hello so this will still work great I just wanted to confirm that in case you having any issues perfect so we just confirm that our uh integration of hono is working so now if you want to you can explore hono yourself or you can also take a look at the workshop on my website so in here we have a workshop where we actually build a very small Twitter clone with hono and we actually use next out drizzle and hono all in one combination so it's really really cool um great so
now let's actually go ahead uh and let's try and do something else so for example if I go to uh let's go ahead and add an app.get here let's go ahead and chain another route. g/o slash test for example like this in order to get the test what we would have to do is get const test and then we would write c. request. pram and you can see how we have an autocomplete for test so that's another thing that I absolutely love about hono is that it has endtoend Type safety so let's go ahead and
return c. Json let's go ahead and give it a message of hello world and let's pass in the test to be well our test from pams so if I go ahead and go to SL API hello123 there we go we just had the exact same thing but you can see that this is even safer because if I try to write test one two three here you can see that in my editor I already have an error here so I don't have to wait for this to be a bug for me and then I go and
debug no I already see that this pram does not exist here so that's why I prefer this way of building API routes but that is not all hono is extremely powerful and what we are going to do with it is we are going to build let me just find it here in the guides they have RPC so RPC is a feature that allows sharing of the API specifications between the server and the client and we're going to do that in combination with the Zod validator so as you can see here for every route that you
initialize with hono you can pass a bunch of middlewares inside so where do the middlewares go so everything between the route SL hello and the initialization of context everything inside is a middleware so there can be as many middle Wares as you want here right so all of these things for example would be a middleware so these are just mocks right but all of these things would be middlewares so all of these are valid middle Wares right here and they can be validators they can be authentication protectors they can be permission protectors so anything right
so what we're going to do here let's see if we can already try this out so I want to use the Zod validator here and I want to validate my pm here for example so I'm going to go ahead and see whether I need to inst install the Zod validator we do so let's go ahead and add Zod validator here so go ahead and do this along with me in your terminal I will shut down the app and do bun add hono Zod validator right here and let's do Bun Run Dev and we also need
Zod so Bun Run Zod bun add Zod all right so now I'm going to go ahead I'm going to import Z from Zod and then I'm going to add the Zod validator here like that and then in here we can add Zod validation on the params so let me show you how you can do this so in here you would add Zod validator let's see if we can do this did they add the Zod validators or Z validator and inside of Z validator you can Define what you are validating so if this is a post
request you can validate the form or the Json in our case we want to validate the param so inside of here I'm going to write z doob and I'm going to write the test needs to be a type of Z c. string like this and then instead of using c. request. pram I would use C uh. request. valid let's see like this and I would D structure test in here and this would be so valid params and there we go test is a type of string and if I change this to number for example there
we go test is a type of number now and using the same method you can also for example validate a form so now we don't need this right let's say I chain a post method here let me just get the context and let's return a c. Json here so in here we can now add Z validator Json and z. object inside and for example we would need I a name which is a type of Z string and we would need for example I don't know a user ID Z string like this so usually you would
access the body you know without any validation here but now that we have the Zod validator what we can do here is we can extract the body from c. request. valid and we would specify valid Json and then in here we would get the name and user ID and we know exactly what type of uh let's say user ID is a number for example right so we would know exactly those types right here and here's the cool thing so right now we are typing the Z object here right we are defining the schema here but
later when we add our database we're going to go ahead and we're going to share the schema from the drizzle database and we're going to reuse it for validation of our API routes and and for our form values on the front end so it's going to be a seamless experience using rpcs using hono drizzle and Zod it's going to be very very smooth and end to endend type safety so if you want to you can also chain validators so let's say you had this to be I don't know create slash uh I don't know something
post ID if you want you can chain validator so you can add another one and this one would not be ajacent but this one would do the params right post ID and then below that you would have valid params and it will only extract the post ID not pams but Pam if in case you were wondering right so as I explained in the beginning everything between the route and the returning of the context is a middleware here perfect so I hope that kind of summed up hono what I want to do now is I want
to create create a protected API route so so far uh we know that we can use the middleware to protect the routes so if we wanted to technically we could add API Asterix dot if this is the syntax I'm not sure I think it is so now I think I'm logged out right so if I go to Local Host 3000 uh I will be prompted to log in all right so I did something wrong here so I'm going to go and SL API slash uh let me just find uh the correct syntax I think it's
the opposite order so I add a DOT and then an asteris let's see there we go so now if you attempt to go to slash API SL hello you get redirected back to the signin route so if that is okay for you you can do that you can protect all of your API routes right but I don't want to do that I don't want my API routes to be redirected here right so I want to be able to access API hello but at the same time I also want them to throw a Json error if
user is not authenticated so we can do that as well inside of the documentation of hono here uh inside of middleware here you can find all kinds of authentication middleware that exists and even this is not the whole list because then you have this third party middleware here and inside of here you see that they support Sentry Firebase quick drpc uh whatever all of this stuff is and we also have clerk out and if you're interested also Al the JS or previously called Next out so I'm going to go ahead and use clerk out here
so I need to install this I need to install hono clerk out and I also need the clerk backend so let's go ahead and do those one by one here so bun add hono clerk out and I also need the clerk back end like that let's confirm that we have all the necessary environment Keys here so clerk secret key and clerk publishable key let's see if we have that so inside of my DOT environment we have the clerk publishable key and we also have the clerk secret key but I think that we are going to
have to add the clerk publishable key one more time separately so I don't know how much you know about next public uh sorry about environment uh variables in next but if you want to expose them to the client you add the next prefix to it but this one requires a clerk public publishable key but in a form of server only so here's what I want you to do I want you to copy and paste your existing next public clerk publishable key and remove the next public in front of it so it needs to match exactly
what this documentation says clerk publishable key just like that and later we're going to try to remove it just to see if it it would have worked without it and let's see how we use it so first of all we have to Define uh well in here they set all the routes to use clerk middleware I don't want to work like that I want to be specific around which routes are protected so let's go ahead and import this two so Clerk middleware and get out from hono clerk out I'm going to go inside of my
routes here inside of app API route let me just zoom in here and I'm going to go ahead and add those two so from hono clerk out which we've just installed alongside clerk backend we now have the clerk middleware and get out so I'm going to go ahead and do the following how about I clear up this entire thing where we learned some parameters and stuff about hono and I'm going to go ahead and I'm going to add a middleware here so remember everything in here in this space between this last method is a middleware
so let's go ahead and collapse this and let's write clerk middleware right here there we go so now what we can do inside is we can go ahead and we can extract the user like this get out and pass in the context and then in here we can check if there is no out user ID we can go ahead and return c. Json error unauthorized I think I misspelled it but it doesn't matter for this case I just wanted to demonstrate this so make sure you are not logged in so just make sure that on
Local Host 3000 and also make sure you have the app running just a second b r Dev there we go let's just wait for this to refresh all right so it's redirecting me here if I go to/ API hello there we go I get an error back that I am unauthorized perfect so now this is working I'm going to go ahead and try and log in now and I'm also going to extend this method so alongside message let's also write out. user ID here if we have one like this so I'm going to pause the
screen and I'm going to go ahead and sign in all right so I'm signed in let's go ahead and go to SL API hello and there we go we have a matching user ID from our clerk user perfect so now our clerk middleware is working and we are separately protecting our API routes as opposed to doing it in the middleware I think this is a better way of doing it because the middleware will redirect you uh to the root page uh perfect so now we have that and I want to resolve one more more thing
before we wrap up this chapter that I think you probably uh you know are thinking of and that is all right but if we have a bunch of these routes isn't this file going to become absolutely huge yes but luckily for us there is a solution for that no we're not going to write it all in one file here so let's go ahead and see what they say about best practices here so I think that you probably have something in mind of building controllers as they wrote here right so for example in here you wouldn't
write a route but instead you would have you know uh hello controller right something like that well as you can see in here they say don't make controllers when possible so they call that rubby on rail rubby on Rails like controllers so the issue is related to types for example let's see the path parameter cannot be inferred in the controller without writing complex generics so that is the problem right remember when I showed you how we can have type safety 10 infer perams you can't do that if you build controllers right they do have a
solution for that if you really really want to create a controller you can use something called create Factory here uh but we're not going to do that here instead what you can do is you can build separate files like this one so let's go ahead and just copy this example so I'm going going to go ahead and copy this right here and I'm going to go inside of my route and I'm going to create authors. DS and I will just paste this here right so let me just return that there we go and then I'm
going to copy this again the one below it and I'm going to call this books. DS so separate files right there we go so we have a get a post and a get of a specific ID and now back in my root file right here what we can do is we can import those two so let's do that there we go like this and we don't need clerk middle anymore so just import your authors and your books and instead of using app.get you would do app. rout authors and you would just use authors and then
below that you would do slash books slash books there we go so now this is much more manageable each entity has its own uh file and we still have access to the context and all the type safety inside so if you want to go to SL API authors uh we should get a return of text list authors if I go to the book my apologies books list books if I go to SL book SL uh test there we go it says get test or authors there we go API authors test get test perfect so this
is working as well but this actually will require some changes later on when we build RPC right so for RPC in here you can already check this out uh they have best practices not best practices but is it in the guides RPC here so in here we're going to have to have one uh app type which will hold all of our routes so regardless if they are in different files or not we're going to have to somehow find a way to hold all of those types together and in here they actually explain to you how
you can do that so let me go ahead and scroll down right here they should have something called there we go using RPC with larger applications so as you can see the difference is that we're not going to have this separate uh let's go inside of authors in inside of here they keep calling the app again so app.get app. poost and app.get again when we switch to RPC we would have to chain those together so we wouldn't have app.at app. poost instead it would be app.get dopost doget right so we would chain the elements and
same is true for this example of authors and books you would chain them all under one app and then your app type will be able to hold all of the type and context tests and params and all the Zod validations together but that's for later I just want to give you a quick introduction to Hano so go ahead and play with hono a bit yourself what we're going to do in the next part of the tutorial is we're actually going to go ahead uh and start going back into our UI so that we can build
our navbar great great job so now that we've learned how hono works you can go ahead and leave the API route behind right so we're going to go back on the front end now so if you made these authors and books you can leave them if you haven't made this you didn't have to right so what I'm actually going to do is I'm just going to return this back to hello and I'm going to go ahead and simply get the context and return c. Json hello world so just the most basic API route here and
I'm going to remove the authors and the books and I will also remove the respective Imports for that there we go so just the simplest implementation possible and you can leave that behind for now we're going to come back to this later for now let's go inside of the app let's go inside of the dashboard so one more thing that the route groups are good for is that they can share layouts so what we can do now is for every future route that is going to exist inside of the dashboard rout crout group it's going
to share a unique layout so let's go ahead and create not a unique layout I wanted to say it's going to share a common layout so layout. TSX as you can see we immediately get an error here so the rules of layouts are similar to the rules of page it needs to have a default export so I'm going to call this dashboard layout the naming does not matter because this is a default export so just make sure you export something and let's for now return div and say layout so what's going on now why am
I only getting the text layout rendered here let me zoom in so you can see so it only says layout but what I expect is that my user button is rendered right or we can call this dashboard page so how do I get that rendered here well another rule of layout is that it needs to render the children so let's quickly create the props children to be a type of react. react node and then in here we are able to destructure those props children and instead of this we just render the children and there we
go so now through this layout we render the dashboard page and every additional route inside is going to share this layout so whatever we create on top of here is going to be reused so how about we go ahead and change this from being wrapped in a big fragment instead of using div here let's use Main and let's give it a class name of PX3 and on large devices is going to be px14 and then what I want to do here is I want to create a header component so let's go ahead inside of our
components and let's create head header. DSX so inside of here I'm going to export con header and I'm going to return a div saying header component now I can go back inside of app layout and I can import header from components header and there we go so now all of my routes inside of dashboard are going to have the header component above them perfect so we are now ready to start styling this instead of a div we're going to use the header element and let's give it a slight gradient so background gradient to bottom so
you can always hover over a class name to see if it actually exists in Tailwind but that will only work if you have the Tailwind CSS intellisense extension that I told you to install I believe uh so let's define from what color so from Blue 700 to Blue 500 there we go so a slight gradient as you can see perfect let's give it a px of four and a py of eight and I also want to Define different values for large devices so 14 and padding bottom of 36 there we go and now let's go
ahead and let's just style this further so I'm actually going to zoom out because I want to be looking at this as if it were a desktop view so I recommend you do the same so either expand your screen or zoom out until uh well just keep it you know a a bit larger because when we go ahead and create our navigation we're going to have separate navigation for mobile and separate for desktop right so let's go ahead and create a div here with a class name Max with screen to excel and MX of Auto
so what does this do so if I create a text header here you can see that at one point so let me just see at what on What at one point my header stops expanding right so that's what this does Max width screen so we're only going to handle the responsivity up to a certain uh withth of the monitor right so I don't want to support responsivity if the monitor is is wider than this we're simply going to push everything in the middle if it breaks from this right you can see how header is expanding
expanding expanding and now it stops expanding so that's what this does great now inside of here let's open up a new div with a class name of full width Flex item Center justify between and a margin bottom of 14 and then in here another div which is going to hold two elements together so for that I'm going to need Flex again and I'm just going to give them a bigger gap between if we are on desktop and now let's create a header logo component so inside of components let's create a header logo. DSX and inside
of here I'm going to import link from next link and I'm going to import image from next image and I'm going to export con header logo this is going to be a link with an HRA to the root page and a div which for now can just say logo let's go back inside of header and we can now import the header logo from slhe header logo or components logo however you prefer it and now you should just have a link which says say logo at the top let's go ahead and give it an image which
we already used inside of our sign in and sign up components logo.svg so inside of here let's give this div a class name items Center is going to be hidden by default right so that's why I told you to try and expand your browser because I want this to be visible on large devices so hidden on mobile but when we hit the large breakpoint we give it Flex like that and then inside of here let's render our image component with a source of/ logo.svg and out of logo and let's give it a height of 28
and width of 28 below the image let's give it a paragraph Finance so that can be the name of your app right you can write here whatever you want so I'm going to give this a font semi bold text White text to Excel and mL of 2.5 so if I expand this there we go we now have a nice logo and whenever we click on this it's going to redirect us to the root page so right now it has no effect because we are on the root page perfect so we have our finance and it
disappears on mobile so we want that because as I told you we're going to have a bit of a separate design on mobile perfect so that's it for our header logo so we can can uh close everything here and let's get back inside of our header so I said that two elements are going to be inside the second element is the navigation element so let's go ahead and create that inside of components let's create navigation dosx right here and I'm immediately going to mark this as Ed client because I'm going to be using some hooks
here not immediately but definitely in a moment and let's go ahead and just fix the import error here so navigation and let's return a div navigation and I don't need a semicolon here and I can now import navigation from do/ navigation or components navigation great now inside of the navigation here let's go ahead and let's define all of the routes that we are going to have so I'm going to write export con actually no need to export it so we can just use routes it's going to be an array of objects first object is going
to have an a of root page and a label of overview or dashboard however you like it let's copy and paste this and for the second one we're going to have slash transactions and that's going to lead us to transactions let's copy and paste this remember to add the columns sorry the commas then we're going to have accounts whoops then we're going to have categories and lastly we're going to have the settings page there we go perfect so now we have that and now I want to go ahead and I want to create uh a
component called a nav button so actually before we do that let's try and just iterate over this routes array so that we can work with that so it's not going to be a div whoops it's not going to be a div it's going to be a nav element with a class name of hidden on mobile but Flex or visible on large and items are going to be centered gap between each of the element is going to be two and overflow X outo in case we need to scroll and now we're going to go over the
routes so routes. map we get the individual route and for now we can just write route. label you can ignore the error for now so expand your screen and you should be able to see on desktop mode so expand uh that much that you can see the logo and then you should finally be able to see overview transactions accounts categories and settings so now what we're going to do is instead of rendering them as a paragraph we're going to reuse this href and label as props for a component called nav button so remove the paragraph
and create a nav button here so it doesn't yet exist first of all give it a key of route. hre because that's unique for each of our routes and then uh give it an actual hre to be route. H and label is going to be route. label and now let's go ahead and let's add our hook so that's why I marked this as used client called use path name so inside of here I'm going to get the path name from use path name like that and I'm going to go ahead and call this is active
if path name is identical to route. HRA so if you're confused about the use client connection with the hooks I'm going to go back in a moment and explain this but let's just resolve this uh error here so inside of components folder I'm going to create a nav Das button. DSX file right right here and inste of here well let's just quickly export con button so we don't have to have that error so D nav button and right now we're going to have prop errors so how about we also wrap up defining the props so
type props in here let's give it an hre of string let's give it a label of string and is active of Boolean and and let's go ahead and assign those props here so Props there we go so that should work fine let's go back inside of the navigation and let's import this to slnv button or components nav button I like to separate this from the Global Imports there we go so uh let's go ahead and try something if I remove use client there we go I get an error here what does it say it says
you are importing a component that needs use path name it only Works in a client component but none of its parents are marked with use client so they are server components by default right so if you want to learn more about that you can follow the documentation on this error here where you will learn the fundamentals right so server components allow us to write UI and it can be rendered and optionally cached on the server in nextjs the rendering work is further split by Route segments to enable streaming and blah blah blah so you can
read all about that so what's important for you to understand if you're coming from a background which is not nextjs meaning that if you're used to you know react andite react and VB pack so that is a form of building single page applications what we're doing here is we are building uh applications with server components right so server components can be used as we just read to write UI and well one cool thing about them is that you can directly fetch from the database inside of them so you can imagine them as your get API
route if you want to one thing that they cannot do is work in with interactivity and that also includes hooks so whenever you need a hook in a component you will Mark that as use client all right when I save this the error goes away but let me just comment it out for a second just to discuss this so why did then they then say uh none of its parent are marked with use client what does that mean so if you want to use client shouldn't exactly be explained as all right make this component interactive
use client is more of a boundary right you you open the client server boundary at this point so if you want to you don't have to do it at the navigation level you can do it for example in the parent in the header level right so header is the one that renders the navigation so if I were to Mark the entire header as use client that means that the boundary of client server has been opened and that allows me to use the hook inside of navigation component without marking it specifically as used client so I
just wanted to quickly explain that so now you might be thinking all right so if I ever Mark something as used client that means that nothing inside can ever be a server component well it seems like that but there is a trick to do that as well you can insert a client component uh inside a server component inside of a client component and we actually are going to do that in this tutorial and you do that by using children like this so if you pass server components through children then you can safely Mark something as
use client all right we're going to come to that example at some point and I'm going to explain it again now let's stop the confusion and let's do the following in the header we don't need use client right so if possible try and make the most of your routes serers side rendered my apologies not serers side rendered by but server components so yes server components and serers side rendering is not the same thing if you mark something as used client they that is still serers side rendered it's just not a server component right so that's
a big misconception that people have marking something as used client still means that it's going to be rendered on the server it's just not as server component nevertheless we need use client inside of our navigation component because we want to access the use name hook and we're going to use it to check whether the route is active or not now let's go inside of the nav button here and let's go ahead and give it some cool cool Styles so first of all let's destructure the a the label and the is active prop and let's go
ahead and let's let me see if I have inside of my UI we already have the button component perfect so let's render that button component so from do/ UI button or components UI button and inside of here we're going to add a link component from next link we're going to give it an H of hre and inside of Link we are going to render the label the link was imported from next SL link like this great now let's go ahead and give this first of all as child prop and then let's give it a size
of small let's give it a variant of outline and now let's give it a class name but not just any class name we are going to revisit in our lib utils this CN method which I told you was going to be used whenever we need to dynamically change Tailwind classes so technically we could be doing this we could open uh we could do this like is active then something otherwise something we could do that right but you're going to notice that very soon when working with you know very uh with different variants and dynamic classes
there's a lot of things that can go wrong especially that one thing people don't consider is that Tailwind has a just in time compiler which means that you need to write uh classes in full right so a lot of people try to do some weird tricks with this taries and stuff what works best is truly having this combination clsx and Tailwind merge so we're going to use that so here's how that function Works import CN from lib utils as I just did and open up this function in the first argument it's going to take all
the default classes and then every additional argument can be a Turner for example is active can be something otherwise this right so that's how we're going to do that so let's focus in the first argument here and let's define how we want our button to look I want to give it a full width on large I want to give it an auto width I want to give it a Justified between I want to give it a font normal on Hover I want to give it a background white slash2 meaning it's going to have an opacity
on Hover I also want to give it a text white I want to remove any kind of border on the button on Focus visible I want to remove the ring by using ring offset and setting it to zero again on Focus visible I want to remove the ring by setting its color to transparent I want to also remove the outline of the button I want to make the text white by default on Focus I want to change the background to be white sl30 and I want to make all of that a bit animated using the
transition class and now if the element is active I'm going to give it a background white with opacity of 10 otherwise background is going to be transparent and besides this let's also give it a text white there we go let's try this out now perfect so now you can see how since this is an active uh route because we are on the root page it has a different style as opposed to all the other ones and you can see how hover has its own style if you try and click on any of those you're going
to get a 404 because we didn't build any of those now what's missing now is the mobile view right so you can clearly see that here so for that we are going to need to have a separate component uh called a sheet so let's go ahead inside of our terminal here and let's do bun uh bux Shad CN UI l test add sheet like this and let's go ahead and import everything we need from that uh and let me just not forget to run my app so Bun Run Dev there we go so inside of
here let's import everything we need from components UI there we go we are going to need the sheet itself s the content and the trigger like that and then uh I actually want to install one more package which we're going to well use a couple of times throughout the project so bun add is going to be called react use quick reminder I'm using bun if you're using node you should write mpm install or yarn ad or pnpm whatever it is right all right so make sure you add react use package and then in here let's
from react use import use media like this uh and I'm doing this in a wrong component my apologies so I'm doing this in nav button where I meant to do this was in the navigation component so go back inside the navigation component and add the sheet components here so I'm going to remove them from the nav button and same is true for this react use import so not in the nav button in the navigation whoops there we go perfect so now that we have that uh we're going to go ahead and do the following uh
let's go ahead and define whether our drawer is going to be opened or not so is open and set is open use state from react by default false so just make sure you add the use State import here and then what I want to do is I want to go ahead and add the router from use router so that's going to come from next navigation make sure you don't accidentally import it the use router from next SL router so next navigation all right and let's define is mobile to be used media if open up parenthesis
is Max width is 1,24 pixels and important for hydration by default let's assume that uh the view is not mobile and then let's add on click here to accept an HRA which is a string router. push a and set is open is going to be pulse you're going to see why we need this in a moment and that's because if we are on mobile we're going to have a completely separate return happening here so we're going to use our sheet component here and we're going to control it using the is open Boolean and on open
change is going to call the set is open so just like this now inside of here we need a trigger component and for that we're going to use a button so make sure you import the button I'm going to change the to go to components like like this so components UI button there we go and let's go ahead and do the following let's give it a variant of outline let's give it a size of small let's give it a class name of font normal background white sl10 on Hover background white sl20 hover is going to
have text white border is not going to exist and we can just copy from our nav button the focus visible ones so those two Focus visible ring offset zero and ring transparent those two uh outline is going to be none text is going to be white Focus BG white sl30 and transition like that and I think we also need border none there we go we already have it great so font normal BG white 10 on Hover BG white 20 on Hover we're going to have text white border none the reused Focus visible with ring offset
zero and ring transparent outline none text white and on Focus background white sl30 and transition great and inside we're going to use a menu icon from Lucid react so make sure you've added this there we go and let's go ahead head and give this a class name of 84 and width four one cool thing in the new Tailwind update is that this class name can now be replaced with size four so that's the equivalent right if you want to great so now we have a trigger but now we need a Content here like this so
inside of here let's go ahead and give it a side of left and let's give it a class name of px2 and and let's open up our small navigation here with a class name of flex Flex column Gap y of two and padding top of six so it gives a little space to the top because of the close button and now inside of here let's go over our routes do map let's get the individual route and we are simply going to render a button component here like that so I'm going to give this a variant
if route. hre is equal to path name in that case it's going to be secondary otherwise it's going to be ghost and let's go ahead and give it this a key route. hre and let's give it an on click to Be an Arrow function which calls the onclick method and route. hre like that so why are we using onclick here instead of adding a link because link by itself will not close the drawer right so I want to handle the mobile view by going through our onclick method which will use the router hook to redirect
and manually close the drawer there we go and inside of here we just render route. label and I believe that should be it so if I refresh my page make sure you have your Local Host running here so let's see if we did something wrong so this seems to work but nothing is appearing here let's see uh why is that happening right here so I have Max with 1224 pixels I'm going to go ahead and conso log is mobile here and try and debug oh looks like I just had to do a hard refresh yes
looks like it was just cash so nothing I did fixed it okay so if you want to you can uh if you have the same reason let's go ahead and just do Bun Run Dev together looks like it was some weird cash inside so now again desktop works fine but on mobile now I have a drawer like this perfect uh one thing that I might want to change is I want to move the text to be uh here in the beginning so how about I go inside of my is mobile and how about I add
a class name here full with and justify start there we go so that now looks better in my opinion so when I click overview there we go you can see how it closes the drawer and it's going to do that even in this 404 routes perfect so that handles our navigation component uh right here uh what we have to build now is the welcome message right here and below that we're going to have some filters so before we wrap up the chapter uh I want to do that I want to add uh well a little
log out button here and a welcome message here and we're going to leave the filters for later so let's go ahead and go inside of our components navigation right here uh not navigation header so we have header logo and we have navigation and now what I want to do is outside of this div I want to add a user button component and I want to give it an after sign out URL to go slash to the root page so from clerk nextjs import the user button uh here's one thing that I don't like when I
refresh it doesn't exist for a second so we can s solve that by using the clerk loading and clerk loaded components so clerk loading and clerk loaded if it is loaded we're going to display this but if it is not if it's still loading in that case let's add loader two from Lucid react and let's give it a class name size8 animate Spin and text slate 400 so let's try it out now when I refresh there we go we have a nice little spinner and from here we can sign out perfect now let's go ahead
and let's create a welcome message so uh clerk Lo that this is fine so outside of this div right here let's add a welcome MSG like this so I'm going to go ahead and create a new component welcome MSG vsx and let's use client here because we are going to need to use a hook called use user from clerk nextjs let's export con welcome message and let's destructure the user and is loaded from use user right here let's return a div element and inside of here we're going to have an H2 element which will say
welcome back below that a paragraph This is your financial overview report or whatever message you prefer so go back inside of the header and import the/ welcome message I'm going to use the components and there we go we now have the welcome message here so I'm going to go ahead and give this div a class name of space Y2 and margin bottom of four I'm going to give the H2 element a class name of text 2 Excel on large it's going to be text 4 Excel text is going to be white font is going to
be medium flex uh actually no need for that right and for the paragraph we're going to give it a class name of text small l G text base and text is going to go ahead and be a specific color 8 9 B 6fd like this there we go so this looks nice what I want to do now is I want the welcome back message to uh write out the user's name so how about we do this let's go ahead and at the end of this welcome back open an object and write if is loaded I
well I told you to open an object what I meant to say was open curly brackets right so if it's loaded add a comma and a space otherwise just space so it's important that you add space here and then chain along user question mark first name and then if you want to you can add additional space and you can add a little Emoji like I'm going to do like this like a wave and there we go so this will read the name I have this weird name from my gmail because I didn't want to enter
my name so this will load the name from your Google account uh if you used email login perhaps your first name won't exist so if you want to you know you can just say welcome back with a smile like this you can just do this as well however you want great so that wraps up our uh header component our navigation perfect so what's going to be below this are two filters the account filter and the date filter right but I kind of want to work on them later right what I want to build next is
uh I want to start actually working on uh setting up our database and setting up our schema so that we can create our first transaction right that's what I I want to do next uh great great job so now let's go ahead and create our database connection with Drizzle orm and neon postgress so visit neon.pdf project after my database has been created I need to find my connection string and you can find that down here so inside of here you need this connection string so we are going to be using neon driver but it might
be easier for you to copy it from here so just select the simplest postgress option and just copy the connection string or you can use the copy button right here go inside of your environment variables and separate this from the clerk variables so it's easier for you to see and name this database uncore URL and simply paste the postgress connection string so it needs to start with postgress SQL and it needs to use this kind of format great save this file and make sure that you have no typos in your database URL so now we're
going to follow drizzle orm instructions to connect to Neon so I'm visiting the documentation here for drizzle and I pressed on postgress SQL right here and the first option is actually neon but you can also take a look at all the other options like they have like super base node postgress and all the other options so I'm going to be using bun so I will select bun if you're using npm feel free to leave it at MPN so let's go ahead and first add drizzle orm and neon database SLS serverless so I'm going to go
here inside of my terminal and I believe so this is me in post production here just giving you a quick tip so during the recording of this course a new version of drizzle kit came out it is a great update but unfortunately it doesn't work with the scripts that I have prepared for this tutorial so because of that I want to teach you exactly which version of drizzle kit drizzle orm and neon database to install and how to do that so looking at my package Json right here let's go ahead and search for drizzle so
this is the most important thing for this project your drizzle kit needs to be this version if your drizzle kit is a version 21 that is the newer version and the newer version is great and if you want to you can learn it on your own it has some great adjustments the problem is the migration scripts the running of the database studio and all the other things are not going to work if you are using 21 so when installing drizzle kit go ahead and install it like the following if you are using npm for you
it would be mpm install Das capital D drizzle dkit and then you would add an add sign and you would specify the version so 0.207 like this this is how you would install a specific version right here if you were using bun you would do bun add- D drizzle dkit and then add a specific version 02017 like this and lastly I also want to show you my drizzle orm package and my neon database serverless package so I don't think any big changes are in those two packages but still in case you want to do the
exact same version there we go so for my drizzle orm I'm using 03010 and for drizzle Zod I'm using 0.5.1 and for my neon database serverless I'm using 0.9.1 so I recommend that you when you install this in this chapter you do the exact version installation so mpm install drizzle dorm and then go ahead and and type out the exact version that I have so 03010 or bun add drizzle omm 0.3.1 right or yarn whatever you're using just use these versions but again as I said the most important part is this the drizzle kit needs
to be this version otherwise none of the scripts are going to work and the config f which we are later going to create so you don't have this yet but this config file also changes it introduces something called the dialect which as you can see in here does not exist but if I look at the drizzle documentation here in the drizzle kit and look at the configuration they have a dialect right so a lot of things has changed we can do that in the next course or in additional chapters but I already recorded a good
portion of the tutorial so please use this version off drizzle kit in your Dev dependencies I also have my app running so I will shut that down for now and instead I'm just going to add bun add drizzle orm and neon database serverless or npm install two of those packages and then we also need to add inside of our development dependencies drizzle kit so for me that's bun add- capital D drizzle kit for you it might be mpm install - capital D drizzle kit there we go so now let's go ahead and let's follow the
further instructions so now what we have to do is we have to create our well let's call that root or entry point for our drizzle connection so I'm going to go ahead and create a new folder and I'm going to call this uh let's see let's call it database and inside of database I'm going to create drizzle. DS endpoint and now inside of here we can copy and paste this snippet right here so I'm going to close this and paste it here there we go I'm going to remove this because this is just the example
but this is what we need so first of all I don't like single quotes so I will replace them to use double quotes and the other thing that we have to do is we have to change this environment variable so for us we don't have the drizzle prefix so what you can do is go inside of your environment local and directly copy the name of this variable that's the safest thing to do and you can leave the exclamation point because otherwise you get an error there we go now let's go ahead and make sure that
both these constants are exported like this there we go what I want to do next is I want to start creating our schema so let's go ahead and see how we can create schemas here so let's go ahead and select the schema or maybe the overview would be better there we go so you have a lot of options about how you can create your schemas so you can just use everything in one file you can have separate files inside of a schema folder or you can even have separate folders if that's what you prefer I'm
going to keep it simple and just use the one file because it's going to be easier for me to export everything from there uh using the asteris option for my imports and then it's going to smoothly go and align with my migration script so I recommend that you do the same so let's go ahead and do that so this is how you basically do that uh we don't have to copy it from here instead what I want us to do is I want us to go inside of the database folder and create a new file
schema. DS right here and let's go ahead and create the very first account which we can call for example uh I told you to create the very first account I meant to say the very first schema which is going to be the account schema right so this is how you do it you write export cons accounts PG table and you can import PG table from drizzle dorm slpg core PG represents postgress so obviously if you were using MySQL you would have different Imports and you can always check that out in the documentation here so as
you can see here we're using the PG core Imports but in my SQL you can see that it has my SQL core great so make sure you work with postgress if that's database you chose so let's open up PG table and now what we do here is we also give this table a name so I'm going to call this accounts and then we open an object as the second argument and inside of here we now write our elements so I'm going to create an ID here to be a type of text and you can also
import test text from drizzle orm PG core so make sure you have text imported from here and now we give this field a Name ID and let's also chain primary key that's it now we have an account with ID perfect so let's go ahead and add the following Fields I'm also going to add name to be text name and let's make it required by adding not now there's no need to add not now to the ID because primary key takes care of that and let's also add user ID which is going to be text user
id. n so what's important to understand with uh drizzle here is that this is just JavaScript right but what's inside of strings it's what's actually being written to your database here right so just because we have Cel case here doesn't mean that that's how it's going to be stored as a table instead it's going to look at this value right here same for this for this and for this perfect so now that we have this I want to go ahead and I want to create a migration script and I want to add a couple of
scripts inside of our package Json so that we can safely uh well push well generate to and migrate to our uh neon database so I want to start by adding a package inside of our project called environment and I'm I'm going to add it as a Dev dependency here so npm install - Capital d. environment if you're using npm and now I found a guide on creating migration scripts right here from neon so it uses schema migration with neon pogress and drizzle omm and we're going to go ahead and skip to the bottom here and
let's go ahead and first create our migration script right here so this is what I want to do first so I'm going to zoom in right and let's go ahead and go inside of our project structure and how about we create a new folder called scripts and inside let's create migrate dods like that and now inside of here I want to import config from environment which we've just installed and let's go ahead and run config path. environment. looc like this so just ensure that this environment. loal is the actual environment. loal that has your database
URL right here so the reason I'm doing this is because this is going to be run by outside uh of nextjs so nextjs is not going to run this project we are going to run that using either node or bun right something like that so now let's go ahead and let's define SQL again using neon from neon database uh serverless here and let's go and call process. environment. databaseurl and then let's define the database which will be drizzle from drizzle orm neon HTTP and let's paste in the SQL so I just like to align this
like that and then lastly let's also add the migrate script from drizzle rm/ neon HTTP SL migrator like that and then let's write const main to be an asynchronous method with an try and catch block let's resolve the catch first so I want a console error and I want to write error during migration and let's simply print out the error and let's exit this process and then in the tri block we're going to do a wait migrate pass in the database as the first options and let's go ahead and write migrations folder to be drizzle
like that so this will be created every time we uh create a migration so let's go ahead and run this script right here so that's important perfect so now that we have this script what we can do is we can go inside of our package.json here and we can add some new scripts here so I'm going to start by actually creating DB generate here and then below that we're going to have DB migrate and below that we're going to have DB studio so we're going to add three scripts here so first of all let's create
the generate one and you can actually take a pick at this script right here so let me go ahead and go up there there we go so we're going to copy this script and we can actually write it together right so let's see what we are going to need we're going to need to call drizzle kit which we've installed as a Dev dependency and we're going to use generate column post because we're using postgress here and then we're going to locate our schema in DB schema. TS so that's what I told you that I recommend
that you use the single file method for schema here right so this whole file will hold our schemas let's go back inside of package Json here so we located our schema file here and now let's write-- out/ drizzle like that so that's our out folder so let me zoom out so you can see this in one line like this there we go now let's go ahead and let's create our migrate script so for migrate script what we need is to run this folder right here scripts migrate dods and here's the thing you cannot run this
with node right because node doesn't support Imports by default and it also I think doesn't support maybe some other things here uh well yeah I I think it doesn't support typescript as well so here's what you can do you can either simply write bun. /script migrate dods so that's one option or you can write DSX if you're not using bun right I think that actually give you that tip here so let's see after we created this migration script here uh there we go they use TSX right so TSX will allow you to run this script
safely if you don't want to use bun for any reason right here on npm typescript execute the easiest way to run typescript in node.js right so you can just go ahead and install that using npm install DSX or you can install it globally uh great so instead of THS X I'm going to be using bun because I'm using bun for this project and for DB Studio we're simply going to call drizzle dkit BB uh my apologies drizzle kit Studio like that perfect so now that we have this uh three scripts right we have generate migrate
and Studio we can run them in that order so first things first we have to generate from our schema some new migrations and then we have to actually migrate them through the database so let's try this out I'm going to do bun run so if you're using npm it's mpm run for you so Bun Run database generate and there we go so it created One account or one table called account with three columns right here and you can already see that migration script here so there we go we have the drizzle folder and in here
we have the migration script perfect now what we have to do is we have to push that table so it's visible right here in our neon console so right now if I go inside of tables here nothing is visible so let's go ahead and run our second script let's see what it's called so it's called DB migrate so let's go ahead and run that now BN run database migrate uh and let's see what I did wrong so script is the wrong uh folder it's called scripts multiple let's run that again make sure you don't have
the same mistake as I do and there we go no error has been thrown which would mean that this was successfully pushed so let's refresh the tables here and there we go we have accounts with ID name and user ID and now I want to try out the last script which is to run our database Studio locally so let's try and run that bun run database studio and we have an error here so first of all we are missing the drizzle. config.js and then we also need to do one more thing so first of of
all let's go ahead and let's create the drizzle config file so I'm going to write drizzle. config.js here my apologies. ts that's what we are going to use so drizzle. config.sys from drizzle kit and in here let's export default Define config let's target our schema to be/ database schema. DS file driver is going to be postgress database credentials are going to use connection string to be process. environment database URL veros can be true and strict can be true as well there we go so now we have our config file so let's try running the bun
script again and let's see what happens there we go so we have a different error now which is the missing package PG so you can easily add that PG Das capital D so add it to dependencies there we go and let's do Bun Run database Studio again and there we go we can now finally see the studio so I'm going to close this I'm going to paste that here and there we go we should have our studio load any minute now and we have the accounts with ID name and user ID and we can add
records from here if we want to perfect so let's go ahead and confirm everything here so I just want to show you my package Json because we added a couple of stuff so I have added independencies neon database serverless I've added the drizzle orm in my Dev dependenc I've added environment and drizzle kit and postgress itself there we go perfect and if you're wondering where did I get the information about building the config file from uh you can find that in drizzle documentation here under drizzle kit I believe uh configuration I think there we go
right here in the configuration so it can be drizzle. config.sys or Json so we are using the TS file that's where you can find that uh perfect so we now have a working um schema and working scripts so there's just a couple of more things that I want to do here so first of all I want to go inside of here want to show you something so right now if you want to fetch uh accounts for example you would use database. select from and then you would import accounts right and if you wanted uh for
example to use um let's call this accounts 2 there we go so it it conflicted with this variable that's why I renamed this so now let's say if you wanted to I don't know connect a relation to the accounts right you would need to add some inner join or something like that so you would basically write SQL like query but uh something that drizzle also offers you if you want to use that instead uh is let's go ahead and find it uh is it queries right here or is it let's see uh there we go
so find many find first so if you want to you can use drizzle like this so this is quering similar to Prisma there we go so find many with comments so this is how you would include a relation with comments right but we're not going to do that in this project instead what we're going to do is we're going to go ahead and we're going to use the SQL like querying so we're going to use select from users and stuff but in case you don't like this and you yourself want to build it like this
you have to create one small modification here and that is to import everything as schema from schema that's inside of this drizzle file here and then you would simply add the schema here uh schema there we go and then what you are able to do is you would write database query. account accounts do find first and then you can write where right so this is Prisma like query right but we're not going to do that in this project we are going to use the select and then from accounts this would be schema. accounts right and
then we were going to do uhw and then inside of here we would use the equals and stuff like that right so that's what we are going to do so I just wanted to give you a quick tip if you want to use it differently uh great so now that that is done let's go ahead and practice the migration one more time so inside of database schema how about we add another field here which we're actually going to need later called played ID so let's define this as text and playcore ID like that so what
are the steps to add this to our database we would go inside of the terminal here we would do Bun Run database generate and then Bon run database migrate and there we go Bon run database Studio should now uh open up and have the played ID here and it does it's right here play ID perfect let's refresh the neon console and inside of here we have play ID as well and if you take a look at your drizzle here folder you now have have the change for that as well if you ever want to you
can completely remove the drizzle folder and then your migrations will start over again uh great so I hope that kind of gave you a quick intro into drizzle and neon and how to do migrations and how to do the basic changes what we're going to do next is we're going to go ahead and fully write our schema so that we can finally create some accounts and transactions great great job so now that we have our first table inside of our schema we can actually go back inside of our hono backend API and we can create
an endpoint for that so just as we had an example with books and authors we're going to have uh a separate file holding all the routes for every individual entity in our schema so right now we have accounts so let's go ahead and do the following I'm going to go inside of API rout route. TS right here and I'm going to go ahead and inside of here I'm going to create accounts. DS just like that and then in here let's go ahead and import hono from hono like that and let's write const app new hono
like this and then we can W uh write app Dot get slash like this let's get the context here and for now let's just return Json accounts and an empty array here and make sure to return this just like that and let's also just export default app like that now let's go back inside of the route here individually and let let's add app. route slac accounts and in here let's import accounts from do/ accounts so it's a default export so we have to call it like this and we just Add accounts here like this uh
and I believe that we can try it out now so let me just see if I have my Local Host running or not so Bun Run Dev like this and I'm going to go ahead and I'm going to go inside of Local Host 3000 SL API SL accounts and there we go so Local Host 3000 API SL accounts is working perfectly fine we get an empty array which is exactly what we have defined right here so what I want to do now is I want to enable RPC on my project here so inside of the
main route. TS here what we need is we need to export a type called app type and it needs to be a type of app right but right now this is not exactly going to work so as you can see in here my app type is some weird blank schema and all information it has is the slash API so what are the changes we need to do so first of all we have to change this to be a constant called routes and then we're going to chain every individual route in here so accounts accounts and
then routes are going to be right here type of routes so we are still going to use the app for the handlers but for generating our RPC type we need this all right so this is an improvement because now it's not blank right but it's still an empty object so something is still wrong here so what we have to do is we have to go inside of accounts now and we have to modify this as well so we are no longer going to use app separately here instead we're going to immediately change it like this
so immediately after new hono we chain. getet like that so when you hover over this there we go you see the types we have a slash endpoint with get input doesn't exist and output is just a never an empty array because that's what we Define here and now when I hover our app type that's exactly what we get we know exactly the type of/ API SL account accounts so just in case you are confused about how I know about this chaining practices and how I knew to modify that in order to achieve this proper types
here is because of the hono documentation right here so if you go inside of guides RPC and scroll all the way to the bottom you have using RPC with larger applications and they explain that you need to do that so you need to chain directly to new hono in this separate route managers and you have to do the same thing in your main uh index or in our case route. TS right so this is the routes constant which simply chains all the routes and then this is what we need perfect so now if let's see
if this is still working so if I refresh this still works perfectly so that's exactly what we need so now let's go ahead and improve the types so what I want to do is I actually want to use drizzle and fetch the data here so how about we do this let's go ahead and let's import the database from at/ database drizzle and inside of here I'm going to get the data by using await database do select and let's choose ID which comes from account accounts which we get from database schema so accounts. ID and we
need name accounts. name so those are the only two Fields I want right so inside of my schema I have ID play ID name and user ID but I only want to return these two to the front end and since I'm using a weight I also need to turn this controller into an asynchronous method so let's do that from accounts and from now let's just do like that so I want to return everything and instead of accounts let's return back data like that so first of all let's see what's changed so if I refresh Local
Host nothing has changed except that now it says data instead of accounts but it's still an empty array but if I hover over app right here you can see that now we have the output to be data ID string and name String so exactly what I selected here using drizzle and if I go over here and hover over the app type I also have those instructions here so that's what we achieved with this app type endtoend type safety and that is going to be extremely useful for us when we combine this with react query so
we are know going to know exactly what our mutation and our queries are going to return to us perfect so let's go ahead and just wrap this up so I want to properly do this uh so let's go ahead and try and create uh a you know a frontend hook which will handle this slash account get route and store this data inside so for that we're going to need to set up react query so just confirm that this endpoint works for you and confirm that you have this exact types as I do so your app
type should hold / API accounts the get option should have an empty input and the output should be on array of ID and name and that's going to be in the data object great so let's go ahead and let's see how to install react query with nextjs so right here first let's go ahead in the documentation and choose our way of installing so I'm using bun so I'm choosing the last option here so let me go ahead inside of the terminal here and let's add 10 stack react query great now that we have that we
have to create a provider which is going to uh work with nextjs so for that you have to find uh a sequence here called uh where is it Advanced server rendering right here so you have uh two options you can simply Google advanced server rendering search it inside of the tack query or use the link in the description or just visit my source code uh and you can see exactly what we have to copy from here so we need this initial setup we need to create a query client provider so it's this code right here
so I'm going to go ahead and copy this entire code and I'm going to go ahead and I'm going to call this query provider so what I'm going to do actually is I'm going to create a separate folder for this in the root of my project called providers and inside of here I'm going to create query Das provider. DSX and I'm going to p the entire thing inside so we need use client right that is true we don't need use State here you can leave the comments if you want to if they help you understand
uh what's going on here so I'm not going to modify any anything here and I also want to uh write types for the children here so I'm going to write type props here children react. react node and I'm just going to assign the props here and I don't want this to be a default expert so I'm just going to remove the default so it just says export function and instead of providers I'm going to call it query provider like that and I will add some semic colums here there we go we now have our uh
query provider method here if you're unsure about the changes you can always visit my source code and now what we have to do is we have to go inside of our app folder and inside of our layout right here and I want to go ahead and wrap all of my children here inside of query provider make sure you import this query Provider from our at/ providers query provider so the thing that we just created here and removed the default export and we renamed this and we added a props here so let's immediately discuss about how
come this is marked as used client why am I wrapping my entire project in that won't that make all of my children use client as well because we had a lesson about that well no because if you pass in components through children in that case not all children are going to become client components so if you pass them as children you can safely use server components inside so just to clear that up no if you wrap your children inside of a client provider that will not automatically transform all children to stop being uh server components
your children can safely be server components inside you can read more about that in nextjs documentation perfect so now that we have that uh I want to go ahead and I want to create my uh co-located folder where I'm going to keep all common account components account queries account hooks so I want to keep all of that together and I'm going to call that folder features and inside I'm going to create the accounts folder and let's create API folder inside so features accounts API and let's create a very simple one use get account accounts. TS
so this is going to be the first hook that we are going to create and this Hook is going to communicate with this accounts. TS get endpoint right here so let's go ahead and let's create that so first of all let's import use Query uh from 10stack react query and then what we have to do which is one thing that I forgot to do actually is we need to create uh an RPC client which will use this app type so it's very simple to do that so let's just go ahead and do this let's just
save this file as it is and let's go inside of our lib and let's create hono dodx s like that and then let's import HC from hono client and let's import app type from App SL API SLA API our catch all route route so this file right here this is what we are looking for and then what we have to do is we have to export cons client to be HC and use the app type and inside of here we need a process environment. next public app URL and add the exclamation point at the end
and now what we have to do is we have to add this inside of our environment file right here so copy it from here go inside of environment and I'm going to add it here so let me just copy it next public app URL and this is very simply going to be HTP Local Host 3000 or whatever Port you are running your application on and later in production we're going to change this so it's important that that's how it works here you can write it here of course but you're going to have to change it
for production great so now that we have the client we can head back safely inside of our features accounts API use get accounts right here and let's import the client from here there we go and now what we can do is client. api. accounts. getet and you can see how we have amazing type safety here so we know exactly what we're going to do so let's export con use get accounts let's define our query right here so our query key is going to be accounts multiple and our query function is going to be an asynchronous
method here which gets the response and it's going to be a wait client. api. accounts. getet and execute this method and and now you can see that our response is type safe and we know exactly what it's going to return so first of all we don't have to worry if our uh route is correct so usually we would do something like fetch API accounts right first of all we don't have to worry about any typos in this because we're using typ save RPC second what we would have to do is it would probably have to
do something like uh account type and then that would be a type of fetch right/ API accounts so something like this so we would have to Define that as well but now we don't have to do that at all because it automatically infers that because of the way we defined our app type right here which is extremely type safe and everything is Chained and works just fine and hono knows exactly what data it expects back perfect so now that we get the response we still have to take care of response okay so if not response
okay we are going to break this query by throwing throwing a new error here my apologies so just error and we're going to say failed to patch accounts like this and then let's go ahead and destructure the data from await response. Json right here and let's go ahead and return data like that and and in the end just return the query there we go so now we have our reusable hook here so what we can do here now is the following well first of all you can hover over uh use get accounts and in here
you can see exactly what you're going to get in the used query result you're going to get an array of objects with ID and name so how did I know that I can destructure data here well first of all because of type safety if I try to destructure something else it's not going to work because we know that we return the data object back so if I change this to thex data in that case in here we can destructure back X right so that's the type safety I keep talking about right that's why I'm so
excited about this stack because I feel very confident in our code here's one kvat you might have thought of so you might be thinking okay so what I can do is I can just wrap this into a try and catch and then I don't even have to take care of this remember this is not axio so axio Works in a way that if it encounters an error it automatically goes into the catch option but this is not axios so in here we have to separately take care of the error by checking if the response is
not okay great so now we have this very cool hook and we can already try it out for example uh in the dashboard in page I'm going to go ahead and remove this and I'm going to mark this as use client here and I'm going to go ahead and get cons let's call this accounts query use uh get accounts like that and then in here you can already see how you can work with this so for example you could do accounts query. data do map you would get individual account and if you were to return
a div here you can safely use type safety so account and you can see how it autocompletes do ID and inside account. autocompletes do name so our query is working just fine or if you want to you can use data and then in here you can write accounts for example and then you would skip over this and write accounts and furthermore you also have is loading so you can also use that if is loading and then in here you can return a loading element right so that's how we going to use these Hooks and what's
cool about it is is that we're going to collocate a bunch of useful API both queries and mutations inside of here so each of our entities which exist in here in schema so alongside accounts we're going to have transactions for example and then after we create all the uh routes for that using hono we're going to go inside of features and we can just copy and paste you know existing accounts and just slightly modify it so we would change this later to be transactions right and the query key will be transactions so so a lot
of reusable code and consistent code in our code base that's why I'm so excited about this but let's just wrap it up by doing one more thing which is error handling so uh you can if you want to leave this or you can just you know I'm actually going to reset the home to be as it was so I'm going to return the homepage I just wanted to demonstrate how we're going to use this hook now so all that's important is that you have this hook completed right right and that it has all the type
safety that I keep showing you but here's a thing so for example if I go inside of app API and if I go inside of accounts first of all this is not complete so what I want to do is I want to ensure that only authenticated users can see this so I'm adding the clerk middleware from clerk nextjs server so we learned that in the section about hono that we can chain as many middlewares as we want inside uh but I believe I shouldn't be using clerk middleware uh like this my apologies so clerk middleware
not from here but instead from at hono clerk out from here right so just ensure you have this package installed I believe we did that uh when we uh explored hono in one of the previous chapters right so in the dependencies I have hono clerk out great so clerk middleware make sure you execut it right here and now let's go ahead and let's get the authenticated user so const out will be get out and pass in the context and the first thing uh we also need get out my apologies so get out there we go
so what I want to check is if there is no out question mark user ID I want to throw back an error so we can do that in many ways first let's try this let's return c. Json error unauthorized and let's write back 401 so yeah the second argument in here can be uh the status and let me just indent this like that so this seems fine and if we try it out so if I go here all right uh let me just refresh my page to see that it's working so if I go to
SL API accounts now I should just get an empty array because I'm logged in uh but if I go ahead and if I log out and if I attempt to go to/ API accounts uh I get back an error unauthorized right that seems fine it seems like this is working as intended and it technically is but let's take a look at what's happening now if I revisit my route. DS and if I hover over the app type all of a sudden you can see that my output has changed so now my output can either be
the error or it can be uh the data here right and and that will cause problems inside of our hook here called use get accounts right here so because of this right now let's just see so as you can see here I can still extract the data right so perhaps it actually is working fine but here's another way you can handle errors uh if you want to so if you want to you can do this instead of returning errors like this what you can do is you can throw an HTTP except so I'm going to
go ahead and import this HTTP exception from at Pono HTTP exception like that and then in here you can throw that instead so HTTP you would throw new HTTP exception 401 like this response c. Json error unauthorized and 401 uh let me just see if I did this correctly uh my apologies like this there we go so what changed now well what changed is that now uh you no longer have that written in here so now the only typed response can be data because otherwise we throw the exception right we throw the error we break
this method and in order to for this to work as it should so now if I refresh it still works exactly uh as written right but we do need uh to do one more thing and that is to go back inside of our uh route. PS right here and just below this let's write app on error here error context and inside of here let's check if error is instance of HTTP exception which you can import from hono HTTP exception here and let me just see if I wrote this correctly so if uh error instance of
should be lower case like that instance off in that case let's return error. get response so we can do that because inside of the accounts here we assign the response so we are simply going to return this but in case this is an unhand error what we can do is we can standardize our Json output so it never shows anything else in the API so I'm going to write error internal server error or maybe just internal error server is redundant there we go so now nothing should change but our type safety should be intact so
our app type here see output is now intact we know exactly what data will be returned here right so if you want to you can explore you know with the alternative where you return c. Json here error unauthorized and a 401 right so I will uh comment this out and this still works fine but now you can see that you have two different types of output here one being an error and one being the success message and that can be fine but it causes problems for rpcs because let's go and do this again so if
I were to import use get accounts and if I were to try and get my accounts query here use get accounts like this now what's possible is that uh I get an error here as well right so I believe that if I try and go over accounts query. data uh map well looks like it's working now but I remember that I had a couple of issues here uh before because of that error so you can decide for yourself we can try it out if you want to you know you can use this method uh my
apologies where is my accounts file right here so if you want to you can either use this you can return C the Json error unauthorized or you can use the throw new exception right so I'm going to go ahead and go with throw new exception simply because uh I use this in my original source code right but if you want you can play around you know uh I invite you to explore hono yourself uh but I found to have less type safety errors when using this right here basically we're going to see whether it causes
problems or not later when we actually have a chance to use this query so for now let's just leave it as it is let's turn the homepage back to homepage I'm going to remove use client here we don't need it and what's important is let's recap everything we've changed here so we added the query provider we added the honol lib which uses the app type and the new next public app URL we added the use get accounts which handles the error for us here and we've also added the accounts and we' also added the onerror
Handler here and we added this app type here as well as the separate accounts uh route here which very simply throws all the uh accounts here one thing that I want to extend this with is the where so in here I want to add equals from drizzle orm so make sure you add this so equals and we are very simply going to check whether accounts. user ID equals the out. user ID which is currently logged in so I only want to return accounts from this user so this is very important and if I refresh I
should still get unauthorized here so I'm going to go ahead and log in there we go so I'm back inside so if I go to SL API slash accounts and there we go I now get empty data because I don't have any accounts to my name perfect so what we're going to do in the next part of the tutorial is we're going to add uh uh a post route inside of this account right here and I want to keep playing around with throwing errors because it looks like there are alternative ways of doing it right
and I just want to give you the proper information about them because when I was first developing this project the only way I could ensure type safety was by using the HTTP exception but it looks like hona has a new version where it handles errors in a bit of a different way but I still think this is going to work just fine but as I said we're going to see that fully in action once we actually get to use uh this hooks right here great uh amazing job and see you in the next chapter so
just a quick explanation about our errors which I feel like might be a confusing segment before I finish this chapter so I just went into my original project and I took a look at why exactly I used this the HTTP exception and turns out it was because I had an older version of hono so hono has since been updated and it seems to handle this errors perfectly fine so here's what I propose we do for this tutorial let's try and use this method instead because I honestly prefer it this way so go inside of your
API route accounts and remove the throw new HTTP exception entirely and just leave the simple return of a Json object but it's crucial that you add a 401 at the end here so that's all I want to do and I want to remove this as well and let's also go inside of the route. THS right here and let's go ahead and remove this on error also I believe we are not going to need that we can always bring it back if it turns out that I'm wrong but I think with the new version of hono
and let me just ensure and show you which version I'm talking about here so I'm talking about hono 4.3.2 in my case right here so let's go ahead and do the following uh I want to show you uh how this works so inside of here inside of features API use get account in my old project if I used this type of Json error I would get an error here it will it would tell me that data doesn't exist on this response because well there's no data in this response and I believe if I remove the
status 401 and save this I get that exact error here data does not exist on type error so that's the error I was getting but in the older version just adding the status did not fix that but now it seems to fix the types entirely and that's great so hono was uh improved by the time I finished my course well not course but my initial code to the time where started recording this so this is great we can actually just use this you can just set the status 401 for the error and that will automatically
in this response ensure that there is a separate client response for the 401 status and there is an entirely different client response for all the other statuses here which has the proper data and also what brings me confidence here is that if we don't handle the error we also get this typescript error so this handling of error gives me confidence with this type safety to tell me yes you handled this error properly all you have to do is take a look at the if response is not okay and just throw the error and react query
is not going to attempt to destructure this if there's no data in here at all so I believe that we can just do this safely so let's go ahead and do it like that if if we encounter any problems we can easily bring back our HTTP exception great great job all right so now that we have our API endp point for fetching the accounts I want to create an endpoint for creating an individual account and we will start that by revisiting our database schema right here so what I want to do is I somehow want
to have one schema that will be generated using this table right here and then I want to reuse that schema everywhere and luckily for us there is a package called drizzle Zod so head inside of your terminal and do bun add or mpm install drizzle Zod and this will add that package so now that we have that go ahead and add it in your schema file so you will import this as create insert schema from drizzle Zod like that and then what you can do is write export const insert account schema create insert schema and
simply pass in the accounts like that there are of course more options than just this somehow sometimes you will have to slightly modify the schema as to osed what's in the database right so for example you can take a look at the documentation for drizzle Zod and in here you can see that you can just create the schema like we did you can also create the select schema or you can also overwrite specific Fields if for any reason uh you want to change the role to be a number and you defined it as text in
the table right so you can play around and it's very powerful and you can also add some useful refining here right and you can also do pick and omit all of those things so in my case I want to keep it exactly like this and then perhaps I will change it later on great so now that we have our schema here uh let's go ahead and let's create our post request so we're going to go back inside of app API inside of the accounts here and I'm going to close the get request actually I'm not
going to close it just to not confuse you so this is what I'm doing I will remove the semicolon and I will simply chain the post like this so that's what I am doing here right but I will close get now just so you know I have more space to work with so let's go ahead and collapse this elements here let's go ahead and uh get the context let's mark this as asynchronous and let's return c. Json and an empty object here and I'm going to add the clerk middleware here here and then I'm going
to go ahead and do the same thing that I usually do is get the out using get out and passing in the context if there is no out user ID I will return c. Json with an error and authorized and important give it a status of 401 so we have proper types Here and Now what we have to do is we have to uh validate using Z what kind of Json can this post request accept and thankfully for us we can do that by adding another middleware here called Z validator and let's just see where
can we import the Z validator from so let's import Z validator from slono Zod validator there we go so we have the Z validator here and what are we validating ating well we are validating Json input here and what is our uh schema for that well insert account schema which we've just created from database schema and here's the thing so the insert account schema will accept the ID the pl ID the name and the user ID so user ID is required and name is required but here's the thing I don't want user ID to be
passed through the Json object I'm going to assign the user ID using this right here when I create it that's how I'm going to do it so I will tell the front end okay I want you to use the insert account schema but only specific Fields you will take care of the name field that's what uh the front end form is going to take care of so that's how easily we can modify this reusable schema if as I just showed you sometimes we need a bit of a different uh object to work with and then
what we can do here is we can get the values using c. request. valid Json inside of these values we are going to just find an object with the name String you could also if you want to destructure that specifically like this name there we go but I'm going to work with values here and then what I'm going to do is very simply create this so I'm going to write const data and I'm going to write await database do insert accounts. values and inside of here I'm going to open an object so here's what I
want to do so I can assign the user ID to be out. user ID I can go ahead and spread values which will take care of the name field we don't need to pass in the played ID but we do need to pass in the ID so ID is correct currently defined as a very simple text string which gives us complete Freedom onto defining the ID however we want so if you want to use uu ID you can use that if you want to use Nano ID you can use that I'm going to be using
CU ID so for that we're going to have to install this package right here so let me expand this there we go so it's Parallel Drive SLC id2 if you want to you can use uu ID V four or something else so I'm going to copy this and I'm going to use bun to add it to my project so let's go ahead and do that inside of terminal here bun add CU ID V2 and now what I can do is I can import from that in package create ID like that and then I'm going to
use that right here there we go and make sure you execute that and that will create me a string which is all the validation I've put here so far you can also uh chain this with for example a default function and then inside of here you could use the create ID for example but I haven't tried that out I think it can work but I don't know how it works with our migration script and all of that and honestly I'm fine with doing it like this it gives me explicit control over idas but you know
you absolutely can automate that in drizzle right don't worry you you can read the documentation and here's another uh thing you ought to know when working with Drizzle so by default whoops so by default uh the select script or the select function will return the data right and it's going to be an array but the insert method by default will not return anything right so this data actually isn't my newly created item if I attempt to do data do something you can see that this is not my object so what you have to do is
you have to chain do returning like this and if you only want to return specific things you can go ahead and specifically write for example uh name I believe this is how you do it or maybe not but basically you need to add do returning and now when you and this there we go you get an array of created items so you would go ahead and return you can either D structure the data immediately here like that and then inside of here what you would do is I simply return the data the single new created
element like that so yes in when using drizzle and when using select you're always going to get multiple you're always going to get an array there is no uh I'm not sure what would be the equivalent do single right or unique that doesn't exist that's because they are staying true to SQL right so in SQL you would always get an array of items so that's why they are always providing you with an array of items so whenever you need a single element and you know it's going to be unique you can just immediately destructure it
like this and then this would be the first item in the array right don't do that for the get route so I'm just demonstrating on the get route here so let me close it get route stays unchanged we are only working on the post out here so yeah in here you can already destructure it if you feel like this is unsafe what you can do is you can do data like this and then maybe data first in the array if that is uh safer for you but I'm going to be doing this and that's pretty
much it so this is exactly our uh post end point for creating something right so what we have to do now is we have to create a form which will be able uh to well create this kind of values so in I hover over post you can now see that inside of here somewhere uh let's see what if I remove the get uh all right so I I thought that somehow I will be able to see uh what I'm accepting here but there's just too too much stuff happening for it to be displayed in this
small box but we're going to see that in a moment all right so what I want us to do next is I want us to add form elements from shaten UI and we can do that by using a script here so bonex shaten dasi latest ad form so what this is going to do is install a bunch of different uh stuff that we need so let's go ahead and take a look so in package Json now uh you should have hook form resolvers you should have the react label you should have react hook form installed
and is that it I believe that could be it and I think that now we have to individually add uh a couple of more Fields so let's see so the UI folder is everything that shaten has added so we have button form label and sheet and we also need to add one more thing B next shaten let me clear this so you can see B next shat nuui at latest add the input that's what we need let's do Bun Run Dev uh perfect so now what I'm going to do uh is the following I want
us to go ahead and I want us to create uh a new account sheet component so let's go ahead inside of our features accounts and let's create a folder components so we're going to collocate our comp account items here so both the hooks and the components everything's going to be here inside of here let's create new- account- sheet. PSX and let's go ahead and simply uh create that component so let's import everything we're going to need from s/ components UI sheet we need the content we need the description we need the header and we need
the title like that great let's go ahead and let's define this method so expert const new account sheet like that and let's return this component right here so inside of here uh we're going to need sheet content let's give this an open uh for now let's make it true right so I want to keep it open class name here space y4 inside of here let's add the header let's add the title and the title can very simply say new count and below that we can have a description which can say create a new account to
track your transactions like that and we don't need need anything more so obviously we're going to add a form but for now I just want to render uh this component right here and let's go ahead and do that inside of a new provider which we are going to create here it's going to be called sheet provider. PSX and let's go ahead and do the following so let's go ahead and Mark this as use client and let's export con sheet provider here and let's render inside of a fragment because we're going to have a lot of
those new account sheet like that directly from add features accounts components new account sheet like this and now let's add this inside of our whoops inside of our app layout right here and we can just add it inside of the query provider above the children so so just make sure that you've added it from our new providers folder and I think that just by adding this if I go ahead inside of my Local Host here I think it should already be opened if I'm not mistaken let's see uh or perhaps not let me refresh one
more time there we go so now you can see that when you refresh you have this sheet opened right here and we also have a hydration error here so all of that is fine we're going to resolve all of this in a second so first of all let's resolve this hydration error and we can do that by going inside of the sheet provider here so as I explained to you before just adding use client will not mean that something is not rendered on the server right so this hydration error is happening because we have a
mismatch between what rendered on the server and what rendered on the client and that's because on the client this turned to an open sheet right so what I'm going to do is I'm going to find a way to only render something on the client and models are perfectly fine for that right there's no need to serers side render models so I'm going to go ahead and I'm going to import use mounted state from react use so what we can do here is write con is mounted to be use mounted State and then I'm going to
write if is not mounted return null and now I'm going to explain to you uh how this works seems like we are still getting an error uh but we're going to go ahead and resolve this later right so let me first explain to you what the use mounted state does so use mounted state is the equivalent of this const is mounted set is mounted use State false by default let me comment this out and then another use effect from react which calls set is mounted to true so these two lines are the equivalent of this
one line right that's why I'm using this because we already have this installed it's very useful and we are using it for media queries right so why does this work for fixing hydration errors well it works because uh this set is mounted can only be called inside of use effect and use effect or the component did life cycle which has the empty dependency array can only be called once the component has been rendered on the server and then it's finally on the client so that's why this solution works so until that happens we don't even
attempt to load this stuff right so let's go ahead and remove this now or you can use this solution whichever solution you like I think this is pretty much cleaner great so what I want to do now is I want to go ahead and add twoand inside of our project so let's do bun add twoand or tand I'm not sure how to pronounce that and what we're going to do now is we're going to create a kind of a controller for our drawer here to create a new account so let's go inside of uh features
accounts and let's create a new folder called Hooks and inside let's create use new account. DS let's go ahead and import create from tand let's create a type new account state to have is open to be a Boolean on open to be a void and on close to be a void as well and let's export cons use new account here to call create with the new account State here go ahead and extract set which immediately returns an object so make sure you wrap it inside of parentheses right so this and this is not the same
this is a method this is an immediate return of an object so let's define is open to be false on open to be a method which calls set and will it will change the object is open to true and then we're going to have on close which does the opposite and sets it to false there we go so now what we can do is we can go inside of our components new account sheet here and we can import use new account from hooks uh use new account but I'm going to change this to Features accounts
so I like to keep my imports like this I'm going to keep the features always above other components like that and in here we can extract is open and on close so I will map the open to not be hardcoded but instead be controlled by this and this will be on open change on close like that so now if I refresh this should be closed by default it should not appear anymore so what we can do now is the following we can copy this hook and we can go well we only have the dashboard page.
DSX so let's use that so let's mark this as use client the homepage right and let's add this and let's import use new account from features accounts hooks use new account and let's add a button here from components UI button add an account and on click is going to be uh let's go ahead and the only thing we actually need is on open here and let's call on open there we go we are going to change this later obviously it's not going to be open from here but we need a button which will open something
and now when you click here there we go and when I refresh there are no hydration errors anywhere so all of this is now perfectly controlled so why did I do all of that well I did it so that we have this useful hook which we can call absolutely anywhere in our project and no matter how we want to open a new account form we can do it using that hook I think it's very useful it has a nice Global control and it doesn't cause too much problems inside of our project uh great so now
that we have that I want to do a slight adjustment so when I am on desktop this is fine I want it to be like this but when I am on mobile when I'm on small devices I want it to fill the entire screen right no need for it to be a half screen so let's go ahead and do that so so let's revisit our sheet component so I'm going to go inside of app my apologies components UI sheet component and let's go ahead and let's find the variance right here sheet variance so first thing
I'm going to do I'm going to find uh my default classes here and I'm simply going to add overflow y AO so I want to make sure that people can scroll in these drawers then I'm going to find the left right here and I want to slightly modify it here so instead of using uh the width 34s here I'm going to use the width full and then I'm going to modify this so it doesn't look at the small breakpoint but instead the large breakpoint and I wanted to use max with MD like that so let
me go ahead and show you like this so you can perhaps notice the changes better so what I added is the sheet variance content uh constant I added the Overflow y AO in the default classes and then I found the left and in here I changed from uh width 3 qus to width full and I found the break for breakpoint for SM to LG if you're unsure you can just copy this from my source code and this slight change I believe should be enough so right now there we go it looks fine here and when
I go into tablet or mobile mode it should become full screen but it looks like it's not let's see am I still making uh a mistake here all right so it looks like I still have to debug looks like something is not right well I made a mistake because I modified the left when I should be modifying the right so let's go ahead and do the same thing here so find the right and change from with 3/4 to with full and change this to use the LG breakpoint Max with MD like this and there we
go now it has the effect that I want so on tablet and mobile I wanted to behave full screen but on desktops I wanted to just take a bit space on the right and when it collapses there we go I want it to be full width and let me just resolve this back for the left side so I'm going to change so in the left I'm going to change this to 3/4s and I'm going to change this to be uh a small break point and SM here there we go so let's see the final changes
again so left stays untouched we only modify the right by changing this to width full instead of width 3/ qus and changing the SM breakpoint to Max with SM to large Max with MD and I also added the Overflow y Auto there we go so now we have this as per my liking and now it's time for us to create our reusable account form so let's head over inside of our features components so inside of the accounts right but that's the only one we have and let's create our account form so we're going to reuse
this both for creating a new one and also for updating one so let's add all the Imports we need so we're going to need Z from Zod we're going to need the trash icon from Lucid react we're going to need use form from react hook form Zod resolver from at hook form resolvers Zod we're going to need the input component from components UI input we're going to need the button component from the same place we're going to need our insert account schema from database schema and lastly we're going to need all the at/ components UI
form elements so that's the form form control form field form item form label and form message like that so these are all the Imports which we are going to need now let's define our form schema so const form schema is going to be insert account schema. pick and we are only requesting the name here so that's the only thing we need this form will only be for creating the name of the account and now let's create that into type called form values which will be z. input type off form schema there we go and now
let's go ahead and let's create a type props here so it's going to accept an optional ID in case we are editing an account it's going to have optional default values which can be a type of form values it's going to have an onsubmit method which accepts the values which again are a type of form values and it returns back a void and it's going to have an optional on delete which can be a void as well and disabled will be an optional Boolean let's export const account form and let's assign these props right here
so inside of here we can D structure ID default values on submit on delete and disabled like that great and now inside of here we can return a form like that and in order to get rid of the error here we need to define the constant form using use form and let's give it the type of form values here because we know what we are working with let's add a resolver to be Zod resolver form schema so we have form schema defined above right here right and then let's pass in the default values to Simply
Be default values like that and let's go ahead and let's create con handle submit which accepts the values which are a type of form values which for now we can just conso log like this and let's const handle delete here for now to just console log or we can actually already do this we can call on delete but since it's optional we're going to add a question mark and only then execute it so this is actually safe right so now what we can do is we can spread the form inside and we can add a
native form element right here and let's give it an onsubmit here to be form. handle submit and pass in whoops form do uh it is yes handle submit and pass in the handle submit method again so this is from the form constant and this is our handle submit so using this wrapper around our method ensures that we only uh trigger this once it is validated right so no uh invalid fields are passed and let's give this a class name of space y4 and space y4 whoops and padding top of four and we can collapse these
two like that there we go and now inside of the form here let's create a form field which is a self- closing tag which accepts the name to be well name how do I know its name well because that's the only field we are controlling so yeah it's a it's a coincidence that the prop is the same thing as the value right but this is uh this refers to this name right here that's why this is controlling the name field we need control to be form. control and we need the render to be a method
which immediately dest structures the field and then it calls not calls but it returns a form item like that and then we have form label which will simply render name like this and below the form label we're going to have form control which will use the input the input will be disabled if the global disabled prop is being passed and we're gonna have a placeholder to explain to the user what we expect them to name this account for example cash Bank credit card something like that and then we're going to spread the field so what
does spreading the field do so inside of field this very cool Library use form uh handles all the event handlers for us right so usually you would have to have onchange right and then inside of onchange you would get the event and you would set value to event Target values right something like this but you no longer have to do that because field does all of that so when you spread the field uh let's take a look at it there we go so inside of here you have on change on blur value disabled so all
of those things are being passed here so I override disabled on my part great uh I think this is enough for us to uh well it won't work yet but I think we can already render it so let's go inside of the new account drawer here and below the header let's add the account form from do/ account form or features components uh features accounts components there we go so I'm going to add that and it requires a couple of fields here so I'm going to give it on submit for now to just be an empty
Arrow fun function and disabled is going to be false so right now if I go ahead and refresh the whole thing and click add account there we go I have my form here which allows me to write name of my new account perfect so let's go ahead and let's add a submit button here so let's head back in the account form and let's go ahead and still let's just go uh outside of here still inside of this uh Native form element Right add a button component which will check if we have the ID if we
have the ID the label is going to be save changes otherwise it's going to be create account class name is going to be with full and disabled if the global disabled prop is passed like that and then below that we're going to check uh well let's go ahead and let's uh render it first so you can see what it looks like so let's add a trash icon here and let's write delete account let's give this a class name of size four and margin right margin right of two like this and let's go ahead and give
some props here so type here is going to be button disabled is going to be disabled on click is going to be handle delete class name is going to be full WID size is going to be icon uh and variant is going to be outline so my apology size is not icon right uh and let me just see why is this not working so MR2 this should be pushing this or is it mi2 uh let's see that's a bit odd uh let's just see Mr how about br2 all right uh just a second so I
can debug this right so I'm going to leave this for later uh it might be due to a different Lucid react version I'm not sure why usually in all of my tutorials margin right to always safely pushes the content let's see what if I wrap it inside of a paragraph will that push it then how about a div no okay so I'm going to debug this later let's go ahead and let's focus on wrapping this up so what's important with this delete button is that it has a type of button because it is inside of
form element so if you don't specify which one it is it will count as submit inside of this form and now what we're going to do is we're only going to render this button if we have the ID like this and what I recommend you do when you use this kind of uh conditional is that you turn the ID into a Boolean by adding double exclamation points like that there we go so you can see that now it disappeared because we don't have uh ID in this case right and you can if you want to
use this kind of syntax if that's something you prefer more like this uh let's see there we go all right so uh what I want to do now is I want to go ahead and I want to pass in the actual onsubmit method here so let's go ahead and use that let's pass in the onsubmit and let's pass in the values like that so we should have the onsubmit right here there we go let's go back inside of the account sheet Here and Now now what I want us to do is I want us to
create the onsubmit here so con onsubmit we'll accept the values which again are going to be a type of form values here so let's go ahead and let's just copy and paste exactly what we had in the account form so this two right here insert account schema from database schema and Z from Zod so I'm going to move move this here and I'm going to move this all the way to the top like that there we go so now we have form values here and let's conso log values in Here and Now pass this on
submit right here there we go let's try it out so I'm going to go ahead and open my uh let's go ahead and do this I'm going to open my inspect element here and I'm going to name this account test um let's see what the errors are right we are having some errors we're going to see where this comes from and there we go the values are name of test perfect so this seems to be working just fine great uh perfect uh and I believe that this error for the controlled input might come because we
don't have default values here so how about I go ahead and do this if I pass default values name to just be an empty object let's see if that will help resolve that bug so let me go ahead and test there we go so now we don't have that and this is still working perfect so now what we have to do is we have to submit this to our newly created endpoint in the accounts right so we have the get request but now we need to call this post request so for that we're going to
go ahead and we're going to create uh a new hook in our features so let's go inside of features API and we're going to call this use create account. THS like this and let's go ahead and let's add a couple of things from hono so let's import infer request type from hono and let's add infer response type from hono and let's go ahead and let's import use mutation from 10stack react query and let's add use Query client from 10stack react query and finally let's import client from lib hono let's define the response type of this
mutation to be infer response type open pointy brackets type of client. api. accounts. post so this is the response type either an error or data right here let's define type request type to be infer request type type of client api. accounts. poost but we are not done yet so the request type is what this endpoint is expecting to accept in the endpoint so what we need is to get this this validator right here we can do that very simply by targeting Json so let me zoom out so you can see it in one line like
this can I zoom out this even more I can't okay so that's what we need and now let's go ahead and let's export con use create account let's define the query client use Query client and let's go ahead and let's define the mutation so con mutation is going to be use mutation open pointy brackets and let's go ahead and let's f in the values so let's add the response type as the first argument then let's add an error type which we have natively you don't need to Define that anywhere and then the request type like
that and then open an object sorry a function let's add the mutation function here asynchronous Json so what is Json Json is what we are going to send from our form and you can already see the type safety here here how do we have that well because of the request type which we have added as the third argument here and this should not be uh it this should be this right like that now inside of here let's get the response using await client API accounts. poost and simply pass in the matching Json and return await
response. Json like that there we go and then you can add on success here and inside of here if we successfully created it what I want to do is I want to use the query client and I want to invalidate all the existing queries with the query key accounts what this will do is it will refetch all accounts every time you create a new account so your use get accounts here will get refetch because a new one was just created so that's how we can do that here and you can also add on error here
for example to do something so how about we do that let's go inside of our terminal here and let's do bun bunx shat CN UI latest ad toaster uh sorry soner let's do Bon run Dev again let's go inside of our app uh where is it layout let's go ahead and let's let's import poster from components UI soner and let's add it just below our uh sheet provider here that's all we have to do here then head back into the use create account hook and at the top here let's import toast from soner and now
on success let's add toast. success account created and on error let's let's add toast. error failed to create account there we go and what we have to do is we have to return the mutation in this hook and this is now ready so let's go inside of our uh components new account sheet right here and below this let's add the mutation from use create account so I'm going to change this import to be consistent features accounts API use create account and let me move it up here so this is all in one line of course
like this and now what we can do here is very simply do mutation do mutate and pass in the values and as you can see the typescript perfectly matches all the way from our form values here from the form values which are being passed in the form all the way to what our API endpoint expects so that's why I love this text St so much and the thing about this is that this is not a promise so you cannot do this if you really want to then you want to use mutate asyn but you don't
have to because for example after this finishes uh alongside this on success here I also want to close the account uh sheet so I can do that by simply adding an object and I'm going to continue with the on success here and call the on close just like that and now what we can also do is we can disable everything while it's mutating so mutation do is pending like this let's go ahead and try this out now so I'm also going to go ahead and open my uh database Studio actually we didn't have to because
we have an API endpoint right so let's write test let's actually refresh everything say it's safer that way so let's write test and let's see there we go account created and if I go ahead inside of Local Host 3000 API accounts there we go we have an ID end name test so it's perfectly working let's go ahead and let's notice the Network tab to see what's happening here so I'm going to go ahead and zoom out a bit on everything so I'm going to write test here so let's see what's going on so we're calling
the accounts API endpoint with this payload and this is the response right so we have a user ID name test but we are not returning played ID well because we don't have that right and when I refresh here now we have two of those perfect so our integration to create accounts is working flawlessly and we can now easily open this form from anywhere and this form will be reused to edit the account which is something we are going to do next great great job so now that we have some routes and some hooks to manipulate
the accounts entity I want to create the actual accounts page so the remind you when you expand on your desktop mode here we do have the accounts page so how about we change this from being a 404 page into actually rendering accounts and making this button displayed there instead let's go ahead and do the following I'm going to go inside of my project here and let's go inside of the app folder and let's go inside of dashboard inside of here I'm going to go ahead and create a new folder called accounts and inside I'm going
to create a page. DSX let's go ahead and write accounts page here and inside of here I am very simply going to return a div which will say accounts page and now when we have this if we try and going either manually to Local Host 3000 SL accounts there we go we have the accounts page and you can also see how in here here the accounts is now highlighted so I can now switch between overview and accounts in case you're having errors here make sure that inside of your navigation component you didn't misspell anything in
the routes here so accounts should go to slash accounts make sure there are no typos between this and your folder name here great so now that we have the accounts page here uh let's go ahead and let's add uh kind of uh some Styles and kind of a card look to it you're going to see what I'm talking about in a moment so I want to use the card component so let me go ahead and wrap this entire thing in a card and let's go ahead and import everything we need from card so import from
at/ components UI card or we don't have card my apologies so I kept thinking that we have the card so let's go ahead and do bonex Shad CN UI at latest let's add card like that there we go so now that we have the card we no longer have an error and now we can import card card content card header and card title there we go so this is going to be the card but then we're going to have card header here inside and inside of card header we're going to have a card title let
me just refresh this page because it shut down the server and now we should start seeing active updates here there we go see accounts page and I just want to style it a bit so for the card I want to give it a class name of Border none and drop shadow small for the card header I want to go ahead and give this a gap y of two I want to give it on large devices a flex row on large I want to give it items Center and on large I want to give it justify
between that's because in this card header alongside card title we are also going to have a button but before we add the button let's go ahead and style the card Title by changing the text to be extra large and line clamp one in case it gets too large great and now below that we can add a button component which comes from component UI button and in here we can add new like that so let me just refresh this there we go so this is how it looks like on mobile but when you expand this is
how it looks here and if you want to you can also add the plus icon from Lucid react so let's give it a size four and margin right of two there we go add new perfect and I also want to give this a size of small there we go uh and let me just confirm that this takes the full width on mobile it does perfect exactly as I want it to look like and now what I want to do is I want to go ahead and I want to add the const new account from use
new account which can import that from features account hooks use new account so I'm going to keep those at the top here and since this is a hook we need to mark this entire page as used client to get rid of that error and I'm going to go ahead and give this button an on click to use uh new account dot onopen there we go so now when you visit the accounts page and click add new this is where you can create your accounts perfect so what I want to do now is I want to
add a kind of a negative margin to this card so it takes this empty space here so it looks a bit better so we're going to do that by giving the effect on this div right here let's go ahead and give it a class name Max with screen to excel MX AO so the first two classes are so it doesn't expand too much right so when I zoom out it will stop expanding at some point the same as our header right without this class so if I remove this then you can see how it expands
all the way here which is not something we want right we want to keep it contained in the center if the screen gets too wide so that's the first thing and then what I want to do is I want to add uh a full width padding bottom of 10 and minus margin top of 24 like this there we go so now this uh uh card is kind of above uh the space here and don't worry it's going to get uh bigger and because we're going to add the data table here below so then it's going
to look better great so what I want to do now is I want to go ahead and I want to build a data table component so chatsi has the data table component and it's built using 10stack table so in order to uh get the data table we actually have to install a couple of separate packages first one being uh the actual table component from shat cnii so I'm going to go ahead and use bun for this so this is just you know installing the presentational table so let's do bun actually let me copy and paste
this there we go so bun shats Andi latest add table uh if you want you can use this prefix I don't know there's no difference for me if I use it with or without that there we go so we have that but we're not done yet because we also need uh to add 10stack react table like this or npm install 10stack react table uh great so once we have that we are ready to start building uh our data table here so we're going to keep that in the accounts page for now actually we're going to
keep the data table component in self in the common components but we're going to have some columns and actions and we're going to keep that here in the accounts you're going to see why in a second right so these are some prerequisites here so this is how our dummy data is going to look and we can work with that data so this is what we're going to do we're going to keep the data table in the common components and we're going to keep the columns in our well they used payments for us its accounts right
so because columns are not exactly reusable they are specific for each each and every entity that we have great so let's start by defining our columns so I recommend that you open this shat cnii uh documentation here and choose the data table and find the first step column definitions or you can copy stuff directly from my GitHub is if that is easier for you so instead of accounts here I'm going to create columns. DS and I'm just going to paste that here there we go so this is some dummy uh type here that we are
going to use so we are pretending that we are loading the status email and amount right we know that we have some different fields in our database but it doesn't matter for now because we just need to get this working great and now we need the actual uh data table component so I'm going to copy the entire thing from step two and I'm going to create the data table inside of components here so reusable datat table. TSX and I'm going to paste the entire thing here so just ensure that you have 10 stack react table
installed and components UI table right here and there we go just by itself you should not have any errors here at all you should just have a data table great let's see what is step three here so the step three is to render the table so for that we need some mock data so let's go ahead and do the following so they do it this way let's see if we're going to do it like that or are we going to do something else so I'm going to go back inside of dashboard accounts page and I'm
going to paste this method of theirs here and we can import the payment from do/ columns because inside of columns we export the type payment so I'm doing this inside of page right but we can't exactly uh call this function anywhere so here's what we can do instead uh perhaps this is going to be easier so let's go ahead and just copy this array like that and inside of here let's find con data and let's make it that array and this single object inside and then I'm going to go ahead and use this payment which
is an array as the type of this like that there we go so then we don't need this as synchronous function at all and we just simplified stuff I'm going to go ahead and move this to the top and I'm going to separate this because this is a different kind of import now that we have the data here we are actually ready to try out our data table component so I'm going to go ahead and copy the data table as they are using it here and below the card header we're going to add card content
I believe we already have card content added here and we're going to add the data table now let's fix the errors so first of all we need to import the data table from at/ components data table I'm going to move here that alongside with a button here the second thing we need is to import columns itself from do/ columns they are right here so we can keep that together here like that so just ensure that when you copied it nothing changed and you still have the export const columns like that so I believe Ju Just
by itself now this should be fine so when I go ahead and refresh this I should see I believe one element here there we go so pending email and amount great so this looks fine right but now what I want to do is I want to make sure that this table has you know some more actions here so the first thing they te you here is how to format inside of your cell right so if for example you want this amount to change from the plain integer 100 to always be uh a type of uh
dollars right what you can do uh is you can go inside of columns right here and find the amount and you can manually change the cell to be some type of component and we are going to do that later but for now I just want to add more functionality to the table itself so I'm going to skip step two which says update columns definition right we can skip that instead let's go ahead and also uh let's also skip row actions we don't need that as well because all of that requires you know separate components and
stuff right you can see that we going to have to copy a lot of things let's start easy let's do pagination right so inside of the data table documentation find pagination and follow the steps so we're going to be working inside of data table component so you can close everything else from now find pagination and first import get pagination row model from 10 stack react table I'm going to go ahead and add that here after that you're going to go ahead and you're going to add the pagination row model let me go ahead uh and
we copied this and now we're going to add that inside of here inside of use react table just below get core row model like that now we have have to add the pagination controls so I'm going to import the button component right here and we have to wrap our entire component in a div here right so find where your component starts in the data table and wrap it inside of a div and go ahead and go to the end of the component and add the end of the div then you can select everything and indent
it inside so this is some weird error if I just reload my window it's going to go away there we go now what we have to do is we have to add two buttons at the end of our table component right so where it ends actually at the end of this div right so go ahead and do that inside of this code you can find it here so I'm going to copy the entire thing you can see it's highlighted and don't copy the last div you don't need to copy the last div because we added
it manually when we added the first div so go right here and simply paste those two elements there we go so no need to copy that additional div if you did copy it you're going to end up with this you're going to have an extra closing div there we go so now we can go ahead and expand this back and this is how it should look like right here at the end of the table and then at the end of the div you open a new div and add two buttons let's see how this looks
so inside of here now you should have pre previous and next and both of them should be disabled because we don't have enough data to trigger the pagination great so pagination is working now let's add sorting so it's going to be quite similar we are still working inside of data table itself first of all let's import everything as react here then let's add the Sorting State below the column definition and let's add get sorted row model right here then let's go ahead and add a use State here when where our component begins just above the
table constant so let me zoom out so you can see this sorting set sorting react use State we give it a type which we've imported from here and we Define it by an empty array at first so I'm going to zoom in now and then let's go ahead and let's go inside of use react table let's add on sorting change to be set sorting and and get sorted row model is going to be get sorted row model but execute the method and state is going to be an object with sorting inside there we go and
by now there is nothing that will change here but the table can now handle the Sorting functionality what we have to do now is we have to visit our columns so let's go inside of accounts columns right here and let's go ahead and do the following so I'm going to import from Lucid react Arrow up and more horizontal as they did here so Arrow up and more horizontal and we're going to find for example the email field and we're going to change the header so header right now is email if I change it to email
2 you can see that in here it changes to email 2 so we're going to turn this into a button which will trigger either the sort by descending or ascending so that's exactly what we can do here we can copy this header right here here like this and then replace it with this and let's see what we are missing uh looks like something is wrong here okay so what's wrong is that the columns is named columns. TS in order to use JavaScript inside or jsx more specifically we have to change from TS to TSX and
now we have a proper and working syntax but we are missing one closing uh bracket here there we go and we also need to import the button component from components UI button like this and I think we don't need more horizontal yes my apologies uh there we go so let me just add this Imports here and now if you go ahead and try and go inside of your data table let's refresh now to see if we still have these errors yes it seems like we have an error now inside of page. TSX because the uh
hot reload is probably confused because we just rename this from TS to TSX so let's try and first saving like this so save this and get an error again and then try and find columns again and save again and there we go now it works or or you can just shut down your app right so now you can see that we can well we don't have enough data so how about we do this let's go ahead and add another data here and let's change this to be a. example and let's go ahead and change some
numbers and some statuses so we can notice the different here okay uh so I have to use success here okay like this there we go so now if I try and click email there we go you can see how it's it can sort by email and that is exactly what I wanted us to do here so we just need one example of the header here and then we're going to copy it everywhere else where we need it right so if you're still getting that error for columns you can just shut down your app and uh
run it again right so let's go inside of data table and now let's add filtering or well searching right so we are going back inside of uh data table component itself so I'm going to close everything else I'm going to import column filters State uh state right below column def then I'm going to import get filtered row model just above the pagination and I'm going to add the input component just below the existing button component input then I'm going to copy this use state from the example and I'm going to add it just below our
existing uh sorting state so let me zoom out just a bit more so you can see so column filters set column filters react. UST state with column filter State and it has the exact same thing as one above an empty array as the default value and now let's go ahead and let's add on colums filter change we're going to get that uh where should we add it uh let's add it right below here on column filters change and then get filtered row model and then we have to modify the state to also have column filters
like this and then we already wrapped our component in a div so all we have to do is copy the highlighted code here which is a div and an input inside and we just have to add it above the rounded MD border there we go so let's try it out now if I refresh and if I try searching for a and there we go it filters by email so uh that's great but I do want to give it a slight modification here because our tables are going to be uh reusable and you will be able
to search by a lot of different things so I want to go ahead and do the following instead of hardcoding it to email in our reusable data table component let's introduce a new data data table prop here which I'm going to call filter key and I'm going to make it a type of string and then I can ex I can D structure it here in my props and then what I'm going to do is I'm very simply going to change this placeholder to be uh like this like an object open template literals and replace emails
with the filter key prop key being capital and the same thing is true for these two instances of email filter key ke and now we have to go back inside of accounts page. vsx and now we have an error here because filter key is missing so every time we use data table we are going to decide what the user should filter by when they search so in my case this can stay email right so now nothing should change if I CH search for at a there we go this is working great so let's see what
is next we just finished fil the ring I want to uh skip over the visibility we don't need that but I do want to get a row selection so for that we it seems like we are going to need the checkbox and the badge components or perhaps we don't need the badge I think badge came from uh the one uh this one maybe right let's go ahead and let's add checkbox we'll see if we have some stuff missing here so I'm going to go ahead and shut down my app and do bonex check scn UI
latest add checkbox there we go and let's do Bun Run Dev again I hope that's going to be enough so first things first it says to update columns definitions so let's do that let's go inside of app dashboard accounts columns and inside of here let's add the checkbox below our button so we need to add kind of a uh before the status right so we want to add that here in place of status this is where we want to keep our check boxes so we have to add it first in this array so we can
copy this entire thing here let's see uh can we just copy the entire thing yes we can just copy the entire thing here but I don't think we need um these two the sorting and the hiding right but let's copy for now so just before the status here paste that inside of here and let me expand if you don't want to copy from the documentation you can pause the screen and you can copy from here so what I want this to do is I want us to remove these two or maybe we don't need maybe
we can just keep them right so let's just copy it exactly as it is so let's see what that did so when I refresh now now we should have before status there we go the ability to can we do this there we go we can do it and you can select and unselect everything perfect now what I want is our data table to be aware of when we select something so when we select this I want a little text telling me how many items I've selected and I also want a button with delete option to
appear if I select a even a single item so let's go ahead and do that first so they're going to go ahead and teach us how to do the first thing which is making the data table aware of our selection so let's go back inside of components data table now and let's see if we have to add any Imports looks like we don't so we only need to add row selection State here it's highlighted you're going to see how it looks in a moment so we're going to add that below column filters here there we
go row selection and set row selection very simple and then we're going to go ahead and copy on row select change to be set row selection and we can add that just below get filtered row model so this line right here and then let's go ahead and let's add a row selection as the last item inside of our state so you can see that in here they have column visibility right but we don't need that uh I didn't find it useful for this project great so now what now the table is aware of our selections
and now we can do this for example uh show selected rows right so let's go ahead and let's do this let's copy this example and we can add it for example just here at the bottom where we have this div which will encapsulate right these two buttons for next and previous so we can add that here for example so it's going to use the get fil aled select row model so it's going to say two out of five rows selected let's see if that is working so zero out of two rows selected one out of
two two out of two one out of two zero out of two perfect works just fine so I believe that are all the options you can actually do for the data table right so the next thing they teach you is reusability so if you want to you can go ahead and explore that but we are done with the documentation and and now we have to do some work on our own here so let's go ahead and let's find our input here so what I want to do uh is I want to check if so open
like an if Clause here or what's the equivalent of this table. get filtered uh selected row model executed. rows. length so only if it's larger than zero meaning at least one item is selected only then go ahead and render another button component here which is going to say delete and let's go ahead and let's give it a size of small let's give it a variant of outline let's give it a class name of margin left AO font normal and text extra small and let's go ahead and let's add a trash icon from Lucid react here
so let me just move that to the top with these there we go let's go ahead and just give the trash icon some Styles so class name it's going to be size four and margin right of two there we go and it's going to tell us exactly how many rows we can delete so open parenthesis and then open curly brackets and inside right table get filtered rows select row model. rows. length like that there we go so let's go ahead and try this out when I select this there we go it says delete two if
I select just one it says delete one perfect so you just adapt that data table on your own without looking at the documentation so this can give you an idea of any other creative thing you might want to add to your data table so uh what I want to do now is I just want to wrap up the props here so we're going to go ahead and add a couple of more props so columns data filter key then we're going to have on delete and we're going to have disabled and now let's go ahead and
add the props for that so on delete is going to accept rows which are a type of row T data and an array and it will return back the void and you can import the row from T stack table so let me show you right here I've added a row from T stack react table all right so we have row and disabled is going to be a Boolean like that and it's going to be an optional Boolean there we go so let's see what are we going to use the on delete method for so on
delete for now can be used uh well let's let's not use it yet we're going to come back to this later because there is one slight thing that I want to do first you're going to see why I want to skip it now uh but what we can do is we can already disable this here so I want this delete button to be disabled if the table itself is marked as disabled so we can do that right uh and I think that should be enough for now perfect so now if we want to get rid
of the errors in our dashboard accounts page we also have to pass in on delete here and let's make it an empty Arrow method for now so now if you go ahead and if you select these two items there we go it says delete two but if you go ahead and pass in disabled then this button is is not available so we're going to use this while we are deleting while the mutation is pending right so the user doesn't fire multiple API requests if there is currently one of the same in progress so you can
remove the disabled for now or you can just mark it as false so you know that you have to change it later great so what we're going to do in the next chapter is we're going to uh enable and complete all the functionality we need inside of the data table which is the uh the confirmation model that's what I want do uh and then we're going to go ahead and create some more endpoints that we need and finally editing the accounts and this is going to be very useful for us so this was a lot
of work but the good thing about this is that uh we don't need to write this anymore so we just created the reusable data table component and we are going to learn the practices for the accounts page and we will easily copy and paste that into transactions and into categories great great job all right so in order to complete the data table component I first want to load actual data from my accounts API inside of here so if you remember inside of our app folder let me just zoom in inside of app dashboard accounts page.
THX right here we created a mock data like this so let's go ahead and slightly modify this so first of all I'm going to remove the type here for data right let's go ahead and leave this to be an error right it doesn't matter for now and let's remove the payment import so I just want you to have import columns from column and make sure you do this make sure you remove that payment uh uh type before you continue into doing what we are about to do so what I want to do now is I
want to give my columns inside a proper type which type well I specifically want the success format of this API route the get route so we have when I hover over get uh there is a possibility of me getting an error and an array of ID and name inside of the data object right so I specifically only want this so I could just copy that and paste it here right that would work but we don't want that we want to reuse it specifically from this endpoint and we can do that so let's remove inside of
accounts columns let's remove the export type payments and these two comment and let's go ahead and add the following stuff so I want us to add infer response type from hono and I want us to add client from lib hono and now what you can do here is write export type response type and we're going to use infer response type and inside of pointy brackets we're going to write type of client. api. accounts. getet let me zoom out just so you can see this in one line but we have an issue here so this seems
fine right but when I hover of a response type it tells me that it can either be an error or it can be this what I want is I just want one element I just want to get this the ID and the name in an object that's the only thing I want so how can I do that well here are some documentation from the hono 4.3.0 release let me scroll up so you can see so the release four days ago as of my recording 4.3.0 added the exact thing we need so check your package Json
just in case and ensure that you have hono higher than 4.3 uh at least 4.3.0 right so you're definitely going to have that if you're watching this tutorial so what we can do is we can use the following we can use status code type so we can specifically so this is the exact thing we just made right and then we can add a comma and specify the status and that way we can only get a success message so it's important that whenever you throw errors inside of your hono endpoints make sure that you add a
comma and an error code that way the hono typing knows all right this is only the type if we throw an error so now inside of my columns here what I can do is I can add a comma and add 200 and then when I hover I only get the success format let's try it again when I remove this then I can have either an error or the data so that's how it works so let's add 200 here but we are not done yet we now specify the data more specifically the first item in the
array that's is our type ID and name let's copy that and let's put it here instead there we go that is our type perfect now let's go ahead and let's remove all of these things which we don't need so we can remove uh this accessor key status and header status you can remove that and leave the email simply because we have this cool uh sorting here already done so we can uh reuse that right so instead of email this is going to be name because well that's the only field we have besides ID and let's
write name here we don't have to modify this at all and we're not going to have amount so let's leave that as it is great now that we have that let's go ahead inside of page. PSX right here uh and well we can leave this as it is what we're going to do instead is we are going to remove the mock data so we no longer need that let's go ahead and give this an empty array there we go so no errors are being thrown now but what I want to do now is I want
to use my hook which I believe we already created in feature accounts API use get accounts so we have the hook which will call all accounts so we can use that right here let's go ahead and find and write con accounts query to be use get accounts and let's add the import from features I'm going to move it along my new account features here and then inside of here let's define const accounts to be accounts query. data or an empty array like that and let's go ahead and let's add accounts inside so now if you
have some accounts inside of your database there we go I have two test accounts right here and if I try another account and click create this will automatically get added why does it automatically get added remember let's take a look at our use create account hook so inside of features accounts API use create account what we do on success is we invalidate the queries accounts which is this one use get accounts which means it will refetch every time you successfully create an account great so we resolved that let's head back inside of accounts page. TSX
right now what I want to do is I want to create a loading state so let's go ahead and do that now so I'm going to go ahead inside of my terminal and I'm going to add a uh component bun X shat CN UI latest and I'm going to add skeleton like that great BN run Dev again then we're going to write here if accounts query is loading in that case let's go ahead and let's just return this div exactly as it is so I want it to be the same I also want to copy
the card element I want that to be the same I also want to copy the card header element so that is also the same for me here but we don't have to add any classes here like that so let's just add card header card header now inside of here I'm going to go ahead and add a skeleton component from components UI skeleton like that let's self close this tag and let's give it a class name h8 and width of 48 like that and then inside of card content here we're going to add a div with
a class name of height 500 pixels full width Flex items Center and justify Center and inside we're going to add a loader to a lucid react icon so loader to from Lucid react and we're going to give it a class name size six text slate 300 and animate spin like that so if I go ahead and refresh my page now I think for a slight second there we go we have a nice loading animation here perfect so what I want to build now is the ability to delete or more specifically to bulk delete my uh
accounts right here so usually for delete actions we would use the delete API method but since this is going to be a bulk delete we need to pass the body and in the body I want to pass an array of IDs to remove because of that I want to use the post request I actually searched you know for some solutions about what other people do here and most common response was to Simply use the post request so I think it is an actual practice that's done in real world that when we have a bulk delete
it might be a better idea to use a post request so let's go inside of accounts right here let's close get and we have post here so let me just see uh I can indent that like this I will remove the semicolon and I will chain another post inside like this so inside of this post it's going to go to slash bu- delete it's going to use Clerk middleware and Z validator is going to be a bit different so usually we reuse some schema but this time we're going to write our own z. object which
means we need to import Zod specifically so Z from Zod let's go ahead and see oh my apologies I didn't add it in a string so it's going to be Z doob so we are valid with the Json body field all we need is IDs which is be which is going to be z. array of strings like that perfect and then finally we are going to create a controller here so let's get the context and inside of here first of all let's get out using get out C then let's get values using C request valid
Json if there is no out user ID with a question mark in that case we're going to return c. Json error unauthorized with a status of 401 remember that is very important and then inside of here let's go ahead and let's write cons data to be await database. delete accounts where and now we're going to chain multiple uh filters so let's add end which you can import from drizzle orm and let's also add in Array so those are the two we need so and we need both equals accounts user ID to match out user ID
so we are ensuring that the user can only delete their own accounts and the second in Array of what we just passed so accounts. ID need to match values. ID uh IDs which we pass right here in the Json and validate that here like that and let's go ahead and let's chain. returning and let's be specific and only write accounts. ID so that's the only thing we care about the return and let's return C.J Jon here data there we go so now that we have that uh what we can do is we can create the
features accounts API use bulk delete so let's do that again features accounts API and let's create a new hook use bulk delete. DS and what we can do to speed things up a bit for us is we can copy use create account so go ahead and copy the entire thing and paste it in use bulk delete because both of these are mutations so first of all I'm going to go ahead and modify my response type so I'm going to zoom out just slightly so you can see me right this in one line so in order
to access this we can't write bu- delete right we need to use the array method of accessing something so there we go bulk delete and then we have to chain another post so we can't continue here with do Post it's not going to work we need to chain it like this post like that and we need to copy this and do the same thing here like that so make sure you have a request type and make sure you have uh sorry make sure you have a response type like this and make sure you have a
request type so our request type only has IDs right our request response type can be either an error or returning the deleted IDs in the array great now let's go ahead and modify this method right here and let's actually rename the query here so use bulk delete accounts now let's change this mutation function which now accepts IDs as a string to not directly go to the Post but instead it needs to go again to bulk delete and since you are calling this as a function you can now chain. poost if you want to or you
can use the same method as above post like this and then inside you would pass in Json again there we go so let me zoom out so you can see API accounts bulk delete post and you're passing in the Json exactly as it expect so now in the on success the message would be accounts deleted and the invalidate queries here would will still do accounts but I'm going to add a Todo also invalidate summary so we don't have summary yet but I don't want to forget this and later I'm going to search through my to-dos
to ensure that everything is working fine and on error is going to say failed to delete accounts there we go so we are ready to try this out now so let's go ahead and let's go back inside of our page. TSX here in dashboard accounts page and now what I'm going to do is I'm going to add the delete accounts from use bulk delete accounts and I'm going to go ahead and I'm going to move this import alongside these two right here so so now we have the delete accounts query and what I want to
do is I want to add a little const is disabled here to be the following if accounts query is loading or if delete accounts do is pending so one of the two right now let's go ahead and let's find our data table here and let's do the following let's change the disabled from hardcoded false to is disable and let's go ahead and modify the on delete now here to do the following it's going to accept row and then we can get IDs using row map r r. original. ID and then we can call delete accounts.
mutate and we can pass in the IDS like this but this is not going to work now because we are missing uh one thing here which is to actually we call the on delete inside of data table so let's go inside of components data table so we are not calling the on delete prop anywhere in our code yet so how about we change that go ahead and find the button where you have the trash icon and the delete text and add an on click here on click is very simply going to do on delete table
get filtered select row model executed and pass in the rows and then do table reset row selection let's go ahead and try it out now so I'm going to go ahead and refresh the entire thing I'm going to select everything and I'm going to delete it and there we go accounts are deleted when I refresh they are nowhere to be seen if I go to/ API accounts they are completely empty great so if I add first account it's created I'm going to add a second account and I'm going to add a third account like that
let's go ahead and select just this one and there we go accounts deleted perfect so this is great but since this bulk delete action is quite powerful it can delete a lot of stuff at once I want to add a confirmation model so I want to do that in a way that I can easily reuse it as a hook everywhere and I found out a method on how to do that so let's go ahead and develop that so first of all I want to give credit where credit is due I found that on this medium
blog post right here so thank you Isaac uh I also think that there is another name here worth mentioning uh Roy so thank you both for this great blog post so I'm going to go ahead and Implement what they talked about inside of that blog post here here so what I'm going to do is I'm going to create a reusable common hooks component and inside of here I'm going to create use- confirm dot uh TSX so TSX is important not just TS TSX let's go ahead and let's import use state from react let's import our
button from components UI button let's import everything we need from components UI dialogue which we don't have so let's quickly head inside of the terminal here and do banex shaty Nui and let's add the dialogue component there we go so I'm going to go ahead and import everything we need we need the dialogue the dialogue content the dialogue description the footer the header and the title so those are the items that we need and let's export cons use confirm right here so these are the props we're going to have title which is a string we're
going to have a message which is a string as well and what this is going to return is going to be the following it's going to be a ma uh method which returns jsx do element so that's the first thing and then another one which returns promise and unknown that's going to be our function right here so right now we get an error because we don't return that but we are going to do that later so let's go ahead and do the following let's go ahead and create a very simple use State here which is
by default going to be null but the type of this state is going to be an object which has resolve which is a method which has a value of Boolean and it returns a void or outside of the object it can be null so that's what is going to be right and inside of const we write promise and set promise as the second argument now let's go ahead and let's write const on confirm U my apologies just confirm let's make this an arrow function which calls new promise inside of the promise it calls resolve and
reject as props for the inner method which very simply is going to call set promise open an object and write resolve inside side like this then we're going to have handle close which is very simply going to set promise to know kind of like resetting the promise and inside of here we're going to have handle confirm so con handle confirm which is going to call promise question mark resolve and pass in the bullion true and call handle close to reset this hope and let's have const handle can it's going to work similarly to close but
it's going to explicitly resolve the promise with a false Boolean like this great and finally let's create con confirmation it's important that you call this confirmation dialogue method with a Capital C because it's going to be used as an element so immediately return a dialogue it's going to be open if promise is not null so every time we call handle close the dialogue will close inside of the dialogue add dialog content a dialog header and a dialog title and inside of the title you can simply render the title and below the title add the dialogue
description and render the message and now still inside of dialogue content but outside inside of dialog header add the dialog footer component give it a class name of padding top of two inside of dialog footer add a button component which we already have imported and write cancel let's go ahead and give this an on click on handle cancel and a variant of outline go ahead and copy and paste this button inside of the footer still and this one will just have handle confirm without any specific specific variant and it's going to say confirm and now
to fix our typescript error we have the return an array first confirm dialogue second confirm there we go we have a hook which has no typescript errors and let me just zoom out so you can hopefully see this in one line again like that and now let's go ahead and let's use this here so I'm going to go back inside of my data table and you're going to see how cool this component is well more specifically this Hook is so I'm going to go ahead and I'm going to call use confirm from hooks use confirm
like this and now let's go ahead inside of the data table method and at the top here let's add use confirm and for the title we're going to say are you sure and for the description we're going to write you are about to perform a b delete and now from here let's extract the confirm dialogue you can name this whatever you want right so this is a named constant and the second argument is our confirm method so this is what we're going to do first let's go ahead and first let's render the confirm dialogue anywhere
in our code it's important that it's somewhere right in this component and then what we're going to do is we're going to find our delete method here so we have onclick on the lead here let's change this to be an asynchronous method and inside of here we're going to get com okay to be await confirm are you kind of seeing what's going to happen now so inside of here if we confirm we're going to pass in true and then this constant right here okay is going to become true but if we pass in false it's
going to become false false so this is a very cool use of promises async and await and now what we can do is very simply if okay only then remove and call on delete let's try it out let's see if we made any mistakes here so make sure you have some accounts here make sure you have the app running refresh let me select everything and click delete and there we go if I click cancel nothing happens but if I go ahead and confirm they are deleted amazing great great job what we are going to do
next is we are going to add two more end points and that is so that we can open this test in a same drawer that we are going to reuse and the form so we need an endpoint to fetch by ID and we also need an endpoint to patch or update the name of the account great great job so now that we have the ability to bulk delete our accounts I want to wrap up the accounts entity by giving us the option to edit them and to also well open them exactly like this as we
would a new account but instead I want it prefilled with the data we have so let's go ahead inside of our app folder API and accounts API inside of here we have a get route and two post routes so just below this first get route I want to go ahead and I want to chain another get route here and this one is going to be slash colum ID like this and inside of here we're going to add a z validator to validate the param which is going to be z. object specifically it's going to be
a string but it's going to be optional and you're going to see why later don't worry we are still going to validate it here so it's also going to have the clerk middleware and then finally let's get our controller here so just make sure that adding this get route didn't mess up the chaining below so just ensure that this still has no errors inside of here we're going to do what we always do so let's go ahead and get the out using get out C and let's also the structure ID from C request valid and
we are specifically looking for param if there is no ID we can return c. Json and we can throw an error here with bad request and let's write 400 or we can be more specific and write missing ID and now let's write if there is out question mark user ID in that case we can copy and paste this error but with a different error message so this one will say unauthorized and a status code will be 401 and now finally we can go ahead and we can D structure data immediately from this query await database
do select so we are only going to get account from accounts do ID and name from accounts. name so remember this accounts variable is the schema in case you forgot about that and all right so we chose what we select now from where well from the accounts schema but where specifically and now we're going to go ahead and use our chaining here first to ensure that what user is trying to fetch is truly dares by comparing the user ID and next we are ensuring that we are only fetching the uh user account that was passed
through the pams which Pam the ID Pam right here so this should take care of that we don't need returning because this is Select right so it's automatically doing that but what we are going to check is if there is no data right so if we couldn't get any anything in the array in that case we can return C Json error not found with a 404 and finally let's return the data there we go so now if you hover over your get here you can see uh all right so looks like it can only load
errors for now so we're going to see a better response in the hook let's go ahead and let's create the hook now that we have a working API and we can already try out the API so go ahead and do the following uh make sure that you have like at least one or two accounts created and go inside of localhost 3000 API accounts and now copy the ID and change your URL to/ API slac account slid and there we go so this is that exact account and if I change it to this ID then it's
test two if I slightly modify the ID I get the error not found perfect now let's go ahead and turn that into a reusable hook here so we're going to go ahead and do the following we're going to go inside of our features accounts API and let's copy use get accounts and let's rename it to use get account so individual make sure this is accounts and this is just a singular account let's rename the hook to use get account as well and this one will have an optional ID which is a string this entire query
is only going to be fetched if we actually have the ID and the query key will be account and an object with ID and now instead of calling accounts. get we going to call Accounts poen id. getet but but we also have to pass in a Pam which just happens to be this ID so this error will say failed to fetch individual account and this can stay exactly the same so the reason I made it so that uh inside of accounts ID is optional is simply because it's easier to get the types right right so
later you can see when we use this us get account there's simply no way for us to ensure that the the ID always exists so we will know because of our code logic we know that almost impossible will it be for it to happen that ID is passed as undefined and even if if it is this query won't even run because of this enabled property right here but it's just easier for the types right so let's go ahead and leave it like that and now what I want to do is I want to go ahead
and I want to create uh a tand controller here so instead of features accounts hooks let's create use openen account. DS so I don't want to call it use edit account because we're going to have that as a name here so let's call this use open account and we can copy everything from use new account and paste it here let's rename this state to open account State let's rename the hook to use open account let's extend the state to accept an optional ID which is a string and let's modify the onop to require an ID
to be passed if we are opening this uh drawer right so then we have to modify this as well ID by default will be undefined and on open we'll have that ID and besides setting is open to true it will also assign the ID and and on close we'll bring back the ID to undefined like that there we go and now let's go ahead and do the following let's go inside of components and let's copy the new account sheet and let's rename this to edit account there we go make sure you are inside of edit
account and inside of here we're going to rename this to edit account as well like that and let's change this for and let's remove use new account completely and instead we're going to use use open account like that so add use open account I'm going to add the features accounts like this and then let's see what else we are going to need so this can stay the same use create account it doesn't matter what matters is that we have the ability to fetch our account here so this is what I'm going to do I'm going
to go ahead here and I'm going to add con account query is going to be use get account so make sure it's not accounts just the single account and let me go ahead and change this to Features accounts and let me move it up with all the other features here so inside of the the account query we're going to pass in use get account and remember we have to pass in the ID and we can now the structure the ID from here as well because we just Chang this to this use open account hook where
we defined the ID and we can now pass that inside of here like that perfect so we now have the account query now so let's go ahead and do this let's define const default values let's make that if we have account query. dat in that case go ahead and make it name account query. dat. name otherwise make it name an empty string and now go ahead and simply pass in the default values here like this there we go so I think this already might be enough for us uh to attempt and do something so we
we need to add this inside of our providers sheet provider here so I'm going to remove this import we don't need it and let's import edit account sheet from features accounts components edit account and simply add it here like that there we go and now we have to find a way to open it so for that I want to go back inside of The Columns of my account page so let's go inside of app inside of dashboard accounts columns right here and we now have the uh checkbox for selection we have the uh name field
right so inside of this columns let's add the last field here which is going to have an ID of actions and cell is going to be row and it's going to return the actions component which at the moment does not exist so let's go ahead and do the following let's in order to create it let's go inside of the terminal and let's shut down the app and let's write bonex Shi at latest and let's add drop-down menu so that's the component that we need for this so Bun Run Dev again now we're going to go
inside of the accounts on the same level as columns and we're going to write actions. vsx let's mark this as use client and let's export const actions and let's return a div saying actions let's quickly just uh give it some props ID of a string and let's D structure the same props here now let's go back inside of columns and let's add the actions from dot SL actions like that and let's pass in the ID to be row. original. ID there we go let's go ahead and let's check out our Local Host 3000 more specifically
the actions page uh the accounts page right here let's see there we go so now we have this third column here which just simply says actions we're not going to change that to be a drop- down menu that's why we've added that so let's go ahead and import everything we need from components UI dropdown menu here we're going to need the drop down menu itself the drop down menu content the item and we going to need the drop- down menu trigger like this great and now let's go ahead and change this entire thing to be
a fragment let's change this to be a dropdown menu let's add a drop- down menu trigger let's give it a property as child so it looks like it's child which is going to be a button component make sure you add the import for the button as I did here and it's going to use an icon more horizontal from Lucid react so ensure that you have have this as well and this button here is going to have a variant of ghost and a class name size eight and padding off zero like this just below the trigger
add a drop- down menu content with an align of end inside of here add a drop- down menu item which is going to say edit before the paragraph edit we're going to have the icon edit so I import icon again from the see the react and I'm going to give the edit a class name of size four and margin right of two and for the drop down menu item I'm going to give it a disabled off false and on click also just an empty void for now let's go ahead and check this out there we
go we can now click and and we can click edit right here so let's go ahead and let's go back ins setad of the actions here and let's give this more horizontal a class name size 4 just so it doesn't look so enormous there we go so now it looks better and now what I want is that when we click on edit is that we call a hook just like this but our new use open account hook so let's add import use open open account from features account hooks use open account I'm going to separate
the features as always so inside of here I'm going to go ahead and I'm going to call this use open account and this will be on open we can actually just the structure on open very simply and inside of here you're going to call on open and you're going to pass in the ID as the prop so what's the flow here on open is going to very simply let's go ahead and open use open account so on open is going to set the ID and is open to True is open in return is going to
make the edit account sheet component rendered and then the moment it renders is it's going to distract from this ID and it's going to enable this query which is going to fetch by ID and then using that it's going to fill the default values right here so let's see if that is working so I'm going to go ahead and refresh the entire thing now I'm going to press here and click edit and there we go so something is opening but it looks like it's not filling out my data that's fine we're going to go ahead
back inside of the edit account sheet anyway now and let's go ahead and continue developing here so what I want us to do is I want us uh to destructure the Loading or we can just do this so how about we just write const is loading to be account query is loading let's just keep it simple and then inside of here we are conditionally going to render the account form right so if we are loading we're going to render our usual loader like this so this is going to be a div with a class name
of absolute in set zero Flex item Center and justify Center and it's going to use a loader to icon from Lucid react with a size 4 text muted for ground and animate spin make sure to import loader to from Lucid react so I'm just going to add that to the top here in the edit account sheet component and let's add the alternative here which will be our account form so let's see if this conditional rendering automatically enables the default values or if we have a bug somewhere else so if I click edit there we go
it says test if I click here it says test to perfect so what we have to modify now is that this account form now accepts ID as well and now what I have to do is I'm noticing a couple of bugs here so first of all it's allow it's showing it changed this save changes and it changed delete account but our uh title and description are still showing new account instead of edit so let's go ahead and change that here and that's actually right here so edit account and in the description here let's pass in
edit an existing account there we go so now it makes more sense when we click add new it's completely blank but when I click on some uh some of these it opens up this perfect so now I want to go ahead and I want to create a patch function so before we can build the patch route we first have to visit app API rout route. TS and remember inside of here we only added two handlers forget and for post so let's go ahead and enable it for patch now as well because that's the one we're
going to need if you don't add this you're going to get 405 methods so let's go inside of accounts now whoops I opened it twice and let's go ahead and do the following so at the bottom here I'm going to change patch and it's going to do the same thing it's going to go to slash ID let's me go ahead and collapse this so ID we're going to have the clerk middleware and a z validator here which is going to validate the perm which is going to be an object ID Z is going to be
a optional string like this and now let's go ahead and let's pass in our asynchronous controller here so first things first let's get the out get out context then let's destructure the ID to be C request valid Ram but we also need uh the Json object here so remember we can chain validators as well Z validator in this one we're going to check for Json and what we are going to check is insert account schema. pick name true so the same thing that we actually did in here Z validator Json insert account schema pick name
but in this case we are chaining two validators first to validate the ID we are trying to patch and the second one the actual Json object great so now what we can do is we can also get the values from C request valid Json like that if there is no ID let's return C do Json error missing ID with a 400 if there is no out user ID with a a question mark between them return C dojon error unauthorized with a status of 401 and then finally let's find and edit our account so con destructure
the single item await database. update accounts do set values where let's use end to ensure that this is this users account and let's also ensure that it's the account that was passed in as the ID and let's add returning as well there we we go if there is no data returned that means that we could not found whoops not found we could not have found that account otherwise we are returning the data itself there we go so now we have a working uh edit uh request so what we have to do is we have to
create use edit account and we can thankfully copy some stuff here so features API we can copy the same thing as create account just rename it use edit account like this and let me go ahead and expand this as much as I can so we are changing to not Target just the accounts but we are targeting well not even post right we are targeting accounts ID and then patch so this is how it looks in one line right so let me copy this and replace this and the Json is still the same right we are
accepting only the name let's rename this to use edit account like this and let's change the Json so there is no error here because the Json is the same for creating the accounts and for our ID patch Json so it's the same thing right uh let's see is it the same thing or did I make a oh well it's not the same thing because it accepts Jason yes that's true Json is correct but it also needs to accept a PM ID where do we get the ID from well from use edit account we will extend
it by adding an optional ID here as well there we go so ensure that you are passing uh the param ID in the Json of the patch function here instead of account created is going to be account updated we are going to update accounts but we are primarily going to update account with that ID as the key I'm going to add to-do invalidate summary and transactions later in the future and this will be failed to edit a account now that we have this hook we can head back inside of our edit account sheet component so
let me go ahead and add this import use edit account I'm going to change this to be features accounts like this and then we're going to go ahead and add some more mutations here so I'm going to add const edit mutation to be used edit account and I'm going to pass in the ID as well and then in here I'm going to go ahead and create const is pending to be edit mutation. pending is pending for now it's only going to be this so that's why I added a space because later we're going to have
another one here so is pending here and let's change the onsubmit here so we are no longer going to have this use create account we can delete the mutation and use create account and we can remove the import use create account no need for that so that's going to break here but now we have the edit mutation so let's add the edit mutation with the values that's going to work just fine uh great and I think that this shouldn't all right so instead of here is pending is not no longer going to be controlled directly
by mutation is pending but we're going to use that constant is pending which for now holds the exact same thing but later it's going to be extended with another mutation which is going to the delete mutation let's try this out my account is named test I'm going to click edit and I'm going to call this edited let's click save changes and there we go it is edited let's try again perfect let's refresh to ensure the data is preserved and it is perfect I can now save this right here great what I want to do now
is I want to create a separate delete endpoint right so we have the bulk delete let's go ahead and let's quickly create the individual delete endpoint so the same way we needed to enable patch requests we also need to do that for delete requests so let's copy and paste this and add delete there we go inside of app API rout route. vs to speed things up we can copy the entire patch ID method because it's going to be very similar so I'm going to copy the entire thing and I'm going to chain it together like
this so I have twice patch and I'm going to change this to delete now and I'm going to remove the validator for Json because it's not needed and no I no longer need the values right but these rules can stay the same if there's no ID I don't know what to delete if there's no user you are unauthorized to do this and now instead of update it's going to be delete accounts and there's not going to be the set chain here the where rule will stay the same and for returning we can just say which
uh ID we remove no need for the entire data and here is the same rule not found 404 that is it that is our delete route so just make sure you've changed it with do delete make sure that your existing patch which you've copied is intact so you didn't accidentally cut it and make sure you've removed the validator for Json we don't need it for the delete requests and of course course ensure you've enabled the delete requests so now what we can do is we can go ahead and copy the use edit account features accounts
API use edit account copy and paste it and change it to use delete account rename this to use delete account and all we're going to have to change is these patch methods here so instead of patch these two are going to be delete and in fact we're not not even going to have the request type for this one so you can remove the request type entirely and in place of it you're simply going to remove it and remove the trailing comma as well go ahead and replace patch with delete in the mutation function as well
and simply rely on the Pam and no need to add Json in the props here either and we're going to write a account deleted here failed to delete account we're going to update account and the ID the accounts we're going to update the summary and transactions as well later in the future when we have those things great let's go ahead and let's do the same thing now inside of edit account go ahead and copy and paste this use delete account from use delete account like this so just make sure you've added that go ahead and
add it here so edit mutation comes delete mutation from use delete account pass in the ID and now we can chain these two together so either edit mutation or delete mutation is pending we're going to disable the form right so the user cannot spam with API requests there we go and let's go ahead now and let's attach that right here so we need to pass in the on delete method which is optional so far so in here it can be uh the delete mutation mutate like this I believe we are not passing anything inside like
that let's just ensure inside of the account form that we are actually using on delete so we're using it it's optional it's right here and it should be used let's see right here in handle delete we have a uh safety check here and we are using it inside of this button which is only enabled if we pass in the ID so make sure you've passed in the ID so if I try this now for example I'm going to open the test two and if I click delete account there we go it says account deleted perfect
and it's actually deleted but there are a couple couple of things we could improve here first of all well it could close the drawer right but it could also uh go ahead and do the confirmation for us so let's go ahead and do that I'm going to go ahead I'm going to import our very cool use confirm Hook from hooks use confirm and then I'm going to go ahead and Define it here use confirm for the title we're going to say are you sure as well as our previous one and we're going to write you
are about to delete this transaction let's extract the confirm dialogue and the confirm method let's make sure that we are rendering the confirm dialogue somewhere so what we can do is we can wrap the entire sheet component in a fragment indented and simply add the confirm dialogue here it is a self closing tag and then let's define our on delete method here let's make sure it is an asynchronous method const okay is going to be a wait confirm so we know whether the user pressed yes or no and if we are okay we are going
to call delete mutation do mutate but we for the variables we're going to pass undefined because there is there are none and then we're going to open the options object and write on on success and in here on close because that's what we wanted to do right and then let's go ahead and use this on theit right here instead there we go so now we should have a much better experience with deleting accounts so if I go into edit and if I click delete are you sure you are about to delete this transaction cancel does
nothing confirm closes it and deletes it perfect so one more thing I want to do is add a shorthand delete here alongside edit so for that we go back inside of our columns inside of app accounts columns right here inside of actions more specifically so let's go ahead uh and let's I believe we have to import uh the handle the we have to Define con handle delete right it is an asynchronous method and we're going to do the following let's add cons delete mutation to be use delete account and pass in the ID so make
sure you've imported this from the features like this alongside use open account and let's also add our use confirm from hooks from shared hooks right so we have the delete mutation here but we also need to add our con use confirm are you sure and description you are about to delete this transaction let's get the confirm dialogue and the confirm method let's render the confirm dialogue right here so we already prepared the fragment and we're going to call const okay await confirm if okay delete mutation. mutate like that and let's go ahead and use handle
delete now we can copy and paste the drop down menu item and this one will'll simply call the handle delete it is going to be disabled if the delete mutation is pending and the edit is also going to be closed if delete mutation is pending and instead of edit this will say delete and we're going to use the trash icon from Lucid react like this there we go and I have a DOT here but I don't have it in my edit account sheet so let me just add the dot here all right now I am
at peace let's try it out now so this should be it for our account entity I believe delete cancel does nothing delete confirm there we go perfect so we just enabled a bunch of things for our accounts we can edit them we can load them we can individually delete them but we can also bulk delete them amazing great great job you're going to see now how easier it's going to be for us to create the categories entity which is practically going to be the same thing as accounts uh variables wise and sizewise right obviously it's
going to serve a different purpose but for code it's going to be almost exactly the same so you're going to see how much these components that we've created and this methods that we've established are going to speed up this process so it's going to take I want to say three times less than it took us to complete the accounts page nevertheless great great job so now that we've wrapped up our accounts entity and we can create new accounts we can individually delete a single account we can go ahead and bulk delete accounts and of course
we can always edit a specific account let's see how fast we can build the equivalent categories page because it's going to be very very simple and we have a lot of components finished now so first of all we start in the schema so let's go inside of database schema. DS and we can copy and paste the existing accounts but I'm just going to do it below the insert accounts schema and I'm going to call this categories and let's of course ensure that our PG table is also named categories it's also going to have an ID
it's going to have a PL ID is going to have a name and a user ID so exact same Fields And if you're wondering what does played ID do in both of these well we're going to come to that later when we connect to an external service which will be able to read from our bank accounts so for now we're not using that but later we will so we are already preparing for that great so we have that now let's go inside of our terminal let's shut down the app and let's run Bun Run database
generate our script to generate SQL migrations and there we go we now have a migration script here so let's go ahead and do BN run database migrate and now this will push it to Neon DB let's go ahead and visit our database studio and let's just confirm that everything is fine so inside of here I should now have categories and I do and they have ID blade ID name and user ID perfect so I'm going to go ahead and do Bun Run Dev on my app now now and let's go ahead and let's start so
what we can do now is we can easily copy and paste our route right so inside of app API inside of Route folder we have accounts let's copy and paste that and let's rename this to be categories like this and let's go ahead inside of route. DS and let's import categories from SL categories like that and now we're going to go ahead and we're going to chain slash categories right here and make sure they use the categories model not model but route right there we go so now if we go ahead and go inside of
localhost 3000 API categories we are going to load accounts right because we just copied and pasted uh the route for accounts so regardless because we mapped this under categories inside of the code we are still working with accounts and insert account schema so now let's go one by one and let's modify this so it works properly so I'm going to go ahead and do the easy thing I'm going to remove the accounts import because that's going to give us errors everywhere where we need to replace them with categories right this is kind of an easy
way of us uh of doing this so let's import categories instead and the same thing with insert account schema well we are missing the insert categor schema right so let's go back inside of our schema scha file let's copy this and let's paste it here and this is going to use the categories and it's going to be called insert category schema like that perfect so let's go back inside of categories and let's import insert category schema there we go so we have two unused Imports and a bunch of Errors we've got to fix let's start
with the first get route so inside of here what we are going to do is we are going to select the same thing I ID and name from categories from the categories table and we are going to ensure that it's only the person that created this category that can see these categories right because users can have personal categories we don't want to share them globally with other users now let's go ahead and go to the other route so right now I think uh I'm not sure if this is going to work now because we have
errors but if I refresh Local Host let's see there we go so we still have errors we can't try this out yet okay but we know that our guest route has no errors but this get does have errors I believe there we go so let's go instead of the get individual ID so the validator for the Pam stays the same right the error codes are the same but inside of here we are no longer working with accounts but instead we are working with categories like this there we go so we resolved our get ID route
now let's get for the creating the route so this time we are using insert category schema right here and I believe the rule can stay the same we are only using the name there we go and we are not inserting into accounts but instead into categories and we are still using the same create ID method here there we go that's our create request now for the bul delete same thing everything is exactly the same but instead of removing accounts and instead of doing filtering with accounts we are all changing that to categories there we go
we just saved a bunch of time from writing this together from writing this again so in the pass request we do the same thing instead of insert account schema it is insert category schema like this and below here we are using categories like that and that resolves our patch request for the categories and I think this is the last one the delete by ID so everything above stays the same but we are manipulating the categories not the account and there we go all of a sudden no errors inside of my application so I recommend that
you expand all of this and just take a pick that nothing is read and there are no errors there we go this is fine and you can also do a search of accounts just to confirm that you by accident don't use account or accounts anywhere in this code because there shouldn't be any so right now as you can see my data here is completely empty because it's actually loading the categories perfect so now let's go ahead back inside of Local Host 3000 and what we have to do now is we have to create the page
for categories so we can also copy and paste our existing accounts page so let's copy and paste that and let's rename these categories so just by doing that nothing should change except that when I click on categories here I will no longer get a 404 instead it's going to load the accounts page because we just copied and pasted it again if you're still getting a 404 here make sure that your navigation component has the proper hre to go to slash categories because that is the name of the folder we just created categories great let's go
inside of categories page. vsx and let's change the simple things first so instead of it being accounts page it's going to be categories page like that that's the first change then what I want to do is I want to change the title instead of accounts page it's categories there we go so it's already looking better what we have to do now is we have to create all of these equivalent hooks but to manipulate uh well categories right so let's go ahead and do that let's see if we can do it just by slight modification of
the ex existing ones so let's go inside of features and let's copy the entire accounts and paste it here and let's rename it categories now first things first let's go ahead and let's resolve our hooks here so we're going to have use new account rename to use new category let's go inside of that instead of new account State this is going to be new category new category without a typo and this can stay exactly as it is then let's rename this to use open category let's go inside and let's change this from open account to
open category again a typo on my side and the logic can stay exactly the same great we have the C hooks resolved now let's go inside of the API let's start with a simple use get accounts and let's rename it the use get categories just ensure that you're doing this changes in the categories folder don't accidentally rename your existing accounts right so we copied and pasted the accounts and renamed it into categories so just make sure you're modifying that so we just renamed this to use get categories and now let's change this to use get
categories as well let's go ahead and modify the query key to the categories and now we have to change the response do not call Accounts but instead call categories and failed to fetch categories like that there we go use C use get categories is now working let's go inside of the individual use get account and rename it to use get category right here let's rename it to use get category the ID stays the same the query key is category individual and an ID and the API is not accounts but categories and targeting a single ID
as since we copied and paste the entire API everything is the same the types are exactly the same let's go ahead and resolve the error so account should be failed to fetch category like this there we go now that we have that let's go ahead and let's do use edit category like this and inside of here what we are going to do is again we're going to replace instances of accounts with categories because everything else stays exactly the same the patch request the Json everything is the same and let's change the categories here as well
categories like that and the types are the same we accept the ID PRM and the Json instead of uh account updated it's going to be category updated and we are going to uh refresh the individual category and we are going to refresh the categories key like that and failed to edit category as the error and this to do can stay the same because later uh we're going to have to refresh that as well now let's go ahead and let's modify the use delete account to be used delete category and let me just close everything uh
besides this all right so use the leete category let's rename this to use delete category let's go ahead and replace the account instances with categories same thing in the mutation function we are working with the individual category category deleted and categories and failed to delete an individual category here there we go now let's go ahead and let's modify use create to be use create category use create category change the accounts instances to categories and same thing for the mutation function fun everything else is exactly the same because they're so similar so category has been created
and we are refreshing the categories here and let's write fail to create category like this there we go and last one use bulk delete uh well use bulk delete can actually stay the same um let's be specific and let's call it use bulk delete categories so I'm going to go ahead and find the previous one in the accounts and I'm going to add it as well because I want to be explicit about the naming so this is the full name use bulk delete categories there we go so use bulk delete categories instead of accounts and
again instead of accounts it is categories using the bul delete method inside of here as well so categories categories deleted and we are refreshing the categories query key and failed to delete categories and I believe that is it so we have everything we need for the API now let's go ahead and let's modify our components here which again are going to be very very similar let's go inside of the account form and let's rename it to category form inside of category form let's go ahead and do the easy thing and that is to remove the
insert account schema and instead import insert category schema so we have the errors telling us exactly what we have to change there we go now this can stay the same form values are the same the props are exactly the same and let's rename this to be category form the form stays the same handle submit handle delete and now we're just going to have to slightly modify the placeholder because the name is still the only field that exists when creating a category so this now instead of uh cash bank and credit card can be for example
food travel Etc and let's go ahead and modify the create account to be create category and below that delete category there we go now let's go ahead and let's go inside of the new account sheet and let's rename it to New category like that again let's do the easy thing and that is well first of all Let's do let's do uh this let's remove the schema and let's write insert category schema so we get this easy error here all right so only this place and now what we have to do let's leave these uh three
Imports for last let's go ahead and modify the labels first so new category sheet like that let's leave this as it is and let's write new category like that and let's change the description create a new category to organize your transactions like this we can leave this as it is and now we can start replacing our components and hooks so first of all I want to go ahead and I want to remove the account form and instead I'm going to import category form from do/ category form or features categories components category form form like that
let's just see if there are any errors there areen so this should be in one line like this so now we have category form and we have this error if I replace it there should be no errors at all there we go it matches perfectly now let's remove the Ed new account and instead let's import use new category from hooks use new category but I'm going to change that to Features categories like this there we go let's find the error instead of use new account it's used new category and nothing else changes and let's do
the last thing for use create account instead let's import use create category from API which I am again going to change to use the add features sign you can leave it like this if you want to I just like to be consistent with my imports let's find the error and there we go use create category that's it that is our new category sheet now let's go ahead and do the last one which is edit account sheet and let's rename it to edit category let's go inside of here and let's do the same thing so easy
things first how about remove the insert account schema in favor of insert category schema and let's replace that with the error here now let's modify the edit account sheet to be edit category like this and now we're going to go ahead and modify a couple of things so first of all let's find some text here so edit C category edit an existing category let's see if there are any other instances there are not uh but I think that in this use confirm I kept writing delete transaction when it's actually delete account right so let's go
ahead and fix it here first uh you're about to delete this category not the transaction it looks like I made a typ great and now we're going to go ahead and delete things one by one so first of all it's the account form we are no longer using that so we can remove that and instead we can import our category form from do/ category form or from features uh categories components category form let's copy the category form and instead let's use it in place of the account form here and nothing should change because it's exactly
the same let's remove the use get account in favor of use get category individual category ROM features categories like this let's find where we have the error and there it is while we are here let's also modify to not use account query but instead category query like this and then let's change is loading to use category query is loading and below here let's modify category query data category query data name and that is it great now let's go ahead and remove uh the use edit account sorry use open account in favor of use open category
and I'm just going to copy this and replace that here let's find where we use use open account and this time it's use open category and nothing else requires a change let's remove the use edit account now in favor of use edit category uh do I have use edit category perhaps I don't let's go ahead and see if I made a mistake here uh use edit category I did not rename it so inside of my features folder inside of uh categories I do have used edit category but I forgot to rename it so use edit
category like that let's try this again use edit category there we go we can now safely import that as well so we are doing this inside of edit category sheet right uh let's see if there are still so features and this should be SL categories like this let's see where we have to use that so it's here and edit mutation logic can stay the same and let's do the last one which is use delete account we no longer have that instead we're going to have use delete category let's go ahead and write features categories API
use delete category like that let's find the error and let's use it here there we go I believe we handled all the logic so to make sure this is what I want to do I want to go inside of features here I want to go inside of categories and what I'm going to do is right click and I'm going to write find in folder and I'm going to search for account there we go so I have no instances of account and I have no instances of accounts but if I search for category I do categories
I do perfect so just make sure that you have no instance of account or accounts in your categories that way you know that you properly replaced and renamed everything great so we now have a fully working features here what we have to do now is we have to go inside of the providers sheet provider and go ahead and copy and paste these two add a little space so you separate features and these two next ones are going to be from features categories and it's going to be new category of okay maybe it's easier for us
to do this right import new category like that and below that import edit category from features categories components great and now we're going to go ahead and write new category and below that edit category there we go perfect and now that we have that let's go inside of the app dashboard let's go inside of categories and let's go inside of page. TSX and let's slowly resolve solve this as well so we changed the labels right so that's fine now let's go ahead and change the individual uh hooks here so we're not going to use use
new account instead we're going to use use new category from features categories hooks let's find the error and let's replace it let's also replace the name of new account and everywhere where we use it to New category like this so new category. open here and I renamed this to new category and that's it let's go ahead and next use bulk delete accounts we remove that in favor of use bulk delete categories and use a delete categories here let's fix the error here and let's fix the mutation here there we go everything matches exactly and let's
remove use get accounts in favor of use get categories so multiple make sure it's mul multiple of them let's replace the use get categories and let's change this to not be accounts query but categories query and in here we're going to use the categories query data and these are now going to be categories is disabled is going to look for the categories query instead of the accounts query and same thing for this if Clause here and then copy and paste this categories constant and we're going to past it here there we go and oh I
left the filter key to be email all right so this should be changed the name both here and also in the uh accounts page I forgot about that we're going to go back and fix that as well uh great so this now actually works fine but we do have to resolve a couple of things so here in the columns right here in the categories folder right we have to change the response type to not use accounts but categories even though they are exactly the same so nothing visible is changing but if you ever add some
new Fields you obviously want to read them here right now let's go inside of the actions and in here we have to modify I think the last of our hooks so let's remove use open account in favor of use open category and let's go ahead and see where we have to use it so right here use open category perfect let's change this label while we are here so you're about to delete this category and let's go and remove use delete account in favor of use delete category like this there we go and let's add that
here and the the lead mutation name is generic so it can stay the same and I think that we didn't have to modify anything else so this delete mutation seems to have some error but I think that's just some weird cache if I'm not mistaken if I go ahead and reload my window uh I think it will go away there we go it went away perfect so if I'm not mistaken we should now be able to fully control our categories separately from the accounts so let's just ensure that we have a couple of accounts I'm
going to call this account one just in case there we go so ensure that your accounts are still working intact now let's go ahead and first of all great the categories are empty meaning that we are loading the categories let's right here there we go new category I'm going to call this food let's see and there we go perfect let's go ahead and edit this to Food 2 this works let's check out the account great nothing is transferred in the accounts meaning that this is fully on its own let's go and try and delete this
individually there we go let's try creating a food category a travel category let's select both of those and let's delete them let's confirm there we go perfectly working now let's go ahead and let's modify just a couple of changes we uh noticed in the accounts right so uh I want to go ahead and go inside of my uh is it features accounts I think inside of here I'm going to go ahead and right click and I'm going to do find in folder and I'm going to search for transaction uh all right so this too is
fine to do is fine but we have the edit account sheet component right here so let me show you where it is it is inside of features accounts components edit account let's change this confirm message to be you about to delete this this account right because that's what we are deleting all right is that the only place let me go ahead and find in folder again transaction that is fine so it was only that one instance that was wrong and there is also another thing that I believe we notice here and that's that we called
this use bulk delete when it should be use bulk delete accounts like this and now this will open up another page where we have to use that and that is our app folder dashboard accounts page. vsx so right here we are this just changed automatically so usually we had just use bulk delete but we renamed that to use bulk delete accounts so now it is use bulk delete accounts and nothing else should require the change because inside we had it properly named it was only the file name which was missing some information and I believe
that we also have to go inside of accounts page. TSX and change our data table do not filter by email but by name there we go so if I go ahead inside of accounts now let's go ahead and confirm that I can search for account one great and let's confirm that our bulk delete is still working and it is accounts deleted perfect and let's see if we have a working uh message here you about to delete this transaction so we still have this issue that is because of our accounts columns or actions it's because of
accounts actions. vsx so you are about to delete this account that's the proper message there we go you about to delete this account and that is perfectly fine that is the correct message and if I try it inside of here you are about to delete this account as well perfect so that's it it took us I don't know how much faster to create categories then it is to then it took us to create our account and that's because we established so many Concepts and so many reusable components that we can easily do that right and
it helps that the schema is almost identic well not almost it is identical to the accounts page so the next one we're going to do is the transactions which are going to be fairly more complicated than the previous two uh especially because they're going to have relations with the two so we are going to have to complicate it a bit and that's why I first wanted to create the accounts and the categories so that now we can fully create transactions without going back to them great great job so now that we've wrapped up the categories
schema and page and API let's go ahead and let's finally create the transactions so head back inside of your schema file so database folder schema and let's export cons transactions we're going to use PG table again and make sure you have a matching name inside the ID will be exactly the same so we can copy that like this and our amount is going to be interesting so it's not going to be a decimal it's not going to be a float it's not going to be the money currency it's just going to be an integer so
we have to add that from PG core right here make sure you import integer and give it the same name amount and make sure it's required by adding not now so why did I choose integer for storing the currency in our datab well let's look at all the ways that we can store currency in our database here so the first idea that comes to mind is using Floats or doubles but we know that this is a nogo because they do not offer Precision it might look good and easy to store it that way but the
moment you start calculating you're going to see that they're not very precise and this will turn into a bunch of Errors the second option we have is using the decimal or numeric fields which come in postgress for example that is fine but the problem is that numeric or decimal does not exist in JavaScript so it has to be parsed as a string which will create some problems with our endtoend type safety especially when it comes to forms it will also require us to use additional libraries to handle this like decimal JS and it's also not
cross language compatible right if we change the database or if we change the language it will require us you know to change the complete additional Library stack and also to find a way to support that in our database so I settled on the third option which is using the integer of the smallest unit of the currency which would mean storing dollars as cents in our database which basically means storing it as an integer right but we're going to go ahead even further and we're going to store it as milliunits so milliunits means that we're going
to multiply our uh value by thousand and this way we can support up to three decimals because later when we integrate pled which we'll read from our bank account they have uh documentation about their amount property which says that it might return three decimals at one point especially if we are working with crypto values so because of that I made a decision to go with Millie units means that when store ing into the database we are going to multiply the amount by a thousand and we are going to store that integer inside of our database
and then when we bring it back to the client the client has to know that and then the client will divide it by the Thousand to display the number to the user so what we ensure here is that we work with types that exist inside of JavaScript ecosystem and we also make it cross language compatible so if we ever change the database we don't have to worry about whether the new database has the decimal field right because all of them have the integer and the same thing is for you know the programming language they all
work with integers in pretty much the same way so I think that's the right choice for this application you can of course you know discuss and try your own solution uh but I recommend that you follow exactly as I'm doing especially because it's going to get complicated with all the calculations and stuff so we have the amount we're going to have the pay option which is a very simple text which says pay so I didn't make pay any kind of a separate entity because pay can change from ATM to ATM from cach register to Cache
register right so it can change depending on the location and everything it's just uh varies too much so it's safest thing to do is to just make it a required string and then we're going to have some notes which are going to be some personal notes which user can write and they are of course optional then we're going to have a date which will be a timestamp option not time time stamp from postgress core so make sure you've added the integer the PG table and the timestamp here let's further Define this by giving this the
date field giving it a mode of date and let's make it required not null we are not going to give it any default values because this is something user will enter themselves right they can add transactions which will happen in the future or ones which have happened in the the past so we don't want to prefill this for them right and now what I want to do is I want to create relations with transactions and categories and with accounts so first let's go ahead and let's do this so right now you can see that this
transaction doesn't belong to anyone right there is no user ID here but we need to find the way for this transaction to only be loaded for that user we can do that by creating a relation with the account so this is what we're going to do we're going to write account ID and we're going to write text accountor ID so why text well that's because we use text for our IDs so our account has text as the type of ID so this is correct so we going to have accountor ID and it's going to reference
to open a function here accounts which we already have do ID so that's what it will reference right and now we're going to add some additional rules here for example what happens if we delete the account well if we delete the account I want to remove all the transactions that that account had so we can do that by defining an additional option here on delete Cascade like that if you don't want that you can set that to be null or something else but here's the problem uh this is required for us right so we cannot
do anything besides remove this because account ID field will be required we cannot do uh set null here because we are doing not null here so we are not getting an error but we will get an error later when this happens so make sure this is on Cascade and we will also create a category ID here so again text category uncore ID and this time we are only going to add a very simple on the lead uh my apologies I didn't add the references at all so do references here is a function which goes to
categories. ID and on the leete here we'll simply set it to now meaning it's uncategorized so if we delete the category that this transaction is attached to no point in removing the transaction right we can just set it as uncategorized so that's why this will not have the not n because it's optional right if we really want to we can leave the transaction as uncategorized but we are not done with defining our relations here right so we just have the references but we haven't defined proper relations like one to one one to many right so
here is the documentation where you can find it just in case you're wondering where I got it from so inside of drizzle documentation access your data we have query and in here we have declaring relations right here so we're going to use this relations import and inside of here we can Define either a one to one relation or one to many relation or many to many relation so in our case we're going to work with one to many or like many to one so let's go ahead and do the following first let's resolve the accounts
so we have to import from drizzle orm itself relations so let's go ahead and let's add relations like that and now in here I'm going to add export con accounts relations like that that's going to be relations field which works on the accounts schema and inside of here we are going to destructure many and we are going to return an immediate object down here and what relation is going to have a relation with transactions so one account can have many transactions like that perfect so that's it for our accounts now we have to create the
same thing for the categories so we can copy this let's go ahead and find this and we're going to rename this into categories relations it's going to work with categories so very important for you to change this and again uh so a category can have many transactions inside great and now we have to create the relations for transactions so let's go ahead and Export con transactions relations here so that's going to be relations transactions and inside of here we're going to D structure one and return an immediate object here so each transaction can only have
one account so accounts like that and now let's define the fields so we are working with transactions do account ID is going to reference accounts. ID so that's how we create a one uh to many relation here right we defined many there and in here we defined that this field account ID references inside of the accounts this ID right here great so we have that and now we can copy and paste this and rename this into categories so categories work with categories categories. idid and this will be category ID just like this there we go
and now let's go ahead and let's export con insert transaction schema to be create insert schema which we already have but this time for transactions and we're going to slightly modify it because date is a type of date but we can't exactly work with that in JavaScript so we are going to add uh we need to import Zod first we have to slightly modif ify it so it doesn't create errors in our forms because we're going to reuse it all the way there so we're going to write z doc. date there we go so this
way we won't have any typescript errors with dates and there we go you can see the date is now a type of Zod date which is compatible with our hook form great perfect so we have that and now it's ready to uh well generate that SQL and migrate it so inside of our drizzle last thing we did is we added categories so let's go ahead inside of our terminal here let me just see what this error is all right so this is some DS config here I think it will go away if I just reload
my window all right let's get inside of the terminal let's shut down the app and let's do B run database generate and there we go three table accounts categories and transactions let's see what happened now so inside of our new uh uh SQL file right here there we go so we created the table transactions and we added foreign keys or relations to that transactions and let's go ahead and go inside of the terminal now and do Bun Run database migrate let's see if we are going to have any errors no errors at all perfect in
case you ever get errors with the bun migrate you don't have to worry you can always just delete your drizzle folder and that will restart the entire migration process and and if you still have errors then I recommend that you create a new database in neon DB and connect again all right and now let's do Bun Run database studio just to check it out so I'm going to go ahead and open this and now we should see the transactions and we should also see the relation so if I go here there we go we have
account ID and category ID and these are our relations right here so inside of my accounts I also now have the transactions Rel ation here same thing here so these are going to appear as actual relations which you will be able to click and this studio will redirect you to that perfect great and now I want to go ahead and I want to build my API endpoint so we're going to leave the front end for another chapter because the front end is going to be well slightly more complicated so let's go ahead and let's do
the easy thing which is to copy our categories right so let's copy the categories and let's paste them and let's rename them to trans transactions so we are doing this inside of the app folder API route right here we have the transactions so let's go inside of route. DS right here and let's go ahead and chain SL transactions and we have to import transactions so import transactions from do/ transactions and add them here there we go so as always if I go ahead into my slash Local Host ,000 API transactions it's now going to load
categories instead right because we have not changed anything so let's go ahead and slowly do that so I'm going to go inside of my transactions and this is where we are going to work so first things first let's remove the categories and insert category schema entirely and instead let's add transactions and insert transaction schema there we go and now we're going to go ahead and resolve each endpoint one by one so the first one is this one and it's immediately going to be different in a way that the uh the simplest get request will have
uh some param options so we add a z validator here and we are specifically going to validate the query which is going to be optional so this is going to be z. object let's just ensure that we have Z import that we do great so it's going to be z. object which will accept the property from which will be a string and it's going to be optional then we're going to have two which will be a string again and optional as well and we're also going to have an account ID which will be a string
and optional as well so as you can see we will be able to filter our transactions by dates and by a specific account ID but we don't have to do that if we don't want to great so our authorization stays the same but let's also do one more thing in here let's go ahead and let's use C request valid query and let's dist structure everything we need so from to and account ID like that perfect and now in here we're going to go ahead and we're going to find const default 2 to be new date
so in case we don't have any filters from end to past we don't want to load the entire history of transactions that can be a huge amount of data so we're going to default if nothing was passed we're just going to default by the last 30 days that's how most of this financial managers work they always show you the last 30 days of data and now we're going to write con default from and we have to find a way to deduct 30 days from the new date so we can do that with a library which
we are going to use throughout the project called Date FNS so npm install or bun add date FNS let's ensure we have that great and now we can use sub days which I'm going to add an import for in a moment so this is going to be subday from default to 30 like that let's go ahead and import sub dayss from date FNS there we go perfect we have default two and now let's write con date to check if we have from in that case we're going to use parse from date FNS so make sure
sure you've added parse import here so we're going to parse the from value in a format of lowercase y- mm- DD like that and new date like this and let's go ahead and do it like this so we have more space so if we have it that if we don't have it it's going to use the default from now let's define the end date here to check if we have two if we have two it's going to use parse two and it will be exactly the same as this one so we can copy that otherwise
it's going to use the default to like that there we go so let's go ahead and just confirm one more time that this is correct we have default two which is used as the end date and default from as the starting date perfect so that works fine and we are format we are parsing it in this way because that's what our drizzle is going to accept great so let's go ahead and modify this now so first of all well I think it might be easier for us to actually build everything from scratch so do select
we are specifically going to select ID which is transactions. ID we're going to select date which is transactions. dat category now this is the interesting thing right so if we write just category ID to be transactions. category ID this is fine right but on the front end we are just going to get the ID right there's that's not something we can work with we want we don't want to display the user a random ID I want them I want to display the name of the category so I can do that by adding category categories from
database schema. name but this by itself will not just work so let's go ahead and make sure you have all the Imports you need transactions insert transaction schema and categories for now so this by itself will not just work we're going to have to join uh the categories later great so that's why we are doing both we are doing category ID so that we can uh well find that category in the array of categories right because a category name doesn't have to be unique so it doesn't give us too much information except presentational wise for
the user so we need both and let's go ahead and go further let's add the pay to be transactions. pay let's go ahead and add the amount uh after the amount let's add the notes and then we're going to have the account ID here and the same way we did it uh with the category we're going to have the account itself come from accounts schema. name name so just make sure you've added the accounts alongside categories here in the schema so we're going to select all of that from transactions and now we have to perform
an inner join and we have to perform a left join so for accounts we are going to use the inner join because this will ensure that the both sides of the table relations exist right so we don't want to load transactions which don't have this account ID so let's go ahead and do the following so we're going to write inner join accounts we already have that import and then we need the equals so let's just confirm that we have equals we do have from drizzle omm so accounts has to equal transactions. account ID and it
needs to match accounts. ID like that so that's the inner join we need to ensure that every transaction has a a matching account ID but for the category we're going to use the left join so so for this we're going to write categories and it's going to be very similar so equals transactions. category id categories. id so in case a category doesn't exist that's fine we are still going to load this transaction right we are just not going to match any category ID or category name and that's fine because category is not required but account
is required so we are using an inner join which means if there is a transaction which for some weird reason somehow doesn't have an account we are not even going to find it here so at least that's I'm not a a backend developer in my career I was a front end developer so I'm learning this the same way you are here so if you think that I explained this in the wrong way feel free to leave a comment right uh and see how you would explain it and now let's chain a where method here and
now we need to use our end uh operator so just ensure you have end there we go so first first of all we're going to check if we have an account ID if we have an account ID in that case we only want to load transactions with that account ID so we ensure that it matches that variable otherwise to skip this we need to add undefined like this so that is the optional filter and now we're going to add some required filters for example accounts. user ID needs to match out user ID so if we
didn't add this inner joint for the accounts we would not be able to perform this filter right here so remember inside of our schema transactions don't belong to users right transactions belong to account ID so that's why we need to perform an inner join on the accounts so that we can check if that matching account has the matching user ID of the currently logged in user so that's how we ensure that only uh the users transactions are being loaded so that's the first one and then we need a greater than or equals I think that's
what this is a short hand for greater than or equals I prefer let's see uh greater than or equal yes so greater than or equal and we're going to look at transactions. date so when it was created to and compare it to our start date and then we're going to copy and paste this but this time we're going to use less than or equal with the end date and we need to import less than or equal from drizzle orm there we go perfect uh and now we also need to add an order by Method right
here so we are going to order by the sending so we need that so import this sending uh from drizzle orm specifically we're going to work with transactions. dat so we going to by default order them by date there we go so that is our query so you can see how I wanted us in this course to learn more about working uh with SQL itself rather than uh you know using the queries Prisma like operator where which you know it's obviously easier to work with but we don't really know what's going on there right so
this this time we are kind of forced to understand this inner joins a bit better I think you can even hover over the inner join and in here you can read the documentation so executes an inner join on the operation calling this method retrieves rows that have corresponding entries in both joined tables so that's exactly what we need for accounts but for left joint for example calling this method Associates each row of the table with the corresponding Row from The Joint table if a match is found if no match row exists it will set all
The Columns of the join table to null so that's fine for us because that's exactly what suits the categories fi here because it is optional perfect so yes if you're confused looks like they have great documentation here for these joints so you can just read them you even have some examples it seems here amazing great so now that we resolved that let's go ahead and let's resolve the individual ID uh fetch here so what I'm going to do is I'm just going to copy this select because I think we're going to need it here so
uh the quering can stay the same right we have the ID the missing ID and unauthorized we're not going to have any querying here so what we're going to do is we are going to uh yeah the problem is we now no longer have those errors right for category so we're going to have to be a bit more careful so in our uh get ID for the transactions this is what we are going to select so we need the ID uh and we also oh I think I forgot a very important field here uh in
my first uh in my get route here so let's go back and inside of here so after ID make sure you add date I completely forgot that we have to return the date right we want to show the user when this was created so make sure you add date inside of here so let's copy this again this time let's go to the bottom here well not to the bottom but just to the next one so let's see we have ID we have date uh and we don't have to return the category name this time because
this field right here will be loaded in inside of the uh drawer inside of the form and form can simply detect the category ID and it's going to display the label by the select component so we don't need the category name and same is true for the account name so we just need the ID the date the category ID pay amount notes and account ID so again we are working from uh this time it's going to be from transactions and let's go ahead and let perform the same inner join here for my uh accounts so
I'm going to copy that and add it here so inner join with the accounts so we only ensure that so we ensure that all these transactions have the accounts and so that we can add some filters here and that filter is well primarily going to be this whoops my apologies let's go to the ID so primarily we are querying by the not categories ID but transactions ID and then the second argument is going to be accounts. user ID so be careful with this right don't make sure you don't have any misspellings here so inside of
the wear here we are querying by the transaction ID because we have the ID as the value here but we are also ensuring that uh this data that the user is trying to load exists in this inner join of the accounts uh and it has a matching user ID of that account great so if there is no data we return a not found here perfect and now let's go ahead and let's define our post method here so this one is going to be a bit simpler so inside of here we're going to do the following
we're going to add insert transaction schema but instead of using pick we're going to write do omit so we want everything except the ID field that's not something I want the user to pass right so the values are fine this is fine and we are going to work with database. insert transactions so not category the ID will stay the same and the uh the user ID doesn't exist so we remove that there we go perfect uh that is fine right here and now let's go ahead and let's perform a bulk delete right here so the
Z validator for Json will stay exactly the same and now here we have to do uh something called I'm not sure what's the right expression I think it it's called a chained query so we need to find a way uh to add a very specific so we can't just query inside of this where to confirm that these transactions we trying to delete has a matching account with a matching user ID right it's a bit complex but drizzle orm does have a solution for that so if you go inside of the documentation for the delete option
so let show you here so access your data delete they have something called with delete Clause so using with Clause can help you simplify complex queries by simply by splitting them into smaller subqueries so they are called Common Table Expressions that's the name all right so that's what we have to do we have to add this dot width so that we can uh make this we query be powerful because we want to be careful what the user can bulk delete right this is a powerful API endpoint and we need to ensure that it's protected so
this is what we going to do uh let's go ahead and do the following I'm going to go ahead and remove this returning and for the delete I'm just going to ensure that we are deleting transactions here right now here's the problem we cannot query where here at the moment because what we have to do is the following we need to write con transactions to delete and let's write that to be our database dot wi and let's go ahead and Define find this as transactions _ 2or delete dot as database. select we are only choosing
the ID from the transactions. ID from transactions we are going to do an inner join on the accounts where transactions account ID matches the accounts. ID where we are going to add an end query inside immediately there is there are the values there are our IDs which we send here IDs are in the array of transaction IDs so transaction my apology this transaction ID is in the array of our IDs that we are passing my apologies it's a it's a tongue twister for me okay so we ensure that we are only looking for uh transactions
which have been passed in this IDs right here right but then we also ensure that accounts user ID are with the currently logged in user ID that's important for us so we are not going to load uh these transactions if the user ID is not matching so what this will create is it will create a list of IDs for us to query here so we going to add before the delete we're going to add width transactions to delete like this and then we're going to add where in Array transactions do ID and then we're going
to use something called a SQL operator here from drizzle RM so that we can write raw SQL so that is here I'm going to move this to the bottom there we go this one we're going to go ahead and open back Tex open parenthesis and we're going to write select ID from and we are going to inject the transactions to delete right here so let me show you this in one line like that there we go and then we're going to add returning here id transactions. id so let's go ahead and let's go over this
again so we are attempting to delete transactions but we are only going to remove transactions where the ID matches this list of IDs and what is this list of IDs right here what well that is a query which will join all the accounts so first of all we are only going to load transactions which have accounts and then we're going to ensure that it only loads transactions which have been passed in these IDs values and then it also ensures that it's only there there where it has a matching user ID great so that's what we
are going to do here perfect and now what we need is we need to resolve our patch request so let's go ahead and do that so very similar to post it's not going to have insert category schema first it's going to have insert transaction schema instead of pick it's going to have omit and it's going to emit ID like that there we go so uh let's go ahead and do the following so what we're going to have to do now is we're going to have to copy this transactions to delete because we have to use
a similar query now because remember transactions don't belong to the user so we always need to do this deep querying by finding the matching account with the user ID right so make sure you are in your patch right here you've changed insert transactions right here you validated the ID the values so everything stays the same but we don't need this at the moment uh or let's keep it simple just do it like this for now let's go ahead and uh paste the transactions to delete from our bulk delete above and we're going to call this
transactions to update here and we're going to change this to be transactions to update as well so this can be the same so select transaction ID from transactions inner join of the accounts where in Array and this is the cool thing so we're not going to do in Array here because this is not bulk update this is just individual update so we're just going to do equals values. ID actually it's not values. ID it's directly ID because we destructure it right here from the Pam there we go so that's the transactions we can update so
only the one with a matching ID and where our user ID has a matching account user ID great and now inside of here we can go ahead and chain dowith transactions to update do update transactions because that's what we are updating set the values which are going to match perfectly L because of our Zod validation here and we're going to do where in Array transactions. ID match the SQL open parenthesis select ID from transactions to update again this is how it looks like in one line there we go and returning like that there we go
so this now works fine and we are safely uh updating our we're safely updating well our uh uh transaction right so now uh we can go ahead and uh make this kind of easier for us we can copy this again transactions to update here and this is the last one to delete by ID right so the error handling here is fine let's immediately add this instead of transactions to update this will be transactions uh to delete so transactions to delete here change it as well and this is actually exactly the same right because this is
just ensuring that the array of IDs that's going to be generated for our we query is only of those transactions which the user should have access to based on their account user ID so we have that transactions to delete Here and Now inside of here let's go ahead and let's remove this entire query so first of all with transactions to delete we are deleting transactions not categories as it was previously where in Array we have transactions. ID and we have a SQL open parenthesis transactions oops uh select ID from transactions to delete there we go
and let's also chain returning inside but we are only going to return id transactions. id there we go so if that was not found it means we probably didn't have access because we could not find it in this array which only loaded by that ID and by our accounts perfect that is it so here's what I want to do I just want to ensure that we didn't have any crucial bugs for example so I'm going to write from here and I just want to check that we are not accidentally loading something from accounts or from
categories right because we copied this from categories so that's fine let's do do update transactions that's fine let's do dot delete transactions transactions perfect I think our uh API endpoint is ready great great job and see you in the next chapter now that we have our API endpoint for transactions ready let's go ahead and let's convert that API endpoint into a set of reusable query hooks so I'm going to go inside of my features folder and I'm going to copy and paste the accounts and I'm going to rename it transactions I'm only going to focus
on the API folder for now starting with use get accounts which in this cas in this case is going to be use get transactions let's rename the hook use get transactions and now let's go ahead and let's import use search params from next navigation and inside of here let's get the params using us search params let's get the from using pam.get from or an empty string to is going to be pams get 2 or an empty string and cons account ID is going to be pam.get account ID or an empty string as well now we're
going to change the query key to be transactions but only for from to and account ID like that and now we're going to modify this API call to go to transactions we're going to open the get request and we're going to add a query here to have from to and account ID and our error is going to say fail to fetch transactions I'm also curious about this right here now that I think about it perhaps it might not be a good idea to make our key unique based on the query so I'll just add a
too check if params are needed in the key we're going to come back to this later but this worked fine in my original implementation but I'm a bit curious about it but we will explore that later now let's go ahead inside of the individual use get account and let's rename it use get transaction so inside of use get transaction right here let's rename the hook use get transaction and the ID prop is the same but the query key and the API and the error should all be transaction so this should be individual transaction and ID
and this API request should go to client API transactions ID and this should be failed to fetch transaction there we go let's go ahead and let's use the use edit account and let's rename it the use edit transaction let's rename the hook use edit transaction and let's replace all instances of accounts so starting from the response type into the request type the mutation function here and let's just leave it like this for now so just these three instances and let's change it to transactions like this so client API transactions client API transactions client API transactions
just like that the Json here is correct we don't have to worry about that because we using the correct response and request types from transactions here now what we are going to invalidate here first of all let's fix the success message so it's going to say transaction updated and this will be failed to added a transaction we are going to invalidate the transaction ID key and we are going to invalidate all transactions here so this is why I'm curious about our use get transactions here so I'm not sure if I invalidate just the transactions will
that invalidate with this as well I'm just not entirely sure so we will see that later uh and inside of here I'm just going to add to-do invalidate summary because we will need to invalidate summary here later and now let's go ahead inside of the use delete account and let's first of all rename it to use delete transaction so individual and inside of here let's first rename this to use delete transaction and we're going to change this to use api. transactions so we have the proper response type and so we have the proper uh well
API call here so this will be transaction deleted we are going to refresh the individual transaction with this ID and we are going to refresh transactions multiple and we are also going to invalidate summary later on and this will be failed to delete transaction now let's go ahead and let's go inside of use create account let's rename it use create transaction let's rename the hook use create transaction and let's do the same thing for our API instances to all go to transactions so response type request type and the API call this is going to say
transaction created and it's going to invalidate the transactions and this will say fail to create transaction there we go and I'm going to add a to-do uh it's going to say invalidate summary later when it exists and let's go ahead into the last one here which is use bulk delete accounts let's rename it to use bu delete transaction transac transactions multiple let's go ahead and fixed the hook name and again we are changing all instances of accounts so these three into transactions so response type should be this request type should be IDs and what's important
is that in here we are calling the transactions bulk delete so this is going to say transactions deleted and we are going to invalidate transactions and we're going to say also invalidate summary here and failed to delete transactions there we go perfect but there is one more uh hook and API end point that I want to add here so we will be able to add transactions in bulk thanks to the CSV file upload so because of that I want to revisit my app API transactions. TS so we have the get all we have get individual
and in here we have post to create an individual and in here we have post to create uh to delete multiple so in between those two or at the bottom it really doesn't matter add another another post which will say bulk create right here and now let's go ahead and let's resolve this uh bulk create so first of all we are going to protect it with Clerk middleware and of course we doing things in the transactions right we add the clerk middleware and then our Z validator is going to be the following so it's going
to validate Json which is going to be a z. array so it's going to be an array of insert transaction schema do omit ID true so very similar as our post request as you can see but instead of just accepting a single Json object this time we accept an array of those objects great so after the validator here let's go ahead and let's open our asynchronous controller and let's go ahead and get the out using get out let's get the values using C request valid Json if there is no out user ID let's add a
question mark here in that case we are going to return c. Json with an error of unauthorized and it's important to add a status code so we infer the correct response types here and now very simply we get all data so no need to destructure the individual because we are creating an array right so await database. insert into transactions values and inside of values we're going to add values. map get the individual value open an immediate object use the ID to be create ID method and spread the individual value inside and just add chain. returning
inside and right here at the bottom we're going to add return c. Json and pass in the data there we go so let me just see if I can uh indent this yes I can indent this one but these ones should not be invented there we go so what's this is going to do is it's going to return an array of those created transactions so inside set of values we are passing in an array of objects so you can do that natively in uh SQL or drizzle orm in this case great so now that we
have this bulk create endpoint let's go ahead and let's add a hook for that so I'm going to go ahead and close everything let's go inside of features transactions API and I'm going to go ahead and copy and paste use delete transactions and this will go this is and this will be use bulk create transactions like this so let's rename this instead of use bulk delete it's going to be use bulk create transactions and instead of bulk delete here we're just going to say bulk create so our response type here will either be an error
or data and our request type here is going to be an array of objects which are necessary to create a transaction so now we have to modify this as well it's not going to be bulk delete but bu bulk create there we go so this is how it looks like in one line and instead of transactions deleted this is going to say transactions created and we are going to invalidate the transactions and a to do can stay the same and this will be failed to create transactions there we go so that is it we successfully
added API end points sorry API hooks for all of our a API end points so in the next chapter what we are going to do is we are going to prepare this transactions page right here and we're going to create the form to create a new transaction so it's going to be a little bit more complicated because we're going to have to create a select component which will be able to well search for existing categories and existing accounts but also we're going to do it so that the user can easily create a new category or
account from that select searchable field so that's going to be for the next chapter because it's a module of its own and it's a fairly complicated one great great job now that we have converted our transactions end points into usable hooks let's go ahead and let's create the transaction page and let's create a drawer which is going to open so that we can create a new transaction we're going to start by going inside of features transaction hooks right here and let's change this use new account to use new transaction inside of here I'm going to
also rename uh the function so I'm going to rename all instances of account to transaction and the rest can stay the same and then inside of components let's go ahead and let's rename new account to new transaction whoops and let's do the same rename inside of the component so it's not going to be new account but new transaction now let's go ahead and find the insert account schema import and let's remove it so we know where we have an error here and let's import insert transaction schema here instead so this will be a slight modification
let's use this insert transaction schema but instead of pick name is going to be omit ID true like that so now we have this error here for the values so let's go step by step first let's resolve this we are no longer using use new account so find that import and remove it in favor of use New transaction which we've just renamed like that next we have use create account well first of all let's use use new transaction here and in order order to fix this values here we have to change this mutation so right
now it is use create account let's remove that in favor of use create transaction again from add features transactions like that so we created this in the previous chapter and let's use it here and I have some typescript error I think if I just reload my window it's it's going to go away and there we go we no longer have any errors here besides in our form here so that's why I'm going to remove the form from now and just write a paragraph to do transaction form let's change this to say new transaction and let's
change the description to say add a new transaction there we go and we can remove the import from account form as we are not going to need it at this moment here so we can just leave the on submit function be unused now let's go ahead and close everything and let's go inside of our providers sheet provider I'm going to add a little space here and let's import new transaction sheet from features transaction components new transaction sheet so the component we just modified make sure you have renamed it here otherwise you won't get an import
so I'm going to add it here there we go and now it can be opened so now we are ready to go ahead and go inside of app folder dashboard and let's create a new folder transactions here actually what we can do is we can copy the accounts that's easier so let's go ahead and copy accounts and write transactions like that let's go inside of page. TSX and let's rename this and the default export to transactions page there we go so now if you try and go into transactions it's going to load accounts because we
just copied that again if you are getting a 404 here confirm inside of your navigation that your transaction HF matches the new folder we've just created transactions here make sure there are no typos or misspellings great let's go ahead and let's modify the this from accounts page to transaction history and let's go ahead now and slightly modify the rest of the stuff here so what I'm going to do is first of all okay we have some typescript errors again this is just some weird type script server which happens when I copy a lot of stuff
right so when I just reload my window or shut down vs code they go away so let's start with this new account use new account we are no longer using that instead we're using use new transaction from features transactions so let's replace that here and instead of new account it's going to be new transaction and the errors are going to tell us where we need to change it so right here new transaction there we go so now if I go ahead and click add new there we go new transaction to-do transaction form perfect so that's
exactly what I wanted to achieve so for now we can leave everything else as it is we can leave use bu delete accounts all of these things we're going to come back to this later because what I want to wrap up now is what we started and that is the form so go back inside of features transactions components new transaction sheet so first of all before we can even start creating this component we need to load some options for for it right so I don't want to use the form component to load options inside I
want to use this uh parent component right which will render the form to prepare all the options so what options am I talking about the category options and account options so that's going to be shown in a drop-down but we first need to fetch it because every user has its own options so let's go ahead and do const category mutation to be use create category so that's going to be from features API use create category I'm going to add that here and I'm going to separate the features so it's visually more pleasant to see then
let's write const category query here to be use get categories again I'm going to align my imports here and then we're going to have const on create category to be a very simple method which accepts the name which is a string and simply calls the category mutation. mutate and passes in the name and then we're going to have con category options which are going to be a very simple check of category query. data question mark question mark empty array so if there is no data we're going to work with an empty array and then we
can safely do map here we can get the individual category and we're going to transform it by returning an immediate object from this do map into label category name and value category. ID there we go so you should have the name and you should have the ID that's why inside of our categories API route when we get all of them in here we are returning the ID as well as the name because we need it right here there we go so now we can copy and paste this and we can do the same thing for
account query so this will be account query use get accounts let me go ahead and fix this import here so I'm going to add it to the top here but I'm going to separate it from categories all right so let's see which one are the originals this is the original this is the new one so the second one is not category mutation but account mutation and that will be use create account so let me go ahead and align the import here as well so I have them all in one place then we have oncreate account
to use the account mutation. update and name is the same and lastly we have the account options which will use the account query do data and this will not be category but account there we go so now we are ready to pass these as values for our form but we don't have the form yet so let's go ahead and let's prepare the form so inside of account form let's change this to be transaction form like this and then let's rename the component itself to also be transaction for form like that and now we can go
back inside of the new transaction sheet component and we can import the new transaction my apologies transaction form from do/ transaction form but I am going to change that to Features transactions components simply so I can keep all of them right here together there we go great so now that we have that let's go ahead and let's pass in all the props that we now have inside of here so that's going to be onsubmit onsubmit it's going to be disabled let's put it false for now we're going to have category options which are going to
be category options then we're going to have oncreate category to be oncreate category and then we're going to have account options to be account options and on create account is going to be on create account like that so we are going to we're going to have to adapt our transaction form a bit to accept all of those and first let's resolve this uh disabled state so we can do that by introducing a constant is pending and it's going to check if our first mutation here so this mutation let's rename it uh create mutation actually so
it's more implicit um again I have to reload my windows so it restarts the typescript server there we go so I just renamed this to create mutation and now inside of my onsubmit I'm also going to change this to create mutation and now instead of is pending let's add if create mutation is pending or if category mutation is pending or if account mutation is pending like that so that's our is pending here and let's now add const is loading so is pending will disable the form whereas is loading will not even show the form so
we are going to show the loading status if we are uh loading our category options so we're going to do category query is loading or if account query is loading like that so now we can do the following we can replace the disabled with is pending and we can also do the following we can check if is loading in that case we're going to show one thing otherwise we're going to show this transaction form so let's move it inside of the other if clause or the else right and now in the first one we're going
to add a very simple div with a class name here which is going to be abolute ins set zero Flex items Center justify Center and inside we're going to use a loader two from Lucid react with a class name of size 4 text muted foreground and animate spin so just ensure you've added loader two from Lucid react I'm going to keep it here at the top with my Zod and let's see if I can just align this better actually this is fine great so now uh we are ready to show our transaction form and you
should now see uh let me go ahead and if I refresh here and if I click here well I it's it loads immediately right but if it were not if it weren't loaded right if these options were not ready the category options and the account options then it would show a little loading indicator before it loaded into the form so now let's go inside of this transaction form which we've just created so transaction form and we have to modify well first of all the form schema and then we have to extend the props a bit
so let's start with the form schema so the thing with form schema here is that we have to kind of create it um we have to modify it a bit so it's actually easier for us to write it out from scratch than to uh to try and modify the one from drizzle because there AR exactly options to override in the way we specifically want to so let's go ahead and do this all over again let's write manually date to be z. course date account ID will be Z string category ID will be Z string nullable
and optional P will be z. string amount will be z. string and notes will be z. string nullable and optional there we go so this is our form schema here so I believe that now uh the values seem to still not match but okay let's go ahead and let's just wrap things up here uh so yeah now what I want to do is I want to have actually two schemas here so we're going to have form values but we're also going to Define type type API form values and that's going to be z. input and
that's going to use type of API schema and we're going to create API schema uh just below form schema so const API schema is going to be insert transaction schema so the one we have from database schema we can remove insert account schema here and inside of here we are just going to Omit ID true there we go so this way we have uh well we are going to need this manually I know it's a bit confusing because we didn't have to do this in the previous ones well that's because these options are just so
specific that it's easier for the types to have two schemas here so now we have form values and we have API form values and we're going to do the following we're going to change the default values to use the form values but the onsubmit will use the API form values and and I think that now there we go onsubmit no longer has an error now we have these errors we're going to resolve that next great so uh on delit is fine disabled is fine and now we have the account options which I want to be
an array of objects label string and value string like that and then I'm going to copy and paste that and call this category options and then let's go ahead and create oncreate account to be a void which accepts a string let's copy and paste that and call this oncreate category there we go and once I save this I believe we should no longer be having any errors inside of our new transaction sheet right here so we are completely fine to just continue working in the transaction form itself let's continue here and let's add the account
options the category options on create account and on create category right here uh and now what we are going to do here is well we're going to have to do some modification here obviously because of the type error but before we can uh start working here we have to install uh a component called react select and then we have to create our reusable select component and style it according to the existing Styles so let's go ahead and do the following let's go inside of our terminal here and let's do bun add react select or npm
install react select depending on what you use right here and now I'm going to go ahead uh and close everything so we have some errors here that's fine but let's go ahead and go inside of components for now and let's create our select. DSX component so make sure you don't put it inside of UI because we're going to have a separate select component which will be here that's going to be the sh atn1 but now we just want our own here so let's mark this as use client and let's import use Memo from react let's
import a single value from react select and let's import creatable select from react-select SLC creatable like this let's define the props here so the props are going to be onchange which will take in the optional value and return a void and on create we'll do the same thing but on create can be optional but the value is required when we are creating something if we are enabling that options itself are going to be optional and there are going to be an array of objects which are in format of label string and value string so exactly
what we are formatting in our uh sheet and our form component value can be optional it can be string null or undefined and disabled is a Boolean placeholder is an optional string so if you're wondering why did I put both the question mark and null and undefined it's simply to satisfy the types right because I've already coded this project before and I know that the types are going to not work with just this but in this combination it works fine let let's go ahead and let's export con select component here and let's D structure from
the props the value the onchange let me scroll up so you can see so value onchange disabled on create options and let's go ahead and ourselves Define the default as an empty array and let's add a placeholder and I believe the errors will go away after we add an opening of the method inside of here we are going to return the create a will select component which is a self closing tag and now let's create some methods here so on select we'll accept the option and the option is going to be the following so I'm
going to collapse this like that so I can write the type in full line so it's going to be the single value which we added an import for open pointy brackets and inside write label string value string and that is going to be our only prop which we accept and inside of here we're going to call on change to be option question mark. value because it's optional right the single value itself can be null so we imported single value from react select so it matches our custom on select and then we can safely pass it
here and let's go ahead and do const formatted value here to be used memo and let's return options. find we find the option using the matching value there we go and inside of the dependency array of use memo we add options and the value so that way we know which value is currently selected because we are going to pass the ID so we have to find it in the array of options and now inside of here let's go ahead and pass in all the necessary props like placeholder to be placeholder class name text small height
10 so in this I'm just matching the Styles which are in the input component right so I think inside of here you can find height 10 and text small right so I I'm doing that here instead and some Styles we are going to have to change like this so react select has its own API for styling so instead of here we are going to get control get the base open and return an immediate object spread the base which basically means keep all existing Styles except border color which we're going to change to be E2 E8
F0 and on Hover we're going to change the same thing so border color and just copy this again like this then we're going to pass in the value to be formatted value and on change it's going to be on select and options are going to be options on create option will be on create my apologies not on create options but on create on create option individual and is disabled is going to be our disabled prop so now what we've created is a generic reusable select component so because of this generic naming and props we can
use this as a select component which will be able to list and use to create new categories new accounts whatever you want in the future right so uh it's very very modular that's what we wanted to achieve here now we can head back inside of our where is it inside of our features transactions components transaction form and now let's go step by step so first of all I'm going to uh log this and instead we're just going to go ahead and log the values for now because we're going to have to do some modification with
the values and you're going to see why later uh so let's start uh with uh accounts right so let's go ahead and choose for the first field to be account ID here and let's change this for to be account so the user is choosing the account and it's not going to use input it's going to use our new select component from components select so just make sure you've added uh an import for that I'm just going to move it here we we are going to have input as well so don't remove it so we have
the uh select here so let's go ahead and give give it a placeholder of Select an account let's give it options to be account options let's give it on create to be on create account value to be field. value and let's give it unchange to be field onchange and disabled is going to be disabled like that so I think that we can already see this in action I'm just going to refresh my Local Host to ensure that the server uh is has hot reload up to date so now let me go ahead and refresh one
more time just in case there we go when I click add new I have select a new account and there we go it loads cash and if I go ahead and type new account and press enter you can see how the form is disabled and we just reused our hook which you can see immediately added the account here as well so yeah okay this is an accident right because this data table is loading accounts so ignore that right don't don't forget that we are loading accounts here because we copied and pasted from accounts but as
you can see that it's here as well so now when I go here you can see that I have new account if I write test again it's immediately added here and it's immediately added here perfect so that's what I wanted us to do with our nice reusable select component beautiful and now let's go ahead and let's copy this form field entirely paste it just below this above o this button here and this one is going to control the category ID right here so this will be choose a category and it's going to work in exactly
the same way so select a category so category options on create category and this I believe is exactly the same so let's go ahead and check that out so I'm going to refresh everything again and now inside of here there we go I have food but if I go ahead and select something new there there we go it's created right here and if I go into my categories it is right here created beautiful so you can see how powerful those hooks are because we can easily add them everywhere and we have the type safety so
we are confident in our code and we know that we are handling this in a correct way beautiful so now above these two Fields I actually want to start with not the account or the category I want to start with choosing uh the date so for that we're going to have to create a component called date picker chaten does have a component called date picker but you cannot install it uh like any other component so it's similar to our form component we need to use popover and calendar to install it so this is what we're
going to do we're going to add those two individually and then we're just going to build our own because ours is going to slightly uh be different from the ones they have in their example so let me go ahead and just uh go inside of my terminal here and do bonex shat CN UI latest and let's add calendar and besides the calendar let's also add uh the popover component right here so let me just confirm that I have everything I need here before I run my app so inside of my package Json uh I think
that now I should have installed react day picker so I think that adding uh adding the calendar automatic installed that so just confirm your package Json that you now have a react day picker component here great we now have the popover and the calendar and react dat picker so let's go inside of components and let's create our date picker TSX component let's go ahead and import everything as react from react let's import format from date FNS which we already have installed because we needed it previously let's import calendar from Lucid react but let's do it
calendar as calendar icon because we are also going to have a component called calendar and now let's simple select single event handler from react day picker let's import CN from lib utils let's add our button component from s/ components UI button and here we have the calendar comp component from /ui calendar or components UI calendar and lastly we need to import all the things from our new popover so that's going to be the popover the popover content and the popover trigger component there we go now let's go ahead and let's define the type props here
so we're going to have value to be an optional type of date on change is going to be select single event handler and disabled is going to be an optional Boolean let's export const date picker let's go ahead and assign these props here and let's also destructure them so we're going to have value on change disabled now inside of here we're going to go ahead and open the popover component so this should be popover inside of here we're going to add the popover trigger component which is going to have as child property with a button
component inside we're going to give some attributes for this button starting with the disabled to be disabled then we're going to have variant to be outline and then we're going to have a class name to use CN in the default classes it's going to have full width justify start text left and font normal and then dynamically if there is no value we're going to show text muted foreground to indicate the user that date has not been selected then in here we're going to use our calendar icon with the class name of size 4 and Mr
of two let's just properly spell class name for jsx components and inside of here we're going to check if we have value we're going to use a format method otherwise we're going to return a span pick a date in the format method we're going to pass in the value in a format of PPP like this and then let's finally add the popover content and inside of here let's add a calendar component which is a self closing tag so inside of here we're going to give it a mode of single we're going to give it a
selected of value we're going to give it on select to be on change and we're going to give it disabled of disabled and initial Focus like this and I forgot to add return around here so this would never actually render if we don't add that and there we go that is our date picker component we can now head back inside of our features transactions components transaction for form and let's go ahead and let's import our date picker from components date picker here and I'm going to ahead and copy the form field here and I'm going
to paste it above so this is going to control the date so I'm going to write well we don't have to say anything here so we can remove the form label entirely and in place of Select we're going to add our date picker component let's give it a value of field. Val value and let's give it unchange to be field. unchange and let's give it disabled to be disabled like that there we go so let's ensure that we have our app running for example I don't and let's try this out so bun runev or npm
runev depending on your package manager or your runtime let's wait a second for Local Host to spin up and now if I go ahead and try and create a new transaction I have pick a date option there we go perfect and it when there is no selection you can see how it's kind of transparent to indicate to the user that nothing has been selected great so we have that and we have category ID and account ID now let's add a simple one for the pay so I'm going to go inside of my transaction form and
I'm going to go ahead and copy the last one category ID and I'm just going to paste it below so this one is going to control the pay field so we're going to write pay here and instead of Select we're going to have a good old input component so just ensure that you haven't accidentally removed the import for the input component here okay let's just scroll here to the P inside of here we're going to give it disabled if it is disabled we're going to give it a placeholder add add a p and we're going
to pass in the field property we're going to spread the Field property so pay is just going to be a plain text right anything can be inside uh great so what I want to uh create next is let's go actually with a simple one let's first add the text area here so let's do bonex shaten UI latest add text area so this is another component that we need for our notes so add this and now we don't have to modify anything here so we can just copy and paste the form field for the pay and
this one will control the notes so let's write notes here instead of input this is going to be text area from components UI text area so just ensure that you've added uh this import Right Here and Now what we are going to change is the following let's move the field to spread here so we overwrite everything after it so it's important the position of where you spread the field so move it here and then add value to be field. value or empty string like this and the placeholder is going to be optional notes if you
refresh your Local Host every time you shut down your app you have to refresh it again uh even though you have Local Host running right because the hot reload has been broken all right so let's we click add new and there we go we have optional notes and we have pay perfect so what's missing is the amount component so that's what we have to develop now let's start by adding a couple of packages required for the amount input component which is next so first of all let's do bun add or npm install react currency input
field if you want to you can of course develop your own right but uh this is a very good one it has the perfect masking that I want and I can easily make it look like shaty and UI component so it works perfectly fine for me and then let's do BX shat CN UI at latest and let's add a tool tip component here so that we can indicate to the user some useful actions and let's do Bun Run Dev so I'm going to go ahead and close everything and go inside of my components and create
a new amount input. TSX let's go ahead and let's import the currency input from react currency input field let's import all the icons we are going to need from Lucid react so those are going to be the info the minus Circle and the plus Circle components let's prepare our CN and now let's add from components UI tool tip everything we are going to need so we're going to have a tool tip component itself the content the provider and the trigger component let's define the props here so value is will be string on change will be
value string or undefined and aoid and placeholder will be a string optional and disabled will be an optional Boolean so why am I using string as the value here well that's because this react currency input doesn't just Mas things it also handles the decimal uh the well what's the correct term for this the numeric value right we remember that problem we talked about that JavaScript works with floats so we don't have that Precision well in here they handle that right and in JavaScript that doesn't exist in any other way than a string right so that's
why we have to work with strings here so let's go ahead and let's just quickly Define this component so const amount input let's go ahead and attach the props here value on change placeholder and disabled and now inside of here all I want to do for now is just return a div hello the reason I want to do that is because I want us to go back inside of the transaction form component features and render it so that we can see it here and so that we can see what we are developing so let's go
inside of the transaction form and let's go ahead and add our new amount input component from components amount input here right here and I want us to go uh and copy the pay and paste it just above notes so next to last so this is going to be the amount field so let's go ahead and let's write amount here instead of input it's going to use the amount input and for now what we're going to do is we can spread the fields like this and we can make the placeholder be 0 point0 0 like this
and I'm going to refresh my Local Host here because I've shut down the server and all I want to do is I want to see that component so for now all that should appear is a text which will say amount input I believe uh hello yes there we go so we have the amount label and we have hello as text perfect so that is enough for us to start developing this so first of all let's go ahead and let's do the following let's go ahead and let's par the value so par value will be parse
float value but we are only going to use it for this to detect whether something is income so if pass value is larger than zero or if something is an expense so par value is less than zero so that's why we are using parse float here just so we can do these comparisons great uh and now I want to do the following I want to define a method const on reverse value so our users can easily change something from being an expense into an income or vice versa so usually they would have to manually write
the minus and they can still do that but I want to give them a useful little button for that so first of all if value was not entered no no point in even doing the math here otherwise we're going to do an onchange here and we're going to pass in uh open parentheses parse float value times minus1 to string like that or you can do const reversed new value you can do that for example and then just do this and then do new value to string perhaps is whoops perhaps this is clearer there we go
uh and now let's go ahead and let's start developing this so first of all we're going to have a div but the div is going to just have a relative class name so we can position elements inside uh and then let's add a tool tip provider here like that and let's go ahead and give it a tool tip child let's give it a delay duration here of 100 so it opens a bit quicker let's add a tool tip trigger component so let's give it an as child option because inside it's going to use a native
HTML button element this will have a type of button on click here will be a very simple on reverse value which we've just created so the user can easily change something from an income to an expense and now let's go ahead and give it some class names here so class name is going to be our usable CN by default it's going to have BG slate 400 on Hover BG will be slate 500 it's going to be have an absolute position so that we can position it how we want so top will be 1.5 left will
be 1.5 you're going to see why this will make sense when we add the input component right so let's add rounded medium padding off two so there we go you can already see the little button right here but it doesn't have anything inside so padding off two Flex items Center and justify Center and transition and then inside of here we're going to do the following if there is no par value at all in that case we're going to add an info like this and we're going to give it a class name of size three and
besides size three is going to have a text white let's copy this two times for the second one we're going to use plus Circle and for the last one we're going to use minus Circle so this one will not just check if there's no pared value instead it's going to check if is income and the last one will check if is expense like that perfect so there we go now we have an info component Here and Now outside of the trigger we add a tool tip content which will simply say use and I'm going to
specify plus like this for income and minus for expenses like this so there we go now when the user hovers they know to use Plus for income or minus for expenses or they can simply click here you're going to see how intuitive it is later let's go outside of the tool tip provider and let's add our currency input component right here Here and Now what we have to do is we have to style it so you can see it's right here but we have to style it a bit so let's go ahead and do the
following let's give it a prefix of a dollar sign we can change this later right but let's keep it dollars for now and now we're going to have going to have to write a pretty big class name here which is going to just basically match uh our input field so what we can do is we can go inside of UI components I input so if you don't want to write it out you can just copy the entire thing from here so just these default class names here and just paste all of that here like that
there we go so you can see how now it looks exactly as our input above but we have a little problem here so what we're going to add in this currency input before everything is a very simple padding left of 10 so we leave space for this there we go perfect so now we have the class name now let's add a placeholder here to be our placeholder let's go ahead and give it a value of value let's give it a decimals limit of two and let's give it a decimals scale of two as well so
I always want to work with two decimals onv value change here is going to be onchange and disabled is going to be disabled and just below that I'm going to add one more thing just a very simple paragraph which will check if is income in that case it's going to say this will count as income otherwise if is expense it will say this will count as an expense and we're going to give this a class name text extra small and text muted foreground and margin top of two like that let's go ahead and try out
our new component now so there we go this will count as income but if I add a minus this will count as an expense and I can switch it from here as you can see well let's go ahead and add some color indication uh just so it looks a little bit better so inside of this button here we Define class name with the initial values and now let's add if is income we're going to add BG Emerald 500 and let's go ahead and do the following let's add we can copy this and we can add
on Hover BG Emerald 600 copy and paste this and let's do is income instead of emerald we're going to use rows like this there we go so now this will count as income actually let's reverse the values I think I did it incorrectly this is is expense yes my apologies uh there we go so this will count as income but if I click here it says this will count as an expense perfect so user can either write like this and then they can add their own minus or they can always switch from here and it
will and they if they have any doubts right here we have a little uh tip for them how this will count so they know exactly what's going on perfect so I believe our transaction form is now ready to be submitted so let's go ahead and try this out let's just confirm inside of the new transaction sheet right here that we're actually using the create mutation it looks like we will we are I think that there's nothing else we have to do here so this is how we're going to try it out so I'm going to
go ahead and open uh my API transactions here there we go so Local Host 3000 API transactions and inside of here I'm going to go ahead and try and create a new one so I'm going to pick a date uh 9th of May I'm going to choose the cash account the food category or whatever you want the pay is going to be pay and I'm going to choose an amount of 500 and I'm going to write test here and I'm going to click create an account and let's see looks like something is wrong here so
we're going to have to debug uh oh we are not submitting anything yes so okay these are the values here account ID amount category D date great so this is fine but I forgot we have to do one change before the submit it's about the amount field so if you remember we made a decision on how we are going to store our amounts and we ended up on this using integers of the smallest unit of the currency or in our case we're going to use milliunits which means we have to convert this to milliunits so
this is an example of 10 and 15 cents in our case we have $500 right so we're going to have to modify this uh using well we're going to create our own little uh reusable lib for that so we can use it everywhere where we need to and the reason I uh want to do this on the front end is uh well I looked at API specifications of existing applications which do this and they are API and they do the same thing and also because it's a good argument that it's the same way we parse
dates right so uh we don't modify how the date is being sent on the back end it just expects a specific format and it returns it in a specific format as well which is the iso string and then the frontend has to take care of making it look good so it makes sense to use the same logic for the our custom amount situation here so what we're going to do is we're going to go ahead and we're going to create a method called convert amount to milliunits so let's go inside of our lid folder inside
of the utils here and let me just go ahead and order these how I like it so let's export function here convert amount function convert amount to milliunits is going to accept an amount which has to be a number and it's going to Simply return math.round amount times a th000 like that let's also create the equivalent opposite which will be convert amount from milliunits which will very simply get the amount and divide it by a th000 so we're going to use oops let me just close this so we're going to use this to create the
amount we can store in the database and we're going to use this to show that amount to the user right because user doesn't care about milliunits user needs to see that in dollars or cents or Euros or whatever your currency is right so now that we have that we can head back inside of our features transactions components new transaction form and we can head back inside of our handle submit method here so what we're going to do is we're going to get con amount in milliunits convert amount to milliunits and pass in let's go ahead
and Define our amount to be pars float values. amount and pass in the amount like this so just confirm you have imported the con convert amount to milliunits Here There we go we have this and now we can safely submit that in here by doing the following spread the values and pass in amount as amount in milliunits and there we go we now have no type errors inside of here perfect uh yes I think yeah the reason we needed the custom form schema was because of the amount we needed to keep it as string here
right that's the important important thing great let's go ahead and try this out again now so I'm going to go ahead and let's use our specific situation which was it 1015 let's say that's our income 1015 here H or maybe it will be make more sense to this to be an expense and let's click create an account oh it still says create account so we have to change that okay let's refresh this and there we go and you can see exactly how it's stored exactly how we wanted so we have the ID we have the
date so 8th of May food category ID so everything is here exactly as we expected it to be perfect so before we wrap up the chapter let's just fix this button do not say create account but instead let's make it say create transaction right here and delete transaction we're going to see this later in the next chapter when we enable editing of individual transactions so we just created a bunch of new components so feel free to take a look in my GitHub source code if you are unsure and see you in the next chapter so
now that we are able to create our transactions let's go ahead and let's modify this page so that it actually loads transactions so that we can see more information and finally so that we enable editing the transactions so let's go inside of the app folder inside of dashboard transactions and let's go inside of page. TSX so inside of here we have a lot of instances of accounts because we copied this from our accounts page so let's go ahead and do the most important one use get accounts so we are no longer going to need this
import so let's remove it and instead let's add use get transactions uh not transaction but use get transactions multiple like this so make sure you have an S at the end and then we're going to use that here so use get transactions and this is no longer going to be accounts query but instead it's going to be transactions query so I'm going to go ahead and resolve these three errors which I have right here and I think I also have one down here so it's loading accounts and let's change that so this is accounts let's
change this to be transactions like this let's go ahead and add transactions right here but we are still going to have an error here and that is because of the columns but let's before we go to the columns let's also remove this so we no longer need use bulk delete accounts instead we need use bulk delete transactions so let's add that here and let's rename this to delete transactions let's fix the errors here and let's add the mutation here there we go so we replaced all hooks I believe now if I search for account there
are no instances of the word account inside of this file great what we have to do now is we have to resolve our columns so let's go inside of columns right here first of all let's fix the response type so we are no longer looking into the accounts here instead we are looking into transactions and I believe that get 200 data and this is exactly the same there we go so this is our response type ID date category category ID pay amount notes account ID and account great and now let's go ahead and let's modify
this so we have uh these accessor keys for name so we're going to change this instead of name the first one is going to be date right here and the header will say date like this and let's go ahead and let's modify the cell right so I'm going to add cell here I'm going to let me show you why I'm doing that right so right now if I just save columns as you can see there is no there are no longer errors here because the columns now match but let me just show you that now
if you have an existing transactions this is how the date will look like right so go ahead and create a transactions let me pick for example 9th of May let me pick something here test test let's create a transaction there we go so you can see how this looks right so let's go ahead and resolve this Al so we're going to go ahead and go inside of columns right here and we're going to add after our header here add a cell property go ahead and the structure row and return a method which will very simply
return a span element Here and Now what we are going to do is we're going to write const date row get value whoops let me scroll up so you can see get value date as date like this and inside of here we're going to use our format method from date FNS so just make sure you've added this and we are going to formit it by passing in the date and then we're going to use a specific format you can use anything you want of course I'm going to go ahead and use this format because I
like it the most so 9th of May 2024 there we go let's just confirm that this date picker is working so if I choose 12th of May and choose some random stuff here let's just see it was created but why is it not here all right we might have a little bug here but we're going come back to this later okay we're going to debug that but let's first just finish our column so we don't get sidetracked all right so we just finished the date and now what I want to do is I want to
copy and paste this entire thing so with the accessor key header and cell let's copy and paste it and this one will go to category so this will be category like that and now inside of here well for now what we can do is we can just say uh row. original do category like this there we go so this will just save food so for now let's keep it like this let's keep it simple now let's go ahead and copy and paste this again right here so just above actions we're going to call this p
and this will be p and inside of here we don't need cell at all because it's just going to be plain text right we don't care so for the one above we're going to change this later that's where I uh rendered cell in a custom way so let's go ahead now and let's copy our existing category so copy category because it has the sell option which is what we'll need for the next one so skip the pay and now let's add the amount so this will say amount let's go ahead and let's get our amount
using pars float row get value amount like this and then inside of here let's go ahead and let's write format uh currency which is something we don't yet have so we're going to have to go inside of our utils and we're going to create the format currency method so let's do that I'm going to go inside of lib folder utils and let's export function format currency so this is going to be for the front end only uh it's going to accept a value which is a number and inside of here what we are going to
do is we are going to return a native JavaScript API for formatting so intl. number format n- us and then open options so style is going to be currency currency will be us doll minimum fraction digits will be two and then we're going to attach dot format and pass in the value there we go now go back inside of columns and you can import format currency from Li utils and pass in the amount so just ensure that you've added this import right here let's check this out and there we we go so you can see
how this looks now and you probably also see a little bug here so this is definitely a larger amount than what I've entered but don't worry we are going to resolve this later because remember we are working with that milliunits value right so we have to take care of that on the front end but we're going to do it later uh all right so we have this and now what I want to do is I want to use the badge element instead so let's go ahead and do the following let's go inside of the terminal
and let's do bonex shat cnii at latest ad batch like this let's do Bon run Dev again let's refresh our local host and in inside of here instead of using span we're going to using be using that new badge component which we've just installed right so just make sure you've add you've added this all right and now we're going to go ahead and give it some properties so variant if amount is larger than zero in that case it's going to be destructive otherwise let's go ahead and let's make it default and let's give it a
class name of text extra small font medium PX 3.5 and py 2.5 like this so let's see if this is going to improve anything all right so it kind of looks better but we're going to go ahead and we're going to modify uh these variants now so let's go inside of our components folder UI and go inside of badge directly so I want to add another uh variant here so right here after outline I'm going to add primary to be border transparent BG blue 500/10 and text blue 500 and then inside of my colums inside
of transactions right so where we just were adding the badge instead of using default I'm going to be using primary like this so let's see how that looks like there we go so this is how I want it to look like for positive income and for negative income I want it to look red but not that red so I'm going to go and find the destructive variant which is what we're using here right so find destructive and I'm going to change this to be uh BG destructive slash1 and I'm going to go ahead and remove
the hover like this and I'm going to leave the text to just be destructive as well so text destructive and BG destructive SL 10 and Border transparent so that's how I'm going to modify the batch component and there we go this looks like something now great so uh let's go ahead and see what else do we have to add so now we have to add the last field here which is going to be the uh account so go ahead and find category and copy it because it's the most similar one so just above the actions
go ahead and add account and this will say account and inside of here row. original. account we are going to modify both the account and the category later and there we go so we now have uh everything we need here to display the proper transactions and now we have to discuss this uh amount option so what's going on this is in milliunits right so this is I believe maybe $1050 but we store it as 1,500 I think right so that's an issue right we we store it in milliunits so you have to find one place
where you would convert those milliunits into uh well dollar right so you can choose how you want to do it if you are certain that uh you are always going to do always going to wrap currency inside of this format currency what you can do is you can uh convert this value for example const final value into convert amount from milliunits and pass in value here and then add final value here instead so just import convert amount from you actually you don't have to import it's right here and if you try it now there we
go so now it looks normal right so that's one option you know but I kind of don't like this I don't know why I don't want to depend on this because sometimes I want to use some different formatting value maybe so uh let's bring this back to just use value and instead I'm going to show you uh what I did in my code so what I did origin you can choose which option you like what I did was I went into features transactions uh API use get transactions inside of here I modified the data so
this is what I did I returned data. map I got the transaction inside of here and I spread the transaction and I only modify the amount by adding convert amount from milliunits so make sure you add this import right here and I simply passed transaction do amount instead so that is what I did and now I don't care about my formatting function right let me just refresh to see if this is still working there we go so you can see how now it's working just fine because uh this is kind of closer to the back
end let's say in some way right this is right here in in use Query you can of course find many different ways to uh transform data with react query there's also some called select it's not here it's outside of so here you can add select for example you would get the data so you could do that here if you want to they have a couple of blog posts about converting data right I think this is completely fine for our case right um You can also play around with memorization or you can do the formatting thing
right uh whatever you you like but I think for the tutorial is this is more than enough and it kind of gives me confidence that my data will behave exactly how I wanted regardless of where I use this hook because I know my data will be normalized here uh great so we resolved that now let's go ahead and let's see uh what's going on with our little bug there so I think if I'm going to keep my terminal open to see what's going on I'm going to create a new transaction and I think I picked
12th of May I picked an account I wrote something I added something and I clicked create a transaction and transaction seems to have been created right here but it's not loaded here and if I go into API transactions I also only have to all right so something is wrong uh so I'm going to go ahead and debug that in the next chapter for now I think it's enough for us to see uh these uh transactions right here all right so I realize what is the buug no need to debug it in the next chapter so
this is actually quite important to mention in case you're encountering this there is no bug nothing is happening so the the records actually do exist but if you remember inside of our used Gap transactions here we have this from and to params and we are passing empty ones at the moment so if you remember our API for transactions inside of the global get right here let me close the other ones instead of the first get one if we don't have from and two we default to the last 30 days which means if you create a
transaction in the future it's not going to be displayed here so go ahead and try and create a transaction in the last 30 days so for me it's the uh 9th of May so if I choose the 1 of May it's not going to have any problem so let me show you this test and let's use this much test create a transaction there we go it's right here great so we can continue developing on this I thought we were going to have to debug something but we don't great so what I want to do now
is I want to modify this category and this uh account field so that we can easily switch it right I want to click here for example and also if something is uncategorized I want to display uncategorized option here so let's go ahead inside of our columns for transactions where we just added the account uh the last one right and let's go ahead and do the account column first so inside of the transactions here we're going to create a new file account- column. DSX like this and let's go ahead and do the following let's import use
open transaction uh use open account my apologies right here and let's go ahead and import CN from at/ li/ I think we're going to need this let's write type props here and let's make it an ID of string account of string or null and account ID of string or null as well let's export const account column and let's make this ID account and account ID and let's just assign the props Here and Now inside of here I want to create a div which will very simply either say account or well it has to have an
account actually there is almost no chance that the account doesn't exist right so we can do this right account always has to exist and this will be account ID uh and let me not misspell account there we go so account always has to exist right we don't have to worry about that here so I'm going to go ahead and give this a class name of flex item Center cursor pointer and hover underline like this and now what we can do is we can also import we can use this use open account here so let's do
that const on open on open account let's give it a Alias right right so it's more readable so const on click very simply it's going to call onopen account with the account ID so we are reusing that hook to edit any account so we can add this on click here there we go and now we can go back inside of our columns we can find the account here find the cell and instead what we're going to do is we're going to use that account column from / account column so just make sure you've added an
import for the account column here and pass in the ID to be row. original. ID account row. original. and account ID row. original. account ID like this and let's see if we are missing anything inside of here we actually don't need the ID property so we can remove this and we can remove the CN import and now we should get an error here but my typescript is slow today oops so I should get an error here there we go so remove that and now if I refresh here when I hover there is an underline and
I should be opening this but it looks like it's not opening so let's see uh is there any error being thrown there is not all right so let's see what's going on uh row original account ID so I'm going to go ahead and do the following I'm going to conso log the account ID and see if that changes anything so let me refresh this and let me keep my console open all right so we do have account ID but it is not opening let's see accounts so this is opening just fine all right so something
is a bit off it seems all right so this is the issue it is very very unusual so I did some changes here because I could not understand why the account was not opening well it's our fault more specifically it's my fault I didn't look get the import we are importing from transactions hooks because we copied the entire features from accounts including the hooks we need this hook but I am importing this one use open account so we don't need that we need the one from accounts my apologies I I was losing my mind over
why this is not working but let's try it out now so if I refresh there we go and I can rename this to cach 2 and okay so now we have to go back I believe inside of our features accounts API use edit account there we go invalidate summary and transactions so let's now add transactions inside so if we try this again now let me pick an account here so cash 23 save changes there we go so we can now easily modify uh an account if we want to and now let's do the same thing
uh for the category so I'm going to go inside of my app folder dashboard transactions and I'm going to copy and paste the account column and I'm going to call this category column inside of here we're going to have category and we're going to have category ID but the thing with this is that it can be uh undefined so we're going to add or null here and we we are also going to have the ID here which is something we didn't have in the last one so inside of here we're going to have an ID
we're going to have a category and we're going to have a category ID like this and instead of use open account this time we're going to have an import use open trans uh category from features categories hooks use open category let's add it here let's Alias hit to onopen category and we're going to pass in the category ID inside uh but only if we have category ID like this and inside of here we are going to write category or uncategorized and we are also going to say if we don't have category in that case we're
going to run render a triangle alert icon from Lucid react so we kind of warn the user let's give this a class name of MR2 size 4 and Shrink Z and let's also add the import for CN from Li utils let me just separate this from the features above and let's rename this to category column of course so I'm going to go ahead and and turn these into hardcode from hardcoded classes into Dynamic so this will be the default ones and then I'm going to add if we don't have transaction sorry if I don't have
category in that case it's going to be text rows 500 like this there we go let's go inside of columns and the same way we used account column let's find the category there we go so find the accessor key category find the cell and change this to use this but instead it's going to be category column so import category column just below the account column here this is going to be category and this will be category ID and we are also missing the ID which will be row. original. ID you're going to see why we
need the ID in a moment so and now let's also prepare ourselves by going inside of features categories API use edit category and go ahead and add invalidate transactions here so we can remove this part and we can also do it in use delete category so we can do that here as well like that and we can do the same thing in accounts delete account same thing transactions all right uh and now let's go ahead and check this out so if I refresh here and if I select food it opens food if I rename it
there we go all categories are now renamed perfect but we still need to handle the case uh if category doesn't exist and we're going to go ahead and do that next so we can yet do it because we are missing a hook and the hook we are missing is features transactions hooks this one so right now we have use open account let's create use open transaction finally so this was the hook that caused that problem because it's named exactly the same because we copied it so open transaction State here and everything else can stay exactly
the same and now we're going to go inside of this component components and we are going to modify the edit account sheet to be edit transaction sheet there we go let's go ahead and let's rename it so edit transaction sheet and let's immediately go inside of providers sheet provider and let's add it here so import edit transaction like that and let's add it right here so we don't forget that great and now inside of here we are not going to use use open account so we can remove that instead let's add import use open transaction
from do/ hooks or features transactions like this so we are basically going to have to replace all instances of anything account based like this so let's go and remove use get account in favor of use get transaction so I'm going to change this to Features transactions like this let's find where we use use get account we use it right here let's modify this to be transaction query and let's use it right here let's see if we're using it anything anywhere else and we are using it right here so let's go ahead and modify that so
if we have transaction query. data in that case we're going to have account ID B transaction query data account ID and then we're going to have category ID then we're going to have uh amount but we're going to do amount dot to string like this uh then we're going to have date so we're going to check if we have the date in that case we're going to parse that as an actual date like this so let me go ahead and collapse this otherwise it's just going to be your basic new date uh next one is
going to be pay so that's going to be transaction query data pay and last one is going to be notes like this so let's go ahead and just make this all empty so account ID empty category ID amount date pay and notes like this there we go so those are our uh default values here let's see what else we have uh so we have use edit account let's remove use edit account in favor of use edit transaction from features transactions API so let's let's see where that is there we go use edit transaction we didn't
have to modify the naming here that's fine uh we have used delete account let's remove that as well in favor of use delete transaction like this from features transactions like that and let's use it here the naming is fine as well great uh now we have to modify the form values so we no longer need insert account schema instead we need insert transaction schema so we're going to modify this to be transaction schema. omit ID true so not pick but omit right there we go so that should resolve uh a couple of errors here now
let's modify the naming here so it's not going to be edit account but instead edit transaction and inside of here it's going to say edit an existing transaction there we go we are no longer going to be using the account form instead we're going to be using the transaction form from do/ transaction form or you can rename it to Features transactions components whoops transaction form and let me just move it with these ones and I can now remove account form and if I search for account there we go I only have this instance it looks
like okay four others so first let's go ahead and do this you about to delete this transaction let's see where else do we have this account ID is fine great this is fine perfect so now we have to resolve this so the default values uh seems to have some issues so we'll resolve that in a moment what we have to do first uh is we have to also add a key here ID so it destroys reenders this form on every CH actually we might not need that I think that was from my previous uh exercise
that I did all right so this is what we have to do now we have to go inside of our new transaction sheet and we have to do this remember we need the category and the account options and let's go ahead and copy all of that and let's add it to the code right here so I'm going to do it just after our transaction hooks we add all of these here and we can actually copy the Imports it might be easier they are right here so accounts and categories or you can just manually add these
so let me separate these from transactions let me save and there we go I no longer have any of these errors right here now let me extend my is pending so besides this edit uh and delete we are also going to have the uh transaction query is loading we are also going to have category mutation is pending and we are also going to have account mutation is pending and for the is loading we are going to have transaction query is loading category query is loading or account query is loading so a bunch of options uh
like this there we go so now let's go ahead and let's add all of those things to the transaction form or we can just copy uh from here the props should be exactly the same and don't forget to bring back ID because I just removed it there we go so make sure you have ID inside of here uh there we go but we also need to pass in the default values right so let's add default values to be default values right here and this is the place where we are getting some errors so the property
uh date is not assignable it seems so let me see if it's maybe because of this what if I just mark this as undefined okay so it's because of that okay so can I just write um new date here okay that seems to solve it so it just must not be a string all right there we go so I think that we are ready to try this out uh of course I have these errors now this is again typescript uh losing its mind okay it's good now so this was a very big component you can
of course copy it uh from my GitHub if you're unsure and we are not using on Delite anywhere so let's go ahead and do that so we have uh on submit but we don't have on delete because this is an edit method there we go on delete let's try this out now before we can try it out we have to go inside of app dashboard transactions uh so inside of columns here but more specific Al we need our actions so the last one right here so this is the last place to replace the account instances
so let's import use open transaction here let's see where we are using it we are using it right here it should be throwing an error there we go it's throwing an error and now it's no longer throwing an error and the last one use delete account replace that with use delete transaction there we go let's add that here and everything else should be exactly the same and let's just change this to say you are about to delete this transaction there we go I think that should be it so now if I go ahead and click
edit there we go I can now edit this perfect so that's exactly what I wanted to do great so let's go ahead and try something else now so now I'm going to go ahead and create a new one make sure it's in the last 30 days but this time I'm not going to select a category there we go so now it says uncategorized so now when I try and open this nothing happens so what I want to happen is that when I click on something uncategorized I want the edit to open so we're going to
go back inside of the columns find the category column so that's why we are passing in the ID so that inside of here I can add an open on open transaction use open transaction like this right so I want another feature here use open transaction and then else if we don't have we're going to use uh onopen transaction here uh with the ID from the props right because we are passing it so now if I finally refresh and if I click uncategorized there we go I can now select the category I can save and it
is resolved perfect let's go ahead and let's try individual delete from actions confirm there we go let's try and open this and delete from here there we go and let's select these three and click delete there we go works perfectly and now what I want to do is since we copied the entire features thing from trans from accounts let's go ahead and right click let's click find in folder and let's search for account all right so account ID is fine that's completely fine right uh use get accounts that's fine use create account all of those
things are fine so I'm looking for something that might tell me that I forgot to rename something but all of these are just options to uh you know fill the account field so that's completely fine now I just want to check inside of my app folder dashboard trans transactions so I'm going to right click find in folder again account so account column that's completely fine columns itself uses the account column that's completely fine as well so nothing strange here perfect so we now have a working transaction history but one thing I think we forgot to
do so let me again try something in the last 30 days and I'm going to call this for example search and let's give this a price so if I try and search this nothing happens I cannot filter here that's because we forgot inside of our app folder dashboard transactions page we forgot to add a proper filter key so let's make that pay like this so now if I write search it's this one if I write something else it does not exist amazing amazing job you've wrapped up the transactions page well not exactly because because in
the next thing what we have to do is we have to enable file upload via CSV so that's what we're going to do in the next chapter we are going to use that bulk create endpoint great great job so now that we've wrapped up the data table for transactions I want to add another button here which is going to be used to upload a CSV file so let's go ahead and do the following let's go inside of our transactions page. vsx right here and let's create an Anum here at the top so I'm going to
create an Anum variants first variant is going to be a list representing the current state that we see right now the list of transactions and then we're going to have an import variant like that and let's also create a constant initial import results and that's going to be an object which has data which is an empty array errors which is an empty array as well and we're also going to have meta which is an object this is uh going to be used for a package that we are going to add right so I'm just preparing
that here now inside of the transactions page let's go ahead and create a use state from react here so just ensure that you've added use State from react like this and now inside of here let's go ahead and let's add a variant set variant here and let's give it a default of variance. list and you can go even further and Define the exact type to be variance inside there we go so now what I want to do is I want to find I want to go after these this if Clause right here before the return
and if variant is variance Import in that case we're going to return a fragment and for now we're just going to write a div saying this is a screen for import so we are going to render a completely different thing if the user selects the import variant so if I refresh my app everything should be exactly as it is now but if I change this to import this should change right here there we go this is a screen for import so that's what we want so let's bring this back to be the list for now
so now let's create a button which is going to go alongside this add new button so I'm going to call this upload button right here and we're going to have a property on upload for now just an empty method like this and we actually don't have to collapse this like that so let's go inside of the transactions here and let's create our upload button. DSX like this and from here I want to go ahead and I want to install a package so we're going to install npm install or bun add react Papa pars so that's
the package that we are going to use let's go ahead and do bun run Dev again let's import the upload icon from licid react and let's import from react Papa pars use CSV reader and let's import a button component from components UI button let's write the props on upload is going to give us the results which can be any and a void and let's export const upload button here assign the props and let's destructure the on upload here now inside of here we're going to go ahead and do the following so we are going to
D structure whoops not here but outside of the return we are going to destructure CSV reader again my apologies CSV reader from use CSV reader like this and then in here here I'm going to add a comment to do add a pay wall so later when we Implement subscriptions and payments we're going to add a pay wall here but not for now so for now what we're going to do is we're going to use this CSV reader which we've just exported from here and inside of here we're going to open a method we are going
to open uh another method and the structure immediately get root props inside like that and give it a type of any go ahead and immediately return a button component let's go ahead and give it some props so it's going to have a size of small it's going to have a class name of full width button LG AO and it's going to have get root props executed like this inside we're going to use the upload icon with a class name size 4 and Mr of two and we are going to write import like this so now
let's go back in page. DSX and let's add the upload button uh export const upload button like this so upload button from do/ upload button there we go so if I refresh this uh I think that we should see our new uh button here next to add new transaction let's see there we go so right here let me expand all right but we do it looks like we have to add some modifications so that they are together and not so far apart so let me just zoom out so I can see what I'm doing here
and let's go back inside of the page here so let's find where we have this so this is here in the card header so what we are going to do is we're going to wrap these two buttons in a div together like this and we're going to give this a class name Flex item Center and GAP X of two there we go so now this is looking better so what you have to obtain next is a CSV statement of a bank account so I have an example here which you will be able to find in
my source code so I extracted the is from revolute so if you're using that you can go ahead and extract your States statements here so you can also pause the screen if you want to write your own so what's important here the most important thing is the dates and how they are written so if you want to write this yourselves you can pause the screen and write it you don't need all of these right you can it will work even with just one right just make sure you have at least two lines here make sure
that the currency has the same uh sorry that's the amount so make sure that the amount has the same format and make sure that the dates have the same format because we are going to work with these formats right here so you can find this file in my source code uh if not you can write one yourself and now what we have to do is we have to import that file so right now if you go ahead and click import you would choose that and you of course save that as a CSV file and you
would select that so now we have to change the screen when the user uploads a file so let's go ahead and do this let's go ahead and let's actually use this upload which we are not using anywhere so I'm going to go ahead and do the following on the CSV reader we are going to add on upload accepted and we're going to pass on upload and then inside side of here we have the on upload and we can use that um to change what we need right so let's go ahead and Define our on upload
here h on upload is going to accept results which will be a type of initial import results so they're going to look like this you can find more about that uh in the documentation of react popup RS so inside of here the first thing we are going to do is we're going to set the variant to variant. import that's the first thing we are going to do and now let's use this on upload and let's assign it here on the button so this on upload will be fired when upload is accepted on the CSV reader
so let's try it out so now once I upload this should change there we go this is a screen for import so that's what we are going to create uh next but first of all let's create another state here for import results so just below this I'm going to write import results set import results use State initial import results so we are going to use this as our default value let me show you how this looks like in one line there we go so we're going to go ahead and set the import results here to
the new result like that and now we are able to work with that so first of all let's also create a on cancel import here so that is very simply going to set import results back to initial import results and it's going to set the variant to variants. list in case we want to cancel the import great and now what I want to do here is instead of rendering a variance. import we are going to render the import card now import card does not exist yet so let's go ahead and create it so inside of
the transactions folder here let's create import DC card. DSX and let's go ahead and Export con import card return a div import card and let's go ahead and import it here slimport card and now there should be no errors and you should see the import card here great uh let's see if we have to give it some props first so inside of the import card I'm going to go ahead and create a type props to have data which will be a string a matrix of uh strings basically and on cancel will be a very simple
void and on submit we'll accept that data which will be any and return a void so unfortunately the react popup ours package is not uh strictly typed so it's it's just easier to use any with this but you're going to see what kind of data that's going to be and now let's assign these props here and let's use the data on cancel and on submit here like this uh great and now we can go back inside of the page here and we can assign all of those things so data will be import results. data on
cancel will be on cancel import and on submit for now will be just an empty method so if you're wondering how do I know this is data well let's try it out so on upload let's console log the results oops all right let's see the results when we upload so I'm going to expand this and I'm going to import a statement and there we go results data and this is how it looks like so it's a matrix of strings right let's go ahead and continue developing the import card right here so now when it has
all the necessary elements let's go ahead and just Define a couple of more constants here so const date format which we accept is going to be y y y- mm- DD HH mmss that's going to be the date format we support and the output format which our database accepts is this like that so we Define that here immediately so we know what we are working with so this is what needs to appear in your CSV files great and now let's also add required options so that's going to be the amount field the date field and
the pay so these three things are required to make a transaction great and I'm also going to go ahead and just prepare one interface here selected columns state so that's going to be uh key string which will be string or null like this and without an equals a sign so very generic you're going to see why in a second all right so what we have to do now is we have to import uh everything we need from card so we can do that here so how about we just quickly add that everything we need from
card uh and now what we can do and let's also add the button from components button what we can do is we can just copy from our main page this div and all the way up to the card content right so we can copy this part and add it in our import card here like this so now obviously we have a couple of errors here so first of all card content will just say hello and then we will close card content and then we will close card and then we will close the div we are
not going to use the upload button and we don't need the plus the on click can just be an empty Arrow method and the this will actually say cancel like this and this will not say transaction history but import transaction there we go so you can see how this looks now like that and now let's go ahead and enable the cancel button so I'm going to go ahead and use the on cancel and use it here so now when you click on cancel there we go it resets the entire thing so if you import a
vsv file it will change this to import transaction like that great uh and now what I want to do uh is I want to create uh something called an import table right but just before we can do that we have to do something else so we have to use this data and we have to separate the headers from the body so if you took a look in the console log right you know that the first item in the array let's take a look at it again so you know the first item in the array in
data whoops it's not here am I still uh adding the log let me just see inside of page here whoops inside of page when I upload something I'm still logging all right so let me add the import inside of data as you can see the first array is the header the type product right these things and everything else is the content so that's what we need to separate in the import card here so inside of here I'm going to add con headers to be data the first in the array and then body will be data
slice one like that so now I know what's the headers and I know what is the body and in here I'm going to prepare a use state from react so just make sure you've added this let me move it to the top here so use State inside of here will be an empty object but we are already going to prepare it to use the ected columns State and by default it's going to have the selected columns and set selected columns like that all right and now let's go ahead inside of the card content and let's
render import table which again does not exist yet but we are going to create it so it's going to accept headers it's going to accept body it's going to accept selected columns selected columns and it's going to select on table head select change which for now can just be an empty Arrow function so now we have to go ahead and create the import table component we're going to do that in the transactions folder as well so import table. TSX and we have to import everything we need from components UI table so let's go ahead import
table table body table cell table head table header and table row like this and let's define the props here so we accept the headers which are array of strings and the body which will be a matrix right so let's go ahead and Define selected columns here to be a record string any or uh it's going to be string or null my apologies this is the correct type and then we're going to have on table head select change so I know you're wondering what this is but you're going to see in a minute this is quite
complex to explain but basically we're going to create a type of uh table which will be able to use these like these things at the top the headers but instead there are going to be select options so you're going to be able to change these to not be category but you would be able to click here and then select that to be pay for example right we need to match the results from the uploaded file into what we expect here so uh basically this method is going to be this value so column index which is
a number value which is a string or null and a void so that's going to be that okay so those are our props let's export con import table here and let's go ahead and assign the props whoops like this let's destructure all the props so headers body selected columns and on table head select change and now inside of here we are going to return let's go ahead and scroll this a div with a class name of rounded MD and border and overflow hidden and inside we're going to add a table a table row uh we're
going to add a table header first and then we're going to wrap this table row inside we are going to give this table header a class name of BG muted and the table row itself will either over the headers map and inside of here we can skip the item and we can just use the index go ahead and open that and for now you can add just the table head here and render the index inside and use the key as index as well for now and then inside of here outside of the header we're going
to add a table body and the table body will iterate over body. map so it will get row which is an array of strings and an index and inside of here we're going to use a table row with a key of index and inside we're going to iterate over the individual row do maps we're going to get the cell and index and we are going to render the table cell finally so let's go ahead and give it a key of index render the cell and close the table cell so quite complicated I know but I
don't think there is an easier way to handle converting a CSV file into something we can accept into our database and allowing the user on the front end to modify that all right so let's import the import table from slimport table like this uh and we passing the headers and the bodies I think we should be good to at least see some results here so if I go ahead and import this statement there we go so this is what I wanted to load you can see how now I can load my exact uh information here
and now we're going to turn this indexes into component which will be able to select and choose which of the required Fields should we map the data for so we would choose this as the date we would choose this as the pay this as the amount so that's what the goal is with this great so let's go ahead and develop that next so again please just use a proper CSV file the date needs to be in this format right here I suggest you simply use uh the one I have in my repo or uh the
one I have in my repo is from revolute so if you have revolute just go ahead and create the statement export from there uh and one important thing right so after we add these keep in mind that most of them are going to be in the pack right so maybe they won't be in the last 30 days so perhaps when we upload them you're actually not going to be able to see them uh right here in the transactions right so don't get scared if that happens it's because of our date filter which only goes back
30 days right so don't get scared that something is wrong that's what we are going to confirm in the database as well so let's go back inside of the import table here and now we have to fix this we shouldn't just render the index instead what we we should do is we should handle table head select here it should be a self-closing tag and it's a custom component we are going to create so column index is going to be the index and selected columns are going to be the existing selected columns and on change will
be on table head select change like this and now in order to develop that component we have to do bonex shaty and UI at latest add select component so we already have react select but we don't have a plain old select component so that what we just added inside of our components now you should see in the UI folder a select component added great so now let's go ahead and let's go inside of the transactions folder and let's create not a folder but a file table head select. DSX right here let's go ahead and let's
define the props for it here column index which is a number selected columns which is a record string string or null and we're going to have onchange which is going to be uh make sure you use columns here so the first parameter will be the column index which is a number and the second is the value which can be string or null so it needs to be null because you need to give the user an option to skip specific Fields right now let's define all options here again amount pay notes and date basically just ensure
that they match where did we write our in the import card perhaps okay so these are the required options and these are the options that we can use right so just make sure that there are no typos with the required options here so amount date pay pay is with two Es so make sure you don't misspell any of those and in here we have the extra notes right because they are not required but user should be able to select that if they want to and now let's go ahead and let's export con table head select
and let's go ahead and return this let's assign the props let's D structure everything we need so column index selected columns and on change here great and let's return a div select now let's go back inside of the import table component where we have this error and let's import that from /table select so now when I import here all of them should say select right they're all Now using that same component great so let's go inside of the table head select and let's develop that component so first of all we're going to need uh to
add an import to everything from s/ components UI select that's the first thing so that is the select select content select item select trigger and select value here and we are also going to need to import the CN library from lib utils and now let's go ahead inside of the method and let's first of all Define the current selection if it exists so selected columns and inside of here we're going to use backx column on underscore column index so that's how we are going to Target our selected columns here let's go ahead and change this
to be the select component let's give it a value of current selection or an empty string so this should be current selection not current select like that so that's how we're going to track what's currently in this header right we have to specify it like this because there is no other uh non-generic way to do that right and this is how we're going to track it in the onsubmit method by looking on the indexes and then we are simply going to map everything under that index as whatever we select in this component so let's do
on value change here to accept the value and pass in on change with the column index as the first argument and the value as the second argument now inside of here we are going to add the select trigger component and let's give it a class name cm so we're going to give it a focus ring offset zero Focus ring transparent outline none border none BG transparent and capitalize and we're going to change if we have a current selection I want to indicate to the user that they already selected for this column by giving it a
blue color and inside we're going to add a select value which the placeholder of skip right if they don't want to select anything they can always Skip and now let's add a select content here and let's add a first select item inside which will have a value of skip by default and a label of skip inside like that so that's going to be the hardcoded option and now we're going to iterate over the options ourselves so options. map we get the option and the index and inside of here we are rendering a dynamic select item
component so first of all inside of it we are going to render the option right so do we have the uh options at all let me just see uh where do we uh oh we have the options right here my apologies I thought that that they were a prop they are not so we have the constant options here all right I was I was I confused myself for a second here so we have the options and now inside of here we can pass in the key to be index we can pass in the value to
be option we can pass in disabled to be disabled uh we don't have disabled yet but we're going to create it here in a second and let's go ahead and give it a class name of capitalize capitalize so when should an option be disabled the option should be disabled if it's already used by another column so if I select that this row is a category I should not be able to select it here again it should be disabled to show the user they already chose that option so I'm going to modify this a bit I'm
not going to immediately return here instead I'm going to open this curly brackets and I'm going to end a curly bracket here and then inside of here I'm going to add a manual return there we go like this so this will resolve the errors what that allows me to do is go here and find the disabled constant so to see if something is disabled we are going to go over object do values of our selected columns and then then we can look at dot includes option if it does include the option we're going to do
end selected columns we have to find our matching column using the column index we're going to check if that is not equal to the option like this so quite a comparison here but it's the only way we can ensure that we already selected that in another uh uh select uh table head select component right all right let's see if this is working I think uh this is it it can now render this great we already addited here so let's try it out so if I go and import this and select this there we go so
everything is Skip by default so I should now be able to select date here all right it's not working and it's not working because we didn't implement the function uh that will actually uh select this so let's go ahead and do that next so for that we have to go back inside of our import card component right here because we passed an empty function for on table head select change we have to create a method which will manipulate the selected columns here based on the information sent from the table head select component so let's go
inside of the import C card right here and just below our body let's define const on table head select change and in this method the first argument will be the column index which will be a number and then we're going to have a value which can be string or null and inside of here we are going to modify the set selected columns right here by getting the previous value of them and then we're going to uh open a method right so don't return an immediate object just open a method here and we're going to define
the new selected columns inside we're just going to spread the previous ones right or the current however you want to call them and then we're going to do for const key in new selected columns we're going to check if new selected columns key so the existing value matches the new value in that case new selected columns key will be reset to null like this and then inside of here we're going to say if value is Skip in that case the value will be null for this next step which is going to be new selected columns
and then we're going to write column underscore column index will be value like that and then return new selected columns like that so just be careful with this names so this repeated a couple of times in our project so make sure that it is exactly the same every single time all right so it should be here in the current SE current selection and in here in the disabled in the table head select great so in now inside of the import card we can use the on table head select change and we can pass it here
there should be no type errors because the types are correct let's try it out again so if I select date here there we go this becomes date and you can see how it's disabled in all the other options so now this will be my pay and this will be my amount and I don't have to fill the notes but if I want to I can put the notes Here For example and I can always bring this back to skipped and then you can see amount is now freed or if I skip this then pay is
freed if I skip this then date is freed so that's what we wanted to achieve great so now uh what we want to do is we want to create the continue button which will indicate to the user how many of the required Fields have they set let's go back inside of our import card component and we we can actually close everything else so now what I want to do is I want to define a constant called progress and that's going to go over object. values selected columns and it's going to filter by Boolean here like
this and include do length like that and then what we are going to do is we are going to go and find the cancel button and here we're going to add a new button which will say continue but we're in the parenthesis we're going to calculate well not calculate but we're just going to show progress divided by but not not actually divided just like uh for example three out of five something like that and then inside of here we're going to say required options. length like this there we go so you can see that now
it says four of three okay so maybe not uh the best logic to handle this so what we can do for now to simplify it is go inside of the table head select component and just remove the notes for now right so make sure you only have uh these three which are exactly the same as these three in the required options so that will simplify things for us and then we can also go ahead and and give this button a disabled property if progress is less than required options. length like this and let's go ahead
and give it an on click like that or do we already have a we have on submit but okay we're going to handle that later so let's just see if this is working so if I import a new CSV there we go so continue zero out of three but if I add a date that's one out of three if I add uh pay that's two out of three and finally I add an amount and that's three out of three if I reset something to skip I get back to two out of three perfect that is
exactly what we wanted to do let's go ahead and let's give this a size of small while we are here great now I just want to slightly modify uh how this looks on mobile right this cancel and continue button so I think that we can do this we can do uh Flex call and then here we can do LG Flex row like that and then we can do Gap Y 2 here we can give give this button a class name of uh let's go ahead and just collapse all these items here so I just wanted
to look a bit nicer and more consistent on mobile so full width on mobile but uh on LG on large is going to be Auto like this and then I want to copy this and paste it for the button below there we go so now when I expand perfect now this looks a bit more uh consistent not perfect you know but uh consistent and I want to do the same thing uh here right it looks a bit weird so let's just fix that as well so let's go inside of page. DSX right here here so
inside of here we do the same thing let me just collapse these to let's give this a class name of full width but on LG Auto and then let's give this a flex call Button LG Flex row and let's give it a gap y of two there we go so now this two is also consistent and when I import this is also consistent perfect so what we have to do now is we have to convert or format these values uh from well this Matrix of strings into something that we can send to our backend and
we know what our backend expects so if we go into transactions API route and if we find the bulk create right here it accepts an array of these objects right here so that's what we're going to have to do here so let's go inside of our uh import card here and now we're going to go ahead and develop this uh on click method right here so that's going to be our handle continue method so we're going to do that just below the progress so const handle continue here is going to be an empty Arrow function
which will uh first of all create a method get column index which we're going to reuse here so we're going to accept column string and we're going to return column do split underscore and the first one basically you know how we name our column and then we do column index so now we're going to have to keep doing this so I don't want us to write that many times so let's just create a little method to help us with that here and and now well here comes the the bit of a confusing part I mean
everything was confusing but this is perhaps the most confusing part so far so basically we now have to turn this into from this weird array right into our very simple array of objects right and we also have to create each of these objects inside because currently we don't know what's the date what's the category what you know the amount none of those things so first let's create a more structured mapped data here so it's going to be an object which will have headers this headers will go headers do map and we are going to do
underscore header because we don't need it we just want the index and inside of here we're going to get our column index for this specific header so what the user selected in the select form in the select field will be get column index from our method above and we're going to pass in the column uncore index like this and then we're going to return selected columns we're going to find column underscore column index here or null so that's the headers and now for the body we're going to do a similar thing so body do map
we get the row in here and first we have to transform the row so const transformed row is going to be row. map get the cell get the index here let's do the same thing so let's get the column index and let's return selected columns back this column underscore column index like this and then if we have that we're going to return cell otherwise we're going to return null so we have to return the value of what's in the cell right but only if we have that selected and matching under this column index from that
header right so it's confusing you have to keep thinking of that Matrix right and then we're just going to do a quick little check here to return transform the row do every and we're just going to ensure that item uh is not empty so if item is null return an empty array like this otherwise transform the row because I found that there is a possibility of a bug here and then we're just add a final filter here to only show full uh full length rows like that so very very confusing I know but let's make
it easier for us and let's at least console log this so we know what just happened right and now let's use this handle continue and let's add it here like this so I'm going to expand this and let's see if this will so make sure you have selected the date the pay and the amount let's click continue and now we have mapped data here so there we go you can see now now I have headers so I skipped the first one I skipped the second one and only on the third one I selected the date
so now I transferred my data here to only pick ex those exact things from my CSV file right do you kind of understand uh what we did here obviously it's very still very confusing right because you keep have to thinking about how that looks in a matrix right so we only populate uh and transform the body fields which have the matching headers selected right I know it's complicated but again I don't know if there's an easier way to do this right it's it's simply a complicated thing to do but we are still not done so
now that we have the mapped data we have to transform it into something our back end can accept so let's do const array of data here to be mapped data. body. map row return row. reduce get the accumulator which can be any then get the cell and then get the index and inside of here we're going to get the header so header is going to be mapped data. headers and it will match the same index because we just took a look at the console log so we know that the indexes match now if in in
case header is not now we are going to add to the accumulator that header and that cell so we are going to create a proper object like date equals and then a date string right otherwise we're just going to return accumulator and let's start with an empty object in the beginning of the reduce method like this and let's conso log this now so array of data so this will now be familiar to something that we can accept in our backend so if I click continue again there we go this is now something that we can
send into our back end much much better great but we're not done yet because we have to format this amounts into milliunits so let's go ahead and do that so the last formatting method here is going to be our const formatted data array of data. map get the item here and we're going to spread everything inside and then we're going to get the amount to be convert amount to milliunits par float item. amount so make sure you've imported convert amount uh to milliunits let me just add this here or here it doesn't matter really and
we also have to modify the date because it doesn't match exactly what we need so let's go ahead and just import format and pars from date FNS so remember we defined this date format and this output format right so your CSV files need to have this date format otherwise is it's not going to work so let's go ahead and do this inside of date we are going to format but first we have to parse the existing item. dat using the date format constant and then new date my apologies for this popping up so new date
and then lastly add the out output format like this there we go and let's conso log our uh my apologies formatted data finally my vs code started getting slow over how many code we have here so let's finally try this out continue and this is the formatted data so the exact dates that we need and let's look at the amount it's converted to milliunits so this is something that we can now finally send to the back end the only thing that's missing is which account do we make this for so that's what we are going
to do next but this is ready we can now do unsubmit formatted data so if your formatted data looks like this you are ready to uh send this further great so what we have to do now is we have to create uh a dialogue which will confirm this uh formatt data by selecting an account but I want to do that in the next chapter because we are already almost an hour in so I'll give you some time to take a look at what we just wrote Because we wrote a ton of things great great job
so we left off at completing the import functionality and giving our table the ability to select a date a pay and an amount and to click continue so now we have to go further and we have to go back inside of our dashboard transactions specifically page. TSX so inside of here we have this onsubmit method which instead of import card is used right here so we have handle continue method where we worked last time we created this mapped data then we created the array of data and then we created the formatted data and last thing
we did was we passed it into the onsubmit formatted data but our onsubmit here is empty so let's change that and write onsubmit import and let's go ahead and write this method right here so I'm going to do that right here at the bottom con onsubmit import is going to be an asynchronous method which will accept the values and the values are going to be a type of transactions Dot let's import transactions first uh let's see transactions do I have oh so I'm using this as transactions all right so this is what we are going
to do then let's go inside of here and let's import from at/ database schema let's import transactions as transactions schema so we have to add an alias here or is is that not working still let's see oh yeah so I have to open this method here basically the problem is we cannot have uh a transactions import because we already have a constant here named transactions so that's why I'm importing transactions as transactions schema right here so let's go ahead and make this a type of transactions schema do infer in insert but make it an array
of items like that so those are the values and then inside of the method here what we would do is we would uh submit this to bulk create mutation right but we can't do that yet the first thing we need to do is we need to open a model and confirm the account where this is supposed to go so for that what I want to do is I want to create a hook similar to my use confirm hook so let's go ahead inside of hooks use confirm and let's copy and paste this and let's call
this use select account but what I'm going to do is I'm going to move it out of hooks here so I'm going to cut it and I'm going to put it inside of accounts hooks because it just makes more sense to be in here for this entity so not here let's go inside of the use select account and let's go ahead and modify it so first of all this use confirm will be called use select account and we are not going to have any props in here right but our return method will stay the same
so jsx element and promise unknown right here and now let's go ahead and let's just add some slight changes here so let's add const account query here to be used get account accounts let's go ahead and add const account mutation to be use create account and and let's define const on create account to accept a name which is a string and pass in the account mutation. mutate and passing the name so that's going to be our method here and const account options here are going to be a check of account query do data or an
Mt array map get the account and return an immediate object with label account. name and value account. ID so we are preparing for our select component here now let me go ahead and just use these two Imports and move them above all components because they are features and I'm going to change the Imports to stay consistent there we go so use get accounts and use create account you don't have to of course change that if you want to use dot dot feel free to use dot dot great so now I'm going to ahead to do
the following so just below the con promise I'm going to add a control for our value but I'm going to use ref for that and I'm going to explain why so let's add select value to be used ref from react with the default value of string well with an accepting value of string right uh and make sure you've added use ref from react here so that's going to be our select value here and now let's go ahead uh and let's go inside of handle confirm and instead of sending true we're going to send select value
do current because we're using the ref right so let's go ahead and also modify this so our promise inside of we have this um type here so let's just go ahead and slightly modify the value here so it's not going to be a Boolean it's going to be either a string or undefined right like this so now you should not be getting an error here and now let's go ahead inside of here and let's send undefined if we canel great and instead of having a dynamic title here we're just going to render select account and
instead of having Dynamic description here we're just going to have please select an account to continue like this and this can stay the same but we are going to modify uh well one thing we're going to leave the footer but just in between the header and the footer we're going to add a select component from components select so not from components UI select from components select this is that component which we created which can accept the on create method so that we can search and create new ones so that's the one we need so make
sure you've added an import for that component select let me move it here all right and now let's go ahead and pass in everything we need so placeholder will be select an account and then options will be account options and on create will be on create account and on change or is going to be a method which takes in the value and it's going to use the ref select value. current to be the value and disabled is going to be if account account query is loading or if account mutation is pending not is posed is
pending there we go uh and I think everything else can stay the same so the reason we are using a ref here and not a value not a use state is because changing the state causes this hook to reender which will cause the flashing effect over this dialogue right so every keyst broke would cause a flashing effect so that's why we are using this in a ref great so we have the use select account and now let's go back inside of our transactions page here and let's go ahead and let's use this uh use select
account so I'm going to go ahead and find my hooks here and I'm going to go ahead and use use select account from my features account hooks so let me go ahead and add this here so I only have these features so there we go I'm going to separate the features for accounts here because all of these are transactions so we now have use select account and from here we are destructuring the account dialogue and the confirm method so now we have to render this account dialogue and we can render it here in the variance
import because it's only going to be used here either way so account dialogue add it inside of this fragment here with the import card and now inside of the onsubmit import what we are able to do is we able to request the user for the account by doing const account ID await confirm like this and then if we don't have an account ID we are going to return so we break the method and we are going to return a toast from soner with an error please select an account to continue make sure you've added an
import for toast so I'm going to add that to the top here great and otherwise let's go ahead and let's get the data to be values. map we get the individual value we spread the value and we attach the account ID account ID as string to all of our data and now we are ready to create a b bulk mutation so let's go ahead here below use new transaction and we're going to add bulk create mutation so that's going to be use bulk create transactions here make sure you've added an import for use bulk create
transactions right here uh all right so again this is some types script weird uh bug when I refresh there we go no errors here or or there are errors uh all right so let's see uh what's going on here use bulk create transaction features API use bulk create transactions and this is bulk delete transactions uh inside of here it looks like we are having some uh circular references here so let's see why that is happening use bulk create transactions uh okay let's go ahead and see if I made uh any mistakes here looks like my
use bulk create transactions is incorrect and all I did was restart my VSS code and the typescript errors went away so it looks like it was the typescript server okay so I didn't change absolutely anything let's go back inside of use uh bulk create transactions here and now we will be able to do that so we have bulk create mutation or we can call it create transactions so it matches the delete transactions here right so what we're going to do here is going to do create transactions that mutate we're going to pass in the data
and on success what we're going to do is we are going to call on cancel import so we close this view so I want to test this in the following way and I recommend you do the same thing so don't rely on the transactions table because remember we only load the last 30 days so go ahead and run a bun database Bas Studio here so this way you will be able to track all the data so I'm going to go ahead and open my drizzle studio and I'm going to go ahead and remove all of
my transactions so it's easier to look at so now my transactions are completely empty so I'm going to import a statement I'm going to select this for a date this for pay and this for amount and if you want you can also open the network tab so that you can keep track of what's going on so let's go ahead and let's click continue and there we go we have an account let's select this into a new account let's click confirm and there we go so it works flawlessly bulk create sent a data with an ID
of all of our items and we got back a response of those created items so as you can see I only have three of those here you might not have a single one because you can see these are in April so uh a lot of those from my import are not here because I only load the last 30 days so I'm going to go inside of my d Studio instead and there we go so in here I can see all of those elements I think it was 23 of them 21 of them great so we
just wrapped up the import great great job and now you can go ahead and categorize these if you want to there we go so now they work much much better perfect so uh what we're going to do next is we are going to go ahead and finally work on the overview page because we now have transactions with categories so we can create some statistics and then we are going to add the filters which will then be used for this page as well so that we can change the date if we want to increase from just
30 days and also load only a specific account for example great great job so now that we've wrapped up the transactions page let's go ahead and do the following let's create an API endpoint to create a summary of our transactions and categories so here's what I want you to do first I want you to have at least a couple of transactions in your recent dates right so make sure it's in the last 30 days so for me it's 10th of May which means everything in the past 30 days I can select here so I'm just
randomly going to add some things with some incomes and some expenses here so I basically want you to have some items here because remember only the last 30 days will be calculated and shown Here and Now switch to overview and let's go ahead and let's develop so you can shut down the drizzle Studio no need for that now just keep your app running right here let's go ahead inside of our app folder API route and let's go ahead and let's create summary. DS and inside of this summary let's go ahead and let's import hono from
hono and let's write const app new hono and let's write do getet slash and for now we can just add a very simple controller summary true like this and let's export default app now let's go inside of route. vs here and let's chain our summary so we are going to have to import summary from do/ summary like this so I'm going to make this first so they all have the same uh increasing length there we go it doesn't matter right uh I mean the order of your chaining matters if you have same named stuff but
all of our are named differently so it does not matter so now you should be able to go to localhost 3000 API summary and you should just get summary true inside that's it and this is going to be our only end point in the summary uh routes so go inside of summary and let's go ahead and let's develop this first of all we're going to add the clerk middleware here from hono uh clerk out right here then we're going to add a z validator from hono Zod validator we're going to validate query field and we're
going to use Zod so import Z from Zod object from z. string. optional and then we're going to have two z. string. optional and we're going to have account ID z. string optional as well there we go and now inside of this asynchronous method let's go ahead and let's get the out using get out C and we need to import get out from hono clerk out let me just change these Imports right here there we go so we can also immediately uh destructure everything we need from our uh C request valid query so that's going
to be promptu and account ID now let's check if we don't have out question mark user ID in that case we can return a c. Json with an error inside saying unauthorized and let's mark this as 401 that's very important to mark this as an error great and now inside of here let's go ahead and let's write const uh default 2 to be new date then con default from to be sub days from date FNS so make sure you've imported sub days from date FNS and let's also add pars and difference in days from date
FNS so sub days pars and difference in days so inside of here we're going to sub 30 days from the default to and then in here we can go ahead and we can uh create our start date so if we have from in that case we're going to parse from using our format so year month day new date as the third argument or otherwise use the default from and then inside of here let's define our end date to check if we have two if we have to we're going to do the exact same thing here
but we're going to pass two like that otherwise default two there we go now let's define period length the difference in days because remember it doesn't have to be 30 days right if users selects their own period it can be 40 days or 15 days so end date start date and plus one for the offset then let's do last period start so every time we calculate the summary we're also going to calculate the period 30 days well not 30 days but the equivalent amount but from before that from the period before that so that we
can show the user all right if you selected last 15 days you made this much money but that is 5% less than the previous period of that same length right so that's why we want to calculate the previous period as well so we're going to sub days inside of here start date and period length and then let's define last period end to be the end date and sub days as well there we go uh great so now I want to do I want to create uh two things so I want to get the current period
from something right and I want to get the previous period or last period from something as well and this something will be our await fetch financial data method which we don't have yet and this fetch financial data will take three arguments the first one will be out. user ID the second one will be start date and the last one will be end date so these ones right here so I'm passing in out. user ID you don't have to obviously but if you if you want to extract this fetch financial data in another folder or file
then you're going to have to pass out user ID but for Simplicity I'm going to write it inside of here so obviously if you write it here you don't need to pass it as an argument but you know just in case uh someone wants to copy it somewhere else I'm going to leave it like this now let's create this as synchronous function fetch financial data so the first one is going to be user ID which is a string uh like this the next one is start date which is a date and end date which is
a date as well well like this let's open this method here and inside of here we're going to return a weight database from database drizzle so just make sure you've added this and we're going to select the following we're going to select income we're going to select expenses and we're going to select remaining so I want to know how much money we've made in this period so for that I'm going to use SQL from drizzle orm so make sure you've added the import I'm just going to move it here I'm going to open backx and
I'm going to sum case when transactions so we have to import transactions from database schema make sure you add this so transactions do amount like this is higher or equal than zero so that's my income right so then we're going to continue writing I'm just going to zoom out so you can see how this looks like in one line I'm going to zoom back in later so if it's higher than zero then this is my income so then I'm going to do the transactions. amount like that else I'm going to write zero and end and
then I'm going to add do map width and pass in the number like this so so let me go ahead and try and zoom out one more time hopefully so you can see it in one line all right I'm not going to zoom in any further so let me show you how this looks like in one line when I have to scroll like this maybe this is easier for you all right I'm going to turn uh word wrap back on so it collapses now great so we have the income and now uh we're going to
go ahead and copy and paste for expenses a similar thing so this is going to be the opposite so it's going to be some case when transactions. amount is this time uh less than zero right so very similar but a different uh Clause here and else zero can be stay and stays the same and MTH width is still the number and then we're going to have a last one which is going to use the native well not native but the drizzle RM sum so let me just show you we have SQ and sum imported here
from JM so in here we can just use this sum because it's a simpler method transactions. amount that's it map with number no uh High logic here right we just have to count everything and now we have to specify from where so from transactions we already added that import but remember transactions don't belong to users they belong to accounts so we have to create an inner join of accounts so we're going to write accounts as from database schema so make sure you've added accounts here and then we're going to write equals so import equals from
drizzle orm and inside of equals for the first argument I'm going to pass the transactions. account ID and then for the second one is going to be accounts. ID like that so that's going to be the inner join and then we can chain where here and in the where I can add and from drizzle RM so make sure you've added and equal SQL and sum and in this end we're going to add for the first argument a check if we have an account ID if we do we're going to add equals transactions. account ID with
account ID otherwise undefined like this uh let's go ahead did I add uh and whoops how did this happen I don't know so and that's what I need and equals uh SQL and sum there we go and this is account ID with a single uh C so that's the first argument then we're going to have equals accounts. user ID to be out user ID uh actually not Al user ID but instead it's going to be this user ID here from the function in case you want to extract that right so it's easier great so we
have that and then we're going to add greater than or equal from drizzle orm for transactions. dat and start date and we're going to copy and paste it and make this less than or equal from drizzle RM for end date so just ensure that you have end equals greater than less than equal SQL and sum from drizzle orm there we go so that is our query here for the periods so if you want to uh you can already try and return this so return C dojon let's return the current period and the last period so
let's see if I refresh this we should now see some changes here there we go so you can see my total income my expenses and how much I have remaining right uh obviously these are milliunits so they have to be divided by a th to be presentable on the front content but we can already calculate something in this period that's why I told you to have some transactions in the period right otherwise it's not going to work uh there we go great so now I want to create a method that will help me calculate the
difference from the current period and the last period in a percentage so let's go inside of the utilus folder and let's export function calculate percentage change so that's going to accept current as a number and previous as a number as well and inside of here what we're going to do is we're going to check if previous is equal to zero in that case let's check if previous is equal to current in that case the result is zero otherwise it's a 100% increase uh otherwise let's return open parentheses uh open double parenthesis inside current minus previous
like this divided by previous and we are going to multiply it by 100 there we go perfect so now we have that change and now inside of here we can Define the following we can write const income change and we can passing the calculate percentage change from our lib utils so just make sure you've added the calculate percentage change here so we're going to open this and we're going to pass in the current period income and last period income uh you could of course you know try if you know SQL better than me uh feel
free I I don't know SQL that well right so if you know a better way to calculate this inside of SQL go ahead and do that it's probably much faster much more optimized right but I'm satisfied with at least you know not using Prisma like queries so this is a big step for me okay but if you know sequel better feel free to try and do that here what I'm doing here with this percentage changes so the next one is going to be expenses change so I'm going to be looking at the expenses here like
this and the last one is going to be the remaining change so remaining there we go so if you want to you can now add uh in here the income change the expenses change and the remaining change to see exactly how much in percentage this has changed over the last period perhaps your for example mine are zero here because I just don't have that many transactions right I have a very small amount of transactions so there's nothing to compare with the last month but I think that if I go into my Local Host 3000 if
I go into transactions for example and if I add one from previous month from maybe March for example if I try that and if I for example make an expense of $500 it's not going to be visible here but I think that maybe if I did this correct maybe it's going to be visible here it's not okay we're going to see that later when we create a seed script which is going to be able to fill from months before and up till now so we're going to see this change working uh okay uh let me
just confirm that we didn't have a bug here all right I think there's no bug we just don't have enough data great uh and now what I want to do is I want to create queries to show the user how much they spend by category so we're going to be using the group bu element so let's go ahead and write const category to be await database do select we are going to select the name which will be category . name and we have to import the categories from database schema so just make sure you've added
this besides the name we are going to add the value which will be a SQL operator sum absolute and then we're going to add the transactions do amount do map with number and then we're going to write from transactions inner join so we want to we want to create two inner joins now right we're going to create an inner join for accounts with an equals here of transactions. account ID and accounts. ID but we also want an inner join for categories usually we use left join for categories but in this case uh the query won't
make sense if we load something that doesn't have a category right uh so let's go ahead and join the categories using transactions. category ID with categories. ID uh let me just see oh I'm missing the uh categories in the inner join there we go so this is accounts account ID accounts. ID categories category id category. id there we go and then let's do a wear here and in the wear uh we can actually copy it from the one above so we save some time this where so let's change that so the user ID method here
doesn't exist so it's going to be al. user ID like this the account ID logic stays the same the equals stays the same but we are going to add one more thing which is less than transactions. amount zero so we only want to count the positive category spend so so we have to import larger than in drizzle omm larger than right here there we go all right we have larger then here so we are only loading those categories right and now we have to finish the query by grouping by category name right so I only
want it to display you know travel and then the amount I don't want uh an array of items where travel repeats multiple times I'm going to group it by name so it aggregates by uh only a single name so it's only going to repeat once right and then let's order by and let's go ahead and write Des sending from drizzle orm so make sure you've added this import and we are going to uh write a SQL here sum absolute transactions. amount uh is this good enough let me just see uh cannot find SQL oh again
something happened my apologies I keep somehow doing this there we go so now uh I think this should be just fine uh so let's go ahead and do this so I'm not never going to send back all the categories because if there's a ton of them right I don't need them so I'm going to write const top categories to be category. slice 0 and three and const other categories are going to be category uh category do slice 3 right so I am doing the constant category here so make sure you don't accidentally do categories because
that is our schema right so make sure you are doing this on this constant where we did the query for categories great so now uh I want to keep everything else as well but I'm going to keep everything else in the other sum it says that's going to be under other I only want to display the top three most spent categories and everything else will be under other so I'm going to write other categories which we just have here dot reduce I will get sum and current and I'm just going to reduce sum plus current
Dov value beginning from zero there we go uh perfect and now let's go ahead and let's finalize this by writing const final categories so that's going to be top categories like this and then let's do if other categories. length is less than is more than zero final categories. push name is going to be other and value will be the other sum so no point in showing the other categories if there are only three categories right so we do this little trick here by assigning this as an array and then we add an additional one if
other categories actually exist so so we can now add the final categories here to the change as well so let's refresh this to see what we spent our money on there we go so this is the most spent category that I have chosen right so I only chose this this one so I think that now if I try and for example go inside of transactions and if I go inside of add new if I pick a date for example a few days ago let me go ahead and create food here test and I'm going to
spend $1,000 in food and I think that now this might be visible in the summary let's see uh all right it's still uh doesn't seem to be uh visible not exactly uh sure why oh I realized why I also think I found a bug here so I just modified this food category to be you know half a million dollars and also we have a bug here we forgot to change our edit transaction so it has this we have to convert from Milli units somewhere but let's do this uh let's go ahead and create for example
$5,000 but make sure it's an expense right that's the important part so now if I go to slash API summary it should be here there we go so we spent a lot of money on food so it's working great okay uh fine and now the last thing that we have to do is we have to to create uh data for our graph to show how much we spent by day in this period that the user selected so const active days is going to be await database. select date will be transactions. dat because we we are
going to group by date later and then income is going to be SQL some case when transactions do amount is higher than greater or equal than zero in that case we're going to sound transactions. amount else whoops else zero and math with number so let me zoom out so perhaps whoops uh where was I my apologies so it's a very similar if not exact the same query as the income we had in the beginning when we were calculating the income the expenses and remaining right so some case when transactions amount is greater or equal than
zero then we use transactions. amount else zero and map with number uh and then you can go ahead and copy and paste this one for oops for expenses so for each day we're going to calculate the income and the expenses so this one will only only calculate if the income is if the amount of that transaction in those days was less than zero everything else can stay the same there we go so let's now go ahead uh and do well the same thing we already did a couple of times so from transactions and then let's
injoin the accounts so let's copy and paste that so we save some time from transactions injoin with transaction account there we go we can also copy the where here so we are checking by account ID here so let's just confirm that we have account ID if we do we do the equals otherwise we do undefined like that uh and we confirm that these are only the users uh transactions so this is kind of the most important equals sign here we don't need this less than that's uh to be removed but we do need greater than
or equal and less than or equal and finally let's do uh group buy so we aggregate by transactions. date so we don't have repeated dates and the last one is to order uh this by increasing dates so transactions. date like this and I somehow did this again okay there we go the problem now is that this will only generate data for the days that we selected but only those that actually have some transactions so I'm going to add active days here so when I refresh inside of this API summary there we go so we have
this dates but that's not enough for us to fill a useful graph I also want to fill the dates where we have nothing right so we're going to create a method for that called fill missing days so let's go inside of our utils and let's write export function fill missing days so the first argument will be active day which is going to be an object and an array of those objects inside of the object we accept the date income which is number and expenses which is a number as well then we have a start date
which is a date and an end date which is a date as well now let's go ahead and let's import each day of interval from date FNS so using that we'll be able to generate uh the rest of the days here so first of all if active days. length is equal to zero we just return back an empty array right then let's write const all days to be each day of interval and let's pass in start to be our start date and end to be our end date and then we can say con transactions by
dat to to be all days. map get the individual day open a function let's attempt to find this in our uh existing uh active days which we passed here so active days. find D short for day is same day using date FNS so we don't add duplicates here so is same day d. date and day itself like this uh and now we're going to write if found return found else we are going to return an empty day so date will be this date income will be zero and expenses will be zero so you're going to
see why we are doing this in a second let's return our transactions by day so remember this is how it looks right now so we only return 3 days even though we selected 30 days days right so our graph needs to show 30 days right so let's go inside of our summary method here and let's go ahead and let's finally write con days to be fill missing days and pass in active days start date and end date like this and make sure you've imported the fil missing days from lib utils and then inside of here
we can go ahead and we can send days instead of active days here let's go ahead and refresh this and let's see this now and there we go you can see how now we have everything we need for our graph and only some days have values but most of these well don't have anything great so this is what we wanted and that's it for this summary end point here so let's just go ahead and uh style this better so we're going to have data first it's going to start from data so our API structure is
consistent then we're going to have remaining amount so that's going to be current period. remaining and then we're going to have the remaining change below that then we're going to have an income amount that's going to be current period income then we're going to have income change then we're going to have expenses amount that's going to be current period. expens and then we're going to have expenses change so I'm just bringing things back that we are going to need for the dashboard and then I will need the categories which will be my final categories and
I will need my days which will be my days they are the same named constant there we go we've wrapped up our summary API endpoint so if I refresh there we go this is enough for us now to display on the dashboard before we wrap this up I just want to go ahead and fix this milliunits issue that we have so when I select something that has an amount of $500 and I click edit in here it appears as half a million dollar so I think I know exactly where the culprate is let's go inside
of our features transactions API use get individual transaction and instead of returning data here we're going to return an object where we spread the data and get the amount and write convert amount from milliunits data. amount and make sure you've added an import convert amount from milliunits let's try this again I'm going to close the drawer and refresh there we go my input my transaction for $500 when I click edit it has $500 here perfect amazing amazing job what we're going to do next is we're going to create the uh well all the elements needed
for the dashboard which will work with the summary API end point so I was experimenting with our summary endpoint and I noticed we do have a bug when it comes to calculating the percentage change of the last period as you can see I have added a bunch of transactions and I have a bunch of transactions in the last period but still uh all of my expenses change percentage is zero so I've noticed a bug inside of our summary endpoint we don't use the last period start and last period end so go ahead and find last
period fetch financial data and instead of start date we have to use last period start and last period end like this so now if you have enough transactions there is a still a chance that you still get zero and it's correct right don't worry but I have added a bunch of transactions and there we go now you can see exactly how much it changes in percentages here great so if you you're wondering how I filled with this much information it's thanks to a seed script so we're going to go ahead and create a seed script
now so you can copy this from my source code uh go ahead inside of your scripts and create seed. TS right here and copy and paste this from the source code you can also pause this screen I'm going to go slowly here if you want to take a look at what's going on but there is a lot of change uh going on here right you don't need the seed script at all but if you just want to get a bunch of data you can use this seed script only thing you have to change is the
seed user ID so let's go ahead and obtain that so you are going to go inside of your clerk dashboard go inside of users find your user the one you want and copy that user and then go ahead inside of here and replace this seed user ID with with that ID and then let's go inside of our package.json and let's go ahead and copy and paste this so you didn't have to do this right you can continue the tutorial without it it doesn't matter uh but if you want to have a bunch of financial data
you can use the seed script and now let's go inside of the terminal here let's shut down the app and let's run Bun Run database seed like this and this will just if there's no error that means it successfully cre created a bunch of data so if I refresh here I should have some different numbers here because it reset everything there we go so now I have different uh numbers and I have all these categories great uh so I think I've noticed one more bug here inside of my summary and that is this calculate percentage
change so I have a type of calculate so calculate this is how it should be so I'm just going to have find all instances and replace that typo and let me reload my window to fix the typescript error there we go so some slight improvements on our summary API endpoint and now let's go inside of our app folder dashboard page. the SX uh but actually before we can do anything here let's go ahead and let's create our features so I'm going to go ahead and create the summary feature here API and I'm going to create
use get summary. DS here uh and we can copy the transactions so API use get transactions we can copy this because we're going to need this params so paste that inside of here let's rename this to use get summary like this uh we can remove this to the key well we can leave it actually yeah because it's we're also going to use those here so we needed to use search forams we need the from to and account ID but our query key here is summary and we are calling the summary here and the query should
be fine because our summary endpoint accepts the same things from to and account ID and this will be failed to fetch summary right here and now we have to modify the data a little bit so the data is an object so we're going to go ahead and spread the data here and then we're going to add income amount to be uh convert uh amount from M units data income amount then we're going to do the same thing for expenses amount and then we're going to do the same thing for remaining amount and let's see what
else we have to do so we have to do the same thing in the categories so let's go over categories and write data. categories map we get individual category and we return an immediate object where we spread the existing categories but change the value to convert amount from milliunits into category. value like that and we have to do the same thing for the days so data. days. map we get the individual day we open and return an immediate object we spread what exists in the day and modify the income to convert amount from milliunits so
day. income and we do the same thing for the expenses for that day that is it make sure you've added convert amount from milliunits here great so we have our use get summary here and now we can go ahead inside of the app folder dashboard page. PSX finally so let's remove use client let's remove the button let's remove everything here we are not going to need pretty much any anything for now we can change this to be our dashboard page and let's give this div a class name of Max width screen 2 Excel MX AO
full width pading bottom of 10 and minus margin top of 24 and then inside of here we're going to use the data grid component so let's go ahead inside uh well we can go ahead and create that in the comp components for example so let's go ahead and create data- grid. DSX like this let's mark this as used client because the parent is a server component right it doesn't exist so let's do export cons data grid here let's return a div data grid let's go ahead and let's import data grid from components data grid like
this so now on my Local Host 3000 let me just refresh there we go I should have data grid written right here so what we are going to do uh is we are going to go ahead and we're going to create a component called data card but just before we do that let's go ahead and give this class name a grid grid calls one LG grid calls 3 gap of eight adding bottom of two and margin bottom of eight like that and now let's go ahead and let's uh add the following hooks so let's add
use search params uh usage params from next navigation this is the one and let's get the params use search perms let's get two and let's make it pam.get two or undefined and we're going to do the same thing for from like this there we go and now let's go back inside of the data grid component oh we are already are here so let's go ahead and do the following we're going to create a little util in our Libs in our utils my apologies and the util will be called format date range so it's going to
accept a period which will be an optional period and we're going to Define type period here so that's going to have a from which can be either a string or date or undefined and the same thing will be two so that's the type period and now let's go ahead and open this method and inside of here let's do const default 2 to be new date con default from to be sub days from date FNS default to 30 so the same thing we keep doing on the back end right we are creating a default uh range
so make sure you've imported subd because we just added it here then we're going to check if we don't have a period from the format date range is going to return a label which will simply say format again from date FNS make sure you've added this okay right here so it's going to say format default from in a format of llll DD and then we're going to add two so this will be like a two right from two format default two in the same llll BD comma y so we also want to add the year
at the end no need to no point in showing the year here so we can save some space we can only show it in the last one like this there we go so I expanded so you can see it in one line like this uh there we go and now we can copy and paste this and this if Clause will say if we have period do2 right here so this will then do period. from and period. to again this is how it looks like in one line and the last one will be the simplest one
return format period from and lll DD comma y like this so we are creating a readable range so the user knows which dates are selected so const date range label we'll now use format date range from lib utils and we're going to pass in our uh period which will be to and from like this just make sure you've added this and and I think you can already try this out so if I render this here there we go the last 30 days from April 10th to May 10th and today is May 10th or 10th of
May right there we go so now we have to create a component called data card it's going to be a self- closing tag and it will accept the following props the title which will be remaining uh let's see remaining then it's going to have value which will be data question mark remaining amount uh oh I forgot to add any data here so we are going to have of course use guess summary from features API use get summary and in here we can get our data there we go so now data should have the remaining amount
there we go perfect and then we're going to have the percentage change which will be data and it's in this case it's just going to be the remaining change icon will be fa piggy bank so let's go ahead and add this clear bun add react uh SL ions so we need that alongside our Leed icons because this ones uh have a specific look so let's do to Bon run Dev and then we can import fa piggy bank from react icons fa like this there we go and variant will be default and date range will be
our date range label there we go so now we have to create this data card component so let's go ahead inside of components and let's create data card. SX let's go ahead and let's create uh the export con data card here and let's return a div data card let's go back inside of the data grid and we can now import data card from do/ data card but I'm going to change it to use components data card there we go now we have to create the props for the data card uh so for that we are
also going to have to create the variance so I'm going to be using the inspiration from shat C Andi you can see how inside of components UI button this is how they create variant right using this default destructive outline so I want the same thing so let's go ahead and let's import what we need so we are going to need uh the CN library from lib utils and we are also going to need the Vari props from class variance Authority and CVA we're also going to need icon type from react icons but no need to
go to slash lib you can just import it directly like this uh all right I think that should be okay for now now let's create a box variant which will be CVA let's give it some default props rounded MD and padding of three open an object and write variants and then inside of here we are going to have the default variant and that's going to be background blue 500 with a 20 opacity now the second one will be success which will be emerald and the third one will be danger with which will be Rose and
the last one will be warning which will be yellow so different variants for our cards so they can have uh uh very different backgrounds now let's set the default variants here variant default so we choose the one from above which we created great let's copy and paste this now and we have to do the same thing for the icon variant so you're going to see in a moment where we're going to use this so for the icon the classes are different this will be size six by default and inside of here we're just going to
be using the fill property and we are not going to need need the opacity here like this and the default variant stays the same now let's create a type box variant to be variant props do we have variant props we do have variant props all right so variant props type of box variant copy and paste this into icon variants that's going to be a type of Icon variant like this and now find finally we can create an interface not a type it needs to be an interface uh which will be data card props extends box
variance and icon variance so this way we are going to infer all of these uh things into typescript props right so let's go ahead and give it an icon of Icon type a title of string a value of an optional number a date range of a string and a percentage change of an optional number let's assign all of those things here so data card props and I think we should not be having any errors and we are not perfect so now we are ready to style this let's just extract everything here so icon will be
remapped to icon with capital I so that we can use it as a component like this icon right so that's why we have to to rename it here this is called an alias and then we're going to add a title and a value which we will default to zero a variant date range and percentage change which will we will also default to zero here instead of using a div we will be using a card from do/ UI card or components UI card let's go ahead and let's extract everything we need uh from our card here
so we need card content card description card header and card title that's everything we need here so let's go ahead now and let's go inside of here and let's add our card header component like this and Let me refresh my Local Host so that we can actually see the changes that we are developing so card header will have a class name of Flex Flex row item Center justify between and GAP X4 the card component will have a border none and drop shadow s inside of the card header we're going to have a card title which
will render our title and the title itself will have a class name of text to Excel and line clamp one let me just refresh this again there we go so remaining I have a typo let's go inside the data grid and fix this remaining there we go so this will have a title title then we're going to have a card description here which will render the date range to tell the user uh where it's coming from so this will have a line clamp one like this there we go uh all right and let's go ahead
and do the following let's create a div around the title and the description and let's go ahead and give it a class name of space Y2 there we go so I want them one below another perfect uh and then what we're going to do outside of this div is another div we're going to give it a class name which will be CN so it's going to be dynamic we already have CN imported here by default it's going to have shrink zero and then we're going to give it a box variant and passing the variant value
inside and it's also well that's it but inside we're going to use the icon component and for it we're going to give it a class name again of CN and pass in the icon variant and a variant prop inside so basically we are going to uh this is how you use the class variance Authority Library so you define your variance inside of here and then using this props you then have some something called a variant prop so in here we don't have it but you can see how I can extract it here right so it
doesn't come typed from here we extend box variance and icon variance and we made sure that all of these have the exact same named prop and all the exact same name values so we can then reuse it in this way when we pass in the icon variant to the icon component what's going to change is the fill colors right and by default it's going to have a size six so for example what we could do inside of here is also add shring zero to the Box variant in the default ones like this shring zero and
then we would not have this at all right it would be even simpler exactly like the one above there we go so you can see how this looks like now our piggy bank is blue color so if I go back to the data grid and change this variant to for example uh danger now the piggy bank is red and the icon is filled with red color or if I go to success now it is green great so I'm going to go ahead and leave it at default here or you actually don't have to pass it
you can be as explicit or not as you want great now I want to go ahead and I want to create a count up component here so let's go ahead and do that I'm going to go ahead inside of my terminal I'm going to do bun add react count up but since this is an older component it doesn't exactly work perfectly with nextjs and server components and hydration but there is a very very easy fix for that go inside of your components and simply create count Das op. vsx Market as used client this is very
important import count up from react count up and Export count up as simple as this this will solve any problems with this library now let's go back inside of our data card component and let's go ahead and let's import count up from do/ count up or components count up like this there we go so now let's go outside of our card header and let's add our card content here we already have the card content important imported and let's create an H1 element here with a class name font B hold text to excel margin bottom of
two line clamp of one and break all inside of here we're going to pass in the count up value it's a self-closed component let's add preserve value so on every uh update it doesn't start counting from zero but it just stays where it is or it starts counting from the last known value otherwise it will start from zero but it's going to end from the value that we have have for this card the decimals are going to be two decimal places are going to be two and then we have to pass in the formatting function
which in our case is going to be our format currency method which we already created in lib utils so just make sure you import that so we already use this throughout the project in the transaction in the columns right there we go so we now have this let's just refresh uh the Local Host so we see it running uh and now outside of the H1 element we're going to add a paragraph here and we're going to add the following so we need to uh create another lib called format percentage so let's go inside of our
utils here I don't think we have format percentage we don't so let's go ahead and create that so exper function format percentage it will accept a value which is a number and options add prefix which will be an optional Boolean and this is a comma here my apologies and by default we're going to give it the value of add prefix to be false now inside of here I'm going to write con result and we're going to use a native API so new inl number format and US Open an object style percent do format value divided
by 100 and now that we have the result if we have options. ADD prefix and value is higher than zero in that case we're going to show this as plus result otherwise we're just going to return the result because it doesn't matter the minus will always be shown regardless if we show the prefix or hide it so now we can go ahead and use the format percentage uh from here so just make sure you've imported it from lib utils and inside of here we're going to pass in the percentage change from our props here and
we're going to write from last period here like this so let's refresh this as well Zero from last period and there we go you can see our count up component here working let's give this a class name of CN text muted foreground by default text small by default and we're going to have line clamp one and then we're going to say if percentage change is higher than zero we're going to mark this as text Emerald 500 otherwise if it is lower than zero we're going to indicate that something is is wrong like this there we
go perfect and now let's go back inside of our data grid component and we're going to go ahead and copy and paste this two times the second one will be our income so it's going to be income amount uh and this will be income change and it's going to use an icon fa Arrow Trend up so let's import this from here uh actually I think this is imported from another one so import fa arot Trend up from react icons fa6 like this and alongside this we're also going to have fa Arrow Trend down so those
two icons uh okay fa Arrow Trend up that is okay and this one we say expenses so expenses amount like this expenses change and this will use the fa Arrow Trend down like this and there we go look at our beautiful cards right here uh currently what looks a bit off is that it stays at zero in the beginning because it's loading so if you like that you can leave it like that but I'm also going to show you how to create a loading component so go inside of data card and first of all add
a skeleton from /ui skeleton here and then go ahead at the bottom and Export cons data card loading go ahead and return a card component give it a class name border none drop shadow SM and height of 192 pixels add a card header component give it a class name of flex Flex row item Center justify between and GAP X4 and I'm just going to go ahead and speed things up a bit so go ahead and copy the well I copied it just to spit things up so create a div with space Y2 and add two
skeletons inside with height six and width 24 height four and width 40 here and then outside of this div you're going to go ahead and paste and create a skeleton with a size of 12 and finally outside of the card header you're going to add card content with two skeletons one with shrink zero height 10 and width of 24 margin modum of two one with string zero height four and width 40 like this and now you can go back inside of your data grid component and you can D structure the is loading from here like
this and then inside of here you can write if is loading return and go ahead and copy this div and inside of here you're going to add data card loading from components data card so let me just add these two together like this and let's just align that and go ahead and add three of those there we go let's take a look at this now so you can see our beautiful loading tabs now perfect so what we have to do next is we have to add uh our data charts so for days right and we
have to add one for categories great great job now that we have our data cards it's time to create some charts here but just before we do that I want to do a slight modification on this label go inside of the data card component find the format percentage and remember we have the options to add a prefix but I forgot to use that so I'm going to go ahead add a comma add prefix true and there we go now it will indicate an increase like this let's go ahead back inside of our app folder dashboard
page. vsx and below that add a data charts like this we're going to create a new component called data charts inside of the components folder data charts. DSX let's mark them as use client and let's go ahead and Export cons data charts here let's go ahead and let's fetch the summary again so we are going to extract the data and is loading from use get summary and now let's go ahead and let's prepare if is loading here with a very simple return div loading we're going to later use skeletons for now let's go ahead and
let's open up a div and this div will also be a grid LG grid calls six and gap of eight in the first one we're going to write call span one on LG call span three on Excel call span 4 and inside we're going to have our chart component with data which is going to be data question mark days like this there we go now let's go ahead ins set of the component and let's create our chart. DSX component so let's go ahead and Define the props for this so it's going to have an optional
data which can have data sorry date which will be a string income which will be a number and expenses which will be a number and it's going to be an array of those let's export con chart here and let's destructure the props inside so we're going to have data and we're going to default it to an empty array and let's return a div chart let's go back inside of data charts and let's import the chart from do/ chart or from components chart let's go back inside of app dashboard page and let's import data charts from
components data charts and you can now safely refresh your Local Host here and you should have a text here which says chart now let's develop the chart component so we're going to have to use uh the recharts library so let's go ahead inside of our terminal and let's do bun add recharts like this all right let's just wait a second for this to be added let's run our app again like this and first of all let's go ahead and let's go inside of the chart component here and let's add everything we need from our uh
components card right so card header card title and some other stuff here great uh and now let's go ahead and let's create this so this is going to have a card with a class name of Border none drop shadow MD sorry SM then we're going to have a card header which will have a class name of flex space Y 2 on large it's going to have space y zero on LG it's going to be Flex row on LG it's going to be items Center and justify between will be on both inside of here we're going
to have a card title which will be transactions like this so this can stay as it is now and let's just give this card title a class name of text extra large in line clamp one like this I'm going to add a too add select great outside of the card header we're going to add a card content let me just refresh my Local Host again let's see what's going on here there we go so we have the transactions uh card here now inside of the card content here we are first going to do the empty
state if data. length is zero in that case we're going to go ahead and we're going to show a div with a class name Flex Flex call Gap y4 items Center justify Center and we're going to fix the height to 350 pixels and inside we're going to add a file search icon from Lucid react we're going to give it a class name of size six and text muted foreground and we're going to add a paragraph here no data for this period and this paragraph is going to have text muted foreground and text small like this
and then we're going to have the else Clause here and we're going to have our area variant here let's go ahead and let's pass in the data as the prop data here so now we have to create the area uh variant option here so let's go ahead inside of components and create area variant. DSX and inside of here let's go ahead and let's import everything we need from the recharts library which we have just added in our project so tool tip xaxis area chart area responsive container and cartisan grid and we are also going to
need the date FNS format so let's copy and paste uh the props from our chart right so they need to be the same here we are accepting the same data but this time the data here is requ required and let's export con area variant here the structure the data passing the props and go ahead and return the following responsive responsive container with a width of 100% and a height of 350 pixels add an area chart which we've added an import for and give it the data as you can see there are no errors because this
data has the proper types for this add a cartisan grid here and give it a stroke Dash array of three space three then add depths here so this will be some SVG elements linear gradient give this lar gradient an ID of income so it matches this uh well this value right income it's going to have X1 of0 so I'm not exactly sure what the these values do this is from the documentation of recharts right uh y one of 0 X2 again of zero and then it's going to have a Y2 of one like this inside
we're going to have a stop element which will be a self closing tag offset here is going to be 2% stop color is going to be 3d8 to F and six stop opacity will be8 we're going to copy and paste this the lower one Stope opacity is going to be zero and the offset here will be 98% then we're going to copy and paste the linear gradient here and this one will be for expenses and these these values X1 X1 y1 X2 and Y2 will stay the same the offset will stay the same here but
we're going to change to a different color f43 f5e and the opacity can stay the same and then inside outside of deps we're going to add the xaxis component which we already have imported give it an axis line of pulse a thick line of pulse this is going to represent the data key date from our option here so this will represent the dates we're going to have the tick formatter to get the value and we're going to format it so this format comes from date FNS right so we're going to go ahead and format the
value in day and then the month style is going to have font size of 12 pixels and TI margin is going to be 16 let's see if we can already uh render this so area variant from do/ area variant or from components area variant let me just move this to the top here so let's see can we already maybe see something we cannot but we can at least see the grid and we can see our date range here at the bottom so let's continue in the area variant here so now now what I want to
add after the x-axis is the area itself so type will be monotone here data key will be income stack ID will be income stroke width will be two stroke will be the blue color so let's go ahead and give it here like this bill will be URL and then hash income so just make sure you didn't misspell income anywhere here right all right and class name will be drop shadow SM like this then copy and paste the area and paste it again this time we are going to represent the expenses and use the red color
from above there we go so you can see how this looks now and now we have this kind of a weird look here because the expenses are shown negative like this if you like this that's fine but we can change that by going back inside of our app API route summary and find the days there we go active days and in the expenses we're not just going to render transaction do amount instead we're going to do then let me just zoom out so you can see then and then we're going to wrap the whole thing
in absolute so we don't care if it's in negative right so just wrap this transactions amount in absolute and save this and let's see the difference now and I refresh here I think it's going to look a bit more pleasant there we go because now you can see the actual you know uh difference between the two so you choose how you like like it let's go back inside of the area variant here and there's only one thing missing here uh and that is the tooltip component so let's go ahead and let's create the Custom Tool
tip which we're going to reuse Custom Tool tip. TSX here let's go ahead and let's import the format function let's go ahead and let's import the format currency and let's go ahead and go inside of our terminal and let's do bonex chat cnii latest ad separator and then let's go ahead and run our app again let's go ahead and let's import the separator component and let's export con Custom Tool tip here active and payload and it's simpler to just give this a type of any and let's go ahead and check if it's not active we
are not going to display the tool tip at all and now let's go ahead and let's extract uh the values from the payload here so date income and expenses so the reason I know exact values which are going to be here are because of the documentation of recharts right so the tool tip works with this payload prop you're going to see where we are going to render the tool tip later and this is how you access the date and this is how you access the income because it's the first in the array and then the
expenses and now let's go ahead and let's return a div with a class name rounded SM BG white shadow SM border overflow hidden and let's go ahead and let's actually add the tool tip so we can see what we are doing so just after the xaxis I'm going to add the tool tip component and we already have this imported right here but now we're going to add our Custom Tool tip import right here at the bottom so make sure you we've added the expert con Custom Tool tip in the components folder and now what we're
going to do is we going to render that in the tool tip so content like this so the props will be transferred now here so right now you're not seeing anything right but the tool tip does exist you're going to start seeing it the more we code here so let's go ahead and first add a div here with a format date and give with the format of months day oops day and then a year let's give this a class name text small padding of two PX of three background muted and text muted foreground and then
let's add a separator component then let's open up a div with a class name P2 PX three space y1 then open up a div again with a class name Flex items Center justify between and GAP X4 and now inside of that add another div with a class name of size 1.5 BG blue 500 and rounded full and this will actually be a self-closing tag and next to it you're going to add a paragraph income and this will have a class name of text small and text muted foreground like this and then outside of this div
you're going to add a paragraph which will format the currency income and give this a class name of text small text right and font medium like this then copy and paste this uh exact da let me just see uh exactly what we have to copy and paste just a second so I made a small mistake here inside of this div with flex item Center justify between and GAP X4 add another div here and that div will encapsulate uh this income here like this and then we we're ending this div here there we go so my
mistake let's now indent all of this like that uh all right let's do this and this and this and this and this I think and then this new div will have a class name of flex item Center and GAP X2 and let's just see if this is now working or not so if I refresh refresh yeah make sure you refresh because we've shut down our server here so let's see the result now all right so we are seeing something but it looks like we have uh a small little bug here so let's go inside of
the Custom Tool dip and I have a typo so BG white like this there we go let's see now there we go now this looks fine and let's also add the expense if it exists so what we're going to do is we are going to copy this entire thing which represents the income here so let's add it and this will be the expenses of that day this will be BG rows and this will use the expenses times minus one so it shows them in a negative way if you didn't change in the summary if you
use absolute then you have to multiply it by minus one here if you don't use absolute then you don't have to do this so when you take a look now there we go you can see your expenses and you can see your income in this beautiful tool tip perfect so now what I want to do is I want to go back inside of my chart component and I want to create uh two more variants here so first I want to create a a bar variant so let's go inside of components here bar variant. TSX let's
add the Imports we need so similar to the one in area uh variant so we need these from recharts and we need format from date FNS the props are exactly the same and we are also going to reuse our Custom Tool tip so this is the basic setup now let's export const bar variant here props and let's D structure the data let's go ahead and let's return our responsive container with a width of 100% and a height of 350 let's do bar chart here with data data and now we add the cartisan grid again with
a stroke Dash array of three and three and then we add an xaxis here and we can actually I think copy from area chart the exact props here right see we want the chart to look exactly the same and then we also add the tool tip inside like this so a tool tip with our Custom Tool tip and then we have a bar so bar will have a data key which will be income and the fill is going to be our blue hex color here and class name will be drop shadow SM then go ahead
and copy and paste this bar and the other one will represent the expenses and it will use our red color and that's it so a simpler chart here just make sure your income and expenses are not misspelled from here and you can try it out now so I'm going to add the bar variant here passing the data to be data and comment out the area variant here and change this to use components and there we go you can already see it right here this is how it looks like and we still have the reusable component
tool great and now I want to create the line variant so let's go ahead and let's copy and paste the bar variant and let's rename it line variant here let me just find it line variant the props are exactly the same here uh but we do need a line chart instead of bar chart and instead of bar we need a line and I think that's the only change here so inside of here we are using the line chart for this the xaxis stays the same and now lines are going to be a little bit different
so this will be line data key is the same and we're going to add dot pulse and this is no longer fill instead it is stroke and we're going to have stroke with to be two so let's go ahead and do the same changes here this is a stroke this is a line uh dot is going to be false and we are missing the stroke with there we go and this is a line chart so slight modific and let's export this as line chart as well don't forget that uh actually not line chart my apologies
line variant or variant now let's go back inside of our chart and let's go ahead and copy and paste this and from line variant we are importing line variant there we go let's try that out so I'm going to comment this out and I'm going to add the um the line variant with data data let's see how that looks like and there we go this is our third line variant and now we're going to create a component which will enable us to switch between uh these three let's go and be and stay inside of this
chart component here and let's go ahead and let's import use state from react and then inside of here I'm going to go ahead and Define chart type and set a chart type from use State and by default it's going to be area then let's add on type change here type string and let's go ahead and say set chart type to be the type and I'm going to add a to-do here add pay wall so later work this is going to be a pay wall and now let's go ahead and let's import everything we need from
our component Library so I'm going to do that at just above the card here so we need select select trigger select content select value and select item from components UI select now that we have that let's go ahead and let's create our select right here where we've added a too next to the card title so instead of here I'm going to add the select component I'm going to give it a default value of chart type on value change is going to be on type change and inside of it we're going to use a select trigger
which will have a class name LG with auto let me just scroll up a bit height is going to be N9 rounded MD and PX of three and inside we're going to use the select value which is a self closing tag and give it a placeholder or of chart type then we're going to add a select content here and inside of here we're going to add our select item this select item will have a value of area we're going to open up a div here with a class name flex and items Center and each of
our items is going to have a little icon so let's add area chart from Lucid react and let's go ahead and give it a class name size 4 margin right of two and Shrink of zero and let's go ahead and give it a paragraph area chart and let's give this a class name of line clamp one like this there we go so now you can see that we have a cool option of area chart right here great what I want to do now is I just want to kind of separate this Chevron down from my
main content here because we now have this little icon here so we can do that by going directly uh inside of the select trigger component so that is inside of our components folder UI our select components so not the react select which is outside of the UI folder make sure you are inside of the UI folder and find the select trigger here and find the Chevron down and simply add a margin left of two here and then that's going to look a bit nicer all right let's see that there we go that looks better now
let's go ahead and let's add the rest of the items here so I'm going to go ahead and copy and paste the select item this one is going to be for line and it's going to say line chart and it's going to use the line chart from Lucid react and the last one is going to be the bar so let's copy and paste the select item this will be bar option and this will be bar chart three from leid react and it's going to say bar chart like this great and now what we can do
is we can modify this so exactly the same as it was but we're going to add a fragment here and we're going to add conditionals so if chart type is line we're going to render the line variant with the data let me see if I have too many spaces here I think I do if our chart type is area we're going to render the area variant if our chart type is bar we're going to render the bar variant so let's go ahead and check this out right now it's area chart if I click on line
it will become a line if I click on bar it will become a bar amazing amazing job now that we have the transactions chart let's create our category spending chart and it's going to be in form of a piie actually so let's copy and paste the chart and let's go ahead and rename this to spending Pi let me just find where it is there we go spending pi and let's change this to spending pi as well now we have to go back inside of our data charts component so inside of here we render the chart
and now what we're going to do is we're going to add a new div here and add the spending Pi like this let's give this a class name of call span one LG call span 3 Excel call span two like that and the data that we are going to pass will be data question mark categories like this so right now this is not exactly going to work because we are using the variance from the chart so let's go inside of the spending p and let's modify everything we need one by one starting with the types
so the type inside of here is going to be different so uh the previous one had date income and expenses the new one will have just name and value of the category now let's go ahead and let's modify the default value of the chart type to be Pi like this and let's go ahead and let's add uh the icons we are going to need here so I'm going to remove this stuff we are only going to need uh file search everything else can be removed so we're going to have a p chart a radar and
a Target those are the icons we are going to use so in the first select item we are going to have pi and a piie chart and this will say piie chart icon then in the second one we're going to have a radar and this will be a radar chart uh actually I think it's a radar icon yes so just radar chart here and the last one will be radial and this will be our Target icon and this will be a radial chart right here now let's go ahead and let's just modify the title here
so this will say categories like this there we go and this for like for empty data inside can stay exactly the same I don't think we have to change anything here and now let's go ahead and let's change this to be Pi let's change this to be radar and let's change this to be radial like this and now instead of having a line variant we're going to go ahead and we're going to have a pi variant so let's go ahead and let's create that so not in the UI but in the components create a pi
variant. TSX let's import everything we need from recharts right here and let's also add our format percentage icon here then let's define a constant with our colors so each of our categories are going to have a different color here and remember we only map four categories everything else is other right and we copy and paste the props from our spending P so it matches this structure right here let's go ahead and let's export const Pi variant here and the structure the data which matches the props and my apologies for my pronunciation of variant and variant
I know I'm I keep mixing it all the time uh let's go ahead and let's return the responsive container we are going to give it a width of 100% and a height of 350 and now I'm going to add the piie chart inside and let's go ahead and let's add the legend like this and now what I want to do is go inside of Pi chart and render the pi uh it's not going to be a self closing taex it's going to be like this and we're going to go ahead and give it some props
so data will be data CX will be 50% Cy will be 50% so that's the positioning outer radius will be 90 inner radius will be 60 padding angle will be two and fill it's going to be this color right here data key will be value and label line will be pulse and inside of the pi we are going to iterate our data using data. map we're going to skip over the entry and just focus on the index and we're going to render a self-closing cell component which will have a key which will be cell- index
and fill will be colors index mod colors length so we get a random color like this there we go let's go ahead and try this out now so I'm going to go back to the spending pi and I'm going to import the pi variant from slpy variant right here let me change this two components and remove the line variant because we don't need it here at all uh components Pi variant looks like I don't have that Pi variant right here did I forget something expert con Pi variant components a typo here all right uh let's
try it out and there we go so rent utilities clothing other perfect but now I want us to improve the label and to add a tool tip so for that let's go back inside of the uh Legend component here and we're going to go ahead and give it some props first so we're going to give it a layout of horizontal vertical align of bottom a line of right and icon type of circle so there we go you can see uh the changes happening now and now we're going to go ahead and manually modify the content
inside so go ahead and destructure the payload here we're going to give it a type of any and we're going to go ahead and return an unordered list with a class name of flex Flex call whoops Flex Flex call and space y two and then inside we're going to go over payload do map we're going to get entry which is any and index which is a number we are going to immediately return a list element with a key open backx item Dash index so that's going to be the first attribute and then we're going to
have a class name for this list flex items Center let me scroll up so you can see and it's going to have space X off two inside of the list we're going to have a span element which will very simply be a self-closing tag in fact and it's going to have a class name of padding to my apologies size two and rounded full and then it's going to have a style background color entry. color so whenever you're doing Dynamic colors do it inside of style don't do it inside of class name here because tailin has
a just in time compiler so stuff like this won't work and now let's go ahead actually that's it for this span now we open up a div and we write a class name here space X1 and we write another Span in which we render entry. value and we give this class name a text small and text muted foreground and then we have another span where we render the format percentage which we've added an import for in the beginning entry. payload do perent times 100 like this and let's give it the class name of text small
and I think this will now be an improved uh component right here let's refresh to take a look there we go so this now looks better like that and it looks like this on mobile now let's create a custom tool tip here so we're going to go ahead and copy the Custom Tool tip and we're going to rename this to category tool tip so it's going to be very similar we're going to rename this to category toolit right here the same logic is going to be here but the values are going to be a bit
different so we're just going to have uh the name from payload zero payload do name and then we're going to have value payload 0. value in this first argument here we're going to render the name and then inside of here we're just going to render the expenses right so you can remove this first div and just leave the expenses and you're just going to render the value times minus one like this and then go back inside of the pi variant and let's go ahead and let's add a tool tip component with content category tool tip
from do/ category tool tip let's try it out now so when I hover there we go you can see nice expenses which happened here here perfect now we have to add more variants for our spending PI right here because these two are not compatible so let's go ahead and let's create inside of components raar variant. CSX and this one is going to be very simple so add these imports from recharts and go ahead and give it the same data structure and then inside of here we export con radar variant we the structure the data inside
of here we are very simply going to return the usual responsive container here like we do and inside we're going to render the radar chart component let's go ahead and let's give it a CX of 50% a c y of 50% an outer radius of 60% and a data of data inside we're going to render a polar grid a polar angle axis which is going to have a style of font size 12 pixels and a data key of name of the category and then we're going to have a polar radius axis which is just going
to have the font Styles and lastly we're going to have a radar itself with a data key of value stroke is going to be our usual blue color same with Phil and we're going to have uh fill opacity 6 like like this so this is our radar chart quite simple let's go back inside of the spending p and let's use the radar chart uh sorry radar variant from SL radar variant right here there we go and let me just change this to uh use the components import uh great and there is one more that we
need here so first of all let's check out the radar variant so when I switch to radar there we go this is how that looks like last one we need is the radial chart so for that we can copy the pi variant so let's copy and paste that and let's call it radial variant let's go ahead and rename this to radial variant the colors can stay the same the data prop is exactly the same but instead of having cell and pi and Pi chart here we're going to have the following we're going to have a
radial bar and we're going to have the radial bar chart and now let's go ahead inside of here and let's use the radial bar chart we're going to go ahead and give it uh the usual CX and Cy props except this one is going to be 30% simply because it's somehow weirdly positioned here let's go ahead and let's give it a bar size of 10 let's give it an inner radius of 90 and an outer radius of 40% and for the data we're going to go ahead and iterate over the items so data. map we
get the item the index and this is how we're going to fill it because that's the only way you can do it because you don't have the individual cell components as you do uh inside of your usual um Pi variant right so we don't have Pi here so we can remove this entire thing and inside of here it's the radar radial bar chart which is ending so let's go ahead inside of the radial bar chart just just above our Legend here and let's add the radial bar which is a self closing tag and inside of
here we're going to go ahead and style the label so it fits our font size our white color and the position will be inside start which is a specific thing for this radial bar and we're going to give it two more props background and data key will be value so our value right here uh and I don't know if we have to modify the legend at all I think the legend can work exactly the same oh except yeah uh we are not exactly going to work with uh we're not going to work with percentages here
we're instead going to work with values so this is what we're going to do uh let's go ahead and change this from to not use format percentage but instead format currency like this so this will be format currency and this will use entry payload value like this and this will stay entry. value uh let's see how this looks like let's go inside of the spending pi and add the radial variant so let me just change this and let's remove the area variant and the bar variant so we just have the compatible ones great let's try
them all out so we have the pi we have the radar and let's see the radial one there we go right so this is why I didn't want to add percent because I think uh we have percentages written out inside of these right here uh or actually know these are the values right so if you want to you can use uh percentages but I kind of feel like you can visually see them here I don't know I think it's just fun to see different variables here so however you want to you can just copy what
you had from the chart and let's see if the tool tip is working so the tool tip here is not working so I think it's not supported at all so go inside of the radial variant and remove the tool tip I don't think we need it at all and remove the tool tip from here uh great so that wraps up our charts now we have to just add uh the loading uh variance so we have to go back inside of our spending p and we have to create the spending P loading so let's go ahead
and let's import the skeleton from UI skeleton and let's import loader to from Lucid react right so I copied and pasted this from my original code to save some time as uh we are getting close to the 12h hour mark here so spending py loing has a card which is exactly the same as our card right here right so I just copied and pasted from there so you can pause the screen and create the spending Pi loading Element Make sure you add an import for skeleton and for loader to so I added loader two and
here is my skeleton great so let's save this and now we also need to do that inside of our chart component so it's very very similar it's actually exactly the same so you can copy this go inside of the chart and paste it here again make sure you've imported the skeleton and the loader to from Lucid react there we go so again I'm going to pause the screen so you pause the screen and you can uh see exactly what I've written here as I said I think it's it's exactly the same yes there are no
differences I think uh great so now let's go ahead and let's go inside of our uh data charts right here which are rendering the spending p and the chart and let's improve uh this loading right here so we're going to go ahead and we are going to copy uh these two here but in instead of loading a chart we're going to load the chart loading here and instead of loading the pi we're going to add the spending P loading here like this so let's go ahead and change these two components spending Pi like this and
let's try this out now when I go ahead and refresh this there we go we have these beautiful loading elements all throughout our page perfect we have changeable graphs on all sides here amazing what we're going to do next is we're going to build the filters so that we can finally modify the data by different dates and accounts great great job now let's go ahead and let's create the filters so we're going to revisit an old component of ours called header so let's go uh I think it's inside of component on right here heather. vsx
and just below the welcome message we're going to add the filters like this there we go now go inside of the components and create filters. TSX component we're going to go ahead and Export const filters and let's go ahead and let's return a div with a class name Flex Flex call large Flex row items Center Gap Y2 LG Gap y0 and LG Gap X2 and then add what's going to be an account filter so for now it's going to be an error right let's go back inside of the header and let's just import filters from
SL filters or in this case from components filters like this now let's go ahead and let's develop our account filter so I'm going to create that in the components as well account filter let's mark it as use client let's export const account Builder let's go ahead and let's import everything we need from the select component so that's going to be select select content select item select trigger and select value as usual and let's also go ahead and let's import our use get accounts here so import use get accounts multiple from feature accounts API use get
accounts let's also prepare a couple of uh things here so I need to add a package B add query Das string let's import Qs from query string like this and let's import from next navigation a couple of of things so we're going to need the use path name we're going to need the use router and we're going to need the use search forams so all three of those now let's go ahead uh and let's start developing this so we're going to go ahead and return a select component here let's go ahead and give it some
props so value for now is going to be empty on value change will be an empty method and disabled will be false now inside we're going to create a select trigger and let's go ahead and give it a specific class name so LG with auto P width height is going to be nine rounded is going to be medium PX is going to be three font will be normal BG white 10 overover BG white 20 cover text white border none Focus ring offset zero Focus ring transparent let's go ahead and add some more outline none text
white and focus BG white 30 and transition there we go now inside we're going to add a select value which will have a placeholder account now let's add a select content here with the first item be a default select item which will say all accounts with a value of all and now what we have to do is we have to add the hook to load the account so we already have that here so let's use it use get accounts will be right here whoops so use get accounts let's destructure uh the data that's going to
be the accounts and let's add is loading is loading accounts let's go ahead and do it like this let's go ahead and use the accounts here so we're going to do that right here just below this item we're going to go over data question mark. map get the account and we're going to add the individual select items so account. name and key will be account. ID and value will be account. ID as well uh and no it's not data it is accounts my apologies right so accounts. map and then we get the individual account there
we go great let's go back inside of the filters and let's use the account filter so I'm going to switch this to components account filter let's refresh our Local Host to see how this looks at the moment let's go ahead and there we go so this is it right now it says account looks like we have uh some clerk hydration error here so I'm just going to refresh this there we go so in here we have all accounts we have checking and we have savings here great so now we're going to go ahead head and
improve this value a bit so let's go ahead and let's try and get the default value so we're going to do that using the use search params here so let's go ahead and write P perms use search perms and then we're going to have uh H account ID B pam.get account ID or all right so this all needs to match this value right here so now when I refresh uh I think that this should uh be this but it's not oh because we're not passing the value here so let's go ahead uh and pass in
the account ID as the value here there we go so now the default is all accounts as you can see here and let's make this select and select account right so we should technically never see the placeholder right because uh we fall back to all here and we just passed the value so by default it's all accounts uh great so we have that and now we have to also get the from or an empty string and we also have to get two because we want to preserve those values right so now we have this let's
go ahead and let's add a router from use router which we have imported and let's get the path name whoops path name from use path name so let's get all of those and now we're going to create a method called const onchange which will get the new value which is a string and inside of here we're going to Define our query account ID new value from and to and then we're going to say if new value is equal to all we're going to go ahead and do query. account account ID and reset the entire thing
and let's go ahead and write const URL Qs stringify url url will be the path name query and let's go ahead and add additional options here skip null with will be true and Skip empty string will be true as well like this so it's important that we preserve the from and to here and lastly router. push URL there we go let's go ahead and let's use this on change now so I'm going to add that here on change and now I want to go ahead uh and try this out so it's going to be disabled
if we are loading the accounts right so let's use is loading accounts and use that for the disabled value here so what should happen now uh in my transactions let's see uh which account am I using so I'm using the checking account it seems everywhere so if I go ahead select savings there we go I have no results if I go into the overview and try savings here no data for this period no data 0000 0 perfect if I go back to checkings everything is working and you can see my URL change using the account
ID so all of that is perfectly transmitted inside of all of those hooks that I have for my transaction so use get transactions for example us as those account ID params from my URL which we now push right here right we align it and we do the same thing in use get summary for example account ID right so let's go ahead now and let's do uh one more thing and that is to go inside of the account filter and we're going to add one more hook here called use get summary and the reason I want
that is because every time we change the account filter the summary will reload so I want to keep track of that so I'm going to go ahead and add const use get summary here and I'm going to add I'm going to extract that is loading is loading summary like this and I'm going to ensure that my select is disabled if I'm loading the summary or if I'm loading the accounts there we go perfect so now I want to go ahead and I want to develop the date filter uh which will be fairly similar so let's
go inside of the filters here and let's add the date filter component so we don't have it yet but we're going to create it in a moment so let me just close all of these things date filter do TSX let's mark it as use client here and let's go ahead and let's just export H date filter and let's return a div now I can go back to my filters component and I can import the date filter and let me just change my type of import here like this and let's go ahead inside of here and
we're going to add a lot of similar things from our account filter especially when it comes to uh the summary the query string and this kind of stuff right so we need the query string we need all of these and we need use get summary right here uh besides that we're also going to uh add a couple of things from react itself so we need use state from date FNS package we need format and sub days from react dat picker we need the date range and we have react date picker because we install shat C
nuui calendar and I also want to add an icon uh just Shevon down uh this is the icon I need Chevon down from Lucid react uh great and we are also going to add a couple of uh utils here so we need CN and format date range and we also need our button component we need our reusable calendar component and we also need everything from the popover and the only thing we don't have here is the popover close so let's go inside of components UI popover here let's add it so const popover close will be
popover primitive. close and what we have to do is we have to to export it like this and now there we go no problems with popover close here perfect let's go ahead inside of the uh date filter component here let me just refresh my Local Host to ensure that uh we see what we develop right so we're going to be using the popover component so let's start with the popover and now we are going to work with popover trigger here as child and we're going to use a button component here let's give it a couple
of props so disabled will be false size will be small and variant will be our outline and now let's go ahead and let's give it a class name uh so I'm going to go ahead inside of my account filter and I'm just going to copy uh my class name right here let's go ahead and copy that and see if that's going to fit exactly what we need here looks like this fits quite well great and inside of this button we're going to go ahead and write a span and inside of here uh we have to
format the existing date range so for that we need the Pam state so this is what we're going to do we're going to go inside of the account filter and we're just going to copy this the params right and we can can also copy the router and the path name so let's do that add it to the date filter here there we go and now we're going to do const default to will be new date and we're going to have const default from will be sub days as we usually do from default 2 and subtract
30 days uh great and now what we can build is we can build a constant called param state so if we have from we're going to add new date from otherwise it's going to be the default from if we have two we're going to use new date two otherwise default to like that there we go and now I want to go ahead and use that Pam state right here and we're going to use the format date range and pass in Pam State inside so there we go now you have the default one from April 10th
to May 10th the same one we can see right here it's the default right without any uh changes great so we have that now uh and now what I want to do is I want to introduce a state so do we have use State we already have the state great so let's go ahead uh and do the following let's add H use State here and inside of the state we're going to keep the date so let's add set date here we need to keep it in the state because it's going to be a range so
we need to keep track of users's uh first input so it can be this or undefined and by default it can be Pam State and now let's go ahead and let's build our const push to URL method so this will accept a date range which can be a date range or undefined let's create our query here which will have from to be format date range question mark from or default from so it's going to use one of the two and our usual format starting with the year month and then the day copy this and do
the same thing from for two so it's going to go ahead and use the date range. two otherwise default 2 and the exact same format goes here uh now inside of here we're going to add const URL to be a Qs query string my apologies stringify url url will be the path name I believe we have added the path name when we copied it there we go we're going to pass into query and let's add Skip null uh let's add both skip empty string and Skip null to be true there we go and lastly let's
do router. push URL there we go and we're just going to create an on reset function here to reset the date and to push undefined to the URL uh great so now what I want to do is alongside this Pam state format date range I want to add a Chevron down so we already have that imported let's give it a class name of margin left two size of four and opacity of 50 like that and let's go ahead and let's go outside of the popover trigger and let's add a popover content with a class name
LG with AO full width and padding of zero and align of start if you want to you can also collapse these so they don't look like that there we go and then inside of here we're going to use our calendar component so let's go ahead and give it a a couple of options here so we're going to have disabled to be false for now let's give it the initial Focus mode will be range so that's important because we're selecting two dates default month will be date from selected will be date so this is from the
state right on select will simply call set date and number of months shown are going to be two by default and then below that we're going to have a div with a class name ping for full width Flex items Center and GAP x two inside of that we're going to add a popover close which will have the as child property and use a button this button will have an onclick which will call on reset and the reset will be disabled if we don't have date from or if we don't have date two and now let's
add class name full withd variant of outline and let's write reset there we go let's copy and paste this below this one will say apply on click will be a method which will call push to URL and P push in the date and the rest can say the same except the variant right here like that so I think that should be it and we also have the account ID so let's use that here as well so the the query account ID will be account ID let's go let's try this out now so I'm going to
go ahead and select a much lower range now for example from 14th to 28th click apply and take a look at my uh from and to so it's from the 14th of April to the 20th of April and it looks like I have no transactions in that period or do I uh let's go ahead and try it from here so from uh this to this let's try no results here and I shouldn't have any there either so let's go ahead and see if I have any in the past can we do this all right so
I'm going to go ahead and see whether we have any bugs here all right so we do have a bug and the bug is in the account ID so our account ID was copied and it looked like this but we should not have this all because this all is not handled here it's only handed in the account filter right here there we go so let's go ahead and let's try this out now so go ahead and remove everything so just go to Local Host 3,000 and go ahead and select something in the past uh or
just some smaller amount like 7 to 13 like this just a week if you're using the seed script you should definitely have that data there we go look at this beautiful information and you can do the same thing right here on these queries like this there we go so now it's only loading those in that and you can also combine that with the checking and the savings here great amazing amazing job well you finished pretty much the entire application uh let's go ahead and see what we are going to do next now let's go ahead
and let's go through our comments to see what we have to resolve so I'm going to go ahead and search for to-do so let's see exactly all the things we have inside of here so I'm primarily interested in these ones so I found out that you can safely invalidate transactions and it doesn't matter what the additional keys are so yeah we can leave this as it is let's go ahead inside of here and let's add the invalidation of the summary because we now have that so summary and we can remove the comment for the delete
transaction as well let's add the summary let's see what else create transaction definitely let's add the summary so basically we want to refresh the dashboard right and of course for bulk delete let's see where else so edit category also needs to refresh the summary because we have the category pi and use get summary this is fine uh use bulk create transactions so definitely also needs the summary and use thead category also needs the summary let's see what else use bulk delete categories same thing we need to invalidate our new summary uh edit account um I
don't know if that needs the summary as well so I'm not going to do it here because I I don't exactly see how this would but you know what let's go ahead and add just in case let's add the summary inv validation here as well and use delete account same thing yeah actually does make sense uh because um if delete if we delete the account also the transactions will get deleted great so I think we resolved all of the uh to-dos in our hooks here uh great so you can go ahead and play along with
the rest of the stuff here and if you find any bugs you can try and resolve them yourself great great job now let's go ahead and let's deploy this to versel to see if we have uh anything missing or anything that won't work so I'm going to go inside of the terminal here and I'm going to go ahead and do the following so I'm going to do get add get commit and I'm going to go ahead and write any message here so for example deployment and now what I'm going to do is I'm going to
create a new repository so for example I'm going to call this Finance tutorial and let's go ahead and create this repository so since this is an existing repository we copy the second snippet here so let's go ahead and push that here there we go and what I want to do now is first of all refresh this page right here to confirm that everything is inside great and then what I want to do is the following I want to go inside of my package Json and I want to build I want to run the build command
so Bun Run build the reason I want to do that is so I see if there are any type errors inside of our project if there are we won't be able to deploy the verell because it's going to break the build so let's see if this will succeed or not I'm going to go ahead and pause and looks like everything is just fine if you have any errors go ahead and resolve that first great so now that we have that uh what we're going to do is we're going to prepare our environment. loal file right
here and we're going to go we're going to go inside of verell let's go ahead and select our new repository right here and let's go ahead and add the environment variables so you can just copy and paste everything inside of here and paste it like this there we go and go ahead and click deploy and we're going to have to change the next public app URL but we're going to do that after we deploy we can only do it after we deploy because we don't know what verell is going to assign our domain name to
so I'm going to pause the screen and wait for the result of the deployment and there we go looks like this is deployed so now I'm going to go ahead and click continue to dashboard here because I need to get my domain name so Finance DT tutorial. verel doapp and now we have to go back inside of the settings environment variables and we have to find the next public app URL and we have to edit it like this but let's just keep the HTTP but in our case it's going to be https like this and
don't add the backslash so https Finance D tutorial verell and let's go ahead and save this and now that you've saved an environment variable you have to go inside of the deployment select the last one and click redeploy and confirm one more time I'm I'm going to pause the screen again and tell you the results and there we go our app is deployed and it fully functional great great job if you're interested in an extended version of this course where we learn how to implement a monthly subscription and a paid plan using lemon squeezy as
well as how to connect to a bank account to fetch transactions using blade make sure to check out the link in the description and join code with Antonio Pro to unlock the source code and all additional content for all of my projects