So now let's go ahead and let's create our shop page which will be used to purchase Lingo Pro and also to exchange our points to refill our hearts. So it's this page right here, slash shop, which is currently a 404 page. So I'm going to go ahead inside of my app folder, and I'm going to go ahead and go inside of main here, and I'm going to create shop. And inside of here, I'm going to create a page dot dsx. Let's go ahead and return this, so shop page, and return a div shop page.
There we go. So we kept a part of our layout. So you can see that we have the sidebar here. And what we are going to do now is we're going to give this div a class name of flex flex row reverse gap, 48 pixels and PX of six. Then inside of here, I'm going to add the Sticky Wrapper.
And inside of there, I'm going to add our User Progress component. There we go. So we are using those reusable components, which we initially used for the learn page. So in here we originally had the sticky wrapper and the user progress, right? So we are doing the same thing, we are reusing our components so we are reusing them here as well.
Great, but something is missing for the user progress. So let's turn this shop page into an asynchronous method and then what we can do is we can call our user progress. So let's go ahead and define user progress data to be get user progress from database queries and then we're gonna go ahead and the structure the user progress from await promise all and we're going to pass user progress data here. So later we are going to add the actual subscription here. So let's go ahead and let's first protect our page.
So if there is no user progress or if there is no user progress.activeCourse in that case, we can just redirect the user from next navigation to slash courses. So just import redirect from next slash navigation. And now we have to give our user progress some properties. So active course is going to be user progress dot active course. And hearts is going to be user progress dot hearts.
Points is going to be user progress points. And has active subscription for now is going to be false. And there we go. If you expand this now, you should see a familiar screen. Now we have to develop the feed part.
So for that, we are going to go outside of the sticky wrapper here. And we're going to add a feed wrapper. So let's go ahead and add the feed wrapper from our components the same way we did with the user progress and the sticky wrapper so all of these are reusable. And inside of here we're going to add first a div with a class name of full width flex flex column and items center. And then in here we're gonna go ahead and create an image component from next slash image.
So make sure you've added that import. And we're gonna go ahead and give it some properties. So source for this one is going to reuse our shop.svg. Alp is going to be a shop inside of here and height is going to be 90 as well as the width. So let's try it out here.
Why is nothing appearing here? There we go. So we are using the same icon that we have in our sidebar, a big shopping bag icon. And then below that, I'm going to open an H1 element with the text shop and a class name. Text center.
Font is going to be bold. Text is going to be neutral. 800. Text is going to be to Excel and my is going to be 6. Below that we will add a paragraph, spend your points on cool stuff.
So later you can add some more things here if you want to. So let's give this a text muted foreground, text center, text large and margin bottom of 6. There we go. So now we have our shop page here and below the paragraph we are going to add the items component which we don't yet have. So let's go ahead inside of the shop page we're going to create items.tsx let's mark this as use client because our original page is not our original page is an asynchronous server component so we have to create a client boundary here and let's write type props here to accept the hearts which is a number points which is a number as well has active subscription which is a boolean and let's export const items here.
Items and let's destructure the props. So that is hearts, points and has active subscription. There we go. Now let's head back inside of our shop page and we can now import items from .slash items And now we have some type errors here because we need to pass some necessary props. So for these items, let's pass in the hearts which are going to be userProgress.hearts and let's go ahead and do the same thing for points.
And then let's go ahead and write has active subscription for now let's make it false and a to do add subscription so we don't forget that and now we can focus on our development inside of here So I'm going to change this from a div to an unordered list and I'm going to give it a class name of full width. I'm going to create a div here and the class name of flex items center, full width again, padding of 4, gap x of 4 and a border top of two. Inside of here, I'm going to add an image component from next slash image. So just make sure you've added that import. Remember, this is also use client, so make sure you've tagged that.
Source for this image is gonna be heart.svg, so we are reusing an image we already have, no need to add anything new. An alt is going to be heart height is going to be 60 the same as our width there we go and that is it besides the image add a div element which will very simple have flex one and a paragraph refill parts. And the paragraph is going to have text neutral 700 text base. And also on large, it's going to be text extra large and font is going to be bold, like that. And then outside of the div we are going to add our reusable button component.
So just make sure you add that to the project. This button is going to have a couple of items here. So first, let's go ahead and do this. Let's use the hearts. So if the hearts are currently full, we are going to render full.
Otherwise we're going to render an element of div, last name flex item center and I'm going to render an image so we already have that. The image will have a source of points.svg so we already have that as well. The alt is going to be points as well. Height is going to be 20, same as the width. And then inside, we're going to render a paragraph 50.
So that is the cost to renew hearts. There we go. So we have that now. And now for this button, well, it can actually stay as it is like that and let's go ahead and just give it a disabled prop if hearts are equal to five. There we go.
So now we have a field to refill our hearts but it says full and it is disabled. So let's do the following. Let's go inside of our terminal here. Let's run npm run database seed like that and in here let's now just refresh our page. If you refresh on the shop page now it should redirect you here.
The reason it does that is because inside of the page here we do that. If there is no user progress or active course we redirect the user to courses. So in here, I'm going to choose Spanish again, and this is what I'm going to do. So I'm going to go ahead and I'm going to select, I'm going to make some wrong choices here, right? So I'm gonna go ahead and reduce my number of hearts here.
And then I'm gonna go ahead and finish this chapter. Actually, you don't have to finish, so you can just lose hearts, right? There we go, I have three hearts now. Oh, and yeah, now if you try and click on these icons, they should lead to slash shop, which should redirect you to here. So yeah, there's many ways we can go to the shop page.
And there we go. Now I can click on this button, which shows to refill the hearts for 50 points. Great! So now that we have that let's go ahead and create the functionality for that. The only thing that I seem to be missing is I only have 20 points so I'm gonna go ahead and modify this.
So I'm gonna pretend that instead of costing 50 points, how about I pretend that this costs 10 points. So let me create a little constant here. Const points to refill will be 10. And then I'm gonna use that throughout here. So instead of saying 50, this I'm going to render points to refill.
There we go. Perfect. There is also another way I want this button to be disabled and that is if my points are less than points to refill. Right now it's enabled because I have 20 points because I answered a couple of correct choices. But if I change this to 50, then you can see how my point, my refill hearts now here says is disabled, right?
So let's bring this back to 10 so that I can actually refill my hearts. And now we have to create a function which will do that. So let's go ahead and do the following. Let's open up pending and start transition from our use transition hook from React. So make sure you've added an import for that.
And then in here, I'm going to add const onRefillHearts. And if it's currently pending, or if hearts are equal to 5 meaning they are full or if points are below my points to refill in any of that case I will not allow the user to refill their hearts otherwise let's go ahead and let's start the transition so now let's go ahead and click on this let's assign that here so on refill hearts to be called on click here and let's also add another case here so we have this, how about I extend this it's also gonna add the if we are pending like that. So if we are pending or if hearts are already full or if there are not enough points. In any of those cases we are going to disable the button to refill the hearts. And now what we have to do is we have to create the refill hearts method.
So let's go ahead inside of the actions, inside of the user progress here. And at the bottom let's export const refill hearts. Make sure this is an asynchronous method. Let's extract the current user progress from await getUserProgress. We should have that from this method.
We should already have that imported. There we go, getUserProgress. So we have that here. If there is no current user progress, throw new error, user progress not found. If current user progress.hearts are at five, we're going to say throw new error.
Hearts are already full. And if currentUserProgress.points is less than, and then we would have to reuse this. So how about we export this points to refill in the items component and can I import it from there? There we go. So I just added an import here.
I'm going to separate this right to do move to constants folder file right so right now actually I'm not sure if this is a smart thing to do because this is a server action so let's let's keep it simple I will not export that from here I will just copy it and I will just sign it here and maybe leave that to do move alongside item component constant into a common file. So let's keep that here as well just in case. There we go. So let's do that and in here I'm gonna throw new error not enough points. So we already have all of this checks on the front end, but this is our API route.
It's not actually, it's a server action, but it should behave exactly like an API route. So you have to do all the checks that you would do in your usual backend stuff right and now let's await the database update user progress so you should have user progress not be defined as a constant user progress when using it like this needs to be imported from database schema. So we are telling Drizzle which schema to update. That's what we are doing here. So userProgress.updateSet, hearts are gonna be refilled back to five and points are gonna be current userProgress.points minus points to refill.
There we go. So minus 10 in our case and of course we need where equals user progress dot user ID matches current user progress dot user ID. And then let's call revalidate path to slash shop. And since hearts are used in a lot of routes, let's also do them here as well. And lesson, actually let's just do this, I think this is enough there we go, so that should be fine now so we now have a method called refill hearts so let's go back to our items component on refill hearts start transition and let's call the on refill hearts here from actions user progress and let's execute it and on catch toast from sonar error something went wrong and let's import toast and move it here there we go so I have three hearts currently if I purchase them there we go Now I have 10 points and five hearts.
Perfect. So our method to refill hearts is working. What we have to do next in the shop is implement Stripe subscription which we are going to do in the next chapter. Great, great job!