@@ -22,7 +22,7 @@ const ProductImage = ({ thumbnail, images }) => (
22
22
< img
23
23
src = { thumbnail }
24
24
alt = "Product Image"
25
- className = { `${ sharedClasses . roundedLg } ${ sharedClasses . mb4 } w-full h-auto max-h-[50vh] object-contain` }
25
+ className = { `${ sharedClasses . roundedLg } ${ sharedClasses . mb4 } w-full h-auto max-h-[50vh] object-contain bg-gray-100 border-2 border-green-700 hover:cursor-pointer ` }
26
26
/>
27
27
< div
28
28
className = { `${ sharedClasses . flexSpaceX2 } ${ sharedClasses . mb4 } justify-center` } >
@@ -31,16 +31,14 @@ const ProductImage = ({ thumbnail, images }) => (
31
31
key = { i }
32
32
src = { i }
33
33
alt = { `Thumbnail ${ i } ` }
34
- className = "w-1/4 h-auto rounded-lg object-cover"
34
+ className = "w-1/4 h-auto rounded-lg object-cover bg-gray-100 border-2 border-green-700 hover:cursor-pointer "
35
35
/>
36
36
) ) }
37
37
</ div >
38
38
</ div >
39
39
) ;
40
40
41
41
const ProductDetails = ( { product } ) => {
42
-
43
-
44
42
return (
45
43
< div className = "w-full lg:w-1/2 lg:pl-6" >
46
44
< h2 className = "text-2xl font-bold mb-2 sm:text-2xl md:text-3xl md:mb-8 text-black" >
@@ -50,7 +48,9 @@ const ProductDetails = ({ product }) => {
50
48
{ product ?. description }
51
49
</ p >
52
50
< p className = "text-xl font-semibold mb-2" >
53
- ₹{ product ?. newPrice } < span className = "line-through text-zinc-500" > { product ?. price } </ span > ({ product ?. discountPercentage } % OFF)
51
+ ₹{ product ?. newPrice } { " " }
52
+ < span className = "line-through text-zinc-500" > { product ?. price } </ span > (
53
+ { product ?. discountPercentage } % OFF)
54
54
</ p >
55
55
< p className = "text-zinc-500 dark:white mb-4" > Inclusive of taxes</ p >
56
56
< ColorOptions />
@@ -60,7 +60,7 @@ const ProductDetails = ({ product }) => {
60
60
< ProductInfo product = { product } />
61
61
< ProductRatings product = { product } />
62
62
</ div >
63
- )
63
+ ) ;
64
64
} ;
65
65
66
66
const ColorOptions = ( ) => (
@@ -114,7 +114,7 @@ const ActionButtons = ({ product }) => {
114
114
const dispatch = useDispatch ( ) ;
115
115
const cartItems = useSelector ( ( state ) => state . cart . items ) ;
116
116
const wishlistItems = useSelector ( ( state ) => state . wishlist . items ) ;
117
- const navigate = useNavigate ( )
117
+ const navigate = useNavigate ( ) ;
118
118
119
119
const onAddToCart = ( ) => {
120
120
const quantity = 1 ;
@@ -130,93 +130,97 @@ const ActionButtons = ({ product }) => {
130
130
131
131
return (
132
132
< div className = { `${ sharedClasses . mb4 } space-y-2` } >
133
- {
134
- cartItems . find ( ( item ) => item . id === product ?. id ) ? (
135
- < button
136
- className = { `${ sharedClasses . buttonGreen } w-full hover:bg-[#3d9970ff] transition-colors disabled:opacity-45 disabled:pointer-events-none` }
137
- onClick = { ( ) => navigate ( "/cart" ) }
138
- aria-label = "Buy now"
139
- >
140
- Buy now
141
- </ button >
142
- ) :
143
- (
144
- < button
145
- className = { `${ sharedClasses . buttonGreen } w-full hover:bg-[#3d9970ff] transition-colors disabled:opacity-45 disabled:pointer-events-none` }
146
- onClick = { onAddToCart }
147
- >
148
- Add to cart
149
- </ button >
150
- )
151
- }
133
+ { cartItems . find ( ( item ) => item . id === product ?. id ) ? (
134
+ < button
135
+ className = { `${ sharedClasses . buttonGreen } w-full hover:bg-[#3d9970ff] transition-colors disabled:opacity-45 disabled:pointer-events-none` }
136
+ onClick = { ( ) => navigate ( "/cart" ) }
137
+ aria-label = "Buy now" >
138
+ Buy now
139
+ </ button >
140
+ ) : (
141
+ < button
142
+ className = { `${ sharedClasses . buttonGreen } w-full hover:bg-[#3d9970ff] transition-colors disabled:opacity-45 disabled:pointer-events-none` }
143
+ onClick = { onAddToCart } >
144
+ Add to cart
145
+ </ button >
146
+ ) }
152
147
< button
153
148
className = "w-full px-4 py-2 bg-zinc-200 dark:bg-zinc-700 text-zinc-700 dark:text-zinc-300 rounded-lg hover:bg-zinc-800 transition-colors disabled:opacity-45 disabled:pointer-events-none"
154
149
disabled = { wishlistItems . find ( ( item ) => item . id === product ?. id ) }
155
- onClick = { onAddToWishlist }
156
- >
150
+ onClick = { onAddToWishlist } >
157
151
{ wishlistItems . find ( ( item ) => item . id === product ?. id )
158
152
? "Item added to wishlist"
159
153
: "Add to wishlist" }
160
154
</ button >
161
155
</ div >
162
- )
156
+ ) ;
163
157
} ;
164
158
165
159
const ProductInfo = ( { product } ) => (
166
160
< div className = { sharedClasses . mb4 } >
167
161
< h3 className = "text-lg font-semibold mb-2" > Product Details</ h3 >
168
162
{ product ?. category && < p > Category: { product . category } </ p > }
169
- { product ?. availabilityStatus && < p > Availability Status: { product . availabilityStatus } </ p > }
163
+ { product ?. availabilityStatus && (
164
+ < p > Availability Status: { product . availabilityStatus } </ p >
165
+ ) }
170
166
{ product ?. brand && < p > Brand: { product . brand } </ p > }
171
167
{ product ?. returnPolicy && < p > Return Policy: { product . returnPolicy } </ p > }
172
- { product ?. warrantyInformation && < p > Warranty Information: { product . warrantyInformation } </ p > }
173
- { product ?. shippingInformation && < p > Shipping Information: { product . shippingInformation } </ p > }
168
+ { product ?. warrantyInformation && (
169
+ < p > Warranty Information: { product . warrantyInformation } </ p >
170
+ ) }
171
+ { product ?. shippingInformation && (
172
+ < p > Shipping Information: { product . shippingInformation } </ p >
173
+ ) }
174
174
</ div >
175
175
) ;
176
176
177
177
const ProductRatings = ( { product } ) => {
178
-
179
178
const calculatePercentages = ( ) => {
180
179
const totalReviews = product ?. reviews . length || 0 ;
181
180
182
- const counts = product ?. reviews ?. reduce ( ( acc , review ) => {
183
- if ( review . rating === 5 ) acc . excellent ++ ;
184
- else if ( review . rating === 4 ) acc . veryGood ++ ;
185
- else if ( review . rating === 3 || review . rating === 2 ) acc . good ++ ;
186
- else if ( review . rating === 1 || review . rating === 0 ) acc . low ++ ;
187
- return acc ;
188
- } , { excellent : 0 , veryGood : 0 , good : 0 , low : 0 } ) ;
189
-
181
+ const counts = product ?. reviews ?. reduce (
182
+ ( acc , review ) => {
183
+ if ( review . rating === 5 ) acc . excellent ++ ;
184
+ else if ( review . rating === 4 ) acc . veryGood ++ ;
185
+ else if ( review . rating === 3 || review . rating === 2 ) acc . good ++ ;
186
+ else if ( review . rating === 1 || review . rating === 0 ) acc . low ++ ;
187
+ return acc ;
188
+ } ,
189
+ { excellent : 0 , veryGood : 0 , good : 0 , low : 0 }
190
+ ) ;
190
191
191
192
return [
192
193
{ label : "Excellent" , width : ( counts ?. excellent / totalReviews ) * 100 } ,
193
194
{ label : "Very Good" , width : ( counts ?. veryGood / totalReviews ) * 100 } ,
194
195
{ label : "Good" , width : ( counts ?. good / totalReviews ) * 100 } ,
195
- { label : "Low" , width : ( counts ?. low / totalReviews ) * 100 }
196
+ { label : "Low" , width : ( counts ?. low / totalReviews ) * 100 } ,
196
197
] ;
197
198
} ;
198
199
199
-
200
200
return (
201
201
< div className = { sharedClasses . mb4 } >
202
202
< h3 className = "text-lg font-semibold mb-2" > Product Ratings & Reviews </ h3 >
203
203
< div className = "flex items-center mb-2" >
204
204
< span className = "text-2xl font-bold" > { product ?. rating } </ span >
205
205
< span className = "text-yellow-500 ml-2" > ★</ span >
206
206
</ div >
207
- < p className = { `${ sharedClasses . textGray } mb-2` } > Total reviews { product ?. reviews . length } </ p >
207
+ < p className = { `${ sharedClasses . textGray } mb-2` } >
208
+ Total reviews { product ?. reviews . length }
209
+ </ p >
208
210
{ calculatePercentages ( ) . map ( ( rating , index ) => (
209
211
< RatingBar key = { index } { ...rating } />
210
212
) ) }
211
213
</ div >
212
- )
214
+ ) ;
213
215
} ;
214
216
215
217
const RatingBar = ( { label, width } ) => (
216
218
< div className = "flex items-center mb-1" >
217
219
< span className = { `flex-1 ${ sharedClasses . textGray } ` } > { label } </ span >
218
220
< div className = "w-4/5 relative bg-green-300 rounded-lg" >
219
- < div style = { { width : `${ width } %` } } className = { `bg-green-700 h-2 rounded-lg` } > </ div >
221
+ < div
222
+ style = { { width : `${ width } %` } }
223
+ className = { `bg-green-700 h-2 rounded-lg` } > </ div >
220
224
</ div >
221
225
</ div >
222
226
) ;
@@ -225,28 +229,24 @@ const CustomerFeedback = ({ reviews }) => (
225
229
< div className = "mt-8 px-4 " >
226
230
< h3 className = "text-lg font-semibold mb-2" > Customer Feedback</ h3 >
227
231
< div className = "grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-3 gap-4" >
228
- {
229
- reviews ?. map ( ( review , index ) => {
230
- return (
231
- < div key = { index } className = "p-4 bg-[#798280ff] rounded-lg text-white" >
232
- < div className = "flex items-center mb-2" >
233
- < img
234
- src = { avatar }
235
- alt = "User Avatar"
236
- className = { `w-12 h-12 ${ sharedClasses . roundedFull } mr-2` }
237
- />
238
- < div >
239
- < p className = "font-semibold" > { review ?. reviewerName } </ p >
240
- < p className = "text-sm text-white" > { review ?. reviewerEmail } </ p >
241
- </ div >
232
+ { reviews ?. map ( ( review , index ) => {
233
+ return (
234
+ < div key = { index } className = "p-4 bg-[#798280ff] rounded-lg text-white" >
235
+ < div className = "flex items-center mb-2" >
236
+ < img
237
+ src = { avatar }
238
+ alt = "User Avatar"
239
+ className = { `w-12 h-12 ${ sharedClasses . roundedFull } mr-2` }
240
+ />
241
+ < div >
242
+ < p className = "font-semibold" > { review ?. reviewerName } </ p >
243
+ < p className = "text-sm text-white" > { review ?. reviewerEmail } </ p >
242
244
</ div >
243
- < p className = "mb-2 text-white" >
244
- { review ?. comment }
245
- </ p >
246
245
</ div >
247
- )
248
- } )
249
- }
246
+ < p className = "mb-2 text-white" > { review ?. comment } </ p >
247
+ </ div >
248
+ ) ;
249
+ } ) }
250
250
</ div >
251
251
</ div >
252
252
) ;
@@ -256,15 +256,21 @@ const ProductPage = () => {
256
256
const [ product , setProduct ] = useState ( null ) ;
257
257
258
258
function getNewPrice ( discountPercent , actualPrice ) {
259
- return ( ( 100 - discountPercent ) * actualPrice / 100 ) . toFixed ( 2 )
259
+ return ( ( ( 100 - discountPercent ) * actualPrice ) / 100 ) . toFixed ( 2 ) ;
260
260
}
261
261
262
262
const fetchProduct = async ( ) => {
263
263
try {
264
- const response = await axios . get ( `https://dummyjson.com/products/${ productId } ` ) ;
264
+ const response = await axios . get (
265
+ `https://dummyjson.com/products/${ productId } `
266
+ ) ;
265
267
const requiredProduct = response . data ;
266
- requiredProduct . newPrice = getNewPrice ( requiredProduct . discountPercentage , requiredProduct . price )
267
- requiredProduct . image = requiredProduct . images [ 0 ] || requiredProduct . thumbnail
268
+ requiredProduct . newPrice = getNewPrice (
269
+ requiredProduct . discountPercentage ,
270
+ requiredProduct . price
271
+ ) ;
272
+ requiredProduct . image =
273
+ requiredProduct . images [ 0 ] || requiredProduct . thumbnail ;
268
274
setProduct ( requiredProduct ) ;
269
275
} catch ( error ) {
270
276
console . error ( "Axios error:" , error ) ;
@@ -275,20 +281,23 @@ const ProductPage = () => {
275
281
if ( productId ) {
276
282
fetchProduct ( ) ;
277
283
}
278
- } , [ productId ] )
284
+ } , [ productId ] ) ;
279
285
280
286
return (
281
287
< div className = "bg-[#fff0e3ff] min-h-screen" >
282
288
< div className = "max-w-7xl mx-auto bg-[#fff0e3ff] rounded-lg p-4 sm:p-6" >
283
289
< div className = "flex flex-col lg:flex-row mt-24" >
284
- < ProductImage thumbnail = { product ?. thumbnail } images = { product ?. images } />
290
+ < ProductImage
291
+ thumbnail = { product ?. thumbnail }
292
+ images = { product ?. images }
293
+ />
285
294
< ProductDetails product = { product } />
286
295
</ div >
287
296
< CustomerFeedback reviews = { product ?. reviews } />
288
297
< Similarproducts category = { product ?. category } />
289
298
</ div >
290
299
</ div >
291
- )
300
+ ) ;
292
301
} ;
293
302
294
303
export default ProductPage ;
0 commit comments