@akinon/pz-similar-products 1.92.0-snapshot-ZERO-3457-20250627121541 → 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 +243 -1
- 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 +64 -0
- package/src/utils/index.ts +38 -3
- package/src/views/filters.tsx +59 -9
- package/src/views/header-image-search-feature.tsx +13 -1
- package/src/views/image-search-button.tsx +20 -18
- 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
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import React from 'react';
|
|
4
|
-
import { Modal, Button, Icon, Select } from '@akinon/next/components';
|
|
4
|
+
import { Modal, Button, Icon, Select, Input } from '@akinon/next/components';
|
|
5
5
|
import { useLocalization } from '@akinon/next/hooks';
|
|
6
6
|
import { SimilarProductsFilterSidebar } from './filters';
|
|
7
7
|
import { SimilarProductsResultsGrid } from './results';
|
|
@@ -43,7 +43,11 @@ export function SimilarProductsModal({
|
|
|
43
43
|
fileError,
|
|
44
44
|
showResetButton = true,
|
|
45
45
|
settings,
|
|
46
|
-
className
|
|
46
|
+
className,
|
|
47
|
+
searchText,
|
|
48
|
+
setSearchText,
|
|
49
|
+
handleTextSearch,
|
|
50
|
+
handleClearText
|
|
47
51
|
}: SimilarProductsModalProps) {
|
|
48
52
|
const { t } = useLocalization();
|
|
49
53
|
|
|
@@ -164,6 +168,24 @@ export function SimilarProductsModal({
|
|
|
164
168
|
})) || []
|
|
165
169
|
) || [];
|
|
166
170
|
|
|
171
|
+
const modalCloseIconName = settings?.iconNames?.modalClose || 'close';
|
|
172
|
+
const filterIconName = settings?.iconNames?.filter || 'filter';
|
|
173
|
+
const modalSearchIconName = settings?.iconNames?.modalSearch || 'search';
|
|
174
|
+
const modalSearchClearIconName =
|
|
175
|
+
settings?.iconNames?.modalSearchClear || 'close';
|
|
176
|
+
|
|
177
|
+
const modalCloseIconClassName = twMerge(
|
|
178
|
+
'',
|
|
179
|
+
settings?.customStyles?.modalCloseIcon
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const filterIconClassName = twMerge('', settings?.customStyles?.filterIcon);
|
|
183
|
+
|
|
184
|
+
const modalSearchIconClassName = twMerge(
|
|
185
|
+
'text-gray-500',
|
|
186
|
+
settings?.customStyles?.modalSearchIcon
|
|
187
|
+
);
|
|
188
|
+
|
|
167
189
|
const renderHeader = () => {
|
|
168
190
|
if (settings?.customRenderers?.render?.modal?.renderHeader) {
|
|
169
191
|
return settings.customRenderers.render.modal.renderHeader({
|
|
@@ -246,7 +268,11 @@ export function SimilarProductsModal({
|
|
|
246
268
|
disabled={isLoading}
|
|
247
269
|
className={buttonClassName}
|
|
248
270
|
>
|
|
249
|
-
<Icon
|
|
271
|
+
<Icon
|
|
272
|
+
name={modalCloseIconName}
|
|
273
|
+
size={12}
|
|
274
|
+
className={modalCloseIconClassName}
|
|
275
|
+
/>
|
|
250
276
|
</Button>
|
|
251
277
|
</div>
|
|
252
278
|
);
|
|
@@ -266,7 +292,9 @@ export function SimilarProductsModal({
|
|
|
266
292
|
'',
|
|
267
293
|
onSortChange: handleSortChange,
|
|
268
294
|
onFilterMenuToggle: () => setIsFilterMenuOpen(true),
|
|
269
|
-
isLoading
|
|
295
|
+
isLoading,
|
|
296
|
+
searchText,
|
|
297
|
+
setSearchText
|
|
270
298
|
});
|
|
271
299
|
}
|
|
272
300
|
|
|
@@ -281,15 +309,30 @@ export function SimilarProductsModal({
|
|
|
281
309
|
);
|
|
282
310
|
|
|
283
311
|
const filterToggleClassName = twMerge(
|
|
284
|
-
'md:hidden text-xs',
|
|
312
|
+
'md:hidden text-xs px-3 py-2',
|
|
285
313
|
settings?.customStyles?.filterToggleButton
|
|
286
314
|
);
|
|
287
315
|
|
|
288
316
|
const sortDropdownClassName = twMerge(
|
|
289
|
-
'h-10 px-4 text-
|
|
317
|
+
'h-10 px-3 md:px-4 text-sm md:text-xs bg-gray-200 hover:bg-gray-400 transition-colors duration-200 border-gray-300 focus:border-primary focus:ring-1 focus:ring-primary w-full md:w-40 min-w-[120px]',
|
|
290
318
|
settings?.customStyles?.sortDropdown
|
|
291
319
|
);
|
|
292
320
|
|
|
321
|
+
const modalSearchInputClassName = twMerge(
|
|
322
|
+
'h-10 px-3 md:px-4 text-sm border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500 focus:border-transparent w-48 md:w-60 min-w-[180px]',
|
|
323
|
+
settings?.customStyles?.modalSearchInput
|
|
324
|
+
);
|
|
325
|
+
|
|
326
|
+
const modalSearchContainerClassName = twMerge(
|
|
327
|
+
'relative flex items-center',
|
|
328
|
+
settings?.customStyles?.modalSearchContainer
|
|
329
|
+
);
|
|
330
|
+
|
|
331
|
+
const modalSearchButtonClassName = twMerge(
|
|
332
|
+
'absolute right-2 top-1/2 -translate-y-1/2 p-2 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
|
|
333
|
+
settings?.customStyles?.modalSearchButton
|
|
334
|
+
);
|
|
335
|
+
|
|
293
336
|
const renderItemCount = () => {
|
|
294
337
|
if (settings?.customRenderers?.render?.modal?.renderItemCount) {
|
|
295
338
|
return settings.customRenderers.render.modal.renderItemCount({
|
|
@@ -299,7 +342,9 @@ export function SimilarProductsModal({
|
|
|
299
342
|
}
|
|
300
343
|
return (
|
|
301
344
|
<div className={itemCountClassName}>
|
|
302
|
-
|
|
345
|
+
<span className="hidden md:inline">
|
|
346
|
+
{searchResults?.pagination?.total_count || 0} items
|
|
347
|
+
</span>
|
|
303
348
|
</div>
|
|
304
349
|
);
|
|
305
350
|
};
|
|
@@ -319,13 +364,21 @@ export function SimilarProductsModal({
|
|
|
319
364
|
onClick={() => setIsFilterMenuOpen(true)}
|
|
320
365
|
data-testid="similar-products-filter"
|
|
321
366
|
>
|
|
322
|
-
<Icon
|
|
367
|
+
<Icon
|
|
368
|
+
name={filterIconName}
|
|
369
|
+
size={14}
|
|
370
|
+
className={filterIconClassName}
|
|
371
|
+
/>
|
|
323
372
|
{t('common.product.filters')}
|
|
324
373
|
</Button>
|
|
325
374
|
);
|
|
326
375
|
};
|
|
327
376
|
|
|
328
377
|
const renderSortDropdown = () => {
|
|
378
|
+
if (!searchResults?.products?.length || !searchResults?.sorters?.length) {
|
|
379
|
+
return null;
|
|
380
|
+
}
|
|
381
|
+
|
|
329
382
|
if (settings?.customRenderers?.render?.modal?.renderSortDropdown) {
|
|
330
383
|
return settings.customRenderers.render.modal.renderSortDropdown({
|
|
331
384
|
sorters: searchResults?.sorters || [],
|
|
@@ -351,26 +404,134 @@ export function SimilarProductsModal({
|
|
|
351
404
|
);
|
|
352
405
|
};
|
|
353
406
|
|
|
407
|
+
const renderModalSearchInput = () => {
|
|
408
|
+
if (
|
|
409
|
+
!settings?.enableTextSearch ||
|
|
410
|
+
searchText === undefined ||
|
|
411
|
+
!setSearchText ||
|
|
412
|
+
!searchResults?.products?.length ||
|
|
413
|
+
!searchResults?.sorters?.length
|
|
414
|
+
) {
|
|
415
|
+
return null;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
if (settings?.customRenderers?.render?.modal?.renderModalSearchInput) {
|
|
419
|
+
return settings.customRenderers.render.modal.renderModalSearchInput({
|
|
420
|
+
searchText,
|
|
421
|
+
setSearchText,
|
|
422
|
+
isLoading,
|
|
423
|
+
placeholder: t('common.search.placeholder'),
|
|
424
|
+
onSearch: handleTextSearch
|
|
425
|
+
});
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return (
|
|
429
|
+
<div className={modalSearchContainerClassName}>
|
|
430
|
+
<Input
|
|
431
|
+
type="text"
|
|
432
|
+
value={searchText}
|
|
433
|
+
onChange={(e) => setSearchText(e.currentTarget.value)}
|
|
434
|
+
onKeyDown={(e) => {
|
|
435
|
+
if (e.key === 'Enter' && handleTextSearch) {
|
|
436
|
+
e.preventDefault();
|
|
437
|
+
handleTextSearch();
|
|
438
|
+
}
|
|
439
|
+
}}
|
|
440
|
+
placeholder={t('common.search.placeholder')}
|
|
441
|
+
className={twMerge(modalSearchInputClassName, 'pr-20')}
|
|
442
|
+
disabled={isLoading}
|
|
443
|
+
/>
|
|
444
|
+
{searchText && (
|
|
445
|
+
<button
|
|
446
|
+
onClick={() => {
|
|
447
|
+
if (handleClearText) {
|
|
448
|
+
handleClearText();
|
|
449
|
+
}
|
|
450
|
+
}}
|
|
451
|
+
disabled={isLoading}
|
|
452
|
+
className={twMerge(
|
|
453
|
+
'absolute right-12 top-1/2 -translate-y-1/2 p-2 rounded-md hover:bg-gray-100 disabled:opacity-50 disabled:cursor-not-allowed transition-colors',
|
|
454
|
+
settings?.customStyles?.modalSearchClearButton
|
|
455
|
+
)}
|
|
456
|
+
>
|
|
457
|
+
<Icon
|
|
458
|
+
name={modalSearchClearIconName}
|
|
459
|
+
size={16}
|
|
460
|
+
className={twMerge(
|
|
461
|
+
'text-gray-400 hover:text-gray-600',
|
|
462
|
+
settings?.customStyles?.modalSearchClearIcon
|
|
463
|
+
)}
|
|
464
|
+
/>
|
|
465
|
+
</button>
|
|
466
|
+
)}
|
|
467
|
+
<button
|
|
468
|
+
onClick={() => {
|
|
469
|
+
if (handleTextSearch) {
|
|
470
|
+
handleTextSearch();
|
|
471
|
+
}
|
|
472
|
+
}}
|
|
473
|
+
disabled={isLoading}
|
|
474
|
+
className={modalSearchButtonClassName}
|
|
475
|
+
>
|
|
476
|
+
{settings?.customRenderers?.render?.modal?.renderSearchIcon ? (
|
|
477
|
+
settings.customRenderers.render.modal.renderSearchIcon({
|
|
478
|
+
disabled: isLoading,
|
|
479
|
+
onClick: handleTextSearch
|
|
480
|
+
})
|
|
481
|
+
) : (
|
|
482
|
+
<Icon
|
|
483
|
+
name={modalSearchIconName}
|
|
484
|
+
size={18}
|
|
485
|
+
className={modalSearchIconClassName}
|
|
486
|
+
/>
|
|
487
|
+
)}
|
|
488
|
+
</button>
|
|
489
|
+
</div>
|
|
490
|
+
);
|
|
491
|
+
};
|
|
492
|
+
|
|
354
493
|
return (
|
|
355
494
|
<div className={containerClassName}>
|
|
356
495
|
<div
|
|
357
496
|
className={twMerge(
|
|
358
|
-
'
|
|
497
|
+
'p-3 md:p-4',
|
|
498
|
+
settings?.enableTextSearch &&
|
|
499
|
+
searchText !== undefined &&
|
|
500
|
+
setSearchText
|
|
501
|
+
? 'flex gap-3 md:grid md:grid-cols-3 md:gap-4 md:items-center'
|
|
502
|
+
: 'flex items-center justify-between',
|
|
359
503
|
settings?.customStyles?.controlsInner
|
|
360
504
|
)}
|
|
361
505
|
>
|
|
362
506
|
<div
|
|
363
507
|
className={twMerge(
|
|
364
|
-
'flex items-center gap-3',
|
|
508
|
+
'flex items-center gap-2 md:gap-3',
|
|
365
509
|
settings?.customStyles?.controlsLeft
|
|
366
510
|
)}
|
|
367
511
|
>
|
|
368
512
|
{renderItemCount()}
|
|
369
513
|
{renderFilterToggle()}
|
|
370
514
|
</div>
|
|
515
|
+
{settings?.enableTextSearch &&
|
|
516
|
+
searchText !== undefined &&
|
|
517
|
+
setSearchText && (
|
|
518
|
+
<div
|
|
519
|
+
className={twMerge(
|
|
520
|
+
'flex items-center justify-center w-full',
|
|
521
|
+
settings?.customStyles?.controlsCenter
|
|
522
|
+
)}
|
|
523
|
+
>
|
|
524
|
+
{renderModalSearchInput()}
|
|
525
|
+
</div>
|
|
526
|
+
)}
|
|
371
527
|
<div
|
|
372
528
|
className={twMerge(
|
|
373
529
|
'relative',
|
|
530
|
+
settings?.enableTextSearch &&
|
|
531
|
+
searchText !== undefined &&
|
|
532
|
+
setSearchText
|
|
533
|
+
? 'flex justify-end md:mt-0'
|
|
534
|
+
: '',
|
|
374
535
|
settings?.customStyles?.controlsRight
|
|
375
536
|
)}
|
|
376
537
|
>
|
|
@@ -406,6 +567,8 @@ export function SimilarProductsModal({
|
|
|
406
567
|
isLoading={isLoading}
|
|
407
568
|
handleFacetChange={handleFacetChange}
|
|
408
569
|
removeFacetFilter={removeFacetFilter}
|
|
570
|
+
searchText={searchText}
|
|
571
|
+
setSearchText={setSearchText}
|
|
409
572
|
currentImageUrl={currentImageUrl}
|
|
410
573
|
isCropping={isCropping}
|
|
411
574
|
imageRef={imageRef}
|