
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 provided in the transcript, making it impossible to summarize any key themes or messages.
00:00:02 Once we create a new trip, we'll redirect the user to that trip details page.
00:00:07 But that page doesn't yet exist, and we don't yet have the function to retrieve our trips.
00:00:12 So let's start with that.
00:00:14 I'll head over into app, app-write, and then I'll create a new file called trips.ts.
00:00:23 Within this trips, we can do the full logic for fetching all the trips or fetching just a single trip by ID.
00:00:30 So I'll start by exporting a new function called getAllTrips, and it'll be equal to an asynchronous function, inside of which we'll try to fetch those trips.
00:00:42 So I'll say const allTrips is equal to await database, coming from AppWrite, .listDocuments, and then you know the drill,
00:00:52 we have to provide the database ID, coming from AppWriteConfig.databaseID, as well as the collection ID coming from appredconfig.trip collectionID.
00:01:05 And finally, we have to query the trips that we want to get.
00:01:08 So to implement pagination here as well, we can accept the limit of a type number as well as the offset of a type number into this function.
00:01:18 And then once we turn on the query coming from AppRight, we can limit it by a specific number of elements per page, and we can query it by a specific offset.
00:01:29 So if we have already seen the first page, we can move off to the second one.
00:01:34 And finally, I'll also add the query.order descending based on the created ad, so we can see the newly created trips at the top.
00:01:44 Finally, if allTrips.total is triple equal to zero, in that case, we can simply console.error and say no trips found and we'll return an object that says
00:02:00 all trips is equal to an empty array and the total will be set to zero.
00:02:05 But if we do have some trips I'll return an object that'll say all trips is equal to all trips dot documents and the total number of trips will be set
00:02:17 to all trips dot total.
00:02:20 Perfect.
00:02:21 So this is the function that allows us to fetch all of these different trips.
00:02:25 And I also want to create a function that allows us to fetch the trip details for a specific trip.
00:02:31 So I'll say export const getTripById and it'll be equal to an asynchronous function that accepts a trip ID of a type string.
00:02:44 And it simply fetches it.
00:02:46 So I'll say const trip is equal to await database.getDocument singular this time.
00:02:55 And we again have to provide the database ID.
00:02:57 So apriconfig database ID, the collection ID.
00:03:01 So that'll be apriconfig trip collection ID.
00:03:05 And finally, the ID that we want to fetch it based off of, which is going to be the query.
00:03:10 So just trip ID like this.
00:03:13 And this will be called trip, not trips.
00:03:15 So once we get that trip, I'll check if there is no trip.$id.
00:03:21 In that case, we can simply do a console log and say trip not found, and we can return null.
00:03:29 But else, if we actually fetch the trip, we can return that full trip details.
00:03:36 Perfect.
00:03:36 So now we have those two functions that allow us to fetch the trip details, either a single one or all of the trips together.
00:03:43 So what do you say that we go ahead to our routes and we create a route for the trip details page?
00:03:49 We can do that by creating it like this and then pointing it over to forward slash trips, forward slash colon trip ID.
00:03:59 So this is the first time that we're defining a dynamic route based on the ID And you do that by simply providing a colon right here at the start.
00:04:09 Similar to what you do in Express, for example.
00:04:12 And we can point this over to routes forward slash admin forward slash trip dash detail dot dsx.
00:04:20 And now we can create that trip details page by heading over to roots admin and we can create a new trip detail dot dsx where you can write RAFCE.
00:04:33 And here we just want to be able to quickly fetch the details of that specific trip.
00:04:38 So let's do just that.
00:04:39 I'll create a new loader at the top by saying export const loader is equal to an async function that gets access to params of a type loader function args.
00:04:53 Now what are these params that I'm trying to fetch right here?
00:04:57 Well, the params are going to be whatever is dynamic.
00:05:00 So in this case, remember this id?
00:05:02 Well, that'll be fed into params.id so we can extract that number.
00:05:07 So in this case, I want to say const tripid and I want to destructure it.
00:05:13 from the params then once I have the trip id I'll say if there's no trip id I'll throw a new error saying trip id is required but if there is a trip then
00:05:26 we can fetch it by saying cons trip is equal to await get trip by id this is the function that's coming from AppWrite Trips right at the top And to it,
00:05:38 we have to pass the trip ID, of course.
00:05:41 Once you get it, we can simply return that trip.
00:05:44 So if you do this properly, you'll be able to fetch this trip data right here at the top.
00:05:49 Because you already know the drill, we're getting it through the loader data.
00:05:54 of a type route.component props and this route is coming right at the top by importing the type of route from dot slash plus types forward slash and then
00:06:09 the name of this file.
00:06:10 So if you have done this properly, you should be able to just say const trip data is equal to parse.
00:06:18 trip data.
00:06:20 This is coming from libutils.
00:06:22 And to it, we'll pass the loader data, question mark dot trip.
00:06:27 So let's see what exactly this parse trip data is doing.
00:06:30 Basically, the only thing it's doing is it's taking that trip and it's parsing it.
00:06:35 Because before, remember, we passed it over as a stringified version.
00:06:39 So now we want to extract the actual data.
00:06:41 So what do you say that now that we get this data, we extract the name out of it.
00:06:46 So I'll say const name, and then is equal to trip data, or just so it doesn't break, if we don't have the trip data, try to get it from an empty object.
00:06:56 So now we can show this trip details.
00:06:59 First, I'll wrap everything in a main tag, give it a class name equal to travel, detail, and a wrapper class name.
00:07:09 Then within it, we want to render a header component coming from components with a title of trip details.
00:07:17 a description of view and edit AI-generated travel plans and we can close it right there.
00:07:23 Below it, I'll render a section that'll have a class name equal to container and a wrapper MD so we can see the content in the middle of the screen.
00:07:34 within which I'll render a header, within which I'll render an H1, where we can render the name of the trip.
00:07:42 To style it a bit better, let's give this H1 a class name, equal to p40-semi-bold, and text-dark100, just so we can see it clearly.
00:07:53 So if you do this, it's possible that the app is not working because we have added an additional route.
00:07:58 So let me actually rerun it one more time.
00:08:01 It'll quickly spin up on localhost 5173, which is when we can reload.
00:08:06 And now, if we head over to AI Trips, we don't really have any trips yet.
00:08:11 We could go ahead and create a new trip so we get redirected to it.
00:08:15 But what we can also do is just head over to Trips forward slash, and then we can get the ID of the trip from AppRite database.
00:08:23 So head over to AppRite, Databases, trips and then copy the document ID.
00:08:29 Once you do that, you can append it to a URL.
00:08:31 So we get redirected to the trip details page.
00:08:35 But now the issue is that we don't yet see the name of the trip.
00:08:40 So something could be wrong with us fetching the data.
00:08:42 So first, I want to see what we're getting outside of this loader data by console logging it.
00:08:49 So if I console log it right here and open up the console, It seems that we're getting the object that then contains the trip details within which we finally
00:08:59 have that string.
00:09:00 So what I'll do is I'll try to render the LoaderData.TripDetails instead of a trip.
00:09:08 And if we do that properly, you see that now on the right side, we're getting a luxury creation history and romance, a couples escape.
00:09:15 So that means that we have successfully retrieved the trip details coming from the database.
00:09:20 In this case, we can just automatically return the trip.
00:09:23 No need to declare the variable and then return it.
00:09:27 So I'll just simply say, return await getTripById.
00:09:33 Perfect.
00:09:34 So now we're fetching the trip details and now we can slowly start rendering all of the additional information about this trip.
00:09:41 But before that, we'll need one more component to show the trip duration and location to make sure that it looks exactly like it does on the Figma design.
00:09:51 So let's create a component for these two little chips.
00:09:54 We can do that by creating a new component within components and I'll call it info-pill.tsx.
00:10:04 I'll run RAFCE and I will export it through our components folder as info-pill.
00:10:12 And then we can import it over within our trip details right below the header.
00:10:17 I'll call it info pill.
00:10:21 and render it from components.
00:10:22 Now to it, we'll have to pass some props, maybe such as a text, in this case for the duration, so I will render a dynamic duration,
00:10:33 and then we can say how many days, so 7-day plan for example, but the question is where is that duration coming from?
00:10:42 And the answer is that it's coming from the trip data.
00:10:44 So here we can destructure alongside the name all of the other properties that we'll have.
00:10:50 Such as the duration, the itinerary, the travel style, the group type, the budget, interests, estimated price as well, the description,
00:11:05 the best time to visit, the weather info, and the country.
00:11:11 So we're getting all of that info from the trip data.
00:11:14 And now we can just render this text.
00:11:17 And we'll also render the image alongside that info pill, which will be forward slash assets, forward slash icons, forward slash calendar dot SVG.
00:11:27 So if I now save it, we need to head over into this InfoPill and implement it.
00:11:33 It'll be super simple.
00:11:35 We will just accept those two props, such as the text and the image, which can be of a type InfoPill props, and then we can return a figure with a class
00:11:48 name equal to InfoPill.
00:11:50 Within it, we can also render an image that'll have a source equal to image, with an alt tag of text and below the image we can render the figcaption that'll
00:12:03 simply render the text.
00:12:04 So if you do this you can now see a nice 7-day plan right here and you can simply duplicate this info pill And for the second one,
00:12:14 within here we want to render the location.
00:12:16 Or rather, that'll be the itinerary.
00:12:18 So if you try to render it like this, you'll notice that it's actually an object that has the location and activities.
00:12:25 So we have to parse it so we can properly show it.
00:12:28 So I'll say itinerary?
00:12:30 Question mark dot slice.
00:12:32 from 0 to 2 so we only want to show the first two elements and then we can map over those items so dot map where we get each individual item and we only
00:12:44 want to extract the location for each item and then we want to join them together by using a comma so join with a comma and a space or if it doesn't exist,
00:12:55 an empty string.
00:12:56 So if you do this, now we can see split, split, which doesn't stand for JavaScript split function.
00:13:04 Actually, it is a name of the city in Croatia.
00:13:07 For some other one, maybe it gets two different cities that actually make some sense.
00:13:12 And instead of a calendar, we can render the location mark.svg so you can see that this is actually the location.
00:13:21 Perfect.
00:13:22 So now we have those two pill components right here and that allows us to focus on creating the rest of the layout.
00:13:29 so I will actually put those pills within the header component right below the name and I actually want to put them one next to another so I'll put them
00:13:38 within a div I'll open it here and close it here and I'll give this div a class name equal to flex items-center and a gap of five So now they will appear
00:13:52 one next to another.
00:13:54 And then we can head over below this div, below the header, but still within this section, we can create another section within it.
00:14:04 This one will be for our gallery, the most important point of our trip.
00:14:08 So I'll say class name of gallery.
00:14:11 And within it, we want to map over the image URLs.
00:14:15 So now the question is, where are these image URLs coming from?
00:14:20 And they are going to be a part of the trip as well, but not part of the trip details.
00:14:25 This is the text that was generated through AI.
00:14:27 The image URLs are going to be a separate property under these trips.
00:14:32 How can we know that?
00:14:34 Because if you go into our AppWrite trips, right?
00:14:39 You'll notice that when we return the getTripById, we're returning the full trip.
00:14:45 And if you remember how this trip has been stored into the database, you will know that trip details and image URLs are two separate properties.
00:14:54 So we have to extract them separately.
00:14:57 Image URLs is equal to loaderData?imageURLs or an empty array if we don't manage to get any of them.
00:15:06 So if we do that, we now have image URLs and you can see that we're mapping over three of these image URLs right here.
00:15:14 So instead of simply mapping over them, what we can do is say .map and we are going to get a URL of a type string as well as an index or i of a type number.
00:15:27 And for each one of these, we want to simply return an image that'll have a source of URL and the key of i.
00:15:36 If we save this, you should be able to see three images appear right away, but they are of different aspect ratios, so we want to make sure that it actually
00:15:45 still looks good.
00:15:47 So I'll give it a class name equal to CN.
00:15:52 and it'll always have a w-full, a rounded-excel, and object-cover, so we round up the corners a bit.
00:16:02 But then, if it's the first index, so if i is triple equal to zero, in that case, we'll give it a onMeetingDevices call span to On medium devices,
00:16:16 row span 2 and a bit of a bigger height of 330 pixels.
00:16:23 But if it's not the first one, on medium devices, I want to give it a row span 1. and a height of only 150 pixels.
00:16:34 So if we save this, you'll see that now, the first one takes more space, and the second two take less space.
00:16:43 This is okay on mobile, but where it really shines is of course on desktop, where you can see that now we have this nice looking layout,
00:16:51 that gives you the idea of that trip in a glance.
00:16:55 You don't have to take a look at three separate pictures.
00:16:58 You just take a look at this portion of the screen and you get a general idea of what these places are all about.
00:17:04 Now we can head below this gallery section right here and we can create another section.
00:17:11 This time for different chips talking about what this travel is all about.
00:17:17 So what I'll do here is I'll create a section and give it a class name of flex gap of three.
00:17:24 On medium devices, a bit more gap of five.
00:17:27 Items-center and a flex-wrap so they wrap nicely on mobile devices.
00:17:33 Within this section, I will render a chip list component coming from Syncfusion with an ID of travelchip.
00:17:44 And within it, I will render a chips directive so we can choose exactly how each one of them looks like.
00:17:51 And then we have to figure out what we will map over.
00:17:55 Right above, I'll create a new object, const pill items, which will be equal to an array, where we're going to have different objects we're mapping over,
00:18:07 such as the text, where the first one will be, we'll talk about the travel style, which we're taking from the trip data.
00:18:15 And we can also provide a background color to it.
00:18:17 So we differentiate all four of them.
00:18:20 Maybe something like a BG pink of 50. And text of pink of 50 as well.
00:18:28 And now we can repeat this three more times.
00:18:31 For the second one, we can talk about the group type.
00:18:35 For the third one, we can talk about the budget.
00:18:38 And for the fourth one, we can maybe talk about interests.
00:18:41 And we can change the color for each one.
00:18:44 So the second one will be BG and Text Primary.
00:18:49 After that, we can have BG and Text Success.
00:18:53 And finally, we can have maybe BG and Text of Navy.
00:18:59 And in this case, I think we'll have to make the text a bit more pronounced.
00:19:03 So for the primary I'll call it 500, for success I'll do 700, and for Navy I'll do 500 as well.
00:19:11 Perfect.
00:19:12 So now if we do this, we'll be able to head down and we can map over these pills right here within chips directive.
00:19:21 So I'll go right here and map over the pill items and for each one we'll get access to a pill as well as the index and for each one we want to automatically
00:19:32 return a chip directive, also coming from Syncfusion, which will take in the key of I, a text, where we can render the pill.text and also the CSS class
00:19:48 which we can use to style it a bit more.
00:19:51 So I'll make it dynamic and I'll render the background right here.
00:19:56 So pill.bg text-base font-medium and a padding X of 4. If we do this, you'll be able to see four different chips, but they're not really that visible,
00:20:10 are they?
00:20:11 And it seems like some have multiple words, this one doesn't have any text at all.
00:20:16 So what we can do is we can style the text a bit more.
00:20:20 by wrapping this build text into the call of the getFirstWord function.
00:20:26 What this will do is it will extract only the first word of that text.
00:20:31 You can see it trims the words, splits it, and then returns the first one.
00:20:35 So now here we can see only historical, for example.
00:20:38 Also, the colors don't seem to be matching.
00:20:41 So let's see what's happening with this pink one right here.
00:20:44 Maybe we should do pink 500. in order to be able to see it better.
00:20:48 There we go, that's a bit better.
00:20:49 So now on a glance, we can immediately see what this trip is all about, how long it lasts, which cities are we visiting,
00:20:57 the look and feel of it, and then we can see that it's relaxed, couple-based, luxury, and historical.
00:21:02 What else can we show for this entire trip?
00:21:06 Well, let's follow the design.
00:21:08 Right now we have this part done.
00:21:10 And the last part before we can render the actual AI-generated itinerary is going to be the rating.
00:21:17 So right here we can render the rating.
00:21:19 So to do that, we'll remain in the current section, but we'll go below the Chiplist component.
00:21:25 and I will render a UL, an unordered list with a class name equal to flex a gap of one and items-center and within it we can create a new array of five
00:21:43 elements that will fill with no values so null and then we want to map over those values I'll simply put an underscore right here because we don't really
00:21:54 care about what the value is inside, but we do care about the index, which is the second parameter.
00:22:00 And then for each one of these empty elements in the array, we want to return a list item with a key of index, and we simply want to render an image that
00:22:11 has a source equal to assets icons star.svg with an alt tag of star and a class name of size of 18 pixels.
00:22:26 If you do this, you should be able to see five stars appear right here.
00:22:31 And we can also head below this dynamic part and render one last list item with a class name of ml1.
00:22:41 So margin left one to divide it a bit.
00:22:43 And we'll render one last chip list component with its chips directive, with a single chip directive inside.
00:22:55 So that'll look like this.
00:22:57 And this one will simply render a text of something like 4.9 out of 5. We're not collecting the data on the quality of these strips,
00:23:07 so that might be something that you might want to implement to be dynamic later on.
00:23:12 And I'll give it a CSS class of bgred50 and text-red500 just to see how it looks like.
00:23:20 There we go.
00:23:21 We can also maybe make it yellow just so it matches the stars.
00:23:25 So if you do this, you should be able to see it like this.
00:23:28 We can maybe make it a bit darker because yellow is not that visible.
00:23:32 There we go.
00:23:33 This is looking good.
00:23:34 So now we can exit out of this li, ul, and the section, and we can focus on creating the last section, the one that matters the most,
00:23:42 which will be the header of the trip.
00:23:44 So it'll take the title, the description, and the budget.
00:23:48 and then after that we'll be able to render the entire itinerary.
00:23:51 So let's do that by giving this section a class name of title and then within it we can render an article within which we can render an h3 that'll say
00:24:05 duration dash day country travel style Trip.
00:24:12 So we're combining everything into one.
00:24:13 7-day Croatia relaxed trip.
00:24:17 After that, we can render a P tag that'll render the budget, the group type, as well as interests.
00:24:26 So if we save this, we can also see that this is a luxury couple and historical sites.
00:24:31 And outside of that article, we can also render an H2 that'll render the estimated price.
00:24:38 So we're taking all of these different pieces of info and putting them together.
00:24:42 Finally, check this out.
00:24:44 I'll head below this section for the header and I'll render a P tag that'll render the description.
00:24:50 If you do that, immediately you'll have a very short description.
00:24:54 So we can give it a class name, equal to text-sm, mutant devices, text-lg, font-normal, and text-dark, 400. So we can kind of style it a bit more like
00:25:08 a regular description.
00:25:10 And then below it, we're finally ready to map over the important info.
00:25:15 So I'll head below the P tag.
00:25:18 and I'll create a UL, an unordered list with a class name equal to itinerary.
00:25:25 That simply means kind of like steps in the trip, like how is the trip going to play out.
00:25:29 We want to take that itinerary and map over it by using the dot map and what we get is a day plan.
00:25:39 So for each part of the itinerary we get one special day plan And we get an index of that day.
00:25:49 So then for each one of these, we can automatically return an li.
00:25:53 We have to close it as well properly.
00:25:55 And I'll give it a key equal to index.
00:25:59 Let's see if I have closed that properly.
00:26:02 We have a ul.
00:26:04 Oh, looks like I have one extra parenthesis right here.
00:26:07 Now we should be good.
00:26:09 And within this li, I will also render an h3 that'll say day, day plan dot day, and then day plan dot location.
00:26:21 So this way we can specify in which cities we're in within which day.
00:26:27 So let's see if we can somehow show more of the locations right here.
00:26:32 Maybe instead of just slicing to, we can maybe get more.
00:26:36 Just so we can see that we see different cities right here.
00:26:40 There we go.
00:26:40 I think this is good.
00:26:41 So now we get the idea that there's gonna be more cities.
00:26:44 But you can see all of them right here, right below.
00:26:47 Perfect.
00:26:48 So let's scroll a bit down and alongside just the title of each day, we want to show the activities planned for that day.
00:26:55 That's what actually matters.
00:26:57 So I'll create a UL and map over the day plan dot activities dot map where we get each individual activity and an index for that activity of a type number.
00:27:09 And for each one, we want to automatically return a list item with a key equal to index.
00:27:18 And within it, we can return a span that will render the activity and below it we can render a p tag that will render the activity dot description.
00:27:34 So if we do this, you should be able to see that now we have morning, afternoon, evening, and we have that for each one of the days right here.
00:27:43 We can style it a bit differently by giving this span a class name of flex-shrink-zero.
00:27:52 We can also give the p tag a class name of flex-grow.
00:27:57 So if it needs more space, then it'll actually expand to fit that space.
00:28:02 But already this is looking great on mobile.
00:28:05 What we can do is maybe just bold this span a bit by giving it a P18 semi-bold maybe.
00:28:12 There we go.
00:28:13 That's a bit better.
00:28:14 So now it's standing out.
00:28:15 We see morning, afternoon, and evening.
00:28:19 And we have some emojis so we can very quickly browse or just scan through what's happening.
00:28:24 I won't yet show how it looks like on desktop.
00:28:27 If you want to, you can check it out on your end.
00:28:29 But I want to just finish it off by also sharing some information about the city as well as the weather information.
00:28:37 So below this UL, where we have the itinerary, I want to map over something new.
00:28:43 Actually, I want to map over two different things that I want to combine into one.
00:28:48 So right at the top, below the pill items, I'll create another array called VisitTime and WeatherInfo.
00:29:00 And within it, I'll render the title for each one.
00:29:04 This one will be best time to visit.
00:29:07 And here we can render the items of best time to visit.
00:29:12 And for the second one, the title will be something like weather info.
00:29:17 So we know what clothes to wear and the items will be weather info.
00:29:22 So if we now save this, we can take it over and map over it right here below the UL by saying visit time.
00:29:33 and weatherinfo.map where we get each individual section.
00:29:39 For each one of these sections, we can return a section and let's properly close it as well.
00:29:47 We can give it a key equal to section.title and a class name of visit.
00:29:55 And then, within the section, we can render a div with an h3 that'll render the section.title.
00:30:03 So if we save it, you should be able to see that we have best time to visit and weather.
00:30:08 And then below that h3, I will render another UL that'll simply map over the items.
00:30:15 So section.items.map where we get each individual item.
00:30:22 And for each one of these, we want to return a list item with a key equal to item.
00:30:28 So let's fix this typo right here, that's gonna be a key.
00:30:32 And I'll just add a question mark here in case we don't have access to the items.
00:30:35 And we can render a p tag that'll have a class name equal to flex-grow.
00:30:44 And within it, we can render the item itself.
00:30:46 So if I save this, you can see best times to visit, April to May, June to September, September to October, or November to March.
00:30:54 And we can see different temperatures in different parts of the year.
00:30:58 So if you like summer more, it's very warm, 25 to 35 degrees or 77 to 95 Fahrenheit.
00:31:05 Or in the winter, it's getting pretty cold.
00:31:08 Depending on where you are in the world, this maybe isn't warm and this maybe isn't cold, as maybe you're experiencing some very different temperatures.
00:31:16 So since this is a travel application, comment down below and let me know where you're watching from.
00:31:21 So just drop in the name of your country and maybe a flag emoji, just so we can see where you guys are from.
00:31:27 But with that in mind, we can head over below this section.
00:31:32 Basically below all of the sections.
00:31:34 So I'm gonna get down below this one and below this closing one where we have all the details.
00:31:40 And I'll create one last section.
00:31:43 In this one, we're gonna have an H2.
00:31:46 That'll say popular trips.
00:31:49 So if the user has scrolled to the end of their trip details, it means that they like it so much, so they're going to go for it.
00:31:57 Or maybe it means that that trip isn't really their favorite.
00:32:01 So maybe they want to check out some other popular trips on our site.
00:32:05 So to do that, let's give this section a class name equal to flex, flex-call, and a gap of six.
00:32:14 And we can give this age to a class name of p24-semi-bold, text-dark 100. So that's going to make it stand out a bit more.
00:32:24 And right here, we need to map over our trips.
00:32:28 And for that, thankfully, we have created our server action that does just that.
00:32:33 It fetches all the trips.
00:32:35 We have done that just before we have created the fetchTripDetails function.
00:32:39 So fetching it will be super simple.
00:32:42 The only thing you have to do is say const allTrips is equal to loaderData.allTrips as a trip array or maybe an empty array if we don't have anything else.
00:32:57 And now we have to actually call that within the loader.
00:33:01 So currently we're just returning the trip details.
00:33:04 So you can see that here it's complaining that loader data does not contain all trips.
00:33:09 So what we have to do is right below trying to fetch a specific trip, we can also try to get all trips.
00:33:18 So that'll look like this.
00:33:20 const trips is equal to a weight.
00:33:23 getAllTrips.
00:33:25 And to it, we have to pass the limit of trips.
00:33:28 So maybe we can just limit it to four of the most popular ones.
00:33:33 And make sure to import this getAllTrips from AppWriteTrips.
00:33:37 And then once you have it over here, you can simply return it as well.
00:33:41 So now, we'll actually have to put this into a new variable.
00:33:44 const trip is equal to await getTripByDetails.
00:33:49 So so far, we have been only returning a trip.
00:33:52 But now alongside returning a trip, we also have to return the trips.
00:33:56 So let's actually do it as part of an object where trip will simply be a trip and then allTrips will be trips.allTrips.map And we only want to take what
00:34:12 we need from each one.
00:34:12 We don't need all the info.
00:34:14 So I will destructure the $id, the trip details, as well as the image URLs.
00:34:22 And then we can automatically return, like this, parentheses and then curly braces, the id equal to $id.
00:34:32 We can parse all of the details by saying dot dot dot parse trip data, because remember it's in a string format.
00:34:40 So we have to first pass it into this parser function.
00:34:44 And then we can say image URLs is going to be image URLs or simply an empty array.
00:34:52 Perfect.
00:34:53 So now we're returning that from this function.
00:34:55 You can see we immediately lost all of the trip details because now it'll actually be coming as part of the trip.
00:35:02 So we have to say LoaderData.trip.imageURLs and .trip.tripDetails.
00:35:09 If you do that, you'll see that now we get this data back.
00:35:13 If in case it's not there, I'll just add a question mark so Tabscript doesn't bother me with this.
00:35:18 And now we are ready to show the other popular trips.
00:35:21 We can do that by just mapping over them.
00:35:24 So right at the bottom, I'll create a div below this H2 that'll have a class name equal to trip-grid.
00:35:34 and we'll call the allTrips and then say .map where we get each individual trip and for each trip we want to render a trip card which we have already created
00:35:48 as a reusable component that needs to accept all of these different things.
00:35:52 So let's give it an id equal to trip.id Let's also give it a name.
00:35:59 And just so we don't repeat ourselves, we can destructure those properties directly from the props right here.
00:36:04 So let's do just that.
00:36:06 I will destructure the trip into ID, name, image URLs, itinerary, interests, travel style, as well as the estimated price.
00:36:19 So now we can pass all of them right there.
00:36:22 ID is ID.
00:36:23 Since we're mapping over it, we also have to give it a key.
00:36:27 Name will be name.
00:36:28 After that, we have location, which will be equal to itinerary.
00:36:33 Question mark dot zero, so we're going to take the first location, dot location, or an empty string, in case it doesn't exist.
00:36:41 Image URLs, well, we'll only take the first image.
00:36:44 So image URLs zero.
00:36:46 For the tags, we can pass an array of interests.
00:36:50 So interests.
00:36:52 And the second one can be travel style.
00:36:54 And for the price, we can pass the estimated.
00:36:58 price so if we save this you can now see that all of the details are back and now we have one other popular trip in this case is the same that we're currently
00:37:09 on as it's the only one that we have but later on we'll show different trips right here and how nice it was to just reuse the card we have already created
00:37:18 before Just before we admire this trip details page, I want to show you a little optimization that actually goes a long way.
00:37:27 And that is that here, we're making two separate asynchronous await requests.
00:37:33 Each one of these takes time, maybe about 500 milliseconds.
00:37:38 So if we try to wait for both of these one by one, it's going to take a total of 1000 milliseconds.
00:37:46 which is a lot of time if you're trying to fetch the data.
00:37:49 But the thing is that we don't have to have both of these fetched at the same time.
00:37:54 They don't depend on one another.
00:37:57 We show the trip details right here, and then we show other trips at the bottom.
00:38:01 They can happen at the same time instead of one after another.
00:38:05 To do that, we can use the Promise That All functionality.
00:38:10 That way, they will happen at the same time in parallel.
00:38:13 Okay, that's the key.
00:38:15 The way you do it is you say const and then destructure what you want to get.
00:38:20 So we first want to get the singular trip and then we want to get the trips.
00:38:25 Then you make that equal to an await promise.all call to which you pass an array of different calls.
00:38:34 The first one will be giving us our trip details, so I'll paste it here.
00:38:40 And then the second call will be giving us all of the trips, so we can put it here.
00:38:45 And you don't need a weight because we already have a weight right there.
00:38:49 So if you do it like this, now both of these requests will happen in parallel at the same time and they will reduce the time it takes to load the page
00:38:58 in half.
00:38:59 Pretty cool, right?
00:39:00 So always remember that when you have multiple asynchronous calls that don't depend one on another, you can simply put them within the promise that all.
00:39:08 So with that in mind, check this out.
00:39:11 We're looking at the trip details.
00:39:14 We have this one trip detail that we can see, we can read what it's about at a very first easy glance, see the estimated price,
00:39:23 and then we can start seeing what this trip is all about.
00:39:27 For example, we have a private guided tour first, relax at the pool, romantic dinner at a fine dining restaurant.
00:39:34 Well, this is how life should be lived, right?
00:39:37 Just kidding.
00:39:37 But yeah, in general we have a couple of days of different trips and it's all separated into morning, afternoon, and evening so you know exactly where
00:39:46 you have to be at a specific point in time.
00:39:48 You can get more info on when you might want to visit it based on the season that you like or the weather.
00:39:54 And I just noticed that on desktop, this card looks very narrow, so we might want to expand it a bit.
00:40:00 So if you head right here, we have to take this section for the popular trips, copy it, and then put it below the section for the details.
00:40:10 That way, we'll allow it to take the full space.
00:40:13 as it doesn't depend on the details, so we can show 1, 2, 3, 4 in the full width right here.
00:40:19 Amazing!
00:40:20 With this lesson, we're finally taking everything we have created so far and displaying it right here.
00:40:25 But to reach this page, we had to manually navigate over by grabbing the ID from AppRight, which, let's be honest, regular users won't be able to do,
00:40:33 and then we came over here.
00:40:35 But for now, if you head over to Trips, they're completely empty.
00:40:38 So what do you say that next we implement the All Trips page?
00:40:42 Great work so far!