In this chapter, we're going to go ahead and develop the agents data table. This will include a nice display for existing agents, but also a nice empty state if no agents have been created. So the first step is to implement the data table component. Now as you probably know already inside of our source components UI we have all ShadCN components and that includes the table component but What we are looking for is not the table component. We are looking for a data table component.
So in order to implement that, we have to go back to Shazam UI documentation and find data table. Now, we can skip the first step of the installation here because we already have the table component. Instead, we can immediately go to step 2, adding 10-stack React table. So make sure that you are on your main branch here and make sure you have synchronized all changes. Once you've done that, go ahead and install 10-stack React table and you can add a legacy peer deps to avoid any Peer dependency errors.
Great. Once you have added that, you can go ahead and run your app. I will now open my package.json just to show you the new package version, 8.21.3. You don't have to strictly use the same version, I don't think this package often introduces breaking changes. Great.
So now let's go ahead and build this. So we're going to start by adding columns. Let's go ahead and do that. I'm going to go inside of source, modules, agents, UI components. And inside of here I'm going to define columns.tsx, like that.
And I'm going to copy this snippet. Now in case you're having trouble finding this documentation page or it's changed a lot by the time you found this video, no worries, we're gonna go line by line and see what's going on here. So it's not too big of a file, don't worry. So first of all, make sure it's marked as use client, make sure you added TSX extension and import column def from your newly installed package. In here, we just defined a very simple payment type with ID, amount, status and email.
And then what we do is we return an array of column definitions according to the payment type. So we render the status, the email, and the amount using the accessor key and the header keys. That's it. So the next thing we have to implement is the data table component. Let's go ahead inside of components and let's create data-table.tsx.
And again, I'm going to copy this entire snippet and paste it here. And we're now going to go ahead and look at it. So first of all use client then the necessary imports from 10 stack react table which are column def flex render get core row module model sorry and use react table then table table body table cell table head table header and table row from components ui table which is the component we looked at in the beginning to confirm that we have it. We then created a generic DataTableProps, which accepts any table data and any table value here. And after that we export function data table and we pass it these generics right here and we destructure the columns and the data from the data table props passing the generics.
Our table constant is defined through use react table which simply accepts the data, the columns, and the core row model. Inside of here we have a div with a class name rounded, medium, and border and it encapsulates our entire table. Then we have a table from the actual components UI table inside a table header. And inside of the table header, what we do is we map over each of our header groups and we render out the table row. And then inside of this row we go inside of the individual header group, inside of the headers, and we map out each individual table head which would most likely be the column.
So this would be status, email, and amount in table head components. We check if it's placeholder and render null. Otherwise, we use the flex render function, which we imported from 10-stack React table, and we pass in header, column, column depth header in the first parameter, and header get context in the second parameter. And we end the table head and the table row. And we did the exact same thing for the table body here.
You can see it's not anything more complicated. The only thing we do here is we do a length check. In case it is empty, what we do here is we just render an empty table row and we give it some default height and text center here. And we also give it a proper call span so it takes the entire width of our table. So I hope that this kind of made it easier for you to understand what's going on.
And if you struggled finding the actual snippet, you can now find it here. So now that we have our data table, let's see what is the next step here. So let's go ahead and render that table and let's also invent this get data it seems. So this is how we're going to do this now. We're going to go inside of our views, agents views here and we're going to render the data table component from that dot dot forward slash components data table.
And in here, we're not going to use this data. Instead, we are going to define our own data here. Let me just copy this. Let's just do const mock data. And just do something like this.
Id amount status, it needs to be either pending or processing or success or fail. So make sure it matches this type like this. MockData and just pass in the data to be mockData here. And you also have to pass in the columns. So columns will be the columns which we can import from dot dot components, columns like this.
And let me just see if I did this correctly here. I think the problem here is in the mock data here, we have to assign it to be payment type. And we might have to Oh, it's just payment. Okay. So let's import payment from the columns and set it as an array.
There we go. Now you no longer have any errors here. And now if you visit this and refresh the agents page, you should see your data table here. Let's wait, and there we go. This is our data table with our mock data.
And this is actually enough for us to no longer need to follow this. If you want to, you can take a look to see exactly how you would add some more things here, but we are going to take it from here and develop our own version. So the first thing I want to do is go back inside of the data table and modify it just a bit. So I'm going to go ahead and give this div, besides I'm going to change rounded medium to rounded large. I'm going to leave the border and I'm going to set the BG to be background and overflow to be hidden.
So that's the first thing I'm going to do. Then what I'm going to do is I'm going to delete the table header entirely, like that. So I only want the table body to render. And in the table body, I'm going to go ahead and do the following. I'm going to give this one a class name of cursor pointer, like that.
So when I hover over it looks clickable. And I'm going to introduce a new prop here. After the data on row click like this to accept the row which is a type of the data and return a void. So I can now destructure on row click here. And I will simply add it to the table row.
So on click arrow function on the row click question mark dot and then execute and pass in the row dot original inside. So it's optional. So make sure you added the question mark here. Great. Now, what I want to do is I want to modify the table cell here by giving it a class name, text small and heading of 4.
And in the table row here, I want to change this to be height 19 and also add text muted foreground and change this. Oh, well, this can be no results. That's fine. Yeah. And remove table head and table header from here.
There we go just like that. So now What I want to do is I want to go back inside of my agent's view here and just start modifying this a bit. So give the wrapping div here a class name of flex1, padding bottom of 4, px of 4, md, px of 8, flex, flex column and gap y of 4. There we go. So, looks a little bit better.
What I want to do now is the following. I want to go inside of my columns and in here I'm going to modify this to no longer use this payment type. Instead, I will use agent get one from the types which we did in the previous chapter, right? Remember when I told you to create the get one protected procedure? So we are reusing it one more time here as our column data type here.
And now we're gonna go ahead and modify the actual columns here. So the first one we're going to render will be the name of the agent, and the header will be agent name. And now what we can actually do already is we can go back inside of the agents view here, and we can use this data. We no longer have to use the mock data at all. And you can remove this.
There we go. And you can already see that you now have all of your agents here. So if you add a new agent, it will appear. There we go. So we are now rendering them inside of here.
Perfect. So let's go back inside of the columns and let's improve the way the name field is showing. So we can do that by adding the cell property here, destructure the row, and render a div here with a class name flex-column-gap-y of 1. Inside, another div with a class name flex-items-center-gap-x of 2. And then finally, a generated avatar component, which is a self-closing tag.
And inside of here give it a variant of BotsNeutral, seed of row.original.name and a class name of Size6. So make sure you have added this import here. And already you will see the avatars of your bots. Let's confirm. So test seems to be having, Let's try test2.
It seems to be having this one-eyed row vote. When I click Create, it has the exact same face. Perfect. So now that we have added that, below the avatar, add a span element rendering row original name here. And give the span a class name font semi bold capitalize.
There we go. And now outside of this div, open up a new div with a class name of flex-items-center-gap-x-of-2. And inside of this div, a new div with a class name flex-items-center-gap-x-of-1. And in here, let's add a corner right down icon. Make sure you import this from Lucid React.
And give this icon a class name of Size3 and TextMutedForeground and then add a span element row.original.instructions but give this a class name of TextSmall TextMutedForeground maximum width of 200 pixels, truncate, and capitalize. So we are adding truncate so that it cuts off, right? And let's just see. So this should be corner down right icon. So make sure you don't do the same mistake as I did.
Corner down right icon. There we go. So flex item center gap x1. Let's add maybe 1.5 here. Or maybe let's try 2.
Yeah, maybe 2 is a bit better. And now that I'm looking at it, we don't even need the outer div here. So you can just remove the outer div I think it's exactly the same yeah it is perfect so yeah you can choose gap x1 or gap x2 I think maybe 2 gives it a little bit more reading room Maybe 1.5 as the perfect middle. I don't know. Sometimes I can't decide.
Okay. Great. So we can now render our avatars here nicely, our agents. And now let's also add another key here. Accessor key, MeetingCount.
And the header will be Meetings. And cell here will be a row, like this, add a badge from components UI badge. So you already have this component installed instead of source components UI badge. And in here, what we're going to do is simply render a video icon from Lucid React and give it a class name text blue 700 like this and then add row original dot meeting count which will throw you an error because it does not exist yet and then after it check if row original meeting count is equal to 1 and then add meeting or meetings and give the badge a variant of outline and a class name flex-items-center-gap-x2 and in order to modify the SVG size we have to target it like this and then add size dash 4. So right now you can see that it looks a little bit broken here So let's go ahead and improve it by going inside of our procedures for the agent and finding the get one here.
And in here in the select, What we have to do is we have to add the meeting count option. And I think that we can just import SQL from Drizzle ORM. And for now, just like put a number of five. I think this will render the number of five. But what happens now is that our existing agent will only return this.
And you can also put like a type of number here. But in order to preserve all other fields, we can use a spread get table columns from drizzle ORM. So I imported SQL from here as well, and pass in agents. So now the existing agent has the meeting count, which is a number, and you no longer have any errors here. Let's refresh and see.
Looks like it's not yet working just because I'm not sure how to actually use this to mock a number. Let me just try something. What we can do for now, since this is obviously not working, we can do database.$countAgents. And I think we can just do this. Let's just count all agents.
Will that give us a number? I'm still not getting any number. So how about I try agents where equals and let's try user agents.userId matches. We can now, since this is a protected procedure, we can now extract context from here and we can pass in the context out session user ID. Let's try now.
Okay, I can't get a number from here for whatever reason. So yeah, it's okay. Let's remove the meeting count from now. Let's remove this. Sorry for making you do all this.
It's because we are missing the meetings schema, right? I'm trying to mock a number here. So we're just going to set this to be five meetings, for example. Like this. And you do add a meetings count here and just use the SQL 5 with the type of number and import SQL from drizzle ORM.
And you can add a little to do change to actual count. There we go. So this is how it's going to look like. Besides everything regarding the first agent here, we are also going to actually count the number of meetings it has. So we need the meeting count property to exist here because otherwise the accessor key gets confused.
So That's why I'm trying to mock the number. Later, what we're going to do is we're going to actually count the number of meetings associated with our agent or our agent associated with the number of meetings. Perfect. Yes. So this is what I wanted us to do.
And now what we have to do is we have to implement the empty stage as well. So let's go ahead and let's go to neon.tech. So we can go inside of our project here, tables, or you can use the Drizzle Studio. It's the same thing, right? And let's go inside of agents, select all of them, and delete all agents.
So it's completely empty. And now when you refresh here, you should see no results. But I want this to have this nice view as well here. So let's go ahead and go inside of here. I'm going to fix this error in a moment.
Let's just wrap up the empty state, right? So I will leave this for later. In order to implement the empty state, we can get started faster by copying the error state. So let's copy the error state and let's paste it. Let's rename it to empty state, like this.
And export const empty state, like that. And then what we're going to do is the following I'm going to remove this part and just put flex flex column item center justify center and then I'm going to remove this div entirely like that and then in here I will not render the alert I will render an image from next forward slash image. And in here, for now, I will set the source to be logo.svg. Alt will be empty. Width will be 240.
And height will be 240 as well. And then in here, this will be flex, flex, scroll. Let's set gap Y6 here. Let's set maximum width to be medium, MX to be auto, and let's put text center here. And this will also have a text muted foreground.
There we go, like that. So now that we have the empty state, let's go ahead and render it. So instead of AgentsView here where we have this error, let's go below it and let's do the following. If data.length is equal to 0, go ahead and render the empty state like this. So I will just move it here with the error state here.
And the title will be create your first agent. And the description will be create an agent to join your meetings. Each agent will follow your instructions and can interact with participants during the call. Now let me just fix this. Join, there we go.
So now you should see your big logo here and the text create your first agent. So what we have to do now is do a proper image here. What I've prepared for you is a link you can visit, or you can use the link in the description if this URL stops working. So cwantonia.com forward slash meet dash assets. And when you visit that, you will get redirected to this public GitHub with all of the images and assets for these projects, or if you have access to the source code, you can just go directly into my public folder where I will add them.
So let's go ahead and add empty.svg. Since it's an SVG, you have multiple ways of downloading it. You can download it, or you can click code and just copy the entire code here, and then just go inside of your public folder, add empty.svg, click this, open file using VS Code standard text binary, right, and just paste the code inside, and there you go. You now have the empty placeholder. Perfect.
So now let's go back and set up an empty state and let's use empty.svg. And there we go. Now you have the empty state. Create your first agent, create an agent to join your meetings, each agent will follow your instructions and can interact with the participants during the call. And the moment you add a new agent, test, test, test, click create, And this will now refresh and you will no longer see that empty state.
Perfect. Amazing, amazing job. So that's exactly what we intended to do. We added data table, empty state, and we obtained the assets. So in the next chapter, we are going to focus on this, the pagination, right?
And also this, the filters. But that's going to be for the next chapter. Amazing job. And let's go ahead and create, review and merge this all request. So I'm gonna go ahead and click on my branches here, create a new branch, this will be 12 agents data table, I believe, yes.
Let's go ahead and confirm we're on the new branch. Stage all changes here. Let's wait a second. There we go. Stage changes, 12, agents, data table.
Let's commit and let's publish this branch. There we go. Let's go to our GitHub here. Let's open a pull request. There we go.
And I already remembered something that I will have to do. Yeah, so let's open this pull request. And I'm going to let the reviewer do its thing here. But let's go back inside of, so It's completely fine to stay inside of this branch, right? We can work in it.
Let's go inside of our agent view here. UI view is agent's view. And I just want to see why am I getting this error here. So something's wrong with the columns. Agent get one.
Meeting count. So something is not matching here. Let's go inside of our columns to see. Accessor key is name. Meeting count.
What if I comment this out? Nothing, OK. So for some reason, it's throwing an error here. I'm not exactly sure why, so I will experiment with this a little bit. We can leave it like this for now, actually.
It's not too big of a problem. So I will explore this, actually, in the next chapter. So we're going to fix this error alongside adding our pagination and things. And here we have the code summary. So let's read the walkthrough.
A new table display feature for agent data was introduced using the 10-stack React table library. This includes the new UI components for table columns, a generic data table, and an empty state. The agents view now renders agent data in a styled table. And the back-end query for agents was updated to explicitly select columns and added a computed field which is our mock meetings account. In here we have a whole sequence diagram but we already know how this works right it's but and it goes even further displaying the data table or the empty state here.
And in here it left some expected comments. It really doesn't like that we hard code this so it gives us some action items define or migrate a meetings table in your schema. We will be doing that very soon but I do want to wrap up agents first. In here, it suggests adding an action button to the empty state. So this could be a good idea.
But since we already have a new agent here, I kind of feel weird having another one here. So I'm going to leave it like it is. And in here, of course, it doesn't like that this is hardcoded. We will change that later when we actually have this value here. And in here, it noticed that we removed the table header.
So very good that it noticed that, but we did it on purpose. Great. So let's go ahead and merge this pull request. So in the next chapter, what we're going to have to do is work on these filters here and fix this issue, even though I'm 99% sure these types are confused simply because of... Oh, the types are confused.
I just realized that all this time I've been adding the meeting count to get one. I also have to add it to get many. That's why it's so confused here. Okay, so we know how to fix it, but we don't have to do it now since we just merged this full request, right? Let's not change anything in this branch anymore.
And instead, let's go and change this to main and let's synchronize our changes here. There we go. Let's click on our source control, click on the graph and confirm that you just merged commit 12 inside of here and added all of these things. Perfect. So amazing, amazing job.
Let's mark that as completed here. And See you in the next chapter.