Skip to content

Commit a818c79

Browse files
FelipeCabreraBJoaquinEtchegarayskydiver
authored
Reviews functionality (#173)
* Reviews functionality * Check if user is logged in and add error messages for inputs * Add delete and edit funtionality * Add layout to reviews * Delete deprecated component * Fix build errors * Reviews functionality * Check if user is logged in and add error messages for inputs * Add delete and edit funtionality * Add layout to reviews * Delete deprecated component * Fix build errors * Rating functionality * Add reviews counter * add limit and page to getReviews function * build fix * Add pagination * Add review rating to product * fix hydration error * Add default query value * redirect anon users from account layout * Reviews update --------- Co-authored-by: JoaquinEtchegaray <[email protected]> Co-authored-by: Martín M <[email protected]>
1 parent 96d9186 commit a818c79

File tree

29 files changed

+669
-150
lines changed

29 files changed

+669
-150
lines changed

app/_components/Globals/Spinner.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const Spinner = ({ position = 'center', size = 10, className }: SpinnerPr
99
<div className={`text-${position} ${className ? className : ''}`}>
1010
<div role="status">
1111
<svg
12-
className={`inline mr-2 w-${size} h-${size} text-gray-medium animate-spin dark:text-gray-dark fill-gray`}
12+
className={`inline w-${size} h-${size} text-gray-medium animate-spin dark:text-gray-dark fill-gray`}
1313
viewBox="0 0 100 101"
1414
fill="none"
1515
xmlns="http://www.w3.org/2000/svg"

app/_lib/Store.ts

+55-1
Original file line numberDiff line numberDiff line change
@@ -105,7 +105,8 @@ class Store {
105105
sku: product.sku || null,
106106
images: this.transformImages(product),
107107
categories: product.category_index?.id,
108-
stock: product.stock_level
108+
stock: product.stock_level,
109+
reviewRating: product.review_rating
109110
};
110111
}
111112

@@ -177,6 +178,59 @@ class Store {
177178
limit
178179
};
179180
}
181+
182+
async getReviews(reviewInfo: { productId: string; limit?: number; page?: number }) {
183+
const { productId, limit = 10, page } = reviewInfo;
184+
const reviews = await swell.get('/products:reviews', {
185+
parent_id: productId,
186+
limit,
187+
page
188+
});
189+
return reviews;
190+
}
191+
192+
async postReview(reviewInfo: {
193+
userId: string;
194+
comments: string;
195+
productId: string;
196+
rating: number;
197+
title: string;
198+
}) {
199+
const { userId, comments, productId, rating, title } = reviewInfo;
200+
201+
const reviews = await swell.post('/products:reviews', {
202+
account_id: userId,
203+
comments: comments,
204+
parent_id: productId,
205+
rating: rating,
206+
title: title,
207+
approved: true
208+
});
209+
return reviews;
210+
}
211+
212+
async deleteReview(reviewId: string) {
213+
const review = await swell.delete(`/products:reviews/${reviewId}`);
214+
return review;
215+
}
216+
217+
async editReview(
218+
reviewId: string,
219+
reviewInfo: {
220+
comments: string;
221+
rating: number;
222+
title: string;
223+
}
224+
) {
225+
const { comments, rating, title } = reviewInfo;
226+
227+
const review = await swell.put(`/products:reviews/${reviewId}`, {
228+
comments: comments,
229+
rating: rating,
230+
title: title
231+
});
232+
return review;
233+
}
180234
}
181235

182236
export default new Store();

app/_lib/SwellAPI.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { cookies } from 'next/headers';
2-
import { redirect } from 'next/navigation';
32
import 'server-only';
43

54
import Store from './Store';
@@ -104,6 +103,7 @@ export const getLoggedUser = async (): Promise<SwellAPI_Customer | null> => {
104103
name
105104
}
106105
product {
106+
id
107107
name
108108
images {
109109
file {
@@ -157,7 +157,7 @@ export const getUserInfo = async () => {
157157
const user = await getLoggedUser();
158158

159159
if (!user?.session.accountId) {
160-
return redirect('/account/login');
160+
return { authenticated: false, user: null, userId: null, orders: [], addresses: [], cards: [] };
161161
}
162162

163163
const addresses = await getAddresses();
@@ -166,6 +166,7 @@ export const getUserInfo = async () => {
166166
return {
167167
authenticated: true,
168168
user: user.account,
169+
userId: user.session.accountId,
169170
orders: user.orders.results || [],
170171
addresses: addresses || [],
171172
cards: cards || []

app/_types/Generic/Generic.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
interface Pagination {
2-
total: number;
2+
total?: number;
33
pages: number[];
44
current: number;
55
limit: number;

app/_types/Generic/Products.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ interface Product {
1515
sku?: string | null;
1616
categories?: string[];
1717
stock?: number;
18+
reviewRating?: number;
1819
}
1920

2021
interface ProductImage {

app/_types/Generic/Reviews.d.ts

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
type Reviews = {
2+
count: number;
3+
page_count: number;
4+
page: number;
5+
results: Review[];
6+
pages: {
7+
[key: string]: { start: number; end: number };
8+
};
9+
};
10+
11+
interface Review {
12+
account_id: string;
13+
comments: string;
14+
parent_id: string;
15+
rating: number;
16+
title: string;
17+
name: string;
18+
date_created: string;
19+
approved: boolean;
20+
id: string;
21+
}

app/_types/Swell/Product.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ interface SwellProduct {
3434
sale_price?: number | null;
3535
sku?: string;
3636
category_index: CategoryIndex;
37+
review_rating: number;
3738
id: string;
3839
}
3940

app/_types/Swell/SwellAPI.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ interface SwellAPI_Order {
3737
name: string;
3838
};
3939
product: {
40+
id: string;
4041
name: string;
4142
price: number;
4243
images: {

app/account/(anon)/create-account/_components/RegisterForm.tsx

-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use client';
22

33
import Link from 'next/link';
4-
import { useRouter } from 'next/navigation';
54
import { useState } from 'react';
65
import { useForm, SubmitHandler } from 'react-hook-form';
76
import { AiOutlineEye, AiOutlineEyeInvisible } from 'react-icons/ai';

app/account/(anon)/reset-password/_components/ResetPasswordForm.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client';
22

3-
import { useRouter, useSearchParams } from 'next/navigation';
3+
import { useSearchParams } from 'next/navigation';
44
import { useRef, useState } from 'react';
55
import { SubmitHandler, useForm } from 'react-hook-form';
66
import { AiOutlineEye, AiOutlineEyeInvisible } from 'react-icons/ai';

app/account/(user)/addresses/_components/AddressCard.tsx

+2-3
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,20 @@ const AddressCard = ({ address }: Props) => {
2828
</div>
2929
<div className="flex flex-col justify-between">
3030
<button className="flex justify-center items-center pt-1" onClick={() => setOpen(true)}>
31-
<EditIcon />
31+
<EditIcon strokeColor="black" />
3232
</button>
3333
<DeleteAddressModal
3434
addressId={address.id}
3535
openModal={openModal}
3636
setOpenModal={setOpenModal}
3737
/>
3838
<button
39-
className="hover:stroke-red-500"
4039
data-delete="address"
4140
onClick={() => {
4241
void setOpenModal(true);
4342
}}
4443
>
45-
<TrashIcon />
44+
<TrashIcon strokeColor="red" />
4645
</button>
4746
<EditAddressModal open={open} setOpen={setOpen} address={address} />
4847
</div>

app/account/(user)/layout.tsx

+7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { redirect } from 'next/navigation';
2+
13
import EditProfileModal from './_components/EditProfileModal';
24
import AccountLink from './_components/Link';
35
import LogOutModal from './_components/LogOutModal';
@@ -9,6 +11,11 @@ import { getUserInfo } from '~/_lib/SwellAPI';
911
export default async function Layout({ children }: { children: React.ReactNode }) {
1012
const { user: account } = await getUserInfo();
1113

14+
// Prevent access to account pages if user is not logged in
15+
if (!account) {
16+
return redirect('/account/login');
17+
}
18+
1219
return (
1320
<Container className="mb-10">
1421
<div className="grid gap-10 lg:gap-0 lg:grid-cols-12 pt-10 font-quicksand space-x-10">

app/account/(user)/payments/_components/PaymentsCard.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const PaymentCard = ({ card, defaultCard }: PaymentCardProps) => {
3737
<div className={`flex flex-col ${defaultCard ? 'justify-between items-end' : 'justify-end'}`}>
3838
{defaultCard && <p className="bg-gray shadow p-1 px-2 text-xs">Default</p>}
3939
<button onClick={() => setOpenConfModal(true)}>
40-
<TrashIcon />
40+
<TrashIcon strokeColor="red" />
4141
</button>
4242
</div>
4343
<DeleteCardModal

app/products/[slug]/_components/ProductInfo/ProductInfo.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ const ProductInfo = async ({ product }: ProductProp) => {
2121
<div className="w-full space-y-2 mt-5 md:mt-0">
2222
<ProductTitle title={product.name} />
2323
<ProductStock stock={product.stock} />
24-
<ProductRating rating={3} />
24+
<ProductRating rating={product.reviewRating as number} />
2525
<ProductPriceOptions price={product.price} salePrice={product.salePrice} />
2626
<ProductOptions product={product} />
2727
<AddToCart product={product} isAuthenticated={auth} />

app/products/[slug]/_components/ProductInfo/ProductRating.tsx

+13-14
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
'use client';
22

3-
import Rater from 'react-rater';
4-
import 'react-rater/lib/react-rater.css';
3+
import { Rating } from 'react-simple-star-rating';
54

6-
import Tooltip from '~/_components/Globals/Tooltip';
7-
8-
type Rating = {
9-
rating?: number;
10-
};
11-
12-
const ProductRating = ({ rating }: Rating) => {
5+
const ProductRating = ({ rating }: { rating: number }) => {
136
return (
14-
<Tooltip content="Feature coming soon!">
15-
<div className="w-fit">
16-
<Rater total={5} rating={rating} />
17-
</div>
18-
</Tooltip>
7+
<div className="flex items-center gap-3">
8+
<Rating
9+
allowFraction
10+
initialValue={rating || 0}
11+
readonly
12+
size={20}
13+
SVGclassName="inline"
14+
fillColor="#242323"
15+
/>
16+
<span>({rating || 0} stars)</span>
17+
</div>
1918
);
2019
};
2120

app/products/[slug]/_components/ProductReview.tsx

-18
This file was deleted.

app/products/[slug]/_components/ProductReview/ProductRatings.tsx

-16
This file was deleted.

0 commit comments

Comments
 (0)