In this chapter, we're going to implement the meetings filters the same way we implemented the agent filters. But before we do that, we're going to improve the badge padding, and we're going to fix command select component filter reset. I'm going to showcase both of these issues in a second. We are then going to modify the meetings get many procedure, add the NUX filters and create UI for those filters. So regarding our previous issue where I had problems with displaying the status color, you can see that I no longer have that issue.
Looks like all I had to do was wait for Tailwind's Just-In-Time compiler to analyze the classes. If you are still having that problem, you can try doing rmrf underscore next, and then simply doing npm run dev again. This should clear up the cache and perhaps restart the just-in-time compiler from Tailwind. Now let's go ahead inside of source components UI badge and before you write anything just a reminder make sure you are on your default main branch here and change the py to 1 instead of 0.5 so I just want to make my badges a little bit taller great Now the second problem we have is when selecting something from here and typing, for example, new and closing it and opening it again, it only displays those two. So what we have to do is go inside of source components, command select and go ahead and implement const on close or let's call it handle close like this.
And actually, let's call it handleOpenChange. Value is a Boolean, like this. And then what you're going to do is call the onSearch, but only if it exists, like this. And in here, simply pass an empty string to reset the query. And then set open set to value.
And then use the handle open change for command responsive dialog on open change. So on open change, as you can see, will give you the open Boolean here. So to make it clearer, you could call this open, like that. So when you try it now, click on New Meeting, this still works as usual. And if you search for new, and it loads these two, and then you close it and open again it reset the query back to an empty search.
Great! So that's two problems that we had resolved. Now let's modify the meetings getMany procedure. So let's go inside of the meetings server procedures, specifically let's go inside of the getMany procedure here. Alongside search, let's add agentId, zString, and nullish as well.
And then let's go ahead inside of the meeting types here and let's go ahead and create export type, my apologies export enum meeting status. And in here, go ahead and add the following statuses. Upcoming, active, completed, processing, and cancelled. Always do this by looking at your database schema file and just don't misspell them. You can copy and paste them and add them here.
So this value is what matters. This can be named whatever, but this inside of the strings needs to match this exactly, including the casing as well. So now that you have that, let's go ahead inside of the procedures here and let's add status and let's make that z.enum and inside of here you're going to import meeting status from dot dot slash types like this and very simply add dot upcoming as one of the options and then simply continue adding active completed processing and cancelled all of them and chain nullish to the status enum as well now let's go inside of the query here and after page size extract the status search and the agent id You can remove the search as we already have it here. So just the status and the agent ID. And then inside of the where, let's go ahead and add some new things.
So after search, check if you have a status that user requested and then use equals meetings.status with the status, otherwise undefined. Confirm that you have equals imported from drizzle ORM. Perfect, And now that you've added that, also add agent ID in the same way using the equals and agent ID value. Make sure you're using the proper values here. Like that.
Otherwise, undefined. Perfect. And then I want to copy these two and add them to the total query as well. So I get the proper total amount here. Great, and that's it.
That's all we need for our getMany procedure. So we have added that. Now let's add our NUX filters. Let's go inside of our meetings and let's also open the agents and I think that we can borrow the hooks here. So copy the hooks folder and paste it inside of the meetings here.
So you will now have use agents filters. Go ahead and rename it to use meetings filters. Make sure you're doing that inside of the meetings module. Go ahead and return use meetings filters here. So the search and the page are exactly the same.
So now let's go ahead and add parseAsStringEnum here. And let's import meetingStatus from ./.types. So now you can go ahead and add status to be parse as string enum and open object dot values meeting status. So these are the possible options for the status in the URL. And lastly, add agent ID, which is just a normal string with clear on default here.
And no need for any clear on default for the status. So leave that as is. Now let's go ahead and let's go inside of the agents and let's copy the params and let's paste them in the meetings. And now we are just going to modify the new meetings params with the new things we added in the use meetings filters. So import parse string as enum from Nuke's server.
Make sure you're working in the params we just copied and put in the meetings module. And you also have to import meetingStatusFromTypes. This one. Great. So now let's go ahead and just add these two to the params like this and that's it now let's go ahead and let's add the UI for these filters So I want to prepare from the agents UI components.
In here we have agents search filter. So let's go ahead and copy this and let's go inside of meetings, UI components and paste it here. Let's go ahead and rename this to be Meetings Search Filter and let's rename this to Meetings Search Filter. And inside of the meetings search filter, we are going to use from our hooks use meetings filters and this will be use meetings filters. And everything else can stay exactly the same.
Yes, we probably could have done a better job and reuse this. If you want to, you can do this as a challenge for yourself. Find a way to reuse the search filter since it's exactly the same. The only difference is the hooks. Hint, it will be controlled from the outside of the component, not the inside.
But I already built it this way, so I'm just going to continue. Now that I have the MeetingsSearch filter using the new UseMeetings filter, I will go inside of the MeetingsList header here, and I'm going to add it. So in the ToDoFilters here, let's add SearchFilter from forward slash MeetingsSearchFilter. So now right here, when I do a refresh, I should see filter by name. And then when I write test, my URL has changed to this, search equals to test.
Now let's go ahead and let's build the status filter. So this will be a new component. So let's go inside of modules, meetings, UI components, create status-filters-filter.tsx. Let's go ahead and import the following from Lucid React. Circle X icon, circle check icon, clock arrow up icon, video icon, and loader icon.
After that, we're going to import our new command select component, which we've built in the previous chapter. Let's go ahead and import the meeting status from our types and let's go ahead and import use meetings filters from hooks use meetings filters. Now we have to repair the options for our command select. So this will be the following ID of meeting status dot upcoming and the value of meeting status whoops dot upcoming like that and then add a children which is a react node so open up a div here with a class name flex items center gap x2 and capitalize render a clock arrow up icon and render the actual text meeting the status dot upcoming there we go We are now going to do the same thing for all statuses. So after upcoming, go ahead and add Completed.
It's exactly the same, just using the Completed status and Circle check icon. After Completed let's add Active using the Active and Video icon. After Active let's add processing using the loader icon. And finally, let's add the last one, cancelled, using the Circle X icon. And now we can export const status filter here.
We can grab the filters and set filters using use meetings filters. And we can return command select. Inside of here, set a placeholder to be status, class name to be height 9, options to be the options constant from above. On select, we'll very simply grab the value and set filters with status value as meeting status and value will be filters dot status or an empty string there we go We can now go back to the meetings list header and we can import the status filter here. From dot forward slash status filter.
And now in here when I click, I'm able to select the status that I want. And you will see that my URL is now both the search and the status completed. And notice that I can actually filter. If I want to, I can search. But notice how that works differently than this, where in here, I actually query the database.
So the way this works, and let me just see, is this a bug or is it just not loading here? Looks like we have a bug here. So I'll make sure to fix that. I will just do a refresh just to confirm that it's not something with my local server. All right, I just restarted my server and looks like it was just a connection issue.
Everything is working fine with our filter here. So I can search for new, and it actually queries the database for new. So the reason for the different behavior between the two is that our status filter does not use the onSearch. Whereas inside of our meeting form, when we use the command select, we use onSearch, which basically tells the command select to not use the built-in filter. That's how that works, in case you were wondering.
Great. So one more filter to build, and that is the agent ID filter. Let's go ahead and let's go inside of meetings UI components, and let's do agent-id-filter.tsx. Let's import useState from React. Let's import useQuery from TanStackQuery.
Components and let's do agent dash ID dash filter dot ESX let's import your state from react let's import use query from 10 stack query let's go ahead and import use TRPC from TRPC client command select from components command select and generated avatar from components generated avatar and lastly use meetings filters now let's go ahead and export const agent id filter and as always let's go ahead and add our filters and set filters from useMeetings.filters. Let's prepare our TRPC here. And let's prepare the agent search and set agent search to be useState and an empty string. And then we can query the data using use query as in PRPC agents get many dot query options and inside of the query options increase the page size to 100 and the search to be agent search. This is basically a way to guarantee that they can find their agent, right?
The pagination will not be a problem here. And then we can go ahead and return command select. Let's give it a class name of height nine. Let's go ahead and give it placeholder of agent. And for the options, we're going to go over data, question mark items or fall back to an empty array map over an individual agent.
And then in here, we are going to return an object, like this, with an ID of agent ID, value of agent ID, and children, which are going to be a React node. So give this a class name of flex-items-center-and-gap-x-of-2 inside a generated avatar component and give it the following props. Seed will be agent name, variant will be bots neutral, and class name will be size4. And next to it simply render out the agent name. The onSelect will be identical as in our status filter, so onSelect gets the value and sets the filter to agent ID for that value.
And this time, we're going to have the onSearch prop, meaning that we are going to be using network filtering. So onSearch will change and that will re-trigger the data here, providing us with the new data inside. And finally, the value will be filters agent ID. Perfect. Let's go instead of the meetings list header here and let's add the agent ID filter.
Make sure to import it from the right place. There we go. And now you should be able to select a filter. So for example, I select this. And now this is my URL.
I have the search test, status completed, and an agent ID. And now let's add a button to clear all of that, and then let's finally connect it to the API. Let's go back inside of the Meetings list header, and let's go ahead and add filters and set filters from UseMeetingFilters, like this. Let's go ahead and define whether any filter is modified. Is any filter modified if status exists or search exists or agent ID exists?
Like that. Make sure to use double exclamation points. And let's also add a on clear filters method, which simply resets the filters to their default states. Status is null, agent ID is an empty string, search is an empty string, and page as well. Perfect.
So now what we can do here is we can import the button and we can also import X circle icon from Lucid React and we can go down here after the agent ID filter. If any filter is modified, render the button with the variant outline which has an on click for our new method on clear filters and render the X circle icon which we just imported with the clear button. So now I can click clear and everything is reset. In order to connect these filters we have to go back to our meetings UI view, meetings view. And let's go ahead and let's simply add the filters here.
So I will also add the router because we will need it in a second. So let's just move this here. And then let's also add our filters and set filters here. From use filters. Use meetings filters like this.
And then inside of the query options here simply spread the filters. So already this should now start working. If I select an agent like new one I will get no results here. But When I do a hard refresh here, I'm getting this issue unauthorized. That is, of course, because our filters have not been synchronized with the React server component.
So before we do anything further here, let's quickly go back to app folder, dashboard, meetings page. And let's go ahead and let's import load search params from modulus meetings and let's go ahead and just ask for params there we go And let's also import a type search params from Nux server. So then we can go ahead and create props for this page using the promise search params. We can then destructure the search params from the props here. And let's do params await load search params search params.
If you want to, you can call them filters. And then all you have to do is spread the filters in the prefetch. There we go. So now when you refresh, you will get no errors at all, and you can safely change your filters. That includes the status.
Everything seems to be working exactly as we expected let's just try the name test there we go works like a charm there's one more thing left to do before we can end the chapter. Let's go ahead inside of our agents UI components and let's copy data pagination and let's paste it in source components. So in the same place where we added the data table, because it's generic enough to be reused. And let's now add it to the meetings view. So after the data table, add data pagination from components, data pagination like this.
And then we're just gonna go ahead and give it some props. Page will be filters page, total pages will be data total pages, and on page change will get the page and set filters and simply modify the page. Like this. And for the data table, let's add an on row click here to get the row and simply do router.push, open backticks, forward slash meetings, and then row.id. Now let me just collapse this so it's more visible.
There we go. So make sure to not misspell this, right? Since we already have that, when you click here, it should lead you to the meeting ad page perfect so we now have working pagination for this as well let's try it out by adding another agent here like this Let's go back to the meetings and let's go inside of our constants in the source and change the default page size to one. And you can see it immediately allows me to paginate and see the older agent. Perfect.
So you can now revert this back to 10. Great. One tiny problem that we have is if you have any filter set and go inside of mobile, you can see that this happens. So let's go ahead and quickly fix that. So Let's go inside of components, meetings list header, and wrap this div inside of a scroll area from components UI scroll area.
So it's a chat C and a component. You already have it in your project. Just make sure that you've added an import for it, like this in here. And let's go ahead and add a inside of scroll area, add a scroll bar from components UI scroll area and give it an orientation of horizontal. So make sure to import scroll bar.
And you can see that now this is how it behaves. And now let's go ahead and do the exact same thing inside of the agents list header. So let me just copy the import here. Let me add it here. There we go.
And yeah, you can also, in the meetings list, if you want to replace the magic number, you can use the default age from the constants, which will be the number one. And you also have to copy the usage of it. So let's just wrap this inside of scroll area. Add this and close the scroll area. Great.
So I've just done exactly what we did in the meetings list header to the agents list header, including the scroll area and scroll bar. And I also replaced the number one with default page, which is number one, but in a constant. So now the agents will also have, I mean, it's not really visible. It requires a very small screen, but this is also now scrollable in case it overflows. Great!
Amazing, amazing job. So that's all we wanted to do. We have done all of these. And now let's merge this pull request so 19 meetings filters I'm going to go inside of my source control I will change my new branch create a new branch 19 meetings filters let me just double check there we go and let's write a message actually first let's stage all changes there we go 19 meetings filters let's click commit and let's publish the branch and then let's go and review our pulled request And now let's read the summary of this chapter. Let's read through the walkthrough.
This update introduces a comprehensive filtering and pagination to the Meetings feature. It adds new filter components, state management hooks, and search parameter utilities. The Meetings backend is extended to support filtering by agent and status. The UI now includes horizontally scrollable filter bar, clear filter functionality, and paginated meeting lists. And in here we have a sequence diagram explaining exactly how when the user adjusts the filter it goes to the meeting list header component using the use meetings filters hook we update the filter state which in turn fetches the meetings with new filters and the pagination and finally returns it back to the user.
And no comments. So let's go ahead and merge this pull request. We did a very good job with this one. And after that's done, let's go back to our main branch and let's click synchronize changes to ensure that we are up to date. Perfect, after that, go inside of your source control and confirm that you see 19 merged to main.
Amazing, amazing job and see you in the next chapter.