
No Comments Yet
Be the first to share your thoughts and start the conversation.
Be the first to share your thoughts and start the conversation.
How did you manage to remove the blur property and reach here?
Upgrading gives you access to quizzes so you can test your knowledge, track progress, and improve your skills.
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
There is no content available in the provided transcript to summarize or extract key takeaways from.
00:00:02 And finally, now that we have all the data, we are ready to collect it in one place and show it on this great-looking dashboard page.
00:00:10 We'll make it look like this, where we're tracking the amount of total users, total trips, and active users today to live trips,
00:00:19 and finally, real charts.
00:00:21 That shows you how many users have created their accounts over time, as well as which trips they're creating.
00:00:28 Looks like recently everybody's been into adventure.
00:00:31 Then you'll see which users created how many trips, and you'll see trips based on different interests.
00:00:37 Let's make it happen.
00:00:38 First things first, we have to collect all of this data in one place.
00:00:42 And that won't be an easy task, but we'll do our best.
00:00:46 So head over to AppRite and create a new file called the dashboard.ts.
00:00:52 And within here, we'll have to create many different functions that allow us to fetch all of the different pieces of data that will display on the dashboard.
00:01:01 And I really want to end this course on a high note.
00:01:05 So it won't be easy.
00:01:07 Trust me.
00:01:08 When you're working with charts and tables, you're working with data.
00:01:13 And tables are easier because you're just presenting that data in a very simple structure.
00:01:19 But when you want to present data in charts, especially charts that have X and Y axes that show different pieces of data on hover and you try to present
00:01:29 it in a nice understandable way, it's not easy and you have to deal with a lot of native JavaScript.
00:01:37 reduce functions, date and year processing, making sure that you combine all of these arrays and objects into one to be able to calculate it.
00:01:46 So as I said, we'll end this note on the high note because that's what you came into this video for, to be able to build a proper dashboard.
00:01:56 So back within our code base, first things first, we'll try to get access to the users and trips stats.
00:02:03 the ones that we're going to show on the top cards.
00:02:06 So I'll say export const getUsers and trips stats.
00:02:13 I'll make this equal to an asynchronous function that will return a promise which will be resolved into DashboardStats, and we can open up a function block.
00:02:24 What we need to do at the start is get access to the current date.
00:02:28 I'll do that by saying const d is equal to new date.
00:02:33 Pretty simple, right?
00:02:34 Then we want to get the start of this month, and we can do that by saying const start current and that'll be equal to new date d.getFullYear,
00:02:48 d.getMonth and then pass over a 1 for the date and then get outside of it and call the to ISO string on it.
00:02:59 Now duplicate this two times and we'll want to get the PREV and for the second one we'll want to get the start of the last month.
00:03:07 So I'll call it start PREV which is equal to new date d.getFullYear getMonth minus one.
00:03:16 So we want to get the previous month to ISO string.
00:03:20 then the last thing we want to get the end prev which is the end of the last month so here we want to get a new date for the year so here we'll leave the
00:03:29 date as zero okay so now we have access to these couple of dates now as before we're going to get access to all of the users and trips so i'll say const
00:03:39 destructure the users and trips and make it equal to a weight Promise.all where we're going to call database coming from AppRite.listDocuments and we want
00:03:54 to list them from AppRiteConfig.databaseID and then AppRiteConfig.userCollectionID in this case.
00:04:04 This shouldn't even be within parentheses so I'll just leave it like this and then we can duplicate it below and we can do the same thing to get the trips.
00:04:12 The only thing we have to change is the trip collection ID.
00:04:16 Okay, so now we have different dates, we have different trips and users, and we want to combine that.
00:04:24 But I'll first create a function that'll help us to do that.
00:04:26 const filterByDate of a type filterByDate.
00:04:33 In this type, we can declare above so we know exactly what we're working with.
00:04:38 Type filter by date is equal to a function that accepts the items that we want to filter.
00:04:46 And this item will be of a type document array.
00:04:50 And what is a document?
00:04:51 Well, a document is an interface that will have a key of a type string and then it can have any kind of value so we want to keep it open it can be an array
00:05:03 it can be a string it can be a value it can be a number we're going to have a key as well of a type string we're going to have a start of a type string
00:05:13 and maybe even an end of a type string so we know what to filter And finally, we will return a number when we filter that out.
00:05:22 So we'll know how many of what has happened on which day.
00:05:25 So, now that we have this, we can actually accept all of these params.
00:05:30 Items, key, start and end.
00:05:34 And we can call the items.filter() so we can filter them.
00:05:39 What do we want to filter them by?
00:05:41 Well, first, we get each individual item into the filter function.
00:05:46 and we want to automatically return the filter command.
00:05:50 So I'll check if the item key is greater than or equal to start and if not end or item key.
00:06:01 Oh my god, this is getting complicated.
00:06:03 I remember writing this one time before, but being fully honest, right now this feels like gibberish to me.
00:06:10 So what better to do than get some help from ChatGPT.
00:06:13 It says, that little helper counts how many documents in an array fall within a given date range.
00:06:21 So here's a breakdown.
00:06:23 We take in an array of objects, each is a database document, like a trip or a user.
00:06:29 We take the key, which we want to use to join them at.
00:06:32 Then we take the property name, such as joined at or created at, and then we go from start to the end of a specific period.
00:06:39 Then here is where things get tricky.
00:06:42 Here we have filtering logic.
00:06:44 So we say item.filter and we compare the item key to make it greater than or equal to start.
00:06:51 So we only keep those whose date is on or after the start.
00:06:56 That makes sense.
00:06:58 And then for the second part, we check if not end or item key is lower than or equal to end, which enforces the upper boundary if the end is provided.
00:07:09 So it makes sure that we actually count the numbers and return the number of items in that specific day.
00:07:17 So finally, we have to attach the dot.
00:07:20 length to it.
00:07:21 In other words, filterByDate function, if you call it with users joined at startCurrent and endPrevious will tell you how many users have joined between
00:07:31 the startCurrent and endPrevious.
00:07:33 And this will be super useful for our chart data.
00:07:37 Now, let's continue.
00:07:39 After that, we want to filter out users by role.
00:07:42 So I'll say const filter users by role and I'll make it equal to a function that accepts the role of a type string and returns users.documents dot filter,
00:07:57 where each U is a document, and it only returns the ones where U.status is equal to that specific role.
00:08:06 This one is more straightforward.
00:08:08 And finally, now that we have collected all of this data, we want to return it from this function.
00:08:13 So I want to say return a big object where we first share the number of total users, equal to user.total.
00:08:22 then we share the user's joined, which will be equal to, well, we'll get to the current month by calling the filterByDate function,
00:08:32 to which we'll pass what we want to filter, it's going to be user documents, what do we want to filter it by, so which is the key,
00:08:40 it's the joined ad, Next, what is the start?
00:08:44 So this is the start of the current month.
00:08:46 And when do we end it?
00:08:47 In this case, I'll pass undefined as we don't want to end this specific period of time.
00:08:52 And that'll give us the users joined.
00:08:54 But the second part of the users joined will be the last month.
00:08:59 So how many did we get the last month?
00:09:02 So we can filter by date and we'll again pass the users.documents by joined at.
00:09:08 We'll pass the startprev, so the start of the previous month.
00:09:12 as well as the endprev, so end of the last month.
00:09:15 And here I notice I have to say startCurrent.
00:09:18 Perfect.
00:09:20 So let me actually expand this into a new line so it's a bit easier to see that we're basically calling the same function two times,
00:09:28 but for the previous month and the current month.
00:09:31 There we go.
00:09:32 And this is for the user's joined.
00:09:34 Now what I'll do is I will duplicate this user joined just below and I'll do it for the user role.
00:09:41 So this will filter it based on the user or the admin.
00:09:44 In this case we want to get access to the total.
00:09:47 In the previous one we just had the current and the last but here within this user role we want to get the total and it'll be equal to filter users by role,
00:09:58 the role will be user, and we'll simply get the length of all of the users.
00:10:04 Next, we get the current month, where we'll filter it by date, and we will pass in the amount of users that we want to filter by date,
00:10:11 so when did they join, by filtering users by role of the user.
00:10:17 So this is now getting very meta or very matrix-y where we're calling this filterByRole right here within filterByDate.
00:10:26 But basically what this is trying to do is it's trying to tell us how many users have joined in a specific period of time within this month that have a
00:10:37 role of user.
00:10:38 And the rest will be the same.
00:10:40 For the last month, we once again, we want to take the users, the role of the user.
00:10:45 joined at start and prevent.
00:10:47 This is good.
00:10:49 And one more time we want to do something similar.
00:10:51 So I will duplicate this user role one more time below, but this time we'll do it for the trips.
00:10:58 So I'll extract this total up above the object.
00:11:04 And I'll say total trips is equal to trips.total.
00:11:10 Next, we have trips created.
00:11:12 This should be technically called trips created over time.
00:11:15 That's actually what makes it difficult.
00:11:16 Not the amount of total trips created, but trips over time, right?
00:11:21 So I'll say current mind filter by date.
00:11:23 Here, we want to filter by date and we want to get the trips documents.
00:11:27 So trips documents by created at.
00:11:33 And we want to do the same thing right here below.
00:11:36 Trips documents, created ad.
00:11:38 And here we're returning all of these important pieces of info, users, and trip stats that we will use on our dashboard.
00:11:47 So what do you say that we actually start using them?
00:11:50 I'll head over into my dashboard.tsx and I'll want to cull these pieces of data because everything on the dashboard right now is entirely thick.
00:12:01 So we need to start by modifying our client loader to not only call the user info but to also call the dashboard stats.
00:12:11 So I will expand this function and I will create another await that will fetch the dashboard stats.
00:12:19 So I'll say await getUsersAndTripsStats and we of course have to put all of these into their specific variables.
00:12:29 Or we can once again do a promise all.
00:12:32 So I'll say const user and then dashboard stats.
00:12:36 We can call these stats we just created.
00:12:39 Will be equal to await promise.all.
00:12:44 And here we can then put both of these calls so we can try to call them at the same time in parallel.
00:12:50 Perfect.
00:12:51 So now we're getting these dashboard stats and we need to return them right here by saying return user.
00:12:58 as well as DashboardStats coming from ClientLoader.
00:13:03 Now, we can accept that right here from the ClientLoader.
00:13:07 It'll be...
00:13:08 Now, we're not getting only the user.
00:13:10 The user is only a part of the LoaderData, so it'll be LoaderData.user.
00:13:15 Next, we have to destructure the DashboardStats from the LoaderData.
00:13:19 So I'll say const DashboardStats.
00:13:23 is equal to LoaderData, and now we can display them right here within the stats card.
00:13:30 So, for the total users, I will use DashboardStats.TotalUsers, and let's see if it changes from 13,000 to about 2. Yep, this makes more sense.
00:13:42 Next, we can get the current month count by getting to DashboardStats, dot users joined dot current month.
00:13:52 And now we can see we're down, but we also have to change the bottom one.
00:13:56 So I'll change the last month over to dashboard stats dot users joined dot last month.
00:14:05 So we worked super hard so we can actually get the real stats right here.
00:14:09 Now we can do a similar thing for the total trips.
00:14:12 So we'll show the dashboard stats dot total trips.
00:14:17 We can also show the current month by saying DashboardStats.TripsCreated.CurrentMonth and below we can say DashboardStats.TripsCreated.LastMonth So now
00:14:34 we can see that we have a total of two trips created and we can check out the active users today by doing the same thing DashboardStats.UserRole.Total
00:14:48 And for the current, we can do the DashboardStats.UserRole.CurrentMonth.
00:14:54 And we can repeat it at the bottom.
00:14:57 DashboardStats.UserRole.LastMonth.
00:15:02 So you can see that we only had one active user today.
00:15:05 Okay, that makes sense.
00:15:06 And now we are seeing these three different cards with the real user data.
00:15:12 It was hard to create them, but we needed to get the previous and last months, so we know what the increase is.
00:15:19 Now, to get access to other pieces of data that we need, such as the created trips, which we have already gotten a couple of times so far,
00:15:26 both on the All Trips page, as well as on the Trip Details page, right at the bottom.
00:15:33 So we have to get it one more time so we can show these live cards, but then we'll also have to get the stats for the charts that we'll show at the bottom.
00:15:42 These calls will get a bit repetitive.
00:15:44 We'll have to list the documents from the database, do some logic with dates and time, and finally return it in a format that our frontend will like.
00:15:53 So, to save us some time, I'll provide the full Dashboard.ts file within the video kit.
00:15:59 So just head over to the bottom of the video kit and you can get it right here.
00:16:03 Once you get it, you can override the one that we have right here.
00:16:07 If you check out this file, you'll notice we have a couple of different functions.
00:16:10 So let me collapse all of them so you get the idea.
00:16:14 The one that we created was the toughest one.
00:16:16 So we have it right here, and we have it right here.
00:16:19 Now, I'll collapse these couple of functions that we have right here, but the toughest one was the one that we created.
00:16:26 So I'll collapse that one too.
00:16:28 GetUsers and TripStats.
00:16:31 Then we have the GetUserGrowth per day, which is the data that we'll use for our chart.
00:16:36 GetTrips created per day as well, and then GetTripsByTravelStyle.
00:16:41 So let's take a quick look into this GetUserGrowthPerDay.
00:16:45 Here, we are listing the users, we are taking the current date and time, and we are aggregating the number of users per day that they have joined.
00:16:56 And this is a perfect use case for a reduce function.
00:17:01 Whenever you want to count some numbers up in an array, such as you have a day one, maybe two users have joined.
00:17:08 Then you have a second day, and then maybe four users have joined.
00:17:13 For the third day, you have five users.
00:17:15 When you want to use a map and count something up in that map, well, that's what the reduce function is perfect for.
00:17:23 Basically, what it does is it takes a key.
00:17:26 In this case, the key is going to be the day that you want to map over.
00:17:30 and then you're adding a specific count of users in this case to the day that they have joined within.
00:17:36 It might sound a bit complicated, but that's because it is, and Reduce is not something that we use super often, but as I told you,
00:17:44 with charts, you're going to use it a lot.
00:17:46 And this just reminded me of the complete path to JavaScript Mastery that I recently completely re-recorded from scratch.
00:17:52 It's one of the oldest courses that I had, but now I updated it, And here we have all of these different array methods in detail.
00:18:02 So everything from strings in details to arrays in details, and here we have the array reduce.
00:18:09 And we explain it in a lot of detail.
00:18:12 So for example, If you have a grocery list with all the prices, you want to calculate the total price of all the items.
00:18:19 This is a perfect example to use reduce.
00:18:22 You could do it with a for each, where you're just going to map over each one of these elements and add them together.
00:18:29 but you use an external variable to keep track of the total.
00:18:33 With reduce, it does it all together.
00:18:36 Basically, you get this variable called accumulator, which starts at zero, and then you take the current value for each iteration,
00:18:45 and then you add it together.
00:18:47 But as you can see here in the course, we go super deep into understanding exactly how it works.
00:18:54 So if you want to polish your JavaScript knowledge a bit, Go check out Complete Path to JavaScript Mastery by becoming a JS Mastery Pro member.
00:19:01 Another thing that we cover over there is object entries as well, where you can take both the keys and values of an object and then map them over to an array.
00:19:11 Pretty useful when you want to have a chart that has a day and count attached to each day.
00:19:16 So essentially, you'll end up with an array of objects where each object has a count and a day, which will look something like this.
00:19:23 Great.
00:19:24 So now, let's call these functions right within our dashboard.
00:19:28 Right here, where we called the getUsersAndTripsStats, we'll also call the getAllTrips because we want to render them within the cards.
00:19:36 I think four will be enough.
00:19:38 We also want to get access to the get user growth per day.
00:19:44 We also want to get trips by travel style and we want to get all users but only four will be enough in this case just to show them within a table.
00:19:57 Of course, we have to add a weight in front every single one of these calls.
00:20:02 And we are calling all of these simultaneously, because these pieces of data don't depend one on another.
00:20:08 Now here, we're getting all the trips.
00:20:11 But remember, we have to properly parse the details of those trips.
00:20:15 And I believe we have already done that within the trips page.
00:20:20 Here, we are getting all the trips, but in order to get them with all the proper details, you need to map over them, parse the data,
00:20:28 and attach the images.
00:20:29 So you can copy this part right here, where it says allTrips.map all the way until the end.
00:20:36 Then, right at the bottom, you can say const allTrips is equal to, and you can paste what you just copied.
00:20:43 But in this case, we can call it Trips right here.
00:20:46 And where are these Trips coming from?
00:20:48 Well, we are yet to define the variables where all of these functions will spit out their values.
00:20:55 So let's define them one by one.
00:20:57 First, we have the user.
00:20:58 Then we have the dashboard stats.
00:21:01 After that, we get the trips.
00:21:03 We then have the user growth.
00:21:06 The trips by travel style.
00:21:10 And then all users.
00:21:13 Perfect.
00:21:14 So now we're getting these trips.map right here.
00:21:17 But I think we have to say trips.allTrips because trips also contains the total of the trips.
00:21:23 And allTrips is an actual trips array.
00:21:25 We also want to map over the users.
00:21:27 So I'll say const mappedUsers of a type usersItineraryCount will be equal to allUsers.users.map where we get a individual user And for each one,
00:21:41 we automatically return an object.
00:21:44 Not like this, because this opens up a function, but rather parentheses and then an object within it, which automatically returns an object.
00:21:53 That's just another pro JavaScript tip.
00:21:55 Within it, we can say that we want to get the image URL.
00:21:59 which is equal to user.imageurl.
00:22:02 We also want to get the name, which is equal to user.name.
00:22:06 The count, which is equal to user.itinerary count, like this.
00:22:12 So now that we have collected all of these pieces of data, we can return them.
00:22:16 User, dashboard, stats, all trips, user growth, trips, buy, travel, style.
00:22:28 and finally all users, which will be equal to mapped users.
00:22:34 We're returning all of this from the client loader.
00:22:37 I think at this point, it is safe to remove this destructuring of the fake data, as well as these fake imports right here at the top.
00:22:46 This will momentarily break our application, but will make it work very soon as we extract real data and bring it into this page.
00:22:55 So from the loader data, I will destructure the all trips, the user growth, the trips by travel style, the all users, and I think that's it.
00:23:10 Immediately, this will make our app work with real trip cards.
00:23:15 And then we can scroll down below the created trips, here where we have the trip card, and we can make sure that we're passing the right data.
00:23:22 So we're mapping over the all trips, and this time I will simply say all trips dot map, and we get each individual trip.
00:23:31 For each trip, we want to return the trip ID, trip name.
00:23:37 We also want to get the image URLs, itinerary, tags.
00:23:40 So I'll simply say trip dot in front of every one of these.
00:23:44 For the name, we know that it'll be there, so I'll add an exclamation mark.
00:23:48 And for the tags, tags actually don't exist.
00:23:51 They're a combination of trip interests, as well as the trip.travel style.
00:23:59 And for these, you'll also have to add an exclamation mark, and the same thing for the price.
00:24:03 because you can see that it's showing, so it has the data, but we have to render it properly.
00:24:07 So now, we are ready to focus on the last section of the day.
00:24:11 It'll come below this section that keeps track of our cards.
00:24:16 So I'll create a section with a class name equal to grid, grid-calls-1, On large devices, grid dash calls dash two with a gap of five.
00:24:29 Within it, I will render a chart component coming from Syncfusion.
00:24:34 And within it, I will inject, again coming from Syncfusion, specific services.
00:24:40 An array of services to be exact.
00:24:43 Specifically, the column series, the spline area series, the category, the data label coming from Syncfusion React charts and the tooltip coming from React
00:25:01 charts as well.
00:25:02 I simply copy this over from their docs and you can already see that this gives us some kind of a chart.
00:25:08 So let's give this chart an ID equal to chart 1, a primary x-axis will be equal to user x-axis coming from constants.
00:25:22 So now you can see that it says they at the bottom.
00:25:25 And then we'll have a primary y-axis equal to user y-axis once again coming from constants.
00:25:33 I'll also give it a title equal to user growth and a tooltip equal to an object where enable will be set to true.
00:25:45 So now you can kind of hover over it.
00:25:47 And when we have some real data, you'll be able to see what it says.
00:25:51 but now we actually show the data within it.
00:25:54 So below the inject, I'll render a series collection directive and within it, I'll render a series directive, which will be a self-closing tag.
00:26:06 Here, you actually put a data source of user growth.
00:26:11 But you have to define exactly how each field will look like.
00:26:14 So X name will be day.
00:26:17 Y name will be count.
00:26:20 The type will be column.
00:26:23 The name will be column as well.
00:26:26 And the column width, you can choose how long or wide you want to make it.
00:26:31 I'll do 0.3. And you can go to very small details like choosing a corner radius.
00:26:38 So for example, on the top left I can make it 10, and also on the top right I can also make it 10. So they look a bit rounded.
00:26:48 Like, it's the little details, right?
00:26:50 So currently I only have one day that we have been active, that I have been developing this app.
00:26:56 And you can see that it says that two users have joined today.
00:26:59 Or rather, this was yesterday.
00:27:01 So now, if I go ahead and join as another user...
00:27:04 Okay, now we have logged in as the new user, and we are right here on the homepage.
00:27:11 The non-admin homepage, which right now is completely blank.
00:27:14 So we might actually want to create this route route right next to the sign-in.
00:27:20 I'll call it the travel page.
00:27:23 So travelpage.tsx and I'll run RAFCE.
00:27:29 Then you'll have to head over to the routes and we can add another layout right below our previous layout, which will be forward slash routes,
00:27:41 forward slash route, forward slash page layout.tsx.
00:27:46 And inside of an array, we can give it its first index route this time, coming from React router.
00:27:53 That'll be routes root travel page.tsx.
00:27:59 So we are rendering this new page that we have right here.
00:28:02 When you do this, you might want to reload your application, just so you can see the changes.
00:28:07 And before we rerun it, you also need to create this page layout.
00:28:11 So right here with an app root, you can create a new layout called page-layout.tsx where you can run RAFCE.
00:28:22 Now, if you do this and rerun your application, it'll spin it up on localhost 5173 and you'll be able to see the page layout.
00:28:31 The only reason why I wanted to have this here right now is so we can implement the logout button.
00:28:38 Remember, we have already implemented the logout functionality on the nav items.
00:28:43 So I will simply grab it from there.
00:28:45 Here it is.
00:28:46 A button to handle the logout.
00:28:49 So I will copy it.
00:28:51 Paste it over into the page layout.
00:28:54 And I will also copy the handle logout functionality alongside the use navigate.
00:29:01 And paste it at the top of the page layout.
00:29:05 So here, you can see this little logout button right now, but before you click it, try to navigate over to the dashboard manually,
00:29:12 and you'll notice that you might be able to see it for a second, but then it'll be gone.
00:29:18 So once you do that, you can then log out, your account will have been created, and you can sign in with your admin account.
00:29:25 Once you come back, now you'll be redirected to the homepage.
00:29:28 So if we head over to that page layout, we could add another button right here, Maybe this one will just redirect us to the dashboard.
00:29:38 So I'll say dashboard and on click, I will simply call a callback function that will navigate over to forward slash dashboard.
00:29:49 So now if you click on the dashboard, There we go.
00:29:53 And now we have three users and you can see that our user growth has increased for this new day that we have added another user.
00:30:01 Great.
00:30:03 Now we can continue with the dashboard, but now I will add a second series directive.
00:30:09 I'll do that by copying this one first, right below.
00:30:14 The data source will still be the user growth by day count but this time I'll give it a type of spline area and a name of wave.
00:30:24 If you do that you'll kind of be able to see the trend of how many users are joining.
00:30:30 So we can also give it a fill color equal to rgba something like 71, 132, 238, and then, most importantly, the opacity of 0.3, so it's not actually appearing
00:30:46 over the other elements.
00:30:47 So now we can see the trend.
00:30:49 Of course, this is not super cool when you have two, but later on, when you have more, it'll look much better because you'll be able to see the trend on
00:30:56 each one.
00:30:57 In this case, instead of a column width and a quarter radius, we can give it a border of about width of two, and the color of hash 4784EE.
00:31:08 So now it'll have a border as well, so you can better track the trend.
00:31:16 Now we can exit out of this chart component, and right below it we can create another.
00:31:22 So what I'll do is I'll simply copy the entire chart, and I will paste it one more time right below it.
00:31:30 Starting from the top, I'll call it chart two.
00:31:33 Primary x-axis will be trip x-axis and y will be trip y-axis.
00:31:41 This one will say trip trends.
00:31:44 We'll still inject the same things.
00:31:46 And then it'll have a series directive with a data source of trips by travel style.
00:31:53 with X name of travel style, Y name of count, type of column, name of day, column width of 0.3, and the same corner radius.
00:32:06 And we don't need a second series directive for this chart.
00:32:10 So if you save it, you'll be able to see it just below to track trip trends.
00:32:14 So there you have it.
00:32:15 We have two trips, one relaxed and one cultural.
00:32:20 Now I'll head over below this chart component, and I want to make sure that both of these are in the same section right here.
00:32:28 And they need to say grid instead of grids, columns.
00:32:31 So if we do that, and if you go to desktop, you'll notice that now they both show in the same row.
00:32:37 While we're here, what do you say that we go ahead and create two more trips to fill out our dashboard a bit more?
00:32:43 So I'll head over to create a trip.
00:32:45 I'll select a random country from the list.
00:32:48 Enter a number of days and I'll choose some of these fields at random.
00:32:53 There we go.
00:32:54 Looks like this will be a fun one.
00:32:58 So let's go ahead and generate it.
00:32:59 Let's see if there's some nightlife and bars in Burundi, Africa.
00:33:05 This one should be quick because it's a five-day trip only.
00:33:08 Looks like Burundi has its own Las Vegas and some interesting houses.
00:33:12 And let's do one more.
00:33:14 I'll pick another random country.
00:33:17 Let's go with Guatemala, a short two-day trip with friends.
00:33:23 That's going to be an adventure with hiking and a premium budget.
00:33:28 what's happening at Guatemala.
00:33:30 There we go.
00:33:30 It looks like there's going to be a lot of hiking, but since we're looking at a luxury trip, it's going to be a private transfer to the Volcano National Park.
00:33:39 Perfect.
00:33:40 So if we head over to the dashboard, you'll see that it looks much better, and we can see more trip trends right here.
00:33:46 So the last part that we have to do...
00:33:48 is create some very quick references to our users and trip bookings so that we don't necessarily need to go to their respective pages right here and here.
00:34:00 Rather, we can see it all from our global dashboard.
00:34:03 So to do that, I'll head below this section and I'll create another section with a class name equal to userTrip.
00:34:13 wrapper within here, we have to map over the users and trips both at the same time.
00:34:20 So what do you say that right here at the top, we create a variable called users and trips, and we make it an array where the first object has a title
00:34:31 of latest user signups with a data source equal to all users with a field of count and a header text.
00:34:44 equal to TripsCreated.
00:34:48 Count is also a string.
00:34:50 And we can duplicate this right below.
00:34:53 But this time, instead of latest user signups, we'll have trips based on interests.
00:34:59 We will pass the trips data with the field of interest and the header text of interests.
00:35:06 So now we have to make sure that we have access to these trips.
00:35:10 And in this case, we just need the images, the names and the interests, nothing more.
00:35:15 So we can create those trips variable.
00:35:19 out of the all trips by mapping over them, getting each individual trip and automatically returning an object with an image URL of trip dot image URLs
00:35:34 of zero.
00:35:35 So we return just the first one.
00:35:37 the name of trip.name and the interest of trip.interests.
00:35:44 But make sure that you have properly closed it right here in parentheses and then a curly brace.
00:35:50 So now we have access to it and we can map over it.
00:35:53 So right below, I will map over users and trips.map where we can destructure the title, the data source, the field, and the header text,
00:36:08 and outside of it we also get the index.
00:36:11 So for each one of these, we can return a div with a key of index, a class name equal to flex, flex-col, and a gap of 5, and an H3 within it that'll render
00:36:28 the title.
00:36:29 That'll look like this.
00:36:30 Latest user signups and trips based on interests.
00:36:33 Where we have a class name equal to p20-semi-bold and text-dark 100 to make it a bit stand out.
00:36:44 And then we can render the grid.
00:36:47 This grid will be very similar to the grid in all users.
00:36:51 So head over into all users, find where we used the grid component, and just copy it.
00:36:59 You don't have to copy it until the end.
00:37:01 Maybe just take the first thing where we're actually rendering the username and the photo.
00:37:08 So until the end of the first column directive.
00:37:11 Next, right below the H3, paste that grid component, but change the data source to data source and make sure to import the grid component from Syncfusion.
00:37:23 Import the columns directive and import the column directive.
00:37:29 We'll keep this exactly the same because we want to show the users same as before.
00:37:34 And we'll render just one more column directive.
00:37:37 This one to render the second field with a header text, which will be dynamic header text.
00:37:46 So either the latest user signups or trips based on interests with a width of 150 and text align of left.
00:37:56 And we can end it right here.
00:37:58 Make sure to properly close the columns directive as well.
00:38:03 So in this case, we have to close it and then close the grid component itself.
00:38:09 If you do that, you should be able to see two different tables.
00:38:13 The first one rendering the users and the second one rendering the interests.
00:38:17 Oh, but wait, why is the number of trips created non-existent?
00:38:22 I think we don't have this field stored into the database.
00:38:25 We would need to do some additional logic to connect the user to the trip.
00:38:30 But what we can do for now is just head over all the way up to where we have the count of the users.
00:38:38 And here, maybe we can say if it doesn't exist, do a math.floor math.random times 10. This will give us a random number from 0 to 10. Perfect.
00:38:51 This is looking good.
00:38:52 So with that in mind, if we expand this dashboard so you can see it on full screen, you have now developed a fully fledged dashboard with a real database
00:39:04 where you can see different charts, the users, the trips.
00:39:08 You can see absolutely everything right here from the type of the user to the trips that we have generated so far, with pagination and the ability to generate
00:39:19 new trips using Gemini.
00:39:21 So this is absolutely amazing.
00:39:24 And I almost forgot about the trip details page, which is maybe the star of the show, because here is where you can see the actual trips that we have generated
00:39:33 for each user.
00:39:34 So the last thing we have to do is to deploy this travel agency admin dashboard.
00:39:40 which we'll do right now.
00:39:41 But keep in mind that I really wanted to take this course further than just the admin dashboard.
00:39:46 Of course, at the same time, I wanted to make it complete.
00:39:49 So right here, right now, you have successfully developed the admin dashboard with all of these functionalities.
00:39:56 So the next step, we'll be waiting for you on JS Mastery Pro.
00:40:01 We'll take this app a step further by developing a public landing page.
00:40:06 where different agencies can create their own landing pages with featured destinations and show their hand-picked trips.
00:40:15 Then, users can view those trips and even book them by paying for them using Stripe Checkout.
00:40:23 After that, they'll be redirected to this great-looking success screen.
00:40:27 So, what do you think?
00:40:29 If this is something you'd like to explore, you can check it out right here within the video kit.
00:40:34 Watch it now by becoming a member.
00:40:36 Whether we decide to improve this app or not, one thing is certain, and that is that we have to get it deployed.
00:40:42 So back within our app, I'll stop it from running, and I'll run git add dot git commit dash m, and I'll say final commit.
00:40:52 And finally, run git push.
00:40:55 Once you push it, you can head over to Vercell and add a new project.
00:41:00 You should be able to see it pop up right here, and you'll notice that Vercell will automatically figure out that we're using the React router framework.
00:41:09 Then you'll be able to add your environment variables right here.
00:41:13 So head back over to your application, go to your .env.local, copy these ENVs and paste them right here and click deploy.
00:41:24 This will take about a minute, but while that is happening, you can head over to AppRite and create a new platform.
00:41:32 This will be a web app.
00:41:34 So you can give it a name of travel agency.
00:41:37 And for host name, you can just add a star.
00:41:41 Click next.
00:41:43 Next again, we have already done this and you have your endpoint, which we have already used before.
00:41:48 Now your app should be accessible from the web.
00:41:51 So if we head back over to Versel, you'll see that a new project was just deployed to JS Mastery Pro or to your name in this case.
00:41:59 So you can continue to its dashboard and click visit.
00:42:02 This will redirect you to a very simplistic page for now, but if you log out, you'll be able to see your sign-in page deployed and live on the internet,
00:42:14 and you can now sign in with Google.
00:42:17 After which, if you log in as admin, you'll be able to check out your admin dashboard.
00:42:22 Pretty cool stuff, right?
00:42:24 In this project, I purposefully left a couple of places that you can take to improve it even further.
00:42:31 For example, you can make these trips create a dynamic or you can make these headings point to the users and trips tables.
00:42:39 You can fix a little bug when the user is first time creating an account and they try to visit the dashboard, they can actually see it.
00:42:46 That's a weird edge case that's worth taking a look at.
00:42:49 But overall, congratulations on building such an amazing application.
00:42:54 that acts both as a dashboard as well as an AI generation tool for all of your and everybody else's future travels.
00:43:02 With that in mind, if you'd like to polish a bit of your JavaScript, check out Complete Path to JavaScript Mastery.
00:43:09 Or if you maybe want to do a bit of Next.js, you can check out the ultimate Next.js course.
00:43:14 or just become a JSM Pro member to get it all alongside real interview feedback and even the access to building the public version of this website.
00:43:24 All of that is available on JS Mastery Pro.
00:43:27 So check it out and I'll see you there.
00:43:30 Have a wonderful day.