
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.
Complete source code for this lesson is available at
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 Implementing the user answers tab will be even simpler than the questions because we'll basically repeat the same things that we have done here.
00:00:09 So first things first, head over into action.d.ts and create a new interface.
00:00:17 I'll say interface get user answers params.
00:00:23 which extends the paginated search params with the user ID.
00:00:30 Next, in the validation, we'll have to do the same thing that we have done before, which is to say export const get users answers schema And I'll make
00:00:43 it equal to paginatedSearchParams.extend with the user ID that we're passing into it.
00:00:49 Perfect.
00:00:51 Now let's create that server action within user.action.ts.
00:00:57 And we can do it by duplicating the user questions.
00:01:02 So I'll simply copy the entire getUsersQuestions server action and paste it below, and we'll make the necessary modifications.
00:01:10 This one I'll call getUsersAnswers.
00:01:15 The params will be of a type getUsersAnswersParams and we'll return not the questions, but the answers of a type answer array.
00:01:26 This will remain the same.
00:01:28 This thing too, the pagination will remain the same, but instead of total questions, we'll return the total answers and we'll change it here.
00:01:37 So we have to get it from the answer documents.
00:01:41 Same thing for the questions.
00:01:43 I'll say answers, and we'll get it from the answer model.
00:01:48 In this case, we'll just populate it with the author information, such as the underscore ID, name, and image.
00:01:54 And we'll calculate the isNext by passing over the answers here, here, and here.
00:02:02 And that's it.
00:02:03 We can now call it in the profile details page and then display it.
00:02:07 To call it, I will just duplicate this entire call, including the destructuring from the questions, and we'll rename everything to answers.
00:02:16 Instead of user question success, it'll be user answers success.
00:02:21 Same thing for user answers, user answers error.
00:02:26 And we can get it by calling the get user answers.
00:02:30 Oh, why is it not auto-filling it for me?
00:02:32 Let's go back to see if I renamed it.
00:02:35 Get users.
00:02:36 Oh, it was supposed to be get users answers.
00:02:40 There we go.
00:02:41 So now if I go back and try to auto-fill it, I get it immediately.
00:02:46 And we get out of it the answers as well as has more answers.
00:02:52 Perfect.
00:02:53 We can actually group those two together by bringing this destructuring down.
00:02:59 And there we go.
00:03:00 Now we have two calls to our server functions and we have the two data destructurings.
00:03:07 So now right below the first tabs content where we're rendering the data renderer, we can copy that data renderer and the pagination.
00:03:18 And we can render it within the second tabs content where currently there is a list of answers text.
00:03:25 So simply paste over the data renderer there.
00:03:29 change this to answers.
00:03:31 In the empty, say empty answers.
00:03:34 For the success, we'll get the user answers success.
00:03:39 For the error, we'll say user answer error.
00:03:43 And here, instead of rendering the hot questions, we will simply render the answers.
00:03:48 And then we can say answers.map.
00:03:51 Where, for each answer, we will render an answer card, coming from card's answer, to which we'll pass over a key, since we're mapping over it,
00:04:01 of answer.underscoreID.
00:04:04 We'll spread out the entire answer.
00:04:07 And I'll also pass in a content equal to answer.content.slice from about 0 to maybe like 27 characters.
00:04:18 What does this mean?
00:04:19 Well, this means that we will truncate it so it won't take a lot of space on the page.
00:04:24 Of course here, as we're mapping over the answers, we get one individual answer for each round of the map.
00:04:31 And we'll actually have to modify the answer card to accept all of these properties.
00:04:36 Currently, it just says no answers found.
00:04:39 Is it possible that this user hasn't rendered any answers?
00:04:42 Well, let's see.
00:04:44 Here, we're rendering the total answers.
00:04:47 We are showing a number of three.
00:04:50 Are we doing that properly in the getStats?
00:04:54 Let's see.
00:04:55 Here, we're rendering the total answers coming from the data, and that data is coming from the getUser.
00:05:04 Total answers is being calculated by counting the answer documents.
00:05:08 Yeah, this looks good.
00:05:09 So there should be some answers that this user has provided.
00:05:14 Oh, but it says userQuestions does not contain answers.
00:05:17 Oh yeah, because they have to destructure it not from userQuestions, but rather from the userAnswers, okay?
00:05:23 So right here I'll say userAnswers.
00:05:27 There we go.
00:05:28 So now we actually should try to get some answers.
00:05:32 But it looks like the underscore ID or the slice don't exist on what we're trying to call.
00:05:38 This was supposed to be answer instead of answers.
00:05:42 So if we do this properly, you'll see that if you move over to the answers, we have three different answers which are truncated to be able to fit them
00:05:51 in a single line.
00:05:52 But the upvote and downvote properties still exist.
00:05:56 I guess if you're looking at your profile, you'll just want to go ahead and do this because you want to give yourself the most upvotes,
00:06:02 right?
00:06:03 Oh, and there's one thing that I noticed and that is that when rendering the questions, I actually was mapping over the wrong questions.
00:06:10 I should have been mapping over the questions that we're getting right here from through the render function.
00:06:16 So I'll have to rename this to questions.
00:06:18 It's okay, still seems to function well.
00:06:21 We're not getting any errors here, nor in the console.
00:06:24 But if I go to answers, we cannot really go back.
00:06:28 That's something we'll have to look into.
00:06:29 But before I go ahead and do that, you'll see that we cannot really go through this answer to be able to read more.
00:06:36 we need to have some kind of a read more button.
00:06:38 So let's head over into the answer card and let's make some changes to it.
00:06:43 When we click on the answer, we'll move over to the question page, right?
00:06:48 Because an answer is not a standalone entity.
00:06:51 It belongs to a specific question.
00:06:53 So we need to go to our global.d.ts where we have our answer.
00:07:01 And I'll also pass it a question of a type string, which will be an ID.
00:07:05 So we know which question this answer belongs to.
00:07:08 So let's further style this answer card.
00:07:10 We'll have to make it more reusable.
00:07:12 And when you want to make something look differently, depending on where it's being shown, that typically means you have to pass some more props.
00:07:19 So I'll pass the question prop as well as the container classes.
00:07:24 And showReadMore, which by default will be set to false.
00:07:29 So now let's actually extend these props by saying that these props are of a type props.
00:07:38 And at the top, we can say interface props extends the typical answer props with the container classes optional of a type string and show read more optional
00:07:51 of a type boolean.
00:07:52 Now in this article, we can modify it a bit by using the CN property here.
00:07:58 We'll pass these default styles anyway.
00:08:01 such as light border, border B, and padding Y of 10. But then we'll also add the container classes in case we want to make the answers look a bit differently
00:08:11 right here on the profile page.
00:08:12 I'll also change this ID.
00:08:14 Instead of JSON stringify, I'll actually turn it into a string of answer dash and then dollar sign underscore ID.
00:08:22 This is so that when someone clicks on read more from profile page, it'll redirect to the question details and scroll down directly to that question.
00:08:29 That's pretty cool.
00:08:30 I'll show you how it works.
00:08:31 You know, when you click on a specific link and it scrolls you directly on that page to what you want it to see, well, you just give an element ID and
00:08:39 then later on you scroll to it.
00:08:41 So right at the bottom, below the preview, I'll say if show more or show read more is true, in that case, render a link.
00:08:55 that'll have an href pointing to forward slash questions forward slash dollar sign question and then hash answer- and then the ID of the answer.
00:09:09 So we want to move over exactly to that answer.
00:09:12 Let's also give it a class name of body-semi-bold relative z of 10 font-space-grotesque and text-primary 500. Within it,
00:09:29 we can just display a p tag that'll have a class name of margin top of one.
00:09:36 And it'll say, read more, dot dot dot.
00:09:40 So now we have to go back to the profile page and we have to pass those additional props that'll activate those additional functionalities within the answer card.
00:09:50 such as the container classes, which will be equal to card-wrapper, rounded-10px, padding x of 7, padding y of 9, and on small devices,
00:10:06 padding x of 11. That'll immediately make it look like a card.
00:10:11 And more importantly, we can pass the Show Read More prop, which will render this Read More button.
00:10:19 So now if I click on Read More, check this out, we get redirected directly to the question that this answer belongs to, and then we immediately get scrolled
00:10:28 to that part of the page.
00:10:30 So if we check this out in its full glory on desktop and I head over to profile, we see our top posts and the top answers.
00:10:39 I can find this understanding the data renderer and I'll click read more.
00:10:44 And it points me directly to this part of that specific question.
00:10:50 Pretty cool, right?
00:10:51 And the pagination for the answers will also work by default.
00:10:55 The only thing you have to do is instead of rendering has more questions, you'll have to render has more answers.
00:11:03 Or by default, it can be set to false.
00:11:06 Perfect.
00:11:07 And that brings us to the end of the tabs section of the profile page.
00:11:11 One thing that I noticed is that sometimes you have to target the bottom of the text to be able to move between the pages.
00:11:18 If you target the upper part, sometimes it doesn't do it.
00:11:21 I'll check out what this is about a bit later on.
00:11:23 For now, we're totally good because we can always move if we target the text directly.
00:11:28 But now I want to focus on the last remaining part of the profile page, which is the top text section, which means that we have to fetch the top tags that
00:11:38 this user has interacted with.
00:11:40 So let's first commit what we have here.
00:11:43 I'll say implement user answers, commit and sync, and let's focus on the tags next.