So now that we have some routes and some hooks to manipulate the accounts entity, I want to create the actual accounts page. So to 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 gonna 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 gonna go ahead and create a new folder called accounts and Inside I'm gonna create a page dot 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 localhost 3000 slash accounts, there we go, we have the accounts page and you can also see how in 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, let's go ahead and let's add kind of 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 add slash 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 bonnex chat-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 gonna be the card, but then we're gonna 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 I 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 2. 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 gonna 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 1 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 4 and margin right of 2. There we go. Add new. Perfect. And I also want to give this a size of small.
There we go. 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 newAccount from useNewAccount. We can import that from features, account, hooks, useNewAccount.
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 use client to get rid of that error and I'm gonna go ahead and give this button a click to use a new account dot on open 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 gonna do that by giving the effect on this div right here. Let's go ahead and give it a class name.
MaxWidthScreenToExcel MXAuto. So the first two classes are so it doesn't expand too much. So when I zoom out, it will stop expanding at some point, the same as our header. 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 a full width padding bottom of 10 and minus margin top of 24 like this. There we go. So now this card is kind of above the space here and don't worry it's gonna get bigger and because we are gonna 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, ShadCNUI has the data table component and it's built using TAN stack table.
So, in order to get the data table, we actually have to install a couple of separate packages. First one being the actual table component from Shazzy and UI. So I'm gonna 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 chats.nuilatest.addtable. 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 to add 10 stack react table like this or npm install 10 stack react table. Great. So once we have that we are ready to start building our data table here.
So we're gonna keep that in the accounts page for now. Actually, we're gonna keep the data table component itself in the common components, but we are gonna have some columns and actions and we're gonna keep that here in the accounts. You're gonna see why in a second. So these are some prerequisites here. So this is how our dummy data is gonna look and we can work with that data.
So this is what we're gonna do. We're gonna keep the data table in the common components and we're gonna keep the columns in our well their use payments for us it's accounts right so because columns are not exactly reusable they are specific for each and every entity that we have. Right so let's start by defining our columns. So I recommend that you open this chat CNUI documentation here and choose the data table and find the first step, column definitions. Or you can copy stuff directly from my GitHub if that is easier for you.
So instead of accounts here, I'm gonna create columns.ds and I'm just going to paste that here. There we go. So this is some dummy 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 data table component. So I'm gonna copy the entire thing from step two and I'm gonna create the data table inside of components here so reusable data table dot TSX and I'm gonna 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. Right, let's see what is step 3 here. So the step 3 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 are gonna do it like that or are we gonna do something else? So I'm gonna go back inside of dashboard, accounts page, and I'm gonna paste this method of theirs here.
And we can import the payment from .slash columns because inside of columns, we export the type payment. So I'm doing this inside of page, right? But we can't exactly call this function anywhere. So here's what we can do instead. 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 define const data. And let's make it that array and this single object inside. And then I'm gonna 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 asynchronous function at all. And we just simplified stuff. I'm gonna go ahead and move this to the top. 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 gonna add card content. I believe we already have card content added here. And we're gonna add the data table. Now let's fix the errors.
So first of all, we need to import the data table from add slash components data table. I'm going to move here that alongside with the button here. The second thing we need is to import columns itself from dot slash columns. They are right here. So we can keep that together here like that.
So just ensure that when you copy it, nothing changed and you still have the export const columns. Like that. So I believe 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 some more actions here. So the first thing they teach 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 a type of dollars, what you can do 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 gonna do that later, but for now, I just want to add more functionality to the table itself. So I'm gonna skip step two, which says update columns definition, and We can skip that. Instead, let's go ahead and also, 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're gonna 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 gonna be working inside of data table components. 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 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 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 gonna end up with this. You're gonna 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 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 gonna 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 where our component begins just above the table constant So let me zoom out so you can see this Sorting, SetSorting, React, UseState We give it a type which we've imported from here and we define it by an empty array at first So I'm gonna 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 get sorted row model is gonna be get sorted row model but execute the method and state is gonna 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 gonna 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 two, you can see that in here it changes to email two. So we're gonna 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 like this and then replace it with this. And let's see what we are missing.
It looks like something is wrong here. Oh, 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 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. 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 hot reload is probably confused because we just renamed 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 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 difference here.
Okay, so I have to use success here. Okay. Like this, there we go. So now if I try and click email, there we go. We can see how 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 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 a data table component itself, so I'm gonna close everything else. I'm going to import column filters 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 useState from the example and I'm going to add it just below our existing sorting state. So let me zoom out just a bit more so you can see.
So columnFilters, setColumnFilters, React.useState with ColumnFilterState 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 onColumnsFilterChange. We're gonna get that, where should we add it? 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 at there we go. It filters by email. So that's great, but I do want to give it a slight modification here because our tables are going to be 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 hard coding it to email in our reusable data table component let's introduce a new data table prop here which I'm going to call a filter key and I'm going to make it a type of string and then I can destructure 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 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. 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 search for at a there we go. This is working great. So let's see what is next. We just finished filtering.
I want to skip over the visibility. We don't need that. But I do want to get a row selection so for that it seems like we are going to need the check box and the badge components or perhaps we don't need the badge I think badge came from the one 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 gonna go ahead and shut down my app and do bunx chat-cn 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 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 Can we just copy the entire thing?
Yes, we can just copy the entire thing here, but I don't think we need 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 us 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, 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 even a single item. So let's go ahead and do that first. So they're gonna 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 gonna see how it looks in a moment.
So we're gonna add that below column filters here. There we go. Row selection and set row selection. Very simple. And then we're gonna 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. I didn't find it useful for this project.
Great. So now the table is aware of our selections. And now we can do this, for example, show selected rows. 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 these two buttons for next and previous so we can add that here for example so it's going to use the get filtered 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 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 is I want to check if so open like an if clause here or what's the equivalent of this table dot get filtered selected row model executed dot rows dot 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-auto, 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 gonna be size four and margin right of two. There we go. And it's gonna tell us exactly how many rows we can delete. So open parentheses and then open curly brackets. And inside the right table get filtered rows, select row model dot rows dot 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 adapted 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 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 gonna have on delete and we're gonna 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, pdata and an array and it will return back the void and you can import the row from tanstack table So let me show you right here. I've added a row from 10 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 onDelete method for. So onDelete for now can be used. Well, 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. 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? 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 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 gonna do in the next chapter is we're going to enable and complete all the functionality we need inside of the data table which is the confirmation model. That's what I want to do.
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 we don't need to write this anymore. So we just created the reusable data table component and we're 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!