In this chapter, I want to use the reviews from previous chapter and properly populate them in the components. In order to do that, we have to aggregate our reviews inside of our procedures, specifically products procedures and library procedures. As always, ensure that you are on your master branch and that you have merged all the changes from the previous chapter. Once you've done that, go ahead and run the app. So what we're going to do now is we're going to go inside of our products procedures right here and let's first focus on the get many base procedure.
So after all of these filters right here, when we actually get to the data, so here, instead of returning that data, I'm going to create constant data with summarized reviews and turn that into await promise.all. With promise.all we can safely use async inside of .map. So what I can now do is data.docs.map and then do an async here. Otherwise, you would not be able to use async. I mean, you can, but not in the way you think you're using it, right?
So what this will do is it will return promises, and then we can run them through promise all. Because you might have heard that it's unsafe to use async inside of .map. And that's true, but not if you are doing it inside of promise all. So inside of here, for each document, we're going to get a reviews. So let's call this reviews data, like this.
Await, context, database, find, collection, reviews. Agitation will be set to false, so we load all of them, where will be product equals document id, and that's it. Once we have the reviews data, we're going to return this existing document and some updates here. So for example, review count will be reviews, reviews data, total documents. Review rating will review rating will be if reviews documents length is 0 0 otherwise we are going to do reviews docs reduce get the accumulator and the review, multiply the accumulator with review.rating and set zero for the accumulator and divide that by reviews total documents like this.
There we go. I can zoom out if you want to see it in one line. And once you've done that, what you can do here is spread the data, but for the documents, use data with summarized reviews. So just go ahead and do data with summarized reviews and remove .docs, so directly .map. And all the types should match normally as everything else does.
So now let's go ahead inside of product list, inside of products module. So I'm going to go inside of source here, modules, products, UI, components, product list. And now you can see that usually we hard-coded the review rating and the review card here. So we are now going to modify it. We're going to do product.reviewRating.
And in here, product.reviewCount. So now, we shouldn't have any fake data, I believe. So if I go inside of my localhost here and refresh, so basically you have to be on your main page with the category here. I had an error and then the error disappeared. So I just want to refresh to ensure there is no more errors.
And there we go. You can see that in here, since I have no reviews, let me open the product card so you can see. Since I have no reviews here, it's not even showing. But in here, I have one review with star five. So guess what I can do now?
I can now go inside of this, click view in library here. And once it loads the library, I can edit and give it a three and click update review. I will then go back, back to continue shopping. And there we go, you can see now it's changed to three. So if you go ahead and implement, you can, here's what you can do, because nothing is stopping you, I think, right now.
You can log out and log in as another user, so AntonioDemo.com demo. And I think that you can just create a new review if you want to. Test, test, rating, four or maybe five, and the product, another Antonio's product, and select yourself and click save. And now if you yeah just make sure it's another user otherwise I think it will be invalid data. And now we can see that it took the average of those two.
Great so that now works. So that's one part solved. What we have to do now is we have to display them correctly in here because this is completely wrong at the moment. So in order to do that we have to go back inside of our procedures for the products here but we have to specifically focus on get one. So we're going to do that right after this is purchased.
So just before we return get one, let's go ahead and get our reviews. We can get the reviews we need using the actual ID of the product we are trying to fetch. So const reviews is await context database find collection reviews pagination false where product equals input ID. So those are all the reviews for this product. What we have to do now is we have to get the current review rating.
So again, if reviews docs length is larger than zero, then we do reviews docs reduce, get the accumulator and the review here. Go ahead and add accumulators to review dot rating, set the accumulator to zero and divide by reviews total documents otherwise set the review rating to zero and then what we have to do is we have to create the rating distribution for this part right here. So for that we need a starting point. Rating distribution is going to be an object which accepts the number and a number. Make sure you select through from 1 through 5 like this.
And then what we're going to do is we are going to let me just indent this we're going to do if reviews dot total documents is above zero reviews docs for each review, go ahead and do const rating, review.rating. And then if rating is greater than or equal than one and less than or equal than five. So if it is within the range, rating distribution rating is equal to rating distribution rating or a fallback zero if we haven't calculated for that distribution yet and just increase it by one. So now we're going to go over all of our reviews and we are basically increasing the rating distribution whenever we notice a specific rating, if it is inside of our range. Now after that, my apologies, still inside of the if clause here, we are going to convert counts to percentages.
So object.keys, rating distribution for each key, which represents a distribution, a certain score. Let's do const rating number key, const count, rating distribution, rating for zero if we haven't added any distribution for that. And then rating distribution for that rating will be math.round. And inside, will be math.round and inside go ahead and divide count by review reviews total documents and multiply it by a hundred. There we go.
And now what you can do is add some more fields to the return. So besides isPurchased, add a reviewRating here, add reviewCount, which is reviewsTotalDocs, and the ratingDistribution. So basically, all of these constants and variables that we have added. And now we have to go inside of product view, but specifically inside of modules products, right? So nowhere else, product view.
And if I remember correctly, in here we have prepared all of those ratings and things, but we haven't implemented any real data yet. So let's go ahead and do that now. Starting with the first star rating here. Let me just find it. There we go.
So the first star rating here will use data review rating like this. And in here, same thing, data review rating. And here it will be data review count ratings. So now in here, Let me just see which one is this. Is this the correct one?
I see. OK, so this is what I'm going to do. I'm going to copy this paragraph here and just add it here. It appears that I have forgotten to do that when I develop this. So now you should see two ratings here.
Let me just see if this is correct. So gap one, size four. I feel like I need a little bit more space between these two so we can do gap one and let's do gap one here as well like this. There we go. So you can now see two ratings here.
So that part is solved. What we have to do now is we have to create the proper rating distribution down here. So that will be a little bit tricky. So in here, we have ratings. In here, we do data review rating.
In here, we do data review count. And now in here, we have created this loop here, and we have stars. So let's see, can we do value data rating distribution and just pass in stars and do the exact same thing here. And we can, this is exactly the ratings that we gave it. One for five and one for three, which makes them 50-50.
So our rating distribution is working as well. Great. And let's enable this copy button. I don't know why it's not enabled yet. So we can do it quite easily here.
Let me just find, there we go, link icon. So all this button's got to do is navigate the clipboard, write text, and window location dot href. That's it. And toast success That's it. And toast success URL copied to clipboard.
You can import toast from Sonar. There we go. Let me just move it here. And now when you click this, let me refresh maybe. There we go.
URL copied to the clipboard. So let me see. There we go. We can now easily share it with others. Great.
If you want to, you can create a state and disable it for a second if you think it would look better. Now, let's go ahead and do one more place. So let me just see. We added it to product procedures, we fixed product list, and we fixed product view. Now we have to do it inside of library procedures.
So let's go inside of library procedures right here. Instead of library procedures, it's actually going to be pretty similar. And we only need it for get many. So in here we have the products data. So I'm going to open my procedures in the products just so I can see if we can just copy and paste this.
I think we can. So after we get the products data, which is this, we need the data with summarized reviews. So I can just copy this entire thing here and then just add it here and instead of data it will use products data dot docs And I think nothing else refers to data, so this should be fine. And then in here, we just replace this with this, data with summarized reviews. And now all we have to do is go inside of source, modules, library, UI, components, product list here, and just do product review rating here, and product review count here.
And now I think that even in the library the counts should be completely correct. Let me just do a refresh here because it was in the middle of hot reload. Oh yeah, I have nothing here actually, because I am in another account here. So let me just go ahead and log out into an account where I have some purchases. So I'm just going to go back here and go inside of my library and there we go.
We have the correct text here as well. And we can always change that rating. And I just want to show you a little bit of an improvement we can do here. I have briefly mentioned it, so Let me go inside of product view, inside of modules products UI here. So what we can do here for a small improvement is is copied and set is copied.
Use state and set it to false, like this. And then go ahead and find the link right here and disabled if is copied. So what you can do is set is copied this to true and then set timeout and set is copied back to false after let's say one second so maybe this way it's like a little bit improved, right? Because you can't spam it. Amazing job.
And you can also do, if is copied, you can do double tick icon or double check, check icon, check check icon. That's it. You can import this from lucid react and then when you click it will have the copied right or you can just use check icon from Lucid React. I think that looks cool. I'm just going to remove the redundant one.
Let's see our to-dos. Add real ratings. We can now remove that. We have real ratings. So I will search for to-do.
Product card, add real ratings. I think we now have real ratings in the product card here. Review rating, review count, definitely. Product card here also. Basically We didn't even change anything in the product cards.
We mostly did in the product list components. Yes, so I just removed those to-dos because we now added the reviews. Perfect. So finally, a shorter chapter. We finished all of these, this, and this as well.
So 23 aggregating reviews. Let's add git checkout b. I already forgot 23, OK. Aggregating reviews. Git add git commit 23 aggregating reviews and git push uorigin 23 aggregating our reviews there we go So I am detached on a new branch as you can see now and I'm gonna go inside of my e-commerce here and I will open a pull request and let's go together and review our changes.
And here we have this summary. So some new features. Product listings now display dynamic review information including up-to-date average ratings, total review counts and the rating distribution. The product view has been enhanced with the clipboard URL copy feature that provides visual confirmation when a URL is successfully copied. We can see a more in-depth walkthrough here.
As always, we have the sequence diagram for how everything works. I always find these very interesting. You can pause to take a look. And in here, it gave us a quite good refactor suggestion here. So I will consider it.
It's basically telling us to avoid n plus one queries, which are, well, small queries, right? Because for every product that we have, we do a loop and then we fetch reviews. So it would be better if we created an array of IDs that we need to fetch and then fetch them both in bulk, right? So fetch all reviews for all products in a single query and then group reviews by product ID. So this is definitely something that we can consider.
You might want to do this yourself. So yeah, here's a task for you. If you want to improve this code, go ahead and solve this n plus one query problem, make it faster. And it gave the same suggestion for another place where we copied this but as of now I'm satisfied with this so I'm going to merge it let's go ahead here and let's confirm that we have it and now let's go ahead inside of our main or master branch and get full origin in your branch main or master git status to ensure everything is up to date and a graph to visually confirm that as well. There we go!
So we now have fully functioning reviews finally. Amazing job and see you in the next chapter!