
Join the Conversation!
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
"Please login to view comments"
Subscribing gives you access to the comments so you can share your ideas, ask questions, and connect with others.
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
##Looks like we found a thief monkey By the way, I liked the trick how you reached till here. You have a good sense of humor. You will improve a lot if you join our course with this passion.
var
(function-scoped, outdated)let
(block-scoped, modern and recommended)const
(block-scoped, cannot be reassigned)_
, or $
let let = 5;
is invalid)myVar
and myvar
are different)string
, number
, boolean
, null
, undefined
, bigint
, symbol
Objects
, Arrays
, Functions
Subscribing gives you access to a brief, insightful summary of each lecture to stay on track.
00:00:02 Hello, hello and welcome to the second part of the course, the public facing website.
00:00:09 As you can see, our dashboard is now looking amazing.
00:00:13 We can check it out, we can see all the posts and just see how many users we're getting.
00:00:17 This is looking great for admins, but now real users have to come to the website to be able to find the trip that they like.
00:00:25 So I believe that before we have already headed out to the app and then routes.ts and we have added this index route, which allows us to have this travel page.tsx.
00:00:39 So let's head over into the travel page and wait, why is it not saying dashboard in the logout?
00:00:46 Well, that's because we first have to fix our layout.
00:00:50 So head over to page layout that is currently showing the logout button.
00:00:55 For now, we will entirely delete it, and the only thing that we need within it will be a div with a class name of bgLite200,
00:01:05 and within it, we can render the outlet.
00:01:08 The outlet will allow us to see the page that is rendered within that layout.
00:01:13 In this case, that is the travel page, so we can immediately see it right here.
00:01:17 What we can do in this layout is also implement a quick redirect.
00:01:22 So I will head over into our sign in, and I will copy this current client loader from here.
00:01:31 And then I will paste it right here above the page layout.
00:01:35 Export async function client loader.
00:01:37 We first fetch the user.
00:01:39 We check if a user doesn't exist, then we point them over to the sign in.
00:01:44 Else we can try to fetch the existing user.
00:01:47 So const existing user is equal to await get existing user.
00:01:54 of the user.$id and then we can return if existing user exists.
00:02:02 In that case, we'll return that user else we will await store user data.
00:02:08 So this means that we will actually store the user into the database if they're coming on here for the first time.
00:02:14 And if something goes wrong, we will simply return a redirect pointing to forward slash sign in.
00:02:22 Perfect.
00:02:23 So now we have this client loader and what we can do is also render a navbar above the layout.
00:02:30 So we can show it above every single page within it.
00:02:34 I will create a new component within the components folder and I will call it root navbar.tsx.
00:02:45 I'll run RAFCE and we can just import it right here as a self-closing root navbar so we can see it above the travel page to that navbar I will actually
00:02:57 copy and paste these navigation and handle logouts functions because we'll need it over there and this navbar will be very similar to our original nav
00:03:07 items at least when it comes to this logo that we want to display the rest will be different so I'll copy the logo And then right here,
00:03:15 I'll start this navbar by turning it into an HTML5 semantic nav tag.
00:03:20 Within it, I will render a header.
00:03:23 And within that header, I will render that link.
00:03:26 That'll show you a logo at the top.
00:03:28 We can style the navbar by giving it a class name equal to CN.
00:03:34 And now depending on the page we're on, we can show a bit of a different thing.
00:03:39 So at the top, I want to get access to the current path.
00:03:43 I'll do that by saying const location is equal to use location and this is of course coming from React router so I'll say if location.pathname is triple
00:03:59 equal to forward slash travel forward slash and now we want to get access to the params of this page so i'll say const params is equal to use params coming
00:04:11 from react router so that is equal to travel params.tripid so if we are on a specific trip details page in that case we want to make it bg white Or,
00:04:26 on the home screen, we have this full screen hero section with a nice looking background photo, so we don't want to hide it.
00:04:33 In this case, we can just have a glass morphism.
00:04:37 effect.
00:04:37 And then we can always have a WFool, Fixed, and Z50.
00:04:42 There we go.
00:04:43 So now we can see it right here at the top.
00:04:45 This header will have a class name equal to root nav and a wrapper.
00:04:52 And below this link we'll render the aside where we're going to check for the user status.
00:04:58 So if the user status is triple equal to an admin, in that case, we want to render the link that'll allow the user to go to the admin page.
00:05:11 So we'll point it over to the dashboard like this.
00:05:15 The question is, where do we get access to the user?
00:05:18 And the answer is that it's coming from the client loader that we rendered before.
00:05:22 So I'll say const.
00:05:24 user is equal to use loader data.
00:05:29 And now we have this user.
00:05:30 So if that is the case, we can then render this admin panel link.
00:05:37 And we can also style it a bit by giving it a class name of cn text-base font-normal and text-white.
00:05:48 But we'll apply a text dark of 100 if location path name starts with travel.
00:05:58 So if we are on the forward slash travel page, In that case, we want to make it dark.
00:06:03 Currently, you can see that it is light.
00:06:06 Next, just below that, within the aside, I'll render an image with a source of user?imageurl or forward slash assets, forward slash images,
00:06:18 forward slash david.webp.
00:06:21 with an alt tag of user.
00:06:24 So here we can show the profile photo and then below it we can also render a button to logout.
00:06:30 So I'll give it an on click of handle logout which is a function that we have already added right here before.
00:06:39 And now we can also give this button a class name equal to cursor-pointer And within it, we can render a self-closing image with a source of assets,
00:06:53 icons, logout.svg with an alt tag of logout and a class name of size 6. And you can also give it a rotate of 180. So this is like it's pointing outside,
00:07:12 like you want to log out.
00:07:14 Great.
00:07:15 So this is our very simple root navbar.
00:07:19 And now we're using it within the page layout, which is then also displaying the travel page.
00:07:24 So we'll have to work on that page next.
00:07:26 That page will look something like this.
00:07:28 We'll have a very large hero section with a background photo.
00:07:33 with a large text and a call to action that shows people enjoying their trips.
00:07:39 Then we're going to show some feature destinations and hand-picked trips and a very small footer.
00:07:45 But let's do it section by section, focusing on the hero section first.
00:07:49 Back within our code, I'll wrap this div in a main tag and give it a class name of flex and flex-call so all the elements appear one below another.
00:08:01 Next, we can render our first section, and it'll have a class name of travel hero, which will apply that background.
00:08:11 So within that section, we need to add a div, and within that div, another section that has a class name of wrapper, which will wrap all of the contents
00:08:22 of that hero section.
00:08:24 So you can see that the background photo is there, but now we can focus on the text.
00:08:29 So within it, we can display an article and within an article, we'll display an H1 that'll say plan your trip with ease.
00:08:40 And we can style it a bit by giving it a class name of P72Bold and a text dark of 100. There we go.
00:08:52 And we can also give it a P tag that'll say something like, let's try to get it from the design, customize your travel itinerary in minutes.
00:09:03 Pick your destination, set your preferences, and explore with confidence.
00:09:07 See, the problem with these background images is that on mobile, you'll most likely have to use another image that has a bit of a less contrasting background.
00:09:16 because here the text is on the dark background, but on desktop it looks amazing because it is on the light background.
00:09:24 So this is definitely something that we'll have to play a bit with.
00:09:27 For now, let's head below the article and let's create another link.
00:09:32 This one will be for our primary call to action.
00:09:34 So I'll give it a hash of trips.
00:09:37 It'll just scroll down the page.
00:09:40 We can render a button component within this link coming from Syncfusion.
00:09:46 that'll render a span that'll say get started.
00:09:50 We can style it further by styling the button with a type of button and giving it a class name equal to button-class with an H of 11, W of full,
00:10:06 And the medium device is W of 240 pixels.
00:10:11 So this way, it'll look good on all different screen sizes.
00:10:15 And this pan will have a class name equal to P of 16, semi-bold, and text-white.
00:10:24 There we go.
00:10:25 We can scroll down below the link, below the section, div and another section.
00:10:30 And we can create the second section, which will be for our featured destinations.
00:10:37 So this right here is what we're working on next.
00:10:40 We have to create this bento grid layout.
00:10:42 So I'll give it a class name equal to featured.
00:10:46 And within it, we'll create an article within which we need to have a card for our featured destination.
00:10:54 So this FeatureDestination will most likely have to be reused for all of the different cards.
00:10:59 So what do you say that we just create this FeatureDestination component right above.
00:11:04 const FeatureDestination.
00:11:08 And we can make it equal to just a regular arrow function that looks like this.
00:11:13 It can even have an immediate return because it doesn't have to deal with any logic.
00:11:17 It just has to return.
00:11:19 a section that'll have a div with a class name of bg linear 200 and an h of full and within it we can have an article with a class name equal to feature
00:11:36 card and I'll say featured card for now within it.
00:11:40 So now if we scroll down, and if we render this featured card, or rather the featured destination, you'll be able to see just a single featured destination
00:11:51 right below.
00:11:51 And we'll have to pass some of the props to each one of these featured destinations.
00:11:56 For now, I handpicked a few travel destinations and we're going to populate them with dummy data.
00:12:02 But as an assignment later on, as we're approaching the end of the course, you could render the top rated trips from our entire application right here
00:12:11 in this featured destination section.
00:12:13 Let's think about which fields does each card need to have.
00:12:16 So if we check it out, we can see that it needs to have a rating.
00:12:21 a background image, maybe some container classes because these are different sizes of images.
00:12:27 It has to have a title and the activity count.
00:12:31 Perfect.
00:12:31 So now that we know what it needs to have, we can pass it in, such as a BG image of BG card 1. We can pass a container class.
00:12:41 This first one will be larger, so we can give it an H1 over 3. And on large devices, age of 1 over 2 we can also give it a prop of big card because this
00:12:52 one will be big with a title of barcelona tour and a rating of maybe 4.2 with an activity count of maybe 196 people that have gone there.
00:13:07 So now we're passing all this important info to it.
00:13:10 And before we develop this card, I want to render other cards in a layout that makes sense.
00:13:16 So below this card, I'll render a div that'll have a class name equal to travel featured within which I will duplicate two of these cards.
00:13:27 So make sure to duplicate it and then move it right below it within that div and then duplicate one more.
00:13:37 And then we'll have to change the props that we pass into it.
00:13:40 This one will have a BGCard2.
00:13:44 In this case, we don't have to give it a container class.
00:13:47 And also for the second one, we don't have to give it a container class.
00:13:51 They will have the big card prop though.
00:13:53 The title can be something like London.
00:13:56 Rating can be maybe like 4.5 and activity count of any random number that you can think of.
00:14:02 For the second one, I'll change it to BGCard3.
00:14:06 and I'll change it to something like Australia tour with a rating of let's do 3.5 and an activity of 250. Finally, I will exit out of this div and out
00:14:21 of this article and create one final div that'll have a class name of flex, flex-col and a gap of 30 pixels.
00:14:33 And within it, I will display three more cards, but these ones will have a container class.
00:14:39 So let me immediately add it.
00:14:41 Container class of WFool and an H of 240 pixels with a BG card 4. This one won't be a big card, so I'll remove that.
00:14:52 Title can be something like Spain Tour.
00:14:57 with a rating of 3.8 and an activity count of random.
00:15:02 And now we can duplicate it two more times.
00:15:05 One, two.
00:15:06 We'll keep everything the same besides the title.
00:15:10 This can be Japan, maybe 4.5 or we can do five zero.
00:15:16 And then the last one will be set to Italy tour.
00:15:22 And it'll have the activity count of 500 and the rating of 4.2. Just random numbers for now.
00:15:31 If you save it, you'll see that now we'll get six different cards right here.
00:15:38 So now we can develop this featured card component that allow us to show the images of these cards and all the additional information.
00:15:46 So let's give this section a class name equal to CN.
00:15:53 rounded-14 pixels, so we can actually see the borders, overflow-hidden, bg-cover, bg-center, size-full, and a min-w of 280 pixels.
00:16:12 And of course we have to spell it capital N in name.
00:16:17 Perfect.
00:16:17 We'll also have to pass it some additional properties that we're sending over from props.
00:16:21 So I will destructure all the props, such as the container class, maybe an empty string if you don't have any, a big card,
00:16:31 which is by default set to false unless passed, a rating, a title, an activity count, and a background image.
00:16:43 And these will be of a type, destination.
00:16:48 props.
00:16:49 So now to this CN, we can pass a container class as well as the BG image.
00:16:56 So now we should be able to see different images in each one of these cards.
00:17:01 It looks like for the last few, I forgot to change the card name.
00:17:05 So it's going to be five and then six for the last two.
00:17:10 Perfect.
00:17:11 Now within this article, we can render a div.
00:17:15 And this div will render the rating.
00:17:18 You can see 4.2 right here.
00:17:20 We can style it to appear at the different corner of the card by giving it a class name.
00:17:26 We'll make it CN.
00:17:28 BG White.
00:17:30 Rounded 20. Fond Bold.
00:17:33 text red 100, wfit, padding y, padding x, like this, padding x of 3, and text dash small.
00:17:43 If we do this, you should be able to see it in a pill-like shape right here.
00:17:47 We can then head below this div and render an article that'll have a class name equal to flex, flex dash call, and a gap of 3.5 within which I will render
00:18:00 an h2 that'll simply render the title and we can style it a bit by giving it a class name of cn text-lg font-semi-bold and text-white.
00:18:14 And only if it's a big card, we can then apply a P30 bold.
00:18:19 Once again, only if it's a big card.
00:18:22 So we can put that in an object and say P30 bold big card.
00:18:26 Perfect.
00:18:26 There we go.
00:18:27 Now we can head below this H2 and create a figure that'll have a class name equal to flex, a gap of two, and items-center.
00:18:40 Within which, I'll render an image that'll render the source of assets, images, david.webp with an alt tag of user, and a class name of cn size-4 rounded-full.
00:19:02 aspect dash square and outside of it we'll give it a size of 11 only if it's a big card.
00:19:10 This will show a little David that has obviously visited all of these trips and then below that image we can render a p tag that'll render the activity
00:19:22 count of activities so like how many people have visited we can style it by giving it a class name of cn text-excess font-normal and text-white and only
00:19:39 text-lg if it's a big card perfect so now we can see the number of activities and these cards are missing a bit of height and also the layout is not nice
00:19:49 I think that's because here I said feature card instead of featured card.
00:19:53 This featured card will apply a great looking flex so that the elements are more nicely positioned within it.
00:20:00 So if I save it, these cards will look a bit better now.
00:20:03 And this entire section will also look a bit better.
00:20:07 But obviously, we put it into the full width.
00:20:09 And you can see the issue that happens right now when you have something put into a full width.
00:20:14 That should never happen.
00:20:15 You should give it some breathing room, some negative space.
00:20:18 So, let's see where we're mapping over those feature destinations.
00:20:23 We have a section with a class name of Featured.
00:20:26 But actually, what we can do is give this section a class name of PT20 to give it some space.
00:20:32 We can also give it a wrapper property, flex, flex-call, a gap of 10 in between the elements and a full height.
00:20:43 Okay, so now we can see that it has some space to breathe on the right and left side.
00:20:47 We can then head over into that section and render a header component coming from components with a title of featured travel destinations.
00:20:59 and a description of check out some of the best places you can visit around the world.
00:21:08 And we can close the header right there.
00:21:11 Perfect, that makes sense.
00:21:12 And then we're gonna wrap this article with a div that'll have a class name of featured and we can close that div right here at the end of all of the different destinations.
00:21:23 So just duplicate this div right there.
00:21:26 This will give it some breathing room as well in between the cards.
00:21:29 So now, if you check it out on desktop, you'll see this glass morphism knob bar effect.
00:21:35 Great looking hero.
00:21:37 Featured travel destinations.
00:21:39 And you can see those different destinations right here.
00:21:41 Later on, we can make them clickable so they actually point to some active trips that your agency is offering.
00:21:49 But for now, these can just showcase the different types of travels that our agency is offering.
00:21:54 The actual trips are going to appear just below.
00:21:57 Because the next section are not the travel destinations, but rather the hand-picked trips.
00:22:05 Remember, we had a very similar section to this one on the admin side as well.
00:22:09 So developing it will be super simple.
00:22:12 Right below this section, I'll create another section with a class name equal to padding Y of 20, to give it some space in top and bottom,
00:22:24 a wrapper, flex.
00:22:27 Flex dash call and a gap of 10. I'll also give it an ID equal to trips.
00:22:34 Within it, we can render a header.
00:22:36 So I will copy this header from here and paste it right here.
00:22:42 And here we can say handpicked trips and we can say browse well-planned trips designed for your travel style.
00:22:55 Perfect.
00:22:56 So now we can see this new header appear.
00:22:58 And below the header, we can have a div that'll have a class name of trip grid.
00:23:06 And here is the time that we actually map over those trips.
00:23:10 So we first have to actually fetch them at the top.
00:23:13 So I'll scroll up and at the top of this page, we'll need to render a client loader.
00:23:19 It'll be the same client loader as within the trips page.
00:23:24 Because we're going to implement pagination, we're going to fetch all trips and then display them.
00:23:29 So let's simply copy this client loader as well as all of this data on how we're paginating over it.
00:23:36 So I will copy this entire thing from just above the return to the top of the loader.
00:23:43 Then I will actually head down below the feature destinations.
00:23:48 and I will remove the top part of the travel page and paste what I just copied.
00:23:52 Instead of trips, we have to call it travel page, but the rest will remain the same.
00:23:58 Loader is an async function that limits the number of trips per page to 8, We get access to the current URL, the page, the offset,
00:24:08 and finally we fetch all the trips.
00:24:11 But in this case, we can also maybe fetch the user.
00:24:14 So I'll say const user and trips is equal to await promise.all.
00:24:21 And now for the first one, we can use the getUser call coming from Auth.
00:24:27 And then for the second one, we can call the get all trips right here and pass in the limit and the offset.
00:24:34 And then right here, instead of the trips, we can just destructure the all trips as well as the total.
00:24:40 So we're using both the array and object destructuring in this case.
00:24:45 Perfect.
00:24:45 And now within the travel page, we're using this loader data, taking a look at the search params so we know on which page we're on,
00:24:52 taking a look at the current page and paginating in case we select it.
00:24:57 So now if I save, everything is still good and we are ready to map over those trips.
00:25:03 Because we're getting them right here at the top.
00:25:06 Trips is equal to LoaderData.trips.
00:25:09 So here we can once again get the part from the All Trips page.
00:25:14 I think we just called it Trips.
00:25:17 where we're actually mapping over it.
00:25:20 So we have this div with the trip grid all the way down to below the pager component.
00:25:26 I will remove this div and paste it instead of it.
00:25:29 And I think in this case we don't need the margin bottom.
00:25:32 So if I save it now, you'll immediately be able to see those hand-picked trips in the same layout that we have on the All Trips page on the admin dashboard,
00:25:40 including the pagination.
00:25:43 And the only part that's missing to make this page complete is a little footer.
00:25:48 So right below this section, let's create a footer that'll have a class name.
00:25:55 equal to age of 28 and the background of white within which we'll render a div with a class name of wrapper and footer container.
00:26:06 And within it, I will render a link pointing to the homepage that will render the image of a source of assets, icons, logo.svg.
00:26:19 So we show them that nice looking logo one more time.
00:26:23 and we'll give it a class name of size of 30 pixels and I'll render an h1 of Tor Visto which is the name of our application and right below the link we
00:26:35 can render a div and right here we can create an array that will map over that'll have two links terms and conditions as well as the privacy policy and
00:26:48 then we can map over each one of them by getting the item and then for each item we can render a link pointing to you can add a real link pointing to your
00:26:59 terms and conditions and privacy policy pages and the key of item.
00:27:03 And within it, you can render that actual text for each individual link, which is the item.
00:27:09 So if you do that, you'll see this simple footer at the bottom.
00:27:13 But of course, where this page really shines is desktop.
00:27:17 So you can see the full hero section at the top.
00:27:21 You can also see some feature destinations.
00:27:24 Then the handpicked trips that you can visit.
00:27:27 But right now, we don't really yet have a public page for the trip details.
00:27:33 We'll do that next.
00:27:34 It'll once again be very simple because we have implemented a similar page on the admin panel.
00:27:39 And then we have the footer at the bottom.
00:27:41 So in the next lesson, let's focus on creating the travel details page.