Skip to content

Commit 983d0a9

Browse files
committed
Improve design
1 parent b47a957 commit 983d0a9

File tree

1 file changed

+115
-87
lines changed

1 file changed

+115
-87
lines changed

src/components/Product/SingleProduct.component.tsx

Lines changed: 115 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,76 @@
1-
/*eslint complexity: ["error", 20]*/
2-
// Imports
31
import { useState, useEffect } from 'react';
4-
5-
// Utils
62
import { filteredVariantPrice, paddedPrice } from '@/utils/functions/functions';
7-
8-
// Components
93
import AddToCart, { IProductRootObject } from './AddToCart.component';
104
import LoadingSpinner from '@/components/LoadingSpinner/LoadingSpinner.component';
115

12-
const SingleProduct = ({ product }: IProductRootObject) => {
6+
interface IImage {
7+
__typename: string;
8+
id: string;
9+
uri: string;
10+
title: string;
11+
srcSet: string;
12+
sourceUrl: string;
13+
}
14+
15+
interface IVariationNode {
16+
__typename: string;
17+
name: string;
18+
}
19+
20+
interface IAllPaColors {
21+
__typename: string;
22+
nodes: IVariationNode[];
23+
}
24+
25+
interface IAllPaSizes {
26+
__typename: string;
27+
nodes: IVariationNode[];
28+
}
29+
30+
interface IVariationNodes {
31+
__typename: string;
32+
id: string;
33+
databaseId: number;
34+
name: string;
35+
stockStatus: string;
36+
stockQuantity: number;
37+
purchasable: boolean;
38+
onSale: boolean;
39+
salePrice?: string;
40+
regularPrice: string;
41+
}
42+
43+
interface IVariations {
44+
__typename: string;
45+
nodes: IVariationNodes[];
46+
}
47+
48+
interface IProduct {
49+
__typename: string;
50+
id: string;
51+
databaseId: number;
52+
averageRating: number;
53+
slug: string;
54+
description: string;
55+
onSale: boolean;
56+
image: IImage;
57+
name: string;
58+
salePrice?: string;
59+
regularPrice: string;
60+
price: string;
61+
stockQuantity: number;
62+
allPaColors?: IAllPaColors;
63+
allPaSizes?: IAllPaSizes;
64+
variations?: IVariations;
65+
}
66+
67+
const SingleProduct: React.FC<IProductRootObject> = ({ product }) => {
1368
const [isLoading, setIsLoading] = useState<boolean>(true);
14-
const [selectedVariation, setSelectedVariation] = useState<number>();
69+
const [selectedVariation, setSelectedVariation] = useState<number | undefined>();
1570

1671
const placeholderFallBack = 'https://via.placeholder.com/600';
1772

18-
let DESCRIPTION_WITHOUT_HTML;
73+
let DESCRIPTION_WITHOUT_HTML: string | undefined;
1974

2075
useEffect(() => {
2176
setIsLoading(false);
@@ -25,121 +80,94 @@ const SingleProduct = ({ product }: IProductRootObject) => {
2580
}
2681
}, [product.variations]);
2782

28-
let { description, image, name, onSale, price, regularPrice, salePrice } =
29-
product;
83+
let { description, image, name, onSale, price, regularPrice, salePrice } = product;
3084

31-
// Add padding/empty character after currency symbol here
32-
if (price) {
33-
price = paddedPrice(price, 'kr');
34-
}
35-
if (regularPrice) {
36-
regularPrice = paddedPrice(regularPrice, 'kr');
37-
}
38-
if (salePrice) {
39-
salePrice = paddedPrice(salePrice, 'kr');
40-
}
85+
if (price) price = paddedPrice(price, 'kr');
86+
if (regularPrice) regularPrice = paddedPrice(regularPrice, 'kr');
87+
if (salePrice) salePrice = paddedPrice(salePrice, 'kr');
4188

42-
// Strip out HTML from description
4389
if (process.browser) {
44-
DESCRIPTION_WITHOUT_HTML = new DOMParser().parseFromString(
45-
description,
46-
'text/html',
47-
).body.textContent;
90+
DESCRIPTION_WITHOUT_HTML = new DOMParser().parseFromString(description, 'text/html').body.textContent;
4891
}
4992

5093
return (
51-
<section className="bg-white mb-12 sm:mb-2">
52-
{/* Show loading spinner while loading, and hide content while loading */}
94+
<section className="bg-white">
5395
{isLoading ? (
5496
<div className="h-56 mt-20">
5597
<p className="text-2xl font-bold text-center">Laster produkt ...</p>
5698
<br />
5799
<LoadingSpinner />
58100
</div>
59101
) : (
60-
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
61-
<div className="grid grid-cols-1 gap-4 md:grid-cols-2 lg:gap-8">
62-
{image && (
63-
<div className="flex justify-center items-center">
102+
<div className="container mx-auto px-4 py-8 sm:px-6 lg:px-8">
103+
<div className="grid grid-cols-1 md:grid-cols-2 gap-8 items-start">
104+
<div className="w-full">
105+
{image ? (
64106
<img
65107
id="product-image"
66108
src={image.sourceUrl}
67109
alt={name}
68-
className="max-w-full h-auto transition duration-500 ease-in-out transform hover:scale-105"
110+
className="w-full h-auto object-cover rounded-lg shadow-md"
69111
/>
70-
</div>
71-
)}
72-
{!image && (
73-
<div className="flex justify-center items-center">
112+
) : (
74113
<img
75114
id="product-image"
76-
src={
77-
process.env.NEXT_PUBLIC_PLACEHOLDER_LARGE_IMAGE_URL ??
78-
placeholderFallBack
79-
}
115+
src={process.env.NEXT_PUBLIC_PLACEHOLDER_LARGE_IMAGE_URL ?? placeholderFallBack}
80116
alt={name}
81-
className="max-w-full h-auto transition duration-500 ease-in-out transform hover:scale-105"
117+
className="w-full h-auto object-cover rounded-lg shadow-md"
82118
/>
83-
</div>
84-
)}
85-
<div className="flex flex-col justify-center text-center md:text-left">
86-
<h1 className="text-3xl font-bold mb-4">{name}</h1>
87-
{/* Display sale price when on sale */}
88-
{onSale && (
89-
<div className="flex flex-col md:flex-row items-center md:items-start justify-center md:justify-start mb-4">
90-
<p className="text-3xl font-bold text-red-600 mr-4">
91-
{product.variations && filteredVariantPrice(price, '')}
92-
{!product.variations && salePrice}
93-
</p>
94-
<p className="text-2xl text-gray-500 line-through">
95-
{product.variations && filteredVariantPrice(price, 'right')}
96-
{!product.variations && regularPrice}
97-
</p>
98-
</div>
99119
)}
100-
{/* Display regular price when not on sale */}
101-
{!onSale && (
102-
<p className="text-3xl font-bold mb-4">{price}</p>
103-
)}
104-
<p className="text-lg mb-4">{DESCRIPTION_WITHOUT_HTML}</p>
120+
</div>
121+
<div className="flex flex-col space-y-4">
122+
<h1 className="text-3xl font-bold text-center md:text-left">{name}</h1>
123+
<div className="text-center md:text-left">
124+
{onSale ? (
125+
<div className="flex flex-col md:flex-row items-center md:items-start space-y-2 md:space-y-0 md:space-x-4">
126+
<p className="text-3xl font-bold text-red-600">
127+
{product.variations ? filteredVariantPrice(price, '') : salePrice}
128+
</p>
129+
<p className="text-xl text-gray-500 line-through">
130+
{product.variations ? filteredVariantPrice(price, 'right') : regularPrice}
131+
</p>
132+
</div>
133+
) : (
134+
<p className="text-2xl font-bold">{price}</p>
135+
)}
136+
</div>
137+
<p className="text-gray-600 text-center md:text-left">{DESCRIPTION_WITHOUT_HTML}</p>
105138
{Boolean(product.stockQuantity) && (
106-
<p className="text-lg mb-4">
139+
<p className="text-sm font-semibold text-center md:text-left">
107140
{product.stockQuantity} på lager
108141
</p>
109142
)}
110143
{product.variations && (
111-
<div className="mb-4">
112-
<label htmlFor="variant" className="block text-lg mb-2">Varianter</label>
144+
<div className="w-full">
145+
<label htmlFor="variant" className="block text-sm font-medium text-gray-700 mb-2">
146+
Varianter
147+
</label>
113148
<select
114149
id="variant"
115150
name="variant"
116-
className="w-full px-4 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
117-
onChange={(e) => {
118-
setSelectedVariation(Number(e.target.value));
119-
}}
151+
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-blue-500 focus:border-blue-500"
152+
onChange={(e) => setSelectedVariation(Number(e.target.value))}
120153
>
121-
{product.variations.nodes.map(
122-
({ id, name, databaseId, stockQuantity }) => {
123-
// Remove product name from variation name
124-
const filteredName = name.split('- ').pop();
125-
return (
126-
<option key={id} value={databaseId}>
127-
{filteredName} - ({stockQuantity} på lager)
128-
</option>
129-
);
130-
},
131-
)}
154+
{product.variations.nodes.map(({ id, name, databaseId, stockQuantity }) => {
155+
const filteredName = name.split('- ').pop();
156+
return (
157+
<option key={id} value={databaseId}>
158+
{filteredName} - ({stockQuantity} på lager)
159+
</option>
160+
);
161+
})}
132162
</select>
133163
</div>
134164
)}
135165
<div className="flex justify-center md:justify-start">
136-
{product.variations && (
137-
<AddToCart
138-
product={product}
139-
variationId={selectedVariation}
140-
/>
166+
{product.variations ? (
167+
<AddToCart product={product} variationId={selectedVariation} />
168+
) : (
169+
<AddToCart product={product} />
141170
)}
142-
{!product.variations && <AddToCart product={product} />}
143171
</div>
144172
</div>
145173
</div>

0 commit comments

Comments
 (0)