@akinon/projectzero 1.106.0-rc.85 → 1.106.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -230
- package/app-template/.env.example +0 -1
- package/app-template/CHANGELOG.md +329 -4945
- package/app-template/README.md +1 -25
- package/app-template/package.json +20 -20
- package/app-template/public/locales/en/common.json +1 -42
- package/app-template/public/locales/tr/common.json +1 -42
- package/app-template/src/app/[commerce]/[locale]/[currency]/basket/page.tsx +82 -9
- package/app-template/src/app/[commerce]/[locale]/[currency]/landing-page/[pk]/page.tsx +1 -12
- package/app-template/src/assets/fonts/pz-icon.css +0 -3
- package/app-template/src/components/input.tsx +1 -2
- package/app-template/src/hooks/index.ts +0 -2
- package/app-template/src/plugins.js +1 -2
- package/app-template/src/settings.js +1 -6
- package/app-template/src/views/basket/basket-item.tsx +13 -16
- package/app-template/src/views/basket/summary.tsx +7 -10
- package/app-template/src/views/guest-login/index.tsx +1 -6
- package/app-template/src/views/header/search/index.tsx +5 -17
- package/app-template/src/views/product/product-info.tsx +263 -61
- package/app-template/src/views/product/slider.tsx +73 -86
- package/commands/plugins.ts +2 -29
- package/dist/commands/plugins.js +2 -23
- package/package.json +1 -1
- package/app-template/src/app/[commerce]/[locale]/[currency]/product/[pk]/loading.tsx +0 -67
- package/app-template/src/app/api/image-proxy/route.ts +0 -1
- package/app-template/src/app/api/similar-product-list/route.ts +0 -1
- package/app-template/src/app/api/similar-products/route.ts +0 -1
- package/app-template/src/hooks/use-product-cart.ts +0 -77
- package/app-template/src/hooks/use-stock-alert.ts +0 -74
- package/app-template/src/utils/variant-validation.ts +0 -41
- package/app-template/src/views/basket/basket-content.tsx +0 -106
- package/app-template/src/views/product/product-actions.tsx +0 -165
- package/app-template/src/views/product/product-share.tsx +0 -56
- package/app-template/src/views/product/product-variants.tsx +0 -26
|
@@ -1,73 +1,178 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import clsx from 'clsx';
|
|
4
|
+
import { Button, Icon, Modal } from '@theme/components';
|
|
5
|
+
import { useAddProductToBasket } from '../../hooks';
|
|
4
6
|
import React, { useEffect, useState } from 'react';
|
|
5
|
-
import {
|
|
7
|
+
import { useAddStockAlertMutation } from '@akinon/next/data/client/wishlist';
|
|
8
|
+
import { pushAddToCart, pushProductViewed } from '@theme/utils/gtm';
|
|
9
|
+
import { PriceWrapper, Variant } from '@theme/views/product';
|
|
10
|
+
import Share from '@theme/views/share';
|
|
6
11
|
import { ProductPageProps } from './layout';
|
|
7
12
|
import MiscButtons from './misc-buttons';
|
|
8
|
-
import {
|
|
13
|
+
import { useLocalization } from '@akinon/next/hooks';
|
|
14
|
+
import PluginModule, { Component } from '@akinon/next/components/plugin-module';
|
|
15
|
+
import { Trans } from '@akinon/next/components/trans';
|
|
9
16
|
import { useSession } from 'next-auth/react';
|
|
10
|
-
import { isVariantSelectionComplete } from '../../utils/variant-validation';
|
|
11
|
-
import { useProductCart } from '../../hooks/use-product-cart';
|
|
12
|
-
import { useStockAlert } from '../../hooks/use-stock-alert';
|
|
13
|
-
import { ProductVariants } from './product-variants';
|
|
14
|
-
import { ProductActions } from './product-actions';
|
|
15
|
-
import { ProductShare } from './product-share';
|
|
16
17
|
|
|
17
18
|
export default function ProductInfo({ data }: ProductPageProps) {
|
|
19
|
+
const { t } = useLocalization();
|
|
18
20
|
const { data: session } = useSession();
|
|
21
|
+
const [currentUrl, setCurrentUrl] = useState(null);
|
|
22
|
+
const [productError, setProductError] = useState(null);
|
|
23
|
+
const [isModalOpen, setIsModalOpen] = useState(false);
|
|
24
|
+
const [stockAlertResponseMessage, setStockAlertResponseMessage] =
|
|
25
|
+
useState(null);
|
|
19
26
|
const [isVariantLoading, setIsVariantLoading] = useState(false);
|
|
20
27
|
|
|
28
|
+
const [addProduct, { isLoading: isAddToCartLoading }] =
|
|
29
|
+
useAddProductToBasket();
|
|
30
|
+
const [addStockAlert, { isLoading: isAddToStockAlertLoading }] =
|
|
31
|
+
useAddStockAlertMutation();
|
|
21
32
|
const inStock = data.selected_variant !== null || data.product.in_stock;
|
|
22
33
|
|
|
23
|
-
const {
|
|
24
|
-
addProductToCart,
|
|
25
|
-
productError: cartError,
|
|
26
|
-
clearProductError: clearCartError,
|
|
27
|
-
isAddToCartLoading
|
|
28
|
-
} = useProductCart({
|
|
29
|
-
product: data.product,
|
|
30
|
-
variants: data.variants
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
const {
|
|
34
|
-
addProductToStockAlertList,
|
|
35
|
-
isModalOpen,
|
|
36
|
-
stockAlertResponseMessage,
|
|
37
|
-
productError: stockError,
|
|
38
|
-
isAddToStockAlertLoading,
|
|
39
|
-
closeModal,
|
|
40
|
-
clearError: clearStockError
|
|
41
|
-
} = useStockAlert({
|
|
42
|
-
productPk: data.product.pk,
|
|
43
|
-
userEmail: session?.user?.email
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
const productError = cartError || stockError;
|
|
47
|
-
const clearProductError = () => {
|
|
48
|
-
clearCartError();
|
|
49
|
-
clearStockError();
|
|
50
|
-
};
|
|
51
|
-
|
|
52
34
|
useEffect(() => {
|
|
53
|
-
isVariantSelectionComplete(
|
|
35
|
+
isVariantSelectionComplete() && setIsVariantLoading(false);
|
|
36
|
+
|
|
54
37
|
!inStock && setIsVariantLoading(false);
|
|
55
|
-
}, [data
|
|
38
|
+
}, [data]); // eslint-disable-line react-hooks/exhaustive-deps
|
|
56
39
|
|
|
57
40
|
useEffect(() => {
|
|
58
41
|
if (isVariantLoading) {
|
|
59
|
-
|
|
42
|
+
setProductError(null);
|
|
60
43
|
}
|
|
61
44
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
62
45
|
}, [isVariantLoading]);
|
|
63
46
|
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
setCurrentUrl(window.location.href);
|
|
49
|
+
}, [currentUrl]);
|
|
50
|
+
|
|
64
51
|
useEffect(() => {
|
|
65
52
|
pushProductViewed(data?.product);
|
|
66
53
|
}, []); // eslint-disable-line react-hooks/exhaustive-deps
|
|
67
54
|
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
55
|
+
const addProductToCart = async () => {
|
|
56
|
+
if (!variantsSelectionCheck()) {
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
try {
|
|
61
|
+
await addProduct({
|
|
62
|
+
product: data.product.pk,
|
|
63
|
+
quantity: 1,
|
|
64
|
+
attributes: {}
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
pushAddToCart(data?.product);
|
|
68
|
+
} catch (error) {
|
|
69
|
+
setProductError(
|
|
70
|
+
error?.data?.non_field_errors ||
|
|
71
|
+
Object.keys(error?.data).map(
|
|
72
|
+
(key) => `${key}: ${error?.data[key].join(', ')}`
|
|
73
|
+
)
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const variantsSelectionCheck = () => {
|
|
79
|
+
const unselectedVariant = data.variants.find((variant) =>
|
|
80
|
+
variant.options.every((opt) => !opt.is_selected)
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
if (unselectedVariant) {
|
|
84
|
+
setProductError(() => (
|
|
85
|
+
<Trans
|
|
86
|
+
i18nKey="product.please_select_variant"
|
|
87
|
+
components={{
|
|
88
|
+
VariantName: <span>{unselectedVariant.attribute_name}</span>
|
|
89
|
+
}}
|
|
90
|
+
/>
|
|
91
|
+
));
|
|
92
|
+
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
return true;
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
const isVariantSelectionComplete = () => {
|
|
100
|
+
return data?.variants.every((variant) =>
|
|
101
|
+
variant?.options.some((opt) => opt.is_selected)
|
|
102
|
+
);
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
const addProductToStockAlertList = async () => {
|
|
106
|
+
try {
|
|
107
|
+
await addStockAlert({
|
|
108
|
+
productPk: data.product.pk,
|
|
109
|
+
email: session?.user?.email
|
|
110
|
+
})
|
|
111
|
+
.unwrap()
|
|
112
|
+
.then(handleSuccess)
|
|
113
|
+
.catch((err) => handleError(err));
|
|
114
|
+
} catch (error) {
|
|
115
|
+
setProductError(error?.data?.non_field_errors || null);
|
|
116
|
+
}
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
const handleModalClick = () => {
|
|
120
|
+
setIsModalOpen(false);
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
const handleSuccess = () => {
|
|
124
|
+
setStockAlertResponseMessage(() => (
|
|
125
|
+
<Trans
|
|
126
|
+
i18nKey="product.stock_alert.success_description"
|
|
127
|
+
components={{
|
|
128
|
+
Email: <span>{session?.user?.email}</span>
|
|
129
|
+
}}
|
|
130
|
+
/>
|
|
131
|
+
));
|
|
132
|
+
setIsModalOpen(true);
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
const handleError = (err) => {
|
|
136
|
+
if (err.status !== 401) {
|
|
137
|
+
setStockAlertResponseMessage(
|
|
138
|
+
t('product.stock_alert.error_description').toString()
|
|
139
|
+
);
|
|
140
|
+
setIsModalOpen(true);
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
|
|
144
|
+
const checkoutProviderProps = {
|
|
145
|
+
product: data.product,
|
|
146
|
+
clearBasket: true,
|
|
147
|
+
addBeforeClick: variantsSelectionCheck,
|
|
148
|
+
openMiniBasket: false,
|
|
149
|
+
className: clsx([
|
|
150
|
+
'py-2.5',
|
|
151
|
+
'bg-black',
|
|
152
|
+
'relative',
|
|
153
|
+
'hover:bg-black',
|
|
154
|
+
'before:content-[""]',
|
|
155
|
+
'before:w-6',
|
|
156
|
+
'before:h-6',
|
|
157
|
+
'before:bg-white',
|
|
158
|
+
'before:absolute',
|
|
159
|
+
'before:rounded-r-[18px]',
|
|
160
|
+
'before:left-0',
|
|
161
|
+
'after:content-[""]',
|
|
162
|
+
'after:absolute',
|
|
163
|
+
'after:w-3',
|
|
164
|
+
'after:h-3',
|
|
165
|
+
'after:bg-[#d02c2f]',
|
|
166
|
+
'after:rounded-xl',
|
|
167
|
+
'after:left-1'
|
|
168
|
+
]),
|
|
169
|
+
onError: (error) =>
|
|
170
|
+
setProductError(
|
|
171
|
+
error?.data?.non_field_errors ||
|
|
172
|
+
Object.keys(error?.data).map(
|
|
173
|
+
(key) => `${key}: ${error?.data[key].join(', ')}`
|
|
174
|
+
)
|
|
175
|
+
)
|
|
71
176
|
};
|
|
72
177
|
|
|
73
178
|
return (
|
|
@@ -83,26 +188,72 @@ export default function ProductInfo({ data }: ProductPageProps) {
|
|
|
83
188
|
retailPrice={data.product.retail_price}
|
|
84
189
|
/>
|
|
85
190
|
</div>
|
|
191
|
+
<div className="flex flex-col">
|
|
192
|
+
{data.variants.map((variant) => (
|
|
193
|
+
<Variant
|
|
194
|
+
key={variant.attribute_key}
|
|
195
|
+
{...variant}
|
|
196
|
+
className="items-center mt-8"
|
|
197
|
+
onChange={() => {
|
|
198
|
+
setProductError(null);
|
|
199
|
+
setIsVariantLoading(true);
|
|
200
|
+
}}
|
|
201
|
+
/>
|
|
202
|
+
))}
|
|
203
|
+
</div>
|
|
86
204
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
205
|
+
{productError && (
|
|
206
|
+
<div className="mt-4 text-xs text-center text-error">
|
|
207
|
+
{productError}
|
|
208
|
+
</div>
|
|
209
|
+
)}
|
|
210
|
+
|
|
211
|
+
<Button
|
|
212
|
+
disabled={
|
|
213
|
+
isAddToCartLoading || isAddToStockAlertLoading || isVariantLoading
|
|
214
|
+
}
|
|
215
|
+
className={clsx(
|
|
216
|
+
'fixed bottom-0 right-0 w-1/2 h-14 z-[20] flex items-center justify-center fill-primary-foreground',
|
|
217
|
+
'hover:fill-primary sm:relative sm:w-full sm:mt-3 sm:font-semibold sm:h-12'
|
|
218
|
+
)}
|
|
219
|
+
onClick={() => {
|
|
220
|
+
setProductError(null);
|
|
221
|
+
|
|
222
|
+
if (inStock) {
|
|
223
|
+
addProductToCart();
|
|
224
|
+
} else {
|
|
225
|
+
addProductToStockAlertList();
|
|
226
|
+
}
|
|
227
|
+
}}
|
|
228
|
+
data-testid="product-add-to-cart"
|
|
229
|
+
>
|
|
230
|
+
{isVariantLoading ? (
|
|
231
|
+
<Icon
|
|
232
|
+
name="spinner"
|
|
233
|
+
size={20}
|
|
234
|
+
className="animate-spin mr-4 fill-primary"
|
|
235
|
+
/>
|
|
236
|
+
) : inStock ? (
|
|
237
|
+
<span>{t('product.add_to_cart')}</span>
|
|
238
|
+
) : (
|
|
239
|
+
<>
|
|
240
|
+
<Icon name="bell" size={20} className="mr-4" />
|
|
241
|
+
<span>{t('product.add_stock_alert')}</span>
|
|
242
|
+
</>
|
|
243
|
+
)}
|
|
244
|
+
</Button>
|
|
245
|
+
|
|
246
|
+
<PluginModule
|
|
247
|
+
component={Component.AkifastCheckoutButton}
|
|
248
|
+
props={{
|
|
249
|
+
...checkoutProviderProps,
|
|
250
|
+
isPdp: true
|
|
251
|
+
}}
|
|
90
252
|
/>
|
|
91
253
|
|
|
92
|
-
<
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
inStock={inStock}
|
|
96
|
-
isVariantLoading={isVariantLoading}
|
|
97
|
-
onAddToCart={addProductToCart}
|
|
98
|
-
onAddToStockAlert={addProductToStockAlertList}
|
|
99
|
-
onClearError={clearProductError}
|
|
100
|
-
isAddToCartLoading={isAddToCartLoading}
|
|
101
|
-
isAddToStockAlertLoading={isAddToStockAlertLoading}
|
|
102
|
-
productError={productError}
|
|
103
|
-
isModalOpen={isModalOpen}
|
|
104
|
-
stockAlertResponseMessage={stockAlertResponseMessage}
|
|
105
|
-
onCloseModal={closeModal}
|
|
254
|
+
<PluginModule
|
|
255
|
+
component={Component.OneClickCheckoutButtons}
|
|
256
|
+
props={checkoutProviderProps}
|
|
106
257
|
/>
|
|
107
258
|
|
|
108
259
|
<MiscButtons
|
|
@@ -111,7 +262,58 @@ export default function ProductInfo({ data }: ProductPageProps) {
|
|
|
111
262
|
variants={data.variants}
|
|
112
263
|
/>
|
|
113
264
|
|
|
114
|
-
<
|
|
265
|
+
<Share
|
|
266
|
+
className="my-2 sm:mb-4"
|
|
267
|
+
buttonText={t('product.share')}
|
|
268
|
+
items={[
|
|
269
|
+
{
|
|
270
|
+
href: `https://www.facebook.com/sharer/sharer.php?u=${encodeURIComponent(
|
|
271
|
+
currentUrl
|
|
272
|
+
)}`,
|
|
273
|
+
iconName: 'facebook',
|
|
274
|
+
iconSize: 22
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
href: `https://twitter.com/intent/tweet?text=${encodeURIComponent(
|
|
278
|
+
currentUrl
|
|
279
|
+
)}`,
|
|
280
|
+
iconName: 'twitter',
|
|
281
|
+
iconSize: 22
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
href: `https://api.whatsapp.com/send?text=${
|
|
285
|
+
data.product.name
|
|
286
|
+
}%20${encodeURIComponent(currentUrl)}`,
|
|
287
|
+
iconName: 'whatsapp',
|
|
288
|
+
iconSize: 22
|
|
289
|
+
}
|
|
290
|
+
]}
|
|
291
|
+
/>
|
|
292
|
+
|
|
293
|
+
<Modal
|
|
294
|
+
portalId="stock-alert-modal"
|
|
295
|
+
open={isModalOpen}
|
|
296
|
+
setOpen={setIsModalOpen}
|
|
297
|
+
showCloseButton={false}
|
|
298
|
+
className="w-5/6 md:max-w-md"
|
|
299
|
+
>
|
|
300
|
+
<div className="flex flex-col items-center justify-center gap-4 px-6 py-9">
|
|
301
|
+
<Icon name="bell" size={48} />
|
|
302
|
+
<h2 className="text-xl font-semibold">
|
|
303
|
+
{t('product.stock_alert.title')}
|
|
304
|
+
</h2>
|
|
305
|
+
<div className="max-w-40 text-xs text-center leading-4">
|
|
306
|
+
<p>{stockAlertResponseMessage}</p>
|
|
307
|
+
</div>
|
|
308
|
+
<Button
|
|
309
|
+
onClick={handleModalClick}
|
|
310
|
+
appearance="outlined"
|
|
311
|
+
className="font-semibold px-10 h-12"
|
|
312
|
+
>
|
|
313
|
+
{t('product.stock_alert.close_button')}
|
|
314
|
+
</Button>
|
|
315
|
+
</div>
|
|
316
|
+
</Modal>
|
|
115
317
|
</>
|
|
116
318
|
);
|
|
117
319
|
}
|
|
@@ -7,7 +7,6 @@ import { Product } from '@akinon/next/types';
|
|
|
7
7
|
import { Image } from '@akinon/next/components/image';
|
|
8
8
|
import useFavButton from '../../hooks/use-fav-button';
|
|
9
9
|
import { twMerge } from 'tailwind-merge';
|
|
10
|
-
import PluginModule, { Component } from '@akinon/next/components/plugin-module';
|
|
11
10
|
|
|
12
11
|
type ProductSliderItem = {
|
|
13
12
|
product: Product;
|
|
@@ -36,102 +35,90 @@ export default function ProductInfoSlider({ product }: ProductSliderItem) {
|
|
|
36
35
|
carouselRef.current?.next();
|
|
37
36
|
};
|
|
38
37
|
|
|
39
|
-
const handleThumbnailClick = (index
|
|
38
|
+
const handleThumbnailClick = (index) => {
|
|
40
39
|
setActiveIndex(index);
|
|
41
40
|
carouselRef.current?.goToSlide(index);
|
|
42
41
|
};
|
|
43
42
|
|
|
44
43
|
return (
|
|
45
|
-
|
|
46
|
-
<div className="lg:
|
|
47
|
-
<div className="
|
|
48
|
-
<
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
disabled={activeIndex === 0}
|
|
56
|
-
>
|
|
57
|
-
<Icon name="chevron-up" size={15} className="fill-[#000000]" />
|
|
58
|
-
</button>
|
|
59
|
-
<div className="hidden flex-col items-center overflow-scroll w-[80px] max-h-[620px] lg:block">
|
|
60
|
-
{product?.productimage_set?.map((item, index) => (
|
|
61
|
-
<Image
|
|
62
|
-
key={index}
|
|
63
|
-
src={item.image}
|
|
64
|
-
alt={`Thumbnail ${index}`}
|
|
65
|
-
width={80}
|
|
66
|
-
height={128}
|
|
67
|
-
aspectRatio={80 / 128}
|
|
68
|
-
className={twMerge('cursor-pointer', [
|
|
69
|
-
activeIndex === index && 'border-2 border-primary'
|
|
70
|
-
])}
|
|
71
|
-
onClick={() => handleThumbnailClick(index)}
|
|
72
|
-
/>
|
|
73
|
-
))}
|
|
74
|
-
</div>
|
|
75
|
-
<button
|
|
76
|
-
onClick={goToNext}
|
|
77
|
-
className={twMerge(
|
|
78
|
-
'hidden justify-center p-2 mt-3 border border-gray-100 rounded-full cursor-pointer lg:block',
|
|
79
|
-
[
|
|
80
|
-
activeIndex === product.productimage_set.length - 1 &&
|
|
81
|
-
'cursor-not-allowed opacity-45'
|
|
82
|
-
]
|
|
83
|
-
)}
|
|
84
|
-
disabled={activeIndex === product.productimage_set.length - 1}
|
|
85
|
-
>
|
|
86
|
-
<Icon name="chevron-down" size={15} className="fill-[#000000]" />
|
|
87
|
-
</button>
|
|
88
|
-
</div>
|
|
89
|
-
</div>
|
|
90
|
-
|
|
91
|
-
<div className="relative lg:col-span-5">
|
|
92
|
-
<FavButton className="absolute right-8 top-6 z-[20] sm:hidden" />
|
|
93
|
-
|
|
94
|
-
<PluginModule
|
|
95
|
-
component={Component.ProductImageSearchFeature}
|
|
96
|
-
props={{
|
|
97
|
-
product,
|
|
98
|
-
activeIndex,
|
|
99
|
-
showResetButton: true
|
|
100
|
-
}}
|
|
101
|
-
/>
|
|
102
|
-
|
|
103
|
-
<CarouselCore
|
|
104
|
-
responsive={{
|
|
105
|
-
all: {
|
|
106
|
-
breakpoint: { max: 5000, min: 0 },
|
|
107
|
-
items: 1
|
|
108
|
-
}
|
|
109
|
-
}}
|
|
110
|
-
arrows={false}
|
|
111
|
-
swipeable={true}
|
|
112
|
-
ref={carouselRef}
|
|
113
|
-
afterChange={(previousSlide, { currentSlide }) => {
|
|
114
|
-
setActiveIndex(currentSlide);
|
|
115
|
-
}}
|
|
116
|
-
containerAspectRatio={{ mobile: 520 / 798, desktop: 484 / 726 }}
|
|
44
|
+
<div className="lg:grid lg:grid-cols-6">
|
|
45
|
+
<div className="lg:col-span-1">
|
|
46
|
+
<div className="flex flex-col items-center justify-center md:mr-[6px]">
|
|
47
|
+
<button
|
|
48
|
+
onClick={goToPrev}
|
|
49
|
+
className={twMerge(
|
|
50
|
+
'hidden justify-center p-2 mb-3 border border-gray-100 rounded-full cursor-pointer lg:block',
|
|
51
|
+
[activeIndex === 0 && 'cursor-not-allowed opacity-45']
|
|
52
|
+
)}
|
|
53
|
+
disabled={activeIndex === 0}
|
|
117
54
|
>
|
|
118
|
-
{
|
|
55
|
+
<Icon name="chevron-up" size={15} className="fill-[#000000]" />
|
|
56
|
+
</button>
|
|
57
|
+
<div className="hidden flex-col items-center overflow-scroll w-[80px] max-h-[620px] lg:block">
|
|
58
|
+
{product?.productimage_set?.map((item, index) => (
|
|
119
59
|
<Image
|
|
120
|
-
key={
|
|
60
|
+
key={index}
|
|
121
61
|
src={item.image}
|
|
122
|
-
alt={
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
fill
|
|
62
|
+
alt={`Thumbnail ${index}`}
|
|
63
|
+
width={80}
|
|
64
|
+
height={128}
|
|
65
|
+
className={twMerge('cursor-pointer', [
|
|
66
|
+
activeIndex === index && 'border-2 border-primary'
|
|
67
|
+
])}
|
|
68
|
+
onClick={() => handleThumbnailClick(index)}
|
|
130
69
|
/>
|
|
131
70
|
))}
|
|
132
|
-
</
|
|
71
|
+
</div>
|
|
72
|
+
<button
|
|
73
|
+
onClick={goToNext}
|
|
74
|
+
className={twMerge(
|
|
75
|
+
'hidden justify-center p-2 mt-3 border border-gray-100 rounded-full cursor-pointer lg:block',
|
|
76
|
+
[
|
|
77
|
+
activeIndex === product.productimage_set.length - 1 &&
|
|
78
|
+
'cursor-not-allowed opacity-45'
|
|
79
|
+
]
|
|
80
|
+
)}
|
|
81
|
+
disabled={activeIndex === product.productimage_set.length - 1}
|
|
82
|
+
>
|
|
83
|
+
<Icon name="chevron-down" size={15} className="fill-[#000000]" />
|
|
84
|
+
</button>
|
|
133
85
|
</div>
|
|
134
86
|
</div>
|
|
135
|
-
|
|
87
|
+
|
|
88
|
+
<div className="relative lg:col-span-5">
|
|
89
|
+
<FavButton className="absolute right-8 top-6 z-[20] sm:hidden" />
|
|
90
|
+
|
|
91
|
+
<CarouselCore
|
|
92
|
+
responsive={{
|
|
93
|
+
all: {
|
|
94
|
+
breakpoint: { max: 5000, min: 0 },
|
|
95
|
+
items: 1
|
|
96
|
+
}
|
|
97
|
+
}}
|
|
98
|
+
arrows={false}
|
|
99
|
+
swipeable={true}
|
|
100
|
+
ref={carouselRef}
|
|
101
|
+
afterChange={(previousSlide, { currentSlide }) => {
|
|
102
|
+
setActiveIndex(currentSlide);
|
|
103
|
+
}}
|
|
104
|
+
containerAspectRatio={{ mobile: 520 / 798, desktop: 484 / 726 }}
|
|
105
|
+
>
|
|
106
|
+
{product?.productimage_set?.map((item, i) => (
|
|
107
|
+
<Image
|
|
108
|
+
key={i}
|
|
109
|
+
src={item.image}
|
|
110
|
+
alt={product.name}
|
|
111
|
+
draggable={false}
|
|
112
|
+
aspectRatio={484 / 726}
|
|
113
|
+
sizes="(min-width: 425px) 512px,
|
|
114
|
+
(min-width: 601px) 576px,
|
|
115
|
+
(min-width: 768px) 336px,
|
|
116
|
+
(min-width: 1024px) 484px, 368px"
|
|
117
|
+
fill
|
|
118
|
+
/>
|
|
119
|
+
))}
|
|
120
|
+
</CarouselCore>
|
|
121
|
+
</div>
|
|
122
|
+
</div>
|
|
136
123
|
);
|
|
137
124
|
}
|
package/commands/plugins.ts
CHANGED
|
@@ -29,14 +29,7 @@ async function checkVersion(pkg: PackageJson) {
|
|
|
29
29
|
|
|
30
30
|
if (!semver.satisfies(pkg.dependencies['@akinon/next'], latestVersion)) {
|
|
31
31
|
console.warn(
|
|
32
|
-
`\x1b[
|
|
33
|
-
`\x1b[41m version ${pkg.dependencies['@akinon/next']}`,
|
|
34
|
-
`\x1b[43m Please upgrade it to the latest version (${latestVersion}) to ensure plugin compatibility.`,
|
|
35
|
-
'\x1b[0m\n'
|
|
36
|
-
);
|
|
37
|
-
} else {
|
|
38
|
-
console.log(
|
|
39
|
-
`\x1b[42m Info: The package "${packageName}" is currently in the current version (${latestVersion}).`,
|
|
32
|
+
`\x1b[43mWarning: The "${packageName}" package is currently at version ${pkg.dependencies['@akinon/next']}. Please upgrade it to the latest version (${latestVersion}) to ensure plugin compatibility.`,
|
|
40
33
|
'\x1b[0m\n'
|
|
41
34
|
);
|
|
42
35
|
}
|
|
@@ -61,27 +54,7 @@ export default async () => {
|
|
|
61
54
|
}
|
|
62
55
|
}
|
|
63
56
|
|
|
64
|
-
|
|
65
|
-
const packageJsonPaths = [
|
|
66
|
-
path.resolve(rootDir, './package.json'),
|
|
67
|
-
path.resolve(rootDir, './apps/projectzeronext/package.json')
|
|
68
|
-
];
|
|
69
|
-
|
|
70
|
-
for (const packageJsonPath of packageJsonPaths) {
|
|
71
|
-
try {
|
|
72
|
-
const pkg = require(packageJsonPath);
|
|
73
|
-
if (pkg.dependencies['@akinon/next']) {
|
|
74
|
-
return pkg;
|
|
75
|
-
}
|
|
76
|
-
} catch (error) {
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
throw new Error('Could not find package.json with @akinon/next dependency');
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
const pkg = findPackageJson();
|
|
57
|
+
const pkg: PackageJson = require(path.resolve(rootDir, './package.json'));
|
|
85
58
|
await checkVersion(pkg);
|
|
86
59
|
|
|
87
60
|
const pluginsFilePath = findPluginsFilePath();
|
package/dist/commands/plugins.js
CHANGED
|
@@ -50,10 +50,7 @@ function checkVersion(pkg) {
|
|
|
50
50
|
const pkgInfo = (yield response.json());
|
|
51
51
|
const latestVersion = pkgInfo['dist-tags'].latest;
|
|
52
52
|
if (!semver_1.default.satisfies(pkg.dependencies['@akinon/next'], latestVersion)) {
|
|
53
|
-
console.warn(`\x1b[
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
console.log(`\x1b[42m Info: The package "${packageName}" is currently in the current version (${latestVersion}).`, '\x1b[0m\n');
|
|
53
|
+
console.warn(`\x1b[43mWarning: The "${packageName}" package is currently at version ${pkg.dependencies['@akinon/next']}. Please upgrade it to the latest version (${latestVersion}) to ensure plugin compatibility.`, '\x1b[0m\n');
|
|
57
54
|
}
|
|
58
55
|
}
|
|
59
56
|
catch (error) {
|
|
@@ -75,25 +72,7 @@ exports.default = () => __awaiter(void 0, void 0, void 0, function* () {
|
|
|
75
72
|
throw new Error('plugins.js was not found in either of the expected locations.');
|
|
76
73
|
}
|
|
77
74
|
}
|
|
78
|
-
|
|
79
|
-
const packageJsonPaths = [
|
|
80
|
-
path_1.default.resolve(rootDir, './package.json'),
|
|
81
|
-
path_1.default.resolve(rootDir, './apps/projectzeronext/package.json')
|
|
82
|
-
];
|
|
83
|
-
for (const packageJsonPath of packageJsonPaths) {
|
|
84
|
-
try {
|
|
85
|
-
const pkg = require(packageJsonPath);
|
|
86
|
-
if (pkg.dependencies['@akinon/next']) {
|
|
87
|
-
return pkg;
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
catch (error) {
|
|
91
|
-
continue;
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
throw new Error('Could not find package.json with @akinon/next dependency');
|
|
95
|
-
}
|
|
96
|
-
const pkg = findPackageJson();
|
|
75
|
+
const pkg = require(path_1.default.resolve(rootDir, './package.json'));
|
|
97
76
|
yield checkVersion(pkg);
|
|
98
77
|
const pluginsFilePath = findPluginsFilePath();
|
|
99
78
|
let installedPlugins = [];
|