
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
This lesson lacks any content or substantive points to summarize. Thus, there are no key takeaways or themes to extract.
00:00:02 So now that we have the trip details page, we have to have a page that allows us to visit all of those great trips.
00:00:08 So let's create it.
00:00:09 First, I'll head over into our routes.
00:00:12 Right here, I want to check whether we already have a route for the trips and we do.
00:00:16 It is the trips.tsx page.
00:00:19 So let's head over into trips.tsx.
00:00:22 And currently on there, we only have a header.
00:00:25 But within here, we also want to access all the trips, similarly to how we got access to these popular trips at the bottom of the trip details.
00:00:34 So I'll head over to the trip detail one more time, and I'll copy the part where we have the loader that fetches that data.
00:00:44 So I'll copy this.
00:00:47 alongside the top part of the trip detail that actually has the loader data.
00:00:53 So I'll copy it and I'll override the top part right here.
00:00:58 Of course, we have to change this over to trips, but the loader data will remain the same.
00:01:03 It's just the import of that route that'll change as it'll point to dot slash types forward slash trips instead of trip detail.
00:01:12 Perfect.
00:01:13 And we'll also make the loader a bit simpler.
00:01:16 We don't need to fetch the individual trip, so we don't need the params to get the trip ID, so I can remove that part.
00:01:24 But instead, we'll need a request.
00:01:27 Why?
00:01:27 Because we'll implement the pagination, so we need to know on which page we're on and what is the offset.
00:01:34 So I'll say that the limit per page is 8, and then we have to get access to the current URL, which will be equal to new URL,
00:01:44 and we'll get it from the request.url.
00:01:47 Once we have that, we'll have to figure out on which page we're on.
00:01:51 So we'll say const page is equal to parseInt url.searchparams.get page.
00:02:00 So similar to Next.js, this is working in the principle of search params.
00:02:05 and I'll say if we don't have it, we can default it to a string of 1, and the radix of that parse end can be set to 10. That means that it's a decimal system.
00:02:15 Finally, we need to get the offset to figure out how many pages do we have to skip, and that'll be equal to page minus 1, so we want to take the previous page,
00:02:24 And multiply it by the limit, or in other words, the amount of trips we're seeing on that page.
00:02:30 With that info, we can now call the getAllTrips no longer within the promiseAll.
00:02:37 It can be a very simple call to const allTrips is equal to await get all trips, but now instead of passing the fake limit and offset,
00:02:50 we can actually pass the limit and the offset coming from the page for rams.
00:02:57 Perfect.
00:02:58 And alongside all trips, we're also getting the access to the total number of trips.
00:03:06 So what are we returning?
00:03:08 Well, we'll return trips.
00:03:10 which will be equal to allTrips.map We want to take the ID, trip details, image URLs.
00:03:20 We do the same thing as we did before, but now as the second parameter, we'll also pass over the total number of trips.
00:03:28 Perfect.
00:03:28 So now we're returning that data from the loader and we can consume it right here within this page.
00:03:34 Not the trip details, but rather trips.
00:03:37 So it'll be as simple as saying const trips is equal to loader data dot trips.
00:03:44 as a trip array, so we know what each one of those has, and now we can map over them.
00:03:49 So I'll head right here below the header, and I'll create a new section.
00:03:53 That section will have an H1 that'll have a class name equal to p-24-semi-bold, and text dash dark 100, and it'll say manage created trips,
00:04:07 because we're in the admin dashboard right now.
00:04:11 Then, right below the H1, we can render a div that'll have a class name equal to trip-grid.
00:04:18 And if you remember correctly, we already rendered a trip grid on the details page.
00:04:24 So head over to trip detail, all the way down at the bottom, and you can see that here we have a div where we're mapping over all of those trips.
00:04:34 So copy it.
00:04:35 and paste it right here.
00:04:37 Trips.map where we get each individual trip, and for each trip, we render a card with the trip ID, name, image URL, location,
00:04:48 tags, price, and more.
00:04:51 It is all fitting in very nicely right here.
00:04:53 And if you do just that and save, you'll immediately be able to see the card for one trip that we have created so far.
00:05:01 So what do you say that we go ahead and create another trip, just to make sure that our app still works?
00:05:05 Let's say that I want to visit Japan, and we really want to make it an interesting trip that lasts about 10 days.
00:05:13 I'd like to go there with my fiance, so I'll say as a couple, with a travel style of cultural.
00:05:19 We can put different interests right here, such as historical sites, and a mid-range budget.
00:05:25 You can see Japan right here in the world map, and let's generate a trip.
00:05:29 We can see that it's loading, and the button is disabled, so we cannot click on it like monkeys.
00:05:35 But in a couple of seconds, the trip will be generated.
00:05:39 So let me expand it, and it actually seems like it wasn't redirected.
00:05:43 So something must have gotten wrong.
00:05:46 It looks like the trip details has exceeded 5,000 characters.
00:05:51 That's the limit that we set.
00:05:53 And since I chose 10 days, which is the maximum duration, it's highly likely that the itinerary expanded over 5,000 characters.
00:06:01 So, head over to your dashboard, into Databases, Trips, Attributes, and see if we can edit the trip details.
00:06:11 Let's see what is the maximum size that we can set.
00:06:15 Okay, I updated it to 10K.
00:06:17 Now, if I go back and retry to generate this trip, hopefully 10,000 characters is enough to explain what I should visit in Japan for 10 days.
00:06:27 There we go, that's beautiful.
00:06:29 See how it picked up those very nice photos from Unsplash?
00:06:34 We have a bit of nature, a bit of city, and a bit of culture.
00:06:38 Cultural, couple, mid-range, historical.
00:06:40 It is rated very well.
00:06:42 And we have a bit of a description.
00:06:44 I should arrive at one of these two airports, visit the Imperial Palace, enjoy a welcome dinner, and then go to different places.
00:06:53 This is amazing, I gotta say.
00:06:55 And see, the best time to visit definitely is in the spring, as there's cherry blossoms there as well.
00:07:03 But everything is looking great right now, as you can see that now the second trip has been generated.
00:07:07 So if I head over to the AI trips, you can see that both of them are right here.
00:07:12 So what we could focus on next is the pagination.
00:07:16 And let me just add a bit of a margin bottom of like four to this one right here, just to create some space.
00:07:23 And let's implement the pagination.
00:07:25 For this, we'll have to have a couple of states that'll help us manage it.
00:07:29 So at the top, I will say const search params is equal to useSearchParams.
00:07:40 Once again, similar to Next.js, but now everything is coming from React Router.
00:07:44 We want to access the initial page by parsing the response of search params.getPage.
00:07:52 So we want to get access to the page through the URL.
00:07:56 By default, it can be set to 1. Or instead of parseInt, maybe you can also use just a regular number constructor.
00:08:03 This might be an even simpler way to convert this into a number.
00:08:06 So it's searchParams.get or one.
00:08:10 Then once we have the page, we can also store it to the state by setting useStateSnippet.
00:08:18 Call it currentPage and setCurrentPage at the start equal to the initial page.
00:08:25 And we'll also need a function to handle the page change.
00:08:28 So we'll say const handle page change which accepts a page of a type number and then it simply updates the current page to the new page as well as it updates
00:08:43 the search program in the URL to ?page is equal to the selected page.
00:08:50 Don't forget to import useState from React.
00:08:54 And once you do that, we can actually use a pagination component coming from Syncfusion right at the bottom.
00:09:01 So below this div, still within the section, I'll create a pager component coming from Syncfusion.
00:09:09 And if you save it, you'll immediately see the pages right here at the bottom.
00:09:13 But we can style it a bit further by saying total records count is equal to LoaderData dot total.
00:09:23 We can also pass in the page size equal to 8 so it knows that we can want to show 8 elements per page and we can pass the current page equal to current
00:09:36 page and we can also pass the click.
00:09:38 So what happens on click?
00:09:40 Here we get some arguments of what happened within that click and call the handle page change where we pass the args dot current page to it.
00:09:52 So now if I save this and the reload, you can see that we're in the first page.
00:09:57 So to truly test this out, we can first maybe give some margin bottom of 4 right here so we make some space.
00:10:04 And also we can give this pager component some margin as well so it looks like it's floating.
00:10:09 So I'll say MB4 for the class name or I think it's CSS class like this.
00:10:15 There we go.
00:10:16 So now it's floating like the cards.
00:10:18 And it looks like we still cannot navigate because we only have one page, right?
00:10:23 So what if we set the page size to only one?
00:10:26 Will that work?
00:10:27 And we also have to update the limit right here.
00:10:30 So if I save this, we will now only fetch one trip per page and take a look at this.
00:10:37 We can head over to the second page and we can see the second trip.
00:10:41 And now we're at the second page and all of that is updated right here within the URL.
00:10:47 So if you want to copy this entire thing and share it over with a friend, you can totally do that.
00:10:53 And you can also head back to the previous page to see the first trip.
00:10:56 Of course, it looks a bit empty because we don't have many trips right now.
00:11:00 So, I'll bring back the limit to 8, and also bring back the page size to 8 as well, so later on we'll be able to show more trips.
00:11:08 Two rows of four, actually.
00:11:10 So, with that in mind, believe it or not, the trips page is completely done.
00:11:14 And we have even created one more trip.
00:11:17 It is already done, because we have reused the trip card component that we created before for the dashboard.
00:11:23 So the last thing that we have to do is actually update our dashboard to reflect real stats from our database.
00:11:32 From the stats about the users, trips, and active users to the created trips.
00:11:38 Because right now, our all users page is dynamic, and so is the trips page.
00:11:45 But the only thing that's static is the dashboard.
00:11:48 So in the next lesson, let's make the dashboard dynamic.