1
- /*eslint complexity: ["error", 20]*/
2
1
// Imports
3
2
import { useState , useEffect } from 'react' ;
4
3
@@ -43,109 +42,112 @@ const SingleProduct = ({ product }: IProductRootObject) => {
43
42
if ( process . browser ) {
44
43
DESCRIPTION_WITHOUT_HTML = new DOMParser ( ) . parseFromString (
45
44
description ,
46
- 'text/html' ,
45
+ 'text/html'
47
46
) . body . textContent ;
48
47
}
49
48
50
49
return (
51
50
< section className = "bg-white mb-[8rem] md:mb-12" >
52
- { /* Show loading spinner while loading, and hide content while loading */ }
53
51
{ isLoading ? (
54
52
< div className = "h-56 mt-20" >
55
53
< p className = "text-2xl font-bold text-center" > Laster produkt ...</ p >
56
54
< br />
57
55
< LoadingSpinner />
58
56
</ div >
59
57
) : (
60
- < div className = "container flex flex-wrap items-center pt-4 pb-12 mx-auto" >
61
- < div className = "grid grid-cols-1 gap-4 md:mt-16 lg:grid-cols-2 xl:grid-cols-2 md:grid-cols-2 sm:grid-cols-2" >
62
- { image && (
63
- < img
64
- id = "product-image"
65
- src = { image . sourceUrl }
66
- alt = { name }
67
- className = "h-auto p-8 transition duration-500 ease-in-out transform xl:p-2 md:p-2 lg:p-2 md:hover:grow md:hover:scale-105"
68
- />
69
- ) }
70
- { ! image && (
58
+ < div className = "container mx-auto px-4 py-8" >
59
+ < div className = "flex flex-col md:grid md:grid-cols-2 md:gap-8" >
60
+ { /* Image Container */ }
61
+ < div className = "mb-6 md:mb-0" >
71
62
< img
72
63
id = "product-image"
73
64
src = {
74
- process . env . NEXT_PUBLIC_PLACEHOLDER_LARGE_IMAGE_URL ??
65
+ image ?. sourceUrl ||
66
+ process . env . NEXT_PUBLIC_PLACEHOLDER_LARGE_IMAGE_URL ||
75
67
placeholderFallBack
76
68
}
77
69
alt = { name }
78
- className = "h-auto p-8 transition duration-500 ease-in-out transform xl:p-2 md:p-2 lg:p-2 md:hover:grow md:hover:shadow-lg md:hover:scale-105"
70
+ className = "w-full h-auto object-cover transition duration-500 ease-in-out transform md:hover:scale-105"
79
71
/>
80
- ) }
81
- < div className = "px-4 md:ml-8" >
72
+ </ div >
73
+
74
+ { /* Product Details Container */ }
75
+ < div className = "flex flex-col" >
82
76
< h1 className = "text-2xl font-bold text-center md:text-left mb-4" >
83
77
{ name }
84
78
</ h1 >
85
- { /* Display sale price when on sale */ }
86
- { onSale && (
87
- < div className = "flex flex-col md:flex-row items-center md:items-start mb-4" >
88
- < p className = "text-2xl font-bold text-gray-900" >
89
- { product . variations && filteredVariantPrice ( price , '' ) }
90
- { ! product . variations && salePrice }
91
- </ p >
92
- < p className = "text-xl text-gray-500 line-through md:ml-4" >
93
- { product . variations && filteredVariantPrice ( price , 'right' ) }
94
- { ! product . variations && regularPrice }
95
- </ p >
96
- </ div >
97
- ) }
98
- { /* Display regular price when not on sale */ }
99
- { ! onSale && < p className = "text-2xl font-bold mb-4" > { price } </ p > }
100
- < p className = "text-lg mb-4 text-center md:text-left" >
79
+
80
+ { /* Price Display */ }
81
+ < div className = "text-center md:text-left mb-6" >
82
+ { onSale ? (
83
+ < div className = "flex flex-col md:flex-row items-center md:items-start gap-2" >
84
+ < p className = "text-2xl font-bold text-gray-900" >
85
+ { product . variations
86
+ ? filteredVariantPrice ( price , '' )
87
+ : salePrice }
88
+ </ p >
89
+ < p className = "text-xl text-gray-500 line-through" >
90
+ { product . variations
91
+ ? filteredVariantPrice ( price , 'right' )
92
+ : regularPrice }
93
+ </ p >
94
+ </ div >
95
+ ) : (
96
+ < p className = "text-2xl font-bold" > { price } </ p >
97
+ ) }
98
+ </ div >
99
+
100
+ { /* Description */ }
101
+ < p className = "text-lg mb-6 text-center md:text-left" >
101
102
{ DESCRIPTION_WITHOUT_HTML }
102
103
</ p >
104
+
105
+ { /* Stock Status */ }
103
106
{ Boolean ( product . stockQuantity ) && (
104
- < div className = "mb-4 p-2 bg-green-100 border border-green-400 rounded-lg mx-auto md:mx-0 max-w-[14.375rem]" >
105
- < p className = "text-lg text-green-700 font-semibold text-center md:text-left" >
106
- { product . stockQuantity } på lager
107
- </ p >
107
+ < div className = "mb-6 mx-auto md:mx-0" >
108
+ < div className = "p-2 bg-green-100 border border-green-400 rounded-lg max-w-[14.375rem]" >
109
+ < p className = "text-lg text-green-700 font-semibold text-center md:text-left" >
110
+ { product . stockQuantity } på lager
111
+ </ p >
112
+ </ div >
108
113
</ div >
109
114
) }
115
+
116
+ { /* Variations Select */ }
110
117
{ product . variations && (
111
- < div className = "mb-4 " >
118
+ < div className = "mb-6 mx-auto md:mx-0 w-full max-w-[14.375rem] " >
112
119
< label
113
120
htmlFor = "variant"
114
- className = "block text-lg font-medium mb-2"
121
+ className = "block text-lg font-medium mb-2 text-center md:text-left "
115
122
>
116
123
Varianter
117
124
</ label >
118
125
< select
119
126
id = "variant"
120
127
name = "variant"
121
- className = "max-w-[14.375rem] block w-full px-4 py-2 bg-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
122
- onChange = { ( e ) => {
123
- setSelectedVariation ( Number ( e . target . value ) ) ;
124
- } }
128
+ className = "w-full px-4 py-2 bg-white border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
129
+ onChange = { ( e ) => setSelectedVariation ( Number ( e . target . value ) ) }
125
130
>
126
131
{ product . variations . nodes . map (
127
- ( { id, name, databaseId, stockQuantity } ) => {
128
- // Remove product name from variation name
129
- const filteredName = name . split ( '- ' ) . pop ( ) ;
130
- return (
131
- < option key = { id } value = { databaseId } >
132
- { filteredName } - ({ stockQuantity } på lager)
133
- </ option >
134
- ) ;
135
- } ,
132
+ ( { id, name, databaseId, stockQuantity } ) => (
133
+ < option key = { id } value = { databaseId } >
134
+ { name . split ( '- ' ) . pop ( ) } - ({ stockQuantity } på lager)
135
+ </ option >
136
+ )
136
137
) }
137
138
</ select >
138
139
</ div >
139
140
) }
140
- < div className = "w-full p-4 md:p-0" >
141
- { product . variations && (
141
+
142
+ { /* Add to Cart Button */ }
143
+ < div className = "w-full mx-auto md:mx-0 max-w-[14.375rem]" >
144
+ { product . variations ? (
142
145
< AddToCart
143
146
product = { product }
144
147
variationId = { selectedVariation }
145
148
fullWidth = { true }
146
149
/>
147
- ) }
148
- { ! product . variations && (
150
+ ) : (
149
151
< AddToCart product = { product } fullWidth = { true } />
150
152
) }
151
153
</ div >
@@ -158,3 +160,31 @@ const SingleProduct = ({ product }: IProductRootObject) => {
158
160
} ;
159
161
160
162
export default SingleProduct ;
163
+
164
+ // Types (if needed separately)
165
+ interface IProduct {
166
+ id : string ;
167
+ databaseId : number ;
168
+ name : string ;
169
+ description : string ;
170
+ price : string ;
171
+ regularPrice : string ;
172
+ salePrice : string ;
173
+ onSale : boolean ;
174
+ image : {
175
+ sourceUrl : string ;
176
+ } ;
177
+ stockQuantity : number ;
178
+ variations ?: {
179
+ nodes : Array < {
180
+ id : string ;
181
+ databaseId : number ;
182
+ name : string ;
183
+ stockQuantity : number ;
184
+ } > ;
185
+ } ;
186
+ }
187
+
188
+ interface IProductRootObject {
189
+ product : IProduct ;
190
+ }
0 commit comments