@akinon/pz-similar-products 1.92.0-snapshot-ZERO-3457-20250627111231 → 1.93.0-rc.46
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 +112 -2
- package/README.md +248 -3
- package/package.json +1 -1
- package/src/data/endpoints.ts +37 -9
- package/src/hooks/index.ts +1 -0
- package/src/hooks/use-similar-products.ts +175 -27
- package/src/hooks/use-text-search-feature.ts +41 -0
- package/src/types/index.ts +66 -0
- package/src/utils/index.ts +38 -3
- package/src/views/filters.tsx +713 -645
- package/src/views/header-image-search-feature.tsx +13 -1
- package/src/views/image-search-button.tsx +20 -18
- package/src/views/image-search.tsx +98 -86
- package/src/views/main.tsx +92 -7
- package/src/views/product-image-search-feature.tsx +17 -3
- package/src/views/results.tsx +7 -5
- package/src/views/search-button.tsx +12 -2
- package/src/views/search-modal.tsx +173 -10
package/src/views/filters.tsx
CHANGED
|
@@ -5,7 +5,8 @@ import {
|
|
|
5
5
|
Button,
|
|
6
6
|
Icon,
|
|
7
7
|
Accordion,
|
|
8
|
-
LoaderSpinner
|
|
8
|
+
LoaderSpinner,
|
|
9
|
+
Input
|
|
9
10
|
} from '@akinon/next/components';
|
|
10
11
|
import { useLocalization } from '@akinon/next/hooks';
|
|
11
12
|
import { FilterSidebarProps } from '../types';
|
|
@@ -55,6 +56,8 @@ export function SimilarProductsFilterSidebar({
|
|
|
55
56
|
isLoading,
|
|
56
57
|
handleFacetChange,
|
|
57
58
|
removeFacetFilter,
|
|
59
|
+
searchText,
|
|
60
|
+
setSearchText,
|
|
58
61
|
currentImageUrl,
|
|
59
62
|
isCropping,
|
|
60
63
|
imageRef,
|
|
@@ -166,743 +169,808 @@ export function SimilarProductsFilterSidebar({
|
|
|
166
169
|
className
|
|
167
170
|
);
|
|
168
171
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
maxHeight: 'calc(100vh - 120px)'
|
|
174
|
-
}}
|
|
175
|
-
>
|
|
176
|
-
{(() => {
|
|
177
|
-
if (
|
|
178
|
-
settings?.customRenderers?.render?.filterSidebar?.renderMobileHeader
|
|
179
|
-
) {
|
|
180
|
-
return settings.customRenderers.render.filterSidebar.renderMobileHeader(
|
|
181
|
-
{
|
|
182
|
-
title: t('common.product.filters'),
|
|
183
|
-
itemCount: searchResults?.pagination?.total_count || 0,
|
|
184
|
-
onClose: () => setIsFilterMenuOpen(false)
|
|
185
|
-
}
|
|
186
|
-
);
|
|
187
|
-
}
|
|
172
|
+
const modalCloseIconClassName = twMerge(
|
|
173
|
+
'',
|
|
174
|
+
settings?.customStyles?.modalCloseIcon
|
|
175
|
+
);
|
|
188
176
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
177
|
+
const filterRemoveIconClassName = twMerge(
|
|
178
|
+
'',
|
|
179
|
+
settings?.customStyles?.filterRemoveIcon
|
|
180
|
+
);
|
|
193
181
|
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
<div className={mobileHeaderClassName}>
|
|
197
|
-
<h3
|
|
198
|
-
className={twMerge(
|
|
199
|
-
'text-2xl font-bold',
|
|
200
|
-
settings?.customStyles?.filterSidebarMobileTitle
|
|
201
|
-
)}
|
|
202
|
-
>
|
|
203
|
-
{t('common.product.filters')}
|
|
204
|
-
</h3>
|
|
205
|
-
<Button
|
|
206
|
-
appearance="ghost"
|
|
207
|
-
size="sm"
|
|
208
|
-
onClick={() => setIsFilterMenuOpen(false)}
|
|
209
|
-
className={twMerge(
|
|
210
|
-
'hover:bg-gray-200 rounded-full p-0 w-6 h-6',
|
|
211
|
-
settings?.customStyles?.filterSidebarMobileCloseButton
|
|
212
|
-
)}
|
|
213
|
-
>
|
|
214
|
-
<Icon name="close" size={16} />
|
|
215
|
-
</Button>
|
|
216
|
-
</div>
|
|
217
|
-
<div
|
|
218
|
-
className={twMerge(
|
|
219
|
-
'flex justify-between items-center mb-6 md:hidden',
|
|
220
|
-
settings?.customStyles?.filterSidebarMobileCounter
|
|
221
|
-
)}
|
|
222
|
-
>
|
|
223
|
-
<span
|
|
224
|
-
className={twMerge(
|
|
225
|
-
'text-sm',
|
|
226
|
-
settings?.customStyles?.filterSidebarMobileCounterText
|
|
227
|
-
)}
|
|
228
|
-
>
|
|
229
|
-
{searchResults?.pagination?.total_count || 0} items
|
|
230
|
-
</span>
|
|
231
|
-
</div>
|
|
232
|
-
</>
|
|
233
|
-
);
|
|
234
|
-
})()}
|
|
235
|
-
|
|
236
|
-
{(() => {
|
|
237
|
-
if (
|
|
238
|
-
settings?.customRenderers?.render?.filterSidebar?.renderImageSection
|
|
239
|
-
) {
|
|
240
|
-
return settings.customRenderers.render.filterSidebar.renderImageSection(
|
|
241
|
-
{
|
|
242
|
-
currentImageUrl,
|
|
243
|
-
isCropping,
|
|
244
|
-
onToggleCrop: handleToggleCropMode,
|
|
245
|
-
onFileUpload: handleFileChangeWithCropReset,
|
|
246
|
-
onResetToOriginal: handleResetToOriginal,
|
|
247
|
-
fileError,
|
|
248
|
-
isLoading
|
|
249
|
-
}
|
|
250
|
-
);
|
|
251
|
-
}
|
|
252
|
-
|
|
253
|
-
const imageSectionClassName = twMerge(
|
|
254
|
-
'relative mb-2 md:mb-6 mt-4',
|
|
255
|
-
settings?.customStyles?.imageSection
|
|
256
|
-
);
|
|
257
|
-
|
|
258
|
-
const imageContainerClassName = twMerge(
|
|
259
|
-
'relative bg-white overflow-hidden flex items-center justify-center transition-all duration-300 ease-in-out',
|
|
260
|
-
isCropping ? 'border-2 border-dashed border-gray-300' : '',
|
|
261
|
-
settings?.customStyles?.imageContainer
|
|
262
|
-
);
|
|
263
|
-
|
|
264
|
-
const cropButtonClassName = twMerge(
|
|
265
|
-
`absolute z-10 bottom-3 left-3 p-2 rounded-full bg-white shadow-md w-10 h-10 ${
|
|
266
|
-
isCropping ? 'text-red-500' : 'text-gray-700'
|
|
267
|
-
} ${isLoading ? 'opacity-50 cursor-not-allowed' : ''}`,
|
|
268
|
-
settings?.customStyles?.cropButton
|
|
269
|
-
);
|
|
270
|
-
|
|
271
|
-
const renderCropButton = () => {
|
|
272
|
-
if (!settings?.enableCropping || settings.enableCropping === false)
|
|
273
|
-
return null;
|
|
182
|
+
const modalCloseIconName = settings?.iconNames?.modalClose || 'close';
|
|
183
|
+
const filterRemoveIconName = settings?.iconNames?.filterRemove || 'close';
|
|
274
184
|
|
|
185
|
+
return (
|
|
186
|
+
<>
|
|
187
|
+
{isFilterMenuOpen && (
|
|
188
|
+
<div
|
|
189
|
+
className={twMerge(
|
|
190
|
+
'fixed inset-0 bg-black bg-opacity-50 z-40 md:hidden',
|
|
191
|
+
settings?.customStyles?.filterSidebarMobileOverlay
|
|
192
|
+
)}
|
|
193
|
+
onClick={() => setIsFilterMenuOpen(false)}
|
|
194
|
+
/>
|
|
195
|
+
)}
|
|
196
|
+
|
|
197
|
+
<div
|
|
198
|
+
className={sidebarClassName}
|
|
199
|
+
style={{
|
|
200
|
+
maxHeight: 'calc(100vh - 120px)'
|
|
201
|
+
}}
|
|
202
|
+
>
|
|
203
|
+
{(() => {
|
|
275
204
|
if (
|
|
276
|
-
settings?.customRenderers?.render?.filterSidebar?.
|
|
205
|
+
settings?.customRenderers?.render?.filterSidebar?.renderMobileHeader
|
|
277
206
|
) {
|
|
278
|
-
return settings.customRenderers.render.filterSidebar.
|
|
207
|
+
return settings.customRenderers.render.filterSidebar.renderMobileHeader(
|
|
279
208
|
{
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
209
|
+
title: t('common.product.filters'),
|
|
210
|
+
itemCount: searchResults?.pagination?.total_count || 0,
|
|
211
|
+
onClose: () => setIsFilterMenuOpen(false)
|
|
283
212
|
}
|
|
284
213
|
);
|
|
285
214
|
}
|
|
286
215
|
|
|
216
|
+
const mobileHeaderClassName = twMerge(
|
|
217
|
+
'flex justify-between mb-6 md:hidden',
|
|
218
|
+
settings?.customStyles?.filterSidebarMobileHeader
|
|
219
|
+
);
|
|
220
|
+
|
|
287
221
|
return (
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
{isLoading ? (
|
|
296
|
-
<LoaderSpinner className="w-5 h-5" />
|
|
297
|
-
) : isCropping ? (
|
|
298
|
-
<svg
|
|
299
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
300
|
-
width="20"
|
|
301
|
-
height="20"
|
|
302
|
-
viewBox="0 0 24 24"
|
|
303
|
-
fill="none"
|
|
304
|
-
stroke="currentColor"
|
|
305
|
-
strokeWidth="2"
|
|
306
|
-
strokeLinecap="round"
|
|
307
|
-
strokeLinejoin="round"
|
|
222
|
+
<>
|
|
223
|
+
<div className={mobileHeaderClassName}>
|
|
224
|
+
<h3
|
|
225
|
+
className={twMerge(
|
|
226
|
+
'text-2xl font-bold',
|
|
227
|
+
settings?.customStyles?.filterSidebarMobileTitle
|
|
228
|
+
)}
|
|
308
229
|
>
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
stroke="currentColor"
|
|
320
|
-
strokeWidth="2"
|
|
321
|
-
strokeLinecap="round"
|
|
322
|
-
strokeLinejoin="round"
|
|
230
|
+
{t('common.product.filters')}
|
|
231
|
+
</h3>
|
|
232
|
+
<Button
|
|
233
|
+
appearance="ghost"
|
|
234
|
+
size="sm"
|
|
235
|
+
onClick={() => setIsFilterMenuOpen(false)}
|
|
236
|
+
className={twMerge(
|
|
237
|
+
'hover:bg-gray-200 rounded-full p-0 w-6 h-6',
|
|
238
|
+
settings?.customStyles?.filterSidebarMobileCloseButton
|
|
239
|
+
)}
|
|
323
240
|
>
|
|
324
|
-
<
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
241
|
+
<Icon
|
|
242
|
+
name={modalCloseIconName}
|
|
243
|
+
size={16}
|
|
244
|
+
className={modalCloseIconClassName}
|
|
245
|
+
/>
|
|
246
|
+
</Button>
|
|
247
|
+
</div>
|
|
248
|
+
<div
|
|
249
|
+
className={twMerge(
|
|
250
|
+
'flex justify-between items-center mb-6 md:hidden',
|
|
251
|
+
settings?.customStyles?.filterSidebarMobileCounter
|
|
252
|
+
)}
|
|
253
|
+
>
|
|
254
|
+
<span
|
|
255
|
+
className={twMerge(
|
|
256
|
+
'text-sm',
|
|
257
|
+
settings?.customStyles?.filterSidebarMobileCounterText
|
|
258
|
+
)}
|
|
259
|
+
>
|
|
260
|
+
{searchResults?.pagination?.total_count || 0} items
|
|
261
|
+
</span>
|
|
262
|
+
</div>
|
|
263
|
+
</>
|
|
329
264
|
);
|
|
330
|
-
}
|
|
265
|
+
})()}
|
|
331
266
|
|
|
332
|
-
|
|
333
|
-
if (!settings?.enableCropping || settings.enableCropping === false)
|
|
334
|
-
return null;
|
|
267
|
+
{(() => {
|
|
335
268
|
if (
|
|
336
|
-
|
|
337
|
-
!completedCrop ||
|
|
338
|
-
completedCrop.width <= 10 ||
|
|
339
|
-
completedCrop.height <= 10
|
|
340
|
-
)
|
|
341
|
-
return null;
|
|
342
|
-
|
|
343
|
-
if (
|
|
344
|
-
settings?.customRenderers?.render?.filterSidebar?.renderTickButton
|
|
269
|
+
settings?.customRenderers?.render?.filterSidebar?.renderImageSection
|
|
345
270
|
) {
|
|
346
|
-
return settings.customRenderers.render.filterSidebar.
|
|
271
|
+
return settings.customRenderers.render.filterSidebar.renderImageSection(
|
|
347
272
|
{
|
|
348
|
-
|
|
349
|
-
|
|
273
|
+
currentImageUrl,
|
|
274
|
+
isCropping,
|
|
275
|
+
onToggleCrop: handleToggleCropMode,
|
|
276
|
+
onFileUpload: handleFileChangeWithCropReset,
|
|
277
|
+
onResetToOriginal: handleResetToOriginal,
|
|
278
|
+
fileError,
|
|
279
|
+
isLoading
|
|
350
280
|
}
|
|
351
281
|
);
|
|
352
282
|
}
|
|
353
283
|
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
}`,
|
|
358
|
-
settings?.customStyles?.tickButton
|
|
284
|
+
const imageSectionClassName = twMerge(
|
|
285
|
+
'relative mb-2 md:mb-6 mt-4',
|
|
286
|
+
settings?.customStyles?.imageSection
|
|
359
287
|
);
|
|
360
288
|
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
onClick={() => {
|
|
366
|
-
if (!isLoading) {
|
|
367
|
-
handleSafeProcessCrop(completedCrop);
|
|
368
|
-
}
|
|
369
|
-
}}
|
|
370
|
-
disabled={isLoading}
|
|
371
|
-
className={tickButtonClassName}
|
|
372
|
-
>
|
|
373
|
-
{isLoading ? (
|
|
374
|
-
<LoaderSpinner className="w-5 h-5" />
|
|
375
|
-
) : (
|
|
376
|
-
<svg
|
|
377
|
-
xmlns="http://www.w3.org/2000/svg"
|
|
378
|
-
width="20"
|
|
379
|
-
height="20"
|
|
380
|
-
viewBox="0 0 24 24"
|
|
381
|
-
fill="none"
|
|
382
|
-
stroke="currentColor"
|
|
383
|
-
strokeWidth="2"
|
|
384
|
-
strokeLinecap="round"
|
|
385
|
-
strokeLinejoin="round"
|
|
386
|
-
>
|
|
387
|
-
<polyline points="20,6 9,17 4,12"></polyline>
|
|
388
|
-
</svg>
|
|
389
|
-
)}
|
|
390
|
-
</Button>
|
|
289
|
+
const imageContainerClassName = twMerge(
|
|
290
|
+
'relative bg-white overflow-hidden flex items-center justify-center transition-all duration-300 ease-in-out',
|
|
291
|
+
isCropping ? 'border-2 border-dashed border-gray-300' : '',
|
|
292
|
+
settings?.customStyles?.imageContainer
|
|
391
293
|
);
|
|
392
|
-
};
|
|
393
294
|
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
295
|
+
const cropButtonClassName = twMerge(
|
|
296
|
+
`absolute z-10 bottom-3 left-3 p-2 rounded-full bg-white shadow-md w-10 h-10 ${
|
|
297
|
+
isCropping ? 'text-red-500' : 'text-gray-700'
|
|
298
|
+
} ${isLoading ? 'opacity-50 cursor-not-allowed' : ''}`,
|
|
299
|
+
settings?.customStyles?.cropButton
|
|
300
|
+
);
|
|
399
301
|
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
302
|
+
const renderCropButton = () => {
|
|
303
|
+
if (!settings?.enableCropping || settings.enableCropping === false)
|
|
304
|
+
return null;
|
|
305
|
+
|
|
306
|
+
if (
|
|
307
|
+
settings?.customRenderers?.render?.filterSidebar?.renderCropButton
|
|
308
|
+
) {
|
|
309
|
+
return settings.customRenderers.render.filterSidebar.renderCropButton(
|
|
310
|
+
{
|
|
311
|
+
isCropping,
|
|
312
|
+
onClick: handleToggleCropMode,
|
|
313
|
+
disabled: isLoading
|
|
412
314
|
}
|
|
315
|
+
);
|
|
316
|
+
}
|
|
413
317
|
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
318
|
+
return (
|
|
319
|
+
<Button
|
|
320
|
+
appearance="ghost"
|
|
321
|
+
size="sm"
|
|
322
|
+
onClick={handleToggleCropMode}
|
|
323
|
+
disabled={isLoading}
|
|
324
|
+
className={cropButtonClassName}
|
|
325
|
+
>
|
|
326
|
+
{isLoading ? (
|
|
327
|
+
<LoaderSpinner className="w-5 h-5" />
|
|
328
|
+
) : isCropping ? (
|
|
329
|
+
<svg
|
|
330
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
331
|
+
width="20"
|
|
332
|
+
height="20"
|
|
333
|
+
viewBox="0 0 24 24"
|
|
334
|
+
fill="none"
|
|
335
|
+
stroke="currentColor"
|
|
336
|
+
strokeWidth="2"
|
|
337
|
+
strokeLinecap="round"
|
|
338
|
+
strokeLinejoin="round"
|
|
339
|
+
>
|
|
340
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
341
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
342
|
+
</svg>
|
|
343
|
+
) : (
|
|
344
|
+
<svg
|
|
345
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
346
|
+
width="20"
|
|
347
|
+
height="20"
|
|
348
|
+
viewBox="0 0 24 24"
|
|
349
|
+
fill="none"
|
|
350
|
+
stroke="currentColor"
|
|
351
|
+
strokeWidth="2"
|
|
352
|
+
strokeLinecap="round"
|
|
353
|
+
strokeLinejoin="round"
|
|
354
|
+
>
|
|
355
|
+
<path d="M6 2v14a2 2 0 0 0 2 2h14"></path>
|
|
356
|
+
<path d="M18 22V8a2 2 0 0 0-2-2H2"></path>
|
|
357
|
+
</svg>
|
|
358
|
+
)}
|
|
359
|
+
</Button>
|
|
360
|
+
);
|
|
361
|
+
};
|
|
418
362
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
}
|
|
430
|
-
}
|
|
431
|
-
}}
|
|
432
|
-
onComplete={handleCropComplete}
|
|
433
|
-
onDragStart={() => {}}
|
|
434
|
-
onDragEnd={() => {}}
|
|
435
|
-
ruleOfThirds={false}
|
|
436
|
-
aspect={settings?.cropAspectRatio}
|
|
437
|
-
className="slider-crop"
|
|
438
|
-
disabled={isLoading}
|
|
439
|
-
keepSelection={true}
|
|
440
|
-
>
|
|
441
|
-
<img
|
|
442
|
-
ref={imageRef}
|
|
443
|
-
src={currentImageUrl || ''}
|
|
444
|
-
alt={product?.name || 'Product image'}
|
|
445
|
-
className="max-w-full max-h-[200px] md:max-h-[280px]"
|
|
446
|
-
style={{ transform: `scale(1) rotate(0deg)` }}
|
|
447
|
-
/>
|
|
448
|
-
</ReactCrop>
|
|
449
|
-
) : (
|
|
450
|
-
<div className="relative w-full h-full flex items-center justify-center">
|
|
451
|
-
<img
|
|
452
|
-
ref={imageRef}
|
|
453
|
-
src={currentImageUrl || ''}
|
|
454
|
-
alt={product?.name || 'Product image'}
|
|
455
|
-
className="max-w-full max-h-[200px] md:max-h-[280px] object-contain"
|
|
456
|
-
/>
|
|
457
|
-
{!isCropping && completedCrop && (
|
|
458
|
-
<div className="hidden md:block absolute inset-0 bg-black bg-opacity-50 transition-opacity duration-300 ease-in-out">
|
|
459
|
-
<div
|
|
460
|
-
className="absolute transition-all duration-300 ease-in-out"
|
|
461
|
-
style={{
|
|
462
|
-
width: `${completedCrop.width}px`,
|
|
463
|
-
height: `${completedCrop.height}px`,
|
|
464
|
-
left: `${completedCrop.x}px`,
|
|
465
|
-
top: `${completedCrop.y}px`,
|
|
466
|
-
boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.5)',
|
|
467
|
-
border: '2px solid white'
|
|
468
|
-
}}
|
|
469
|
-
></div>
|
|
470
|
-
</div>
|
|
471
|
-
)}
|
|
472
|
-
</div>
|
|
473
|
-
)}
|
|
474
|
-
</div>
|
|
475
|
-
);
|
|
476
|
-
})()}
|
|
477
|
-
</div>
|
|
363
|
+
const renderTickButton = () => {
|
|
364
|
+
if (!settings?.enableCropping || settings.enableCropping === false)
|
|
365
|
+
return null;
|
|
366
|
+
if (
|
|
367
|
+
!isCropping ||
|
|
368
|
+
!completedCrop ||
|
|
369
|
+
completedCrop.width <= 10 ||
|
|
370
|
+
completedCrop.height <= 10
|
|
371
|
+
)
|
|
372
|
+
return null;
|
|
478
373
|
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
type="file"
|
|
490
|
-
accept="image/*"
|
|
491
|
-
ref={fileInputRef}
|
|
492
|
-
onChange={handleFileChangeWithCropReset}
|
|
493
|
-
className="hidden"
|
|
494
|
-
/>
|
|
495
|
-
{(() => {
|
|
496
|
-
if (
|
|
497
|
-
settings?.customRenderers?.render?.filterSidebar
|
|
498
|
-
?.renderUploadButton
|
|
499
|
-
) {
|
|
500
|
-
return settings.customRenderers.render.filterSidebar.renderUploadButton(
|
|
501
|
-
{
|
|
502
|
-
onClick: handleNewImageClick,
|
|
503
|
-
disabled: isLoading
|
|
504
|
-
}
|
|
505
|
-
);
|
|
506
|
-
}
|
|
374
|
+
if (
|
|
375
|
+
settings?.customRenderers?.render?.filterSidebar?.renderTickButton
|
|
376
|
+
) {
|
|
377
|
+
return settings.customRenderers.render.filterSidebar.renderTickButton(
|
|
378
|
+
{
|
|
379
|
+
onClick: () => handleSafeProcessCrop(completedCrop),
|
|
380
|
+
disabled: isLoading
|
|
381
|
+
}
|
|
382
|
+
);
|
|
383
|
+
}
|
|
507
384
|
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
385
|
+
const tickButtonClassName = twMerge(
|
|
386
|
+
`absolute z-10 bottom-3 right-3 p-2 rounded-full bg-white shadow-md w-10 h-10 text-green-600 ${
|
|
387
|
+
isLoading ? 'opacity-50 cursor-not-allowed' : ''
|
|
388
|
+
}`,
|
|
389
|
+
settings?.customStyles?.tickButton
|
|
390
|
+
);
|
|
514
391
|
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
392
|
+
return (
|
|
393
|
+
<Button
|
|
394
|
+
appearance="ghost"
|
|
395
|
+
size="sm"
|
|
396
|
+
onClick={() => {
|
|
397
|
+
if (!isLoading) {
|
|
398
|
+
handleSafeProcessCrop(completedCrop);
|
|
399
|
+
}
|
|
400
|
+
}}
|
|
401
|
+
disabled={isLoading}
|
|
402
|
+
className={tickButtonClassName}
|
|
403
|
+
>
|
|
404
|
+
{isLoading ? (
|
|
405
|
+
<LoaderSpinner className="w-5 h-5" />
|
|
406
|
+
) : (
|
|
407
|
+
<svg
|
|
408
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
409
|
+
width="20"
|
|
410
|
+
height="20"
|
|
411
|
+
viewBox="0 0 24 24"
|
|
412
|
+
fill="none"
|
|
413
|
+
stroke="currentColor"
|
|
414
|
+
strokeWidth="2"
|
|
415
|
+
strokeLinecap="round"
|
|
416
|
+
strokeLinejoin="round"
|
|
417
|
+
>
|
|
418
|
+
<polyline points="20,6 9,17 4,12"></polyline>
|
|
419
|
+
</svg>
|
|
543
420
|
)}
|
|
421
|
+
</Button>
|
|
422
|
+
);
|
|
423
|
+
};
|
|
424
|
+
|
|
425
|
+
return (
|
|
426
|
+
<div className={imageSectionClassName}>
|
|
427
|
+
<div className={imageContainerClassName}>
|
|
428
|
+
{renderCropButton()}
|
|
429
|
+
{renderTickButton()}
|
|
544
430
|
|
|
545
431
|
{(() => {
|
|
546
432
|
if (
|
|
547
433
|
settings?.customRenderers?.render?.filterSidebar
|
|
548
|
-
?.
|
|
434
|
+
?.renderImageContainer
|
|
549
435
|
) {
|
|
550
|
-
return settings.customRenderers.render.filterSidebar.
|
|
436
|
+
return settings.customRenderers.render.filterSidebar.renderImageContainer(
|
|
551
437
|
{
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
showResetButton && (hasUploadedImage || completedCrop)
|
|
438
|
+
imageUrl: currentImageUrl || '',
|
|
439
|
+
productName: product?.name || 'Product image',
|
|
440
|
+
isCropping
|
|
556
441
|
}
|
|
557
442
|
);
|
|
558
443
|
}
|
|
559
444
|
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
const resetButtonClassName = twMerge(
|
|
564
|
-
`flex items-center gap-2 text-xs md:text-sm justify-center bg-blue-100 hover:bg-blue-200 border-blue-200 text-blue-600 ${
|
|
565
|
-
isLoading ? 'opacity-50 cursor-not-allowed' : ''
|
|
566
|
-
}`,
|
|
567
|
-
settings?.customStyles?.resetButton
|
|
445
|
+
const imageWrapperClassName = twMerge(
|
|
446
|
+
'w-full h-full flex items-center justify-center',
|
|
447
|
+
settings?.customStyles?.imageWrapper
|
|
568
448
|
);
|
|
569
449
|
|
|
570
450
|
return (
|
|
571
|
-
<
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
451
|
+
<div className={imageWrapperClassName}>
|
|
452
|
+
{isCropping ? (
|
|
453
|
+
<ReactCrop
|
|
454
|
+
crop={crop}
|
|
455
|
+
onChange={(newCrop) => {
|
|
456
|
+
if (!isLoading) {
|
|
457
|
+
setCrop(newCrop);
|
|
458
|
+
if (newCrop?.width > 10 && newCrop?.height > 10) {
|
|
459
|
+
setCompletedCrop(newCrop);
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}}
|
|
463
|
+
onComplete={handleCropComplete}
|
|
464
|
+
onDragStart={() => {}}
|
|
465
|
+
onDragEnd={() => {}}
|
|
466
|
+
ruleOfThirds={false}
|
|
467
|
+
aspect={settings?.cropAspectRatio}
|
|
468
|
+
className={twMerge(
|
|
469
|
+
'slider-crop',
|
|
470
|
+
settings?.customStyles?.cropComponent
|
|
471
|
+
)}
|
|
472
|
+
disabled={isLoading}
|
|
473
|
+
keepSelection={true}
|
|
474
|
+
>
|
|
475
|
+
<img
|
|
476
|
+
ref={imageRef}
|
|
477
|
+
src={currentImageUrl || ''}
|
|
478
|
+
alt={product?.name || 'Product image'}
|
|
479
|
+
className={twMerge(
|
|
480
|
+
'max-w-full max-h-[200px] md:max-h-[280px]',
|
|
481
|
+
settings?.customStyles?.cropImage,
|
|
482
|
+
settings?.customStyles?.cropImageActive
|
|
483
|
+
)}
|
|
484
|
+
style={{ transform: `scale(1) rotate(0deg)` }}
|
|
485
|
+
/>
|
|
486
|
+
</ReactCrop>
|
|
487
|
+
) : (
|
|
488
|
+
<div
|
|
489
|
+
className={twMerge(
|
|
490
|
+
'relative w-full h-full flex items-center justify-center',
|
|
491
|
+
settings?.customStyles?.cropImageContainer
|
|
492
|
+
)}
|
|
493
|
+
>
|
|
494
|
+
<img
|
|
495
|
+
ref={imageRef}
|
|
496
|
+
src={currentImageUrl || ''}
|
|
497
|
+
alt={product?.name || 'Product image'}
|
|
498
|
+
className={twMerge(
|
|
499
|
+
'max-w-full max-h-[200px] md:max-h-[280px] object-contain',
|
|
500
|
+
settings?.customStyles?.cropImage,
|
|
501
|
+
settings?.customStyles?.cropImageNonCropping
|
|
502
|
+
)}
|
|
503
|
+
/>
|
|
504
|
+
{!isCropping && completedCrop && (
|
|
505
|
+
<div
|
|
506
|
+
className={twMerge(
|
|
507
|
+
'hidden md:block absolute inset-0 bg-black bg-opacity-50 transition-opacity duration-300 ease-in-out',
|
|
508
|
+
settings?.customStyles?.cropOverlay,
|
|
509
|
+
settings?.customStyles?.cropOverlayBackground
|
|
510
|
+
)}
|
|
511
|
+
>
|
|
512
|
+
<div
|
|
513
|
+
className={twMerge(
|
|
514
|
+
'absolute transition-all duration-300 ease-in-out',
|
|
515
|
+
settings?.customStyles?.cropSelection,
|
|
516
|
+
settings?.customStyles?.cropSelectionHighlight
|
|
517
|
+
)}
|
|
518
|
+
style={{
|
|
519
|
+
width: `${completedCrop.width}px`,
|
|
520
|
+
height: `${completedCrop.height}px`,
|
|
521
|
+
left: `${completedCrop.x}px`,
|
|
522
|
+
top: `${completedCrop.y}px`,
|
|
523
|
+
boxShadow: '0 0 0 9999px rgba(0, 0, 0, 0.5)',
|
|
524
|
+
border: '2px solid white'
|
|
525
|
+
}}
|
|
526
|
+
></div>
|
|
527
|
+
</div>
|
|
528
|
+
)}
|
|
529
|
+
</div>
|
|
530
|
+
)}
|
|
531
|
+
</div>
|
|
597
532
|
);
|
|
598
533
|
})()}
|
|
599
534
|
</div>
|
|
600
|
-
)}
|
|
601
535
|
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
536
|
+
{!isCropping && (
|
|
537
|
+
<div
|
|
538
|
+
className={twMerge(
|
|
539
|
+
'flex flex-col md:flex-row justify-center mt-3 gap-2',
|
|
540
|
+
settings?.customStyles?.cropControls
|
|
541
|
+
)}
|
|
542
|
+
>
|
|
543
|
+
{settings?.enableFileUpload !== false && (
|
|
544
|
+
<>
|
|
545
|
+
<input
|
|
546
|
+
type="file"
|
|
547
|
+
accept="image/*"
|
|
548
|
+
ref={fileInputRef}
|
|
549
|
+
onChange={handleFileChangeWithCropReset}
|
|
550
|
+
className="hidden"
|
|
551
|
+
/>
|
|
552
|
+
{(() => {
|
|
553
|
+
if (
|
|
554
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
555
|
+
?.renderUploadButton
|
|
556
|
+
) {
|
|
557
|
+
return settings.customRenderers.render.filterSidebar.renderUploadButton(
|
|
558
|
+
{
|
|
559
|
+
onClick: handleNewImageClick,
|
|
560
|
+
disabled: isLoading
|
|
561
|
+
}
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
|
|
565
|
+
const uploadButtonClassName = twMerge(
|
|
566
|
+
`flex items-center gap-2 text-xs md:text-sm justify-center bg-gray-100 hover:bg-gray-200 border-gray-200 ${
|
|
567
|
+
isLoading ? 'opacity-50 cursor-not-allowed' : ''
|
|
568
|
+
}`,
|
|
569
|
+
settings?.customStyles?.uploadButton
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
return (
|
|
573
|
+
<Button
|
|
574
|
+
appearance="outlined"
|
|
575
|
+
size="sm"
|
|
576
|
+
onClick={handleNewImageClick}
|
|
577
|
+
disabled={isLoading}
|
|
578
|
+
className={uploadButtonClassName}
|
|
579
|
+
>
|
|
580
|
+
<svg
|
|
581
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
582
|
+
width="20"
|
|
583
|
+
height="20"
|
|
584
|
+
viewBox="0 0 24 24"
|
|
585
|
+
fill="none"
|
|
586
|
+
stroke="currentColor"
|
|
587
|
+
strokeWidth="2"
|
|
588
|
+
strokeLinecap="round"
|
|
589
|
+
strokeLinejoin="round"
|
|
590
|
+
className="text-gray-500"
|
|
591
|
+
>
|
|
592
|
+
<path d="M23 19a2 2 0 0 1-2 2H3a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h4l2-3h6l2 3h4a2 2 0 0 1 2 2z"></path>
|
|
593
|
+
<circle cx="12" cy="13" r="4"></circle>
|
|
594
|
+
</svg>
|
|
595
|
+
{t('common.product.new_image')}
|
|
596
|
+
</Button>
|
|
597
|
+
);
|
|
598
|
+
})()}
|
|
599
|
+
</>
|
|
600
|
+
)}
|
|
601
|
+
|
|
602
|
+
{(() => {
|
|
603
|
+
if (
|
|
604
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
605
|
+
?.renderResetButton
|
|
606
|
+
) {
|
|
607
|
+
return settings.customRenderers.render.filterSidebar.renderResetButton(
|
|
608
|
+
{
|
|
609
|
+
onClick: handleResetToOriginal,
|
|
610
|
+
disabled: isLoading,
|
|
611
|
+
showButton:
|
|
612
|
+
showResetButton &&
|
|
613
|
+
(hasUploadedImage || completedCrop)
|
|
614
|
+
}
|
|
615
|
+
);
|
|
612
616
|
}
|
|
613
|
-
);
|
|
614
|
-
}
|
|
615
617
|
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
618
|
+
if (
|
|
619
|
+
!showResetButton ||
|
|
620
|
+
(!hasUploadedImage && !completedCrop)
|
|
621
|
+
)
|
|
622
|
+
return null;
|
|
620
623
|
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
);
|
|
628
|
-
})()}
|
|
629
|
-
</div>
|
|
630
|
-
);
|
|
631
|
-
})()}
|
|
632
|
-
|
|
633
|
-
{/* Filters */}
|
|
634
|
-
<div className="space-y-2 md:space-y-4">
|
|
635
|
-
{searchResults?.facets
|
|
636
|
-
?.filter((facet) => facet.key !== 'category_ids')
|
|
637
|
-
?.map((facet) => {
|
|
638
|
-
if (
|
|
639
|
-
settings?.customRenderers?.render?.filterSidebar
|
|
640
|
-
?.renderFilterGroup
|
|
641
|
-
) {
|
|
642
|
-
return settings.customRenderers.render.filterSidebar.renderFilterGroup(
|
|
643
|
-
{
|
|
644
|
-
facet,
|
|
645
|
-
onFacetChange: handleFacetChange,
|
|
646
|
-
isLoading
|
|
647
|
-
}
|
|
648
|
-
);
|
|
649
|
-
}
|
|
624
|
+
const resetButtonClassName = twMerge(
|
|
625
|
+
`flex items-center gap-2 text-xs md:text-sm justify-center bg-blue-100 hover:bg-blue-200 border-blue-200 text-blue-600 ${
|
|
626
|
+
isLoading ? 'opacity-50 cursor-not-allowed' : ''
|
|
627
|
+
}`,
|
|
628
|
+
settings?.customStyles?.resetButton
|
|
629
|
+
);
|
|
650
630
|
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
631
|
+
return (
|
|
632
|
+
<Button
|
|
633
|
+
appearance="outlined"
|
|
634
|
+
size="sm"
|
|
635
|
+
onClick={handleResetToOriginal}
|
|
636
|
+
disabled={isLoading}
|
|
637
|
+
className={resetButtonClassName}
|
|
638
|
+
>
|
|
639
|
+
<svg
|
|
640
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
641
|
+
width="20"
|
|
642
|
+
height="20"
|
|
643
|
+
viewBox="0 0 24 24"
|
|
644
|
+
fill="none"
|
|
645
|
+
stroke="currentColor"
|
|
646
|
+
strokeWidth="2"
|
|
647
|
+
strokeLinecap="round"
|
|
648
|
+
strokeLinejoin="round"
|
|
649
|
+
className="text-blue-600"
|
|
650
|
+
>
|
|
651
|
+
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"></path>
|
|
652
|
+
<path d="M21 3v5h-5"></path>
|
|
653
|
+
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"></path>
|
|
654
|
+
<path d="M3 21v-5h5"></path>
|
|
655
|
+
</svg>
|
|
656
|
+
{t('common.product.reset_to_original')}
|
|
657
|
+
</Button>
|
|
658
|
+
);
|
|
659
|
+
})()}
|
|
660
|
+
</div>
|
|
661
|
+
)}
|
|
656
662
|
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
663
|
+
{fileError &&
|
|
664
|
+
!isCropping &&
|
|
665
|
+
(() => {
|
|
666
|
+
if (
|
|
667
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
668
|
+
?.renderErrorMessage
|
|
669
|
+
) {
|
|
670
|
+
return settings.customRenderers.render.filterSidebar.renderErrorMessage(
|
|
671
|
+
{
|
|
672
|
+
error: fileError
|
|
673
|
+
}
|
|
674
|
+
);
|
|
675
|
+
}
|
|
664
676
|
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
677
|
+
const errorClassName = twMerge(
|
|
678
|
+
'mt-2 px-3 py-2 bg-red-50 border border-red-100 rounded-md',
|
|
679
|
+
settings?.customStyles?.errorMessage
|
|
680
|
+
);
|
|
669
681
|
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
682
|
+
return (
|
|
683
|
+
<div className={errorClassName}>
|
|
684
|
+
<p className="text-xs text-red-600 font-medium">
|
|
685
|
+
{fileError}
|
|
686
|
+
</p>
|
|
687
|
+
</div>
|
|
688
|
+
);
|
|
689
|
+
})()}
|
|
690
|
+
</div>
|
|
691
|
+
);
|
|
692
|
+
})()}
|
|
677
693
|
|
|
678
|
-
|
|
694
|
+
{/* Filters */}
|
|
695
|
+
<div className="space-y-2 md:space-y-4">
|
|
696
|
+
{searchResults?.facets
|
|
697
|
+
?.filter((facet) => facet.key !== 'category_ids')
|
|
698
|
+
?.map((facet) => {
|
|
679
699
|
if (
|
|
680
700
|
settings?.customRenderers?.render?.filterSidebar
|
|
681
|
-
?.
|
|
701
|
+
?.renderFilterGroup
|
|
682
702
|
) {
|
|
683
|
-
return settings.customRenderers.render.filterSidebar.
|
|
703
|
+
return settings.customRenderers.render.filterSidebar.renderFilterGroup(
|
|
684
704
|
{
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
705
|
+
facet,
|
|
706
|
+
onFacetChange: handleFacetChange,
|
|
707
|
+
isLoading
|
|
688
708
|
}
|
|
689
709
|
);
|
|
690
710
|
}
|
|
691
|
-
return null;
|
|
692
|
-
};
|
|
693
711
|
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
isLoading,
|
|
705
|
-
isSelected: choice.is_selected
|
|
706
|
-
}
|
|
712
|
+
const Component = getComponentByWidgetType(
|
|
713
|
+
facet.widget_type,
|
|
714
|
+
facet.key
|
|
715
|
+
);
|
|
716
|
+
const choices = facet.data.choices || [];
|
|
717
|
+
|
|
718
|
+
if (!Component) {
|
|
719
|
+
console.warn(
|
|
720
|
+
'Component not found for widget type:',
|
|
721
|
+
facet.widget_type
|
|
707
722
|
);
|
|
723
|
+
return null;
|
|
708
724
|
}
|
|
709
725
|
|
|
710
|
-
const
|
|
711
|
-
'filter-
|
|
712
|
-
settings?.customStyles?.
|
|
726
|
+
const filterGroupClassName = twMerge(
|
|
727
|
+
'filter-group',
|
|
728
|
+
settings?.customStyles?.filterGroup
|
|
713
729
|
);
|
|
714
730
|
|
|
715
|
-
const
|
|
716
|
-
'
|
|
717
|
-
|
|
731
|
+
const filterGroupContentClassName = twMerge(
|
|
732
|
+
clsx('flex gap-4', {
|
|
733
|
+
'flex-wrap flex-row': facet.key === sizeKey,
|
|
734
|
+
'flex-col': facet.key !== sizeKey
|
|
735
|
+
}),
|
|
736
|
+
settings?.customStyles?.filterGroupContent
|
|
718
737
|
);
|
|
719
738
|
|
|
720
|
-
const
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
739
|
+
const renderFilterGroupTitle = () => {
|
|
740
|
+
if (
|
|
741
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
742
|
+
?.renderFilterGroupTitle
|
|
743
|
+
) {
|
|
744
|
+
return settings.customRenderers.render.filterSidebar.renderFilterGroupTitle(
|
|
745
|
+
{
|
|
746
|
+
title: facet.name,
|
|
747
|
+
isCollapsed: choices.some((choice) => choice.is_selected),
|
|
748
|
+
onToggle: () => {}
|
|
749
|
+
}
|
|
750
|
+
);
|
|
751
|
+
}
|
|
752
|
+
return null;
|
|
753
|
+
};
|
|
724
754
|
|
|
725
|
-
const
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
755
|
+
const renderFilterItem = (choice: any, index: number) => {
|
|
756
|
+
if (
|
|
757
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
758
|
+
?.renderFilterItem
|
|
759
|
+
) {
|
|
760
|
+
return settings.customRenderers.render.filterSidebar.renderFilterItem(
|
|
761
|
+
{
|
|
762
|
+
choice,
|
|
763
|
+
facetKey: facet.key,
|
|
764
|
+
onFacetChange: handleFacetChange,
|
|
765
|
+
isLoading,
|
|
766
|
+
isSelected: choice.is_selected
|
|
767
|
+
}
|
|
768
|
+
);
|
|
769
|
+
}
|
|
770
|
+
|
|
771
|
+
const filterItemClassName = twMerge(
|
|
772
|
+
'filter-item',
|
|
773
|
+
settings?.customStyles?.filterItem
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
const filterItemInputClassName = twMerge(
|
|
777
|
+
'filter-item-input',
|
|
778
|
+
settings?.customStyles?.filterItemInput
|
|
779
|
+
);
|
|
780
|
+
|
|
781
|
+
const filterItemLabelClassName = twMerge(
|
|
782
|
+
'filter-item-label',
|
|
783
|
+
settings?.customStyles?.filterItemLabel
|
|
784
|
+
);
|
|
785
|
+
|
|
786
|
+
const filterItemCountClassName = twMerge(
|
|
787
|
+
'filter-item-count',
|
|
788
|
+
settings?.customStyles?.filterItemCount
|
|
789
|
+
);
|
|
790
|
+
|
|
791
|
+
return (
|
|
792
|
+
<div key={choice.value} className={filterItemClassName}>
|
|
793
|
+
<Component
|
|
794
|
+
key={choice.label}
|
|
795
|
+
value={choice.value}
|
|
796
|
+
label={choice.label}
|
|
797
|
+
name={facet.key}
|
|
798
|
+
onChange={() => {
|
|
799
|
+
if (!isLoading) {
|
|
800
|
+
handleFacetChange(facet.key, choice.value);
|
|
801
|
+
}
|
|
802
|
+
}}
|
|
803
|
+
onClick={() => {
|
|
804
|
+
if (!isLoading && facet.key === sizeKey) {
|
|
805
|
+
handleFacetChange(facet.key, choice.value);
|
|
806
|
+
}
|
|
807
|
+
}}
|
|
808
|
+
checked={choice.is_selected}
|
|
809
|
+
data-testid={`${choice.label.trim()}`}
|
|
810
|
+
disabled={isLoading}
|
|
811
|
+
className={filterItemInputClassName}
|
|
812
|
+
>
|
|
813
|
+
<span className={filterItemLabelClassName}>
|
|
814
|
+
{choice.label}
|
|
815
|
+
</span>{' '}
|
|
816
|
+
(
|
|
817
|
+
<span
|
|
818
|
+
data-testid={`filter-count-${facet.name.toLowerCase()}-${index}`}
|
|
819
|
+
className={filterItemCountClassName}
|
|
820
|
+
>
|
|
821
|
+
{choice.quantity}
|
|
822
|
+
</span>
|
|
823
|
+
)
|
|
824
|
+
</Component>
|
|
825
|
+
</div>
|
|
826
|
+
);
|
|
827
|
+
};
|
|
729
828
|
|
|
730
829
|
return (
|
|
731
|
-
<div key={
|
|
732
|
-
<
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
name={facet.key}
|
|
737
|
-
onChange={() => {
|
|
738
|
-
if (!isLoading) {
|
|
739
|
-
handleFacetChange(facet.key, choice.value);
|
|
740
|
-
}
|
|
741
|
-
}}
|
|
742
|
-
onClick={() => {
|
|
743
|
-
if (!isLoading && facet.key === sizeKey) {
|
|
744
|
-
handleFacetChange(facet.key, choice.value);
|
|
745
|
-
}
|
|
746
|
-
}}
|
|
747
|
-
checked={choice.is_selected}
|
|
748
|
-
data-testid={`${choice.label.trim()}`}
|
|
749
|
-
disabled={isLoading}
|
|
750
|
-
className={filterItemInputClassName}
|
|
830
|
+
<div key={facet.key} className={filterGroupClassName}>
|
|
831
|
+
<Accordion
|
|
832
|
+
title={renderFilterGroupTitle() || facet.name}
|
|
833
|
+
isCollapse={choices.some((choice) => choice.is_selected)}
|
|
834
|
+
dataTestId={`filter-${facet.name}`}
|
|
751
835
|
>
|
|
752
|
-
<
|
|
753
|
-
{
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
{choice.quantity}
|
|
761
|
-
</span>
|
|
762
|
-
)
|
|
763
|
-
</Component>
|
|
836
|
+
<div className={filterGroupContentClassName}>
|
|
837
|
+
{choices
|
|
838
|
+
.slice(0, 10)
|
|
839
|
+
.map((choice, index) =>
|
|
840
|
+
renderFilterItem(choice, index)
|
|
841
|
+
)}
|
|
842
|
+
</div>
|
|
843
|
+
</Accordion>
|
|
764
844
|
</div>
|
|
765
845
|
);
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
<div className={filterGroupContentClassName}>
|
|
776
|
-
{choices
|
|
777
|
-
.slice(0, 10)
|
|
778
|
-
.map((choice, index) => renderFilterItem(choice, index))}
|
|
779
|
-
</div>
|
|
780
|
-
</Accordion>
|
|
781
|
-
</div>
|
|
846
|
+
})}
|
|
847
|
+
</div>
|
|
848
|
+
|
|
849
|
+
{/* Mobile Active Filters and Clear Button */}
|
|
850
|
+
{(() => {
|
|
851
|
+
const hasActiveFilters = searchResults?.facets
|
|
852
|
+
?.filter((facet) => facet.key !== 'category_ids')
|
|
853
|
+
?.some((facet) =>
|
|
854
|
+
facet.data.choices?.some((choice) => choice.is_selected)
|
|
782
855
|
);
|
|
783
|
-
})}
|
|
784
|
-
</div>
|
|
785
856
|
|
|
786
|
-
|
|
787
|
-
{(() => {
|
|
788
|
-
const hasActiveFilters = searchResults?.facets
|
|
789
|
-
?.filter((facet) => facet.key !== 'category_ids')
|
|
790
|
-
?.some((facet) =>
|
|
791
|
-
facet.data.choices?.some((choice) => choice.is_selected)
|
|
792
|
-
);
|
|
857
|
+
if (!hasActiveFilters) return null;
|
|
793
858
|
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
.flatMap(
|
|
799
|
-
(facet) =>
|
|
800
|
-
facet.data.choices
|
|
801
|
-
?.filter((choice) => choice.is_selected)
|
|
802
|
-
.map((choice) => ({
|
|
803
|
-
key: facet.key,
|
|
804
|
-
value: choice.value.toString(),
|
|
805
|
-
label: choice.label
|
|
806
|
-
})) || []
|
|
807
|
-
);
|
|
808
|
-
|
|
809
|
-
const handleClearAll = () => {
|
|
810
|
-
if (!isLoading) {
|
|
811
|
-
const facetsToRemove = [];
|
|
812
|
-
searchResults?.facets
|
|
813
|
-
?.filter((facet) => facet.key !== 'category_ids')
|
|
814
|
-
?.forEach((facet) => {
|
|
859
|
+
const activeFilters = searchResults.facets
|
|
860
|
+
.filter((facet) => facet.key !== 'category_ids')
|
|
861
|
+
.flatMap(
|
|
862
|
+
(facet) =>
|
|
815
863
|
facet.data.choices
|
|
816
864
|
?.filter((choice) => choice.is_selected)
|
|
817
|
-
.
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
865
|
+
.map((choice) => ({
|
|
866
|
+
key: facet.key,
|
|
867
|
+
value: choice.value.toString(),
|
|
868
|
+
label: choice.label
|
|
869
|
+
})) || []
|
|
870
|
+
);
|
|
871
|
+
|
|
872
|
+
const handleClearAll = () => {
|
|
873
|
+
if (!isLoading) {
|
|
874
|
+
const facetsToRemove = [];
|
|
875
|
+
searchResults?.facets
|
|
876
|
+
?.filter((facet) => facet.key !== 'category_ids')
|
|
877
|
+
?.forEach((facet) => {
|
|
878
|
+
facet.data.choices
|
|
879
|
+
?.filter((choice) => choice.is_selected)
|
|
880
|
+
.forEach((choice) => {
|
|
881
|
+
facetsToRemove.push({
|
|
882
|
+
facetKey: facet.key,
|
|
883
|
+
choiceValue: choice.value
|
|
884
|
+
});
|
|
821
885
|
});
|
|
822
|
-
|
|
886
|
+
});
|
|
887
|
+
|
|
888
|
+
facetsToRemove.forEach(({ facetKey, choiceValue }) => {
|
|
889
|
+
removeFacetFilter(facetKey, choiceValue);
|
|
823
890
|
});
|
|
891
|
+
}
|
|
892
|
+
};
|
|
824
893
|
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
894
|
+
if (
|
|
895
|
+
settings?.customRenderers?.render?.filterSidebar
|
|
896
|
+
?.renderMobileActiveFilters
|
|
897
|
+
) {
|
|
898
|
+
return (
|
|
899
|
+
<div className="md:hidden mt-6">
|
|
900
|
+
{settings.customRenderers.render.filterSidebar.renderMobileActiveFilters(
|
|
901
|
+
{
|
|
902
|
+
filters: activeFilters,
|
|
903
|
+
onRemove: handleFacetChange,
|
|
904
|
+
onClearAll: handleClearAll,
|
|
905
|
+
isLoading
|
|
906
|
+
}
|
|
907
|
+
)}
|
|
908
|
+
</div>
|
|
909
|
+
);
|
|
828
910
|
}
|
|
829
|
-
};
|
|
830
911
|
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
?.
|
|
834
|
-
) {
|
|
835
|
-
return (
|
|
836
|
-
<div className="md:hidden mt-6">
|
|
837
|
-
{settings.customRenderers.render.filterSidebar.renderMobileActiveFilters(
|
|
838
|
-
{
|
|
839
|
-
filters: activeFilters,
|
|
840
|
-
onRemove: handleFacetChange,
|
|
841
|
-
onClearAll: handleClearAll,
|
|
842
|
-
isLoading
|
|
843
|
-
}
|
|
844
|
-
)}
|
|
845
|
-
</div>
|
|
912
|
+
const mobileActiveFiltersClassName = twMerge(
|
|
913
|
+
'md:hidden mt-6',
|
|
914
|
+
settings?.customStyles?.mobileActiveFilters
|
|
846
915
|
);
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
</h4>
|
|
870
|
-
<div className="flex flex-wrap gap-2">
|
|
871
|
-
{activeFilters.map((filter) => (
|
|
872
|
-
<div
|
|
873
|
-
key={`${filter.key}-${filter.value}`}
|
|
874
|
-
className={mobileActiveFilterTagClassName}
|
|
875
|
-
>
|
|
876
|
-
<span>{filter.label}</span>
|
|
877
|
-
<Button
|
|
878
|
-
appearance="ghost"
|
|
879
|
-
size="sm"
|
|
880
|
-
onClick={() => {
|
|
881
|
-
if (!isLoading) {
|
|
882
|
-
handleFacetChange(filter.key, filter.value);
|
|
883
|
-
}
|
|
884
|
-
}}
|
|
885
|
-
disabled={isLoading}
|
|
886
|
-
className="hover:bg-gray-200 rounded-full p-0 w-5 h-5 disabled:opacity-50 disabled:cursor-not-allowed ml-1"
|
|
916
|
+
|
|
917
|
+
const mobileActiveFilterTagClassName = twMerge(
|
|
918
|
+
'flex items-center gap-1 px-2 py-1 bg-gray-100 rounded-full text-xs',
|
|
919
|
+
settings?.customStyles?.mobileActiveFilterTag
|
|
920
|
+
);
|
|
921
|
+
|
|
922
|
+
const mobileClearAllButtonClassName = twMerge(
|
|
923
|
+
'w-full',
|
|
924
|
+
settings?.customStyles?.mobileClearAllButton
|
|
925
|
+
);
|
|
926
|
+
|
|
927
|
+
return (
|
|
928
|
+
<div className={mobileActiveFiltersClassName}>
|
|
929
|
+
<div className="mb-4">
|
|
930
|
+
<h4 className="text-sm font-medium mb-2">
|
|
931
|
+
{t('common.product.active_filters')}
|
|
932
|
+
</h4>
|
|
933
|
+
<div className="flex flex-wrap gap-2">
|
|
934
|
+
{activeFilters.map((filter) => (
|
|
935
|
+
<div
|
|
936
|
+
key={`${filter.key}-${filter.value}`}
|
|
937
|
+
className={mobileActiveFilterTagClassName}
|
|
887
938
|
>
|
|
888
|
-
<
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
939
|
+
<span>{filter.label}</span>
|
|
940
|
+
<Button
|
|
941
|
+
appearance="ghost"
|
|
942
|
+
size="sm"
|
|
943
|
+
onClick={() => {
|
|
944
|
+
if (!isLoading) {
|
|
945
|
+
handleFacetChange(filter.key, filter.value);
|
|
946
|
+
}
|
|
947
|
+
}}
|
|
948
|
+
disabled={isLoading}
|
|
949
|
+
className="hover:bg-gray-200 rounded-full p-0 w-5 h-5 disabled:opacity-50 disabled:cursor-not-allowed ml-1"
|
|
950
|
+
>
|
|
951
|
+
<Icon
|
|
952
|
+
name={filterRemoveIconName}
|
|
953
|
+
size={10}
|
|
954
|
+
className={filterRemoveIconClassName}
|
|
955
|
+
/>
|
|
956
|
+
</Button>
|
|
957
|
+
</div>
|
|
958
|
+
))}
|
|
959
|
+
</div>
|
|
892
960
|
</div>
|
|
893
|
-
</div>
|
|
894
961
|
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
962
|
+
<Button
|
|
963
|
+
appearance="outlined"
|
|
964
|
+
className={mobileClearAllButtonClassName}
|
|
965
|
+
onClick={handleClearAll}
|
|
966
|
+
disabled={isLoading}
|
|
967
|
+
>
|
|
968
|
+
{t('common.product.clear_all_filters')}
|
|
969
|
+
</Button>
|
|
970
|
+
</div>
|
|
971
|
+
);
|
|
972
|
+
})()}
|
|
973
|
+
</div>
|
|
974
|
+
</>
|
|
907
975
|
);
|
|
908
976
|
}
|