@akinon/pz-similar-products 1.92.0-rc.20 → 1.92.0-rc.22

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # @akinon/pz-similar-products
2
2
 
3
+ ## 1.92.0-rc.22
4
+
5
+ ### Minor Changes
6
+
7
+ - 143be2b: ZERO-3457: Crop styles are customizable and logic improved for rendering similar products modal
8
+
9
+ ## 1.92.0-rc.21
10
+
11
+ ### Minor Changes
12
+
13
+ - d99a6a7: ZERO-3457: Fixed the settings prop and made sure everything is customizable.
14
+
3
15
  ## 1.92.0-rc.20
4
16
 
5
17
  ### Minor Changes
package/README.md CHANGED
@@ -44,11 +44,12 @@
44
44
  - [Modern Pagination with Progress](#markdown-header-modern-pagination-with-progress)
45
45
  - [Advanced Modal with Custom Header & Controls](#markdown-header-advanced-modal-with-custom-header-controls)
46
46
  - [Complete Brand Theme Integration](#markdown-header-complete-brand-theme-integration)
47
+ - [Advanced Component Customization](#markdown-header-advanced-component-customization)
47
48
 
48
49
  ### 📖 Styling Reference
49
50
 
50
- - [Available Style Targets (40+ options)](#markdown-header-available-style-targets-40-options)
51
- - [Available Render Functions (25+ options)](#markdown-header-available-render-functions-25-options)
51
+ - [Available Style Targets (50+ options)](#markdown-header-available-style-targets-50-options)
52
+ - [Available Render Functions (30+ options)](#markdown-header-available-render-functions-30-options)
52
53
 
53
54
  ### ✅ Best Practices
54
55
 
@@ -65,7 +66,7 @@
65
66
  - **✂️ Image Cropping**: Built-in cropping functionality with manual confirmation for precise searches
66
67
  - **🎛️ Advanced Filtering**: Dynamic facet-based filtering system
67
68
  - **📄 Multiple Pagination Modes**: Traditional pagination, load more button, and infinite scroll
68
- - **🎨 Granular Customization**: 40+ style targets and 25+ render points
69
+ - **🎨 Granular Customization**: 50+ style targets and 30+ render points
69
70
  - **📱 Mobile Responsive**: Optimized for all device sizes
70
71
  - **⚡ Performance Optimized**: Lazy loading and efficient rendering
71
72
  - **🔧 TypeScript**: Full TypeScript support with detailed type definitions
@@ -174,49 +175,159 @@ interface SimilarProductsSettings {
174
175
  loadMoreThreshold?: number; // Pixels from bottom for auto load (default: 100)
175
176
  maxPagesLoadMore?: number; // Maximum pages to load in load-more mode
176
177
 
177
- // 40+ granular style targets
178
+ // 50+ granular style targets
178
179
  customStyles?: {
179
180
  // Main components
180
181
  modal?: string;
182
+ modalContent?: string; // NEW: Grid container
181
183
  filterSidebar?: string;
182
184
  resultsGrid?: string;
185
+ resultsContainer?: string; // NEW: Results wrapper
186
+ imageSearchModal?: string;
183
187
 
184
- // Deep nested elements
188
+ // Active filters
189
+ activeFiltersContainer?: string;
190
+ activeFiltersWrapper?: string; // NEW: Filters wrapper
191
+ activeFilterTag?: string;
192
+ activeFilterTagButton?: string;
193
+
194
+ // Controls section
195
+ controlsContainer?: string;
196
+ controlsInner?: string; // NEW: Controls inner wrapper
197
+ controlsLeft?: string; // NEW: Left controls (count, filter)
198
+ controlsRight?: string; // NEW: Right controls (sort)
199
+ itemCount?: string;
200
+ sortDropdown?: string;
201
+ filterToggleButton?: string;
202
+
203
+ // Filter sidebar mobile
204
+ filterSidebarMobileHeader?: string;
205
+ filterSidebarMobileTitle?: string; // NEW: Mobile title
206
+ filterSidebarMobileCloseButton?: string; // NEW: Mobile close button
207
+ filterSidebarMobileCounter?: string; // NEW: Mobile counter wrapper
208
+ filterSidebarMobileCounterText?: string; // NEW: Mobile counter text
209
+ filterGroup?: string;
210
+ filterGroupTitle?: string;
211
+ filterGroupContent?: string;
212
+ filterItem?: string;
213
+ filterItemInput?: string;
214
+ filterItemLabel?: string;
215
+ filterItemCount?: string;
216
+
217
+ // Image section
218
+ imageSection?: string;
219
+ imageContainer?: string;
220
+ imageWrapper?: string;
221
+ cropButton?: string;
222
+ tickButton?: string;
223
+ uploadButton?: string;
224
+ resetButton?: string;
225
+ errorMessage?: string;
226
+ cropControls?: string;
227
+
228
+ // Products grid
229
+ gridContainer?: string;
185
230
  productItem?: string;
231
+ productImageWrapper?: string;
186
232
  productImage?: string;
233
+ productInfo?: string;
187
234
  productTitle?: string;
188
235
  productPrice?: string;
236
+ productPriceContainer?: string; // NEW: Price wrapper
237
+ productOldPrice?: string;
238
+
239
+ // Pagination & load more
240
+ pagination?: string;
241
+ paginationContainer?: string;
242
+ paginationInfo?: string;
189
243
  paginationButton?: string;
244
+ paginationButtonActive?: string;
245
+ paginationButtonDisabled?: string;
246
+ paginationPrevious?: string;
247
+ paginationNext?: string;
190
248
  loadMoreButton?: string;
191
249
  loadMoreContainer?: string;
192
- activeFilterTag?: string;
193
- filterGroup?: string;
194
- imageSection?: string;
250
+
251
+ // Loading & empty states
195
252
  loadingSpinner?: string;
196
- // ... see examples for complete list
253
+ loadingOverlay?: string;
254
+ emptyState?: string;
255
+ emptyStateInner?: string; // NEW: Empty state inner wrapper
256
+ emptyStateIcon?: string;
257
+ emptyStateText?: string;
258
+
259
+ // Image search modal
260
+ imageSearchContent?: string; // NEW: Search modal grid
261
+ imageSearchUploadSection?: string; // NEW: Upload section
262
+ imageSearchUploadTitle?: string; // NEW: Upload title
263
+ imageSearchUploadArea?: string; // NEW: Drag & drop area
264
+ imageSearchUploadButton?: string; // NEW: Upload button
265
+ imageSearchTipsSection?: string; // NEW: Tips section
266
+ imageSearchTipsTitle?: string; // NEW: Tips title
267
+ imageSearchTipsList?: string; // NEW: Tips list
268
+ imageSearchTipsItem?: string; // NEW: Individual tip
269
+ imageSearchModalOverlay?: string; // NEW: Image search modal overlay
270
+
271
+ // Mobile active filters
272
+ mobileActiveFilters?: string;
273
+ mobileActiveFilterTag?: string;
274
+ mobileClearAllButton?: string;
275
+
276
+ // New crop-related custom styles
277
+ cropComponent?: string;
278
+ cropImage?: string;
279
+ cropImageActive?: string;
280
+ cropImageNonCropping?: string;
281
+ cropImageContainer?: string;
282
+ cropOverlay?: string;
283
+ cropSelection?: string;
284
+ cropSelectionBorder?: string;
285
+ cropOverlayBackground?: string;
286
+ cropSelectionHighlight?: string;
197
287
  };
198
288
 
199
289
  // 25+ render functions for granular control
200
290
  customRenderers?: {
291
+ // Component-level renderers (full control)
292
+ Modal?: React.ComponentType<SimilarProductsModalProps>;
293
+ FilterSidebar?: React.ComponentType<FilterSidebarProps>;
294
+ ResultsGrid?: React.ComponentType<ResultsGridProps>;
295
+ ImageSearchModal?: React.ComponentType<any>;
296
+
297
+ // Granular render functions (30+ options)
201
298
  render?: {
202
299
  modal?: {
300
+ renderModal?: (props) => React.ReactNode; // Full modal override
203
301
  renderHeader?: (props) => React.ReactNode;
204
302
  renderActiveFilters?: (props) => React.ReactNode;
205
303
  renderControls?: (props) => React.ReactNode;
206
304
  renderItemCount?: (props) => React.ReactNode;
305
+ renderSortDropdown?: (props) => React.ReactNode;
306
+ renderFilterToggleButton?: (props) => React.ReactNode;
307
+ // ... see examples for complete list
308
+ };
309
+ filterSidebar?: {
310
+ renderSidebar?: (props) => React.ReactNode; // Full sidebar override
311
+ renderImageSection?: (props) => React.ReactNode;
312
+ renderFilterGroup?: (props) => React.ReactNode;
313
+ renderCropButton?: (props) => React.ReactNode;
314
+ renderTickButton?: (props) => React.ReactNode;
315
+ renderUploadButton?: (props) => React.ReactNode;
316
+ renderResetButton?: (props) => React.ReactNode;
207
317
  // ... see examples for complete list
208
318
  };
209
319
  resultsGrid?: {
320
+ renderGrid?: (props) => React.ReactNode; // Full grid override
210
321
  renderProductItem?: (props) => React.ReactNode;
211
322
  renderPagination?: (props) => React.ReactNode;
212
323
  renderGridContainer?: (props) => React.ReactNode;
324
+ renderLoadMore?: (props) => React.ReactNode;
325
+ renderEmptyState?: (props) => React.ReactNode;
213
326
  // ... see examples for complete list
214
327
  };
215
- filterSidebar?: {
216
- renderImageSection?: (props) => React.ReactNode;
217
- renderFilterGroup?: (props) => React.ReactNode;
218
- renderCropButton?: (props) => React.ReactNode;
219
- // ... see examples for complete list
328
+ imageSearchModal?: {
329
+ renderModal?: (props) => React.ReactNode; // Full image search modal override
330
+ renderUploadArea?: (props) => React.ReactNode;
220
331
  };
221
332
  };
222
333
  };
@@ -458,7 +569,18 @@ const settings = {
458
569
  productItem: 'hover:scale-105 transition-all duration-300 shadow-lg',
459
570
  filterSidebar: 'bg-gradient-to-b from-gray-50 to-white',
460
571
  paginationButton: 'rounded-full px-4 py-2 bg-blue-600 text-white',
461
- activeFilterTag: 'bg-blue-100 text-blue-800 px-3 py-1 rounded-full'
572
+ activeFilterTag: 'bg-blue-100 text-blue-800 px-3 py-1 rounded-full',
573
+
574
+ // Advanced crop styling
575
+ cropComponent: 'border-2 border-dashed border-blue-300 rounded-lg',
576
+ cropImage: 'rounded-lg shadow-md',
577
+ cropImageActive: 'brightness-110 contrast-110',
578
+ cropImageNonCropping: 'hover:scale-105 transition-transform',
579
+ cropImageContainer: 'bg-gray-50 rounded-lg p-2',
580
+ cropOverlay: 'backdrop-blur-sm',
581
+ cropOverlayBackground: 'bg-black/60',
582
+ cropSelection: 'border-4 border-blue-500',
583
+ cropSelectionHighlight: 'shadow-2xl shadow-blue-500/50'
462
584
  }
463
585
  };
464
586
  ```
@@ -817,7 +939,8 @@ export function SiteHeader() {
817
939
  settings: {
818
940
  maxFileSize: 5,
819
941
  customStyles: {
820
- imageSearchModal: 'max-w-lg rounded-xl'
942
+ imageSearchModal: 'max-w-lg rounded-xl',
943
+ imageSearchModalOverlay: 'bg-black/70 backdrop-blur-sm' // Custom overlay
821
944
  }
822
945
  }
823
946
  }}
@@ -1226,9 +1349,160 @@ const hybridSettings = {
1226
1349
  };
1227
1350
  ```
1228
1351
 
1352
+ ### Advanced Component Customization
1353
+
1354
+ The plugin now supports complete component overrides and granular element styling for maximum flexibility:
1355
+
1356
+ #### Full Component Override Example
1357
+
1358
+ ```tsx
1359
+ // Custom Image Search Modal Component
1360
+ const CustomImageSearchModal = ({
1361
+ isOpen,
1362
+ setIsOpen,
1363
+ fileInputRef,
1364
+ handleImageFileChange,
1365
+ settings
1366
+ }) => {
1367
+ return (
1368
+ <Modal open={isOpen} setOpen={setIsOpen} className="custom-search-modal">
1369
+ <div className="custom-grid p-8">
1370
+ <div className="upload-section">
1371
+ <h3 className="text-2xl font-bold mb-4">Upload Your Image</h3>
1372
+ <div className="drag-drop-area border-2 border-dashed border-purple-300 rounded-xl p-12">
1373
+ <input
1374
+ type="file"
1375
+ ref={fileInputRef}
1376
+ onChange={handleImageFileChange}
1377
+ accept="image/*"
1378
+ className="hidden"
1379
+ />
1380
+ <button
1381
+ onClick={() => fileInputRef.current?.click()}
1382
+ className="w-full py-6 px-8 bg-gradient-to-r from-purple-600 to-pink-600 text-white rounded-xl"
1383
+ >
1384
+ Choose Image
1385
+ </button>
1386
+ </div>
1387
+ </div>
1388
+ </div>
1389
+ </Modal>
1390
+ );
1391
+ };
1392
+
1393
+ // Using component override
1394
+ const advancedComponentSettings = {
1395
+ customRenderers: {
1396
+ // Complete component replacement
1397
+ ImageSearchModal: CustomImageSearchModal,
1398
+
1399
+ // Or use render functions for granular control
1400
+ render: {
1401
+ modal: {
1402
+ renderModal: (props) => (
1403
+ <CustomModal {...props} className="advanced-modal" />
1404
+ )
1405
+ },
1406
+ filterSidebar: {
1407
+ renderSidebar: (props) => (
1408
+ <CustomFilterSidebar {...props} className="advanced-sidebar" />
1409
+ )
1410
+ },
1411
+ resultsGrid: {
1412
+ renderGrid: (props) => (
1413
+ <CustomResultsGrid {...props} className="advanced-grid" />
1414
+ )
1415
+ }
1416
+ }
1417
+ }
1418
+ };
1419
+ ```
1420
+
1421
+ #### New Granular Style Targets
1422
+
1423
+ ```tsx
1424
+ const advancedStylingSettings = {
1425
+ customStyles: {
1426
+ // NEW: Modal structure
1427
+ modalContent: 'md:grid-cols-3 gap-8',
1428
+ resultsContainer: 'p-8 md:p-12',
1429
+
1430
+ // NEW: Control sections
1431
+ controlsInner: 'border-none bg-gray-50 rounded-lg p-6',
1432
+ controlsLeft: 'gap-6',
1433
+ controlsRight: 'relative z-20',
1434
+ activeFiltersWrapper: 'mb-6',
1435
+
1436
+ // NEW: Mobile filter sidebar
1437
+ filterSidebarMobileTitle: 'text-3xl font-bold text-purple-600',
1438
+ filterSidebarMobileCloseButton: 'w-10 h-10 bg-red-100 hover:bg-red-200',
1439
+ filterSidebarMobileCounter: 'bg-blue-50 rounded-lg p-4',
1440
+ filterSidebarMobileCounterText: 'text-blue-800 font-semibold',
1441
+
1442
+ // NEW: Product grid enhancements
1443
+ productPriceContainer: 'flex flex-col space-y-2',
1444
+ emptyStateInner: 'py-24',
1445
+
1446
+ // NEW: Image search modal sections
1447
+ imageSearchContent: 'gap-12 md:grid-cols-1',
1448
+ imageSearchUploadSection:
1449
+ 'bg-gradient-to-br from-blue-50 to-purple-50 rounded-xl p-8',
1450
+ imageSearchUploadTitle: 'text-2xl font-bold text-gray-800',
1451
+ imageSearchUploadArea:
1452
+ 'border-4 border-dashed border-purple-300 hover:border-purple-500 transition-colors',
1453
+ imageSearchUploadButton:
1454
+ 'bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700',
1455
+ imageSearchTipsSection: 'bg-yellow-50 border-l-4 border-yellow-400',
1456
+ imageSearchTipsTitle: 'text-yellow-800 font-bold',
1457
+ imageSearchTipsList: 'text-yellow-700',
1458
+ imageSearchTipsItem: 'hover:text-yellow-900 transition-colors',
1459
+ imageSearchModalOverlay: 'bg-black/60 backdrop-blur-md' // NEW: Custom overlay
1460
+ }
1461
+ };
1462
+ ```
1463
+
1464
+ #### Combined Approach Example
1465
+
1466
+ ```tsx
1467
+ // Mix component overrides with granular styling
1468
+ const hybridAdvancedSettings = {
1469
+ customRenderers: {
1470
+ // Override only specific components
1471
+ ImageSearchModal: CustomImageSearchModal,
1472
+
1473
+ render: {
1474
+ modal: {
1475
+ renderHeader: ({ title, onClose }) => (
1476
+ <div className="flex items-center justify-between bg-gradient-to-r from-blue-600 to-purple-600 text-white p-6 rounded-t-xl">
1477
+ <h2 className="text-2xl font-bold">{title}</h2>
1478
+ <button
1479
+ onClick={onClose}
1480
+ className="w-10 h-10 bg-white/20 hover:bg-white/30 rounded-full flex items-center justify-center transition-colors"
1481
+ >
1482
+
1483
+ </button>
1484
+ </div>
1485
+ )
1486
+ }
1487
+ }
1488
+ },
1489
+
1490
+ customStyles: {
1491
+ // Fine-tune specific elements
1492
+ modalContent: 'md:grid-cols-4 gap-6',
1493
+ resultsContainer: 'bg-gray-50 rounded-xl p-8',
1494
+ productItem:
1495
+ 'bg-white rounded-lg shadow-sm hover:shadow-xl transition-all duration-300',
1496
+ productPriceContainer:
1497
+ 'bg-gradient-to-r from-green-50 to-blue-50 rounded-lg p-3',
1498
+ controlsInner: 'bg-white rounded-lg shadow-sm border p-6'
1499
+ }
1500
+ };
1501
+ ```
1502
+
1229
1503
  ## Complete Styling Reference
1230
1504
 
1231
- ### Available Style Targets (40+ options)
1505
+ ### Available Style Targets (50+ options)
1232
1506
 
1233
1507
  **Modal & Layout:**
1234
1508
 
@@ -1239,6 +1513,7 @@ const hybridSettings = {
1239
1513
  - `filterSidebar`, `filterSidebarMobileHeader`, `filterGroup`, `filterGroupTitle`, `filterGroupContent`
1240
1514
  - `filterItem`, `filterItemInput`, `filterItemLabel`, `filterItemCount`
1241
1515
  - `imageSection`, `imageContainer`, `imageWrapper`, `cropButton`, `tickButton`, `uploadButton`, `resetButton`, `errorMessage`, `cropControls`
1516
+ - `cropComponent`, `cropImage`, `cropImageActive`, `cropImageNonCropping`, `cropImageContainer`, `cropOverlay`, `cropSelection`, `cropSelectionBorder`, `cropOverlayBackground`, `cropSelectionHighlight`
1242
1517
  - `mobileActiveFilters`, `mobileActiveFilterTag`, `mobileClearAllButton`
1243
1518
 
1244
1519
  **Products & Results:**
@@ -1258,26 +1533,41 @@ const hybridSettings = {
1258
1533
 
1259
1534
  - `loadingSpinner`, `loadingOverlay`, `emptyState`, `emptyStateIcon`, `emptyStateText`
1260
1535
 
1261
- ### Available Render Functions (25+ options)
1536
+ ### Available Render Functions (30+ options)
1537
+
1538
+ **Component-Level Renderers (Full Control):**
1539
+
1540
+ - `Modal` - Complete modal component override
1541
+ - `FilterSidebar` - Complete sidebar component override
1542
+ - `ResultsGrid` - Complete grid component override
1543
+ - `ImageSearchModal` - Complete image search modal override
1262
1544
 
1263
1545
  **Modal Renderers:**
1264
1546
 
1265
- - `renderModal`, `renderHeader`, `renderCloseButton`, `renderActiveFilters`, `renderActiveFilterTag`
1547
+ - `renderModal` - Full modal override
1548
+ - `renderHeader`, `renderCloseButton`, `renderActiveFilters`, `renderActiveFilterTag`
1266
1549
  - `renderControls`, `renderSortDropdown`, `renderItemCount`, `renderFilterToggleButton`, `renderEmptyState`
1267
1550
 
1268
1551
  **Filter Sidebar Renderers:**
1269
1552
 
1270
- - `renderSidebar`, `renderMobileHeader`, `renderImageSection`, `renderImageContainer`
1553
+ - `renderSidebar` - Full sidebar override
1554
+ - `renderMobileHeader`, `renderImageSection`, `renderImageContainer`
1271
1555
  - `renderCropButton`, `renderTickButton`, `renderUploadButton`, `renderResetButton`, `renderErrorMessage`
1272
1556
  - `renderFilterGroup`, `renderFilterGroupTitle`, `renderFilterItem`, `renderFilterItemInput`, `renderFilterItemLabel`, `renderFilterItemCount`
1273
1557
  - `renderMobileActiveFilters`
1274
1558
 
1275
1559
  **Results Grid Renderers:**
1276
1560
 
1277
- - `renderGrid`, `renderGridContainer`, `renderProductItem`, `renderProductImage`, `renderProductInfo`, `renderProductTitle`, `renderProductPrice`
1561
+ - `renderGrid` - Full grid override
1562
+ - `renderGridContainer`, `renderProductItem`, `renderProductImage`, `renderProductInfo`, `renderProductTitle`, `renderProductPrice`
1278
1563
  - `renderPagination`, `renderPaginationButton`, `renderPaginationInfo`, `renderPaginationPrevious`, `renderPaginationNext`, `renderLoadMore`, `renderLoadMoreButton`
1279
1564
  - `renderLoadingState`, `renderLoadingOverlay`, `renderEmptyState`
1280
1565
 
1566
+ **Image Search Modal Renderers:**
1567
+
1568
+ - `renderModal` - Full image search modal override
1569
+ - `renderUploadArea` - Upload section customization
1570
+
1281
1571
  ## Best Practices
1282
1572
 
1283
1573
  ### Performance Optimization
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@akinon/pz-similar-products",
3
- "version": "1.92.0-rc.20",
3
+ "version": "1.92.0-rc.22",
4
4
  "license": "MIT",
5
5
  "main": "src/index.ts",
6
6
  "peerDependencies": {
@@ -58,7 +58,7 @@ export function useSimilarProducts(product: Product) {
58
58
  pagination: {
59
59
  current_page: 1,
60
60
  num_pages: 1,
61
- page_size: 1,
61
+ page_size: 20,
62
62
  total_count: 0
63
63
  }
64
64
  }),
@@ -91,14 +91,6 @@ export function useSimilarProducts(product: Product) {
91
91
  searchParams.page = String(page);
92
92
  }
93
93
 
94
- // Test için farklı parametre isimlerini deniyoruz
95
- searchParams.page_size = '1';
96
- searchParams.pageSize = '1';
97
- searchParams.limit = '1';
98
- searchParams.per_page = '1';
99
- searchParams.size = '1';
100
- console.log('🔍 API çağrısı searchParams:', searchParams);
101
-
102
94
  if (facets) {
103
95
  facets.forEach((facet) => {
104
96
  if (String(facet.key) === 'category_ids') return;
@@ -633,7 +625,21 @@ export function useSimilarProducts(product: Product) {
633
625
  if (!file) return;
634
626
 
635
627
  try {
636
- const validation = await validateImageFileWithTranslation(file);
628
+ let processedFile = file;
629
+
630
+ if (file.type === 'image/webp') {
631
+ try {
632
+ const { convertWebPToJPEG } = await import(
633
+ '../utils/image-conversion'
634
+ );
635
+ processedFile = await convertWebPToJPEG(file);
636
+ } catch (error) {
637
+ console.error('WebP conversion failed:', error);
638
+ processedFile = file;
639
+ }
640
+ }
641
+
642
+ const validation = await validateImageFileWithTranslation(processedFile);
637
643
  if (!validation.isValid) {
638
644
  setFileError(validation.error!);
639
645
  return;
@@ -657,7 +663,7 @@ export function useSimilarProducts(product: Product) {
657
663
 
658
664
  reader.onerror = () =>
659
665
  setFileError(t('common.similar_products.errors.file_read_error'));
660
- reader.readAsDataURL(file);
666
+ reader.readAsDataURL(processedFile);
661
667
  } catch (error) {
662
668
  setFileError(t('common.similar_products.errors.processing_error'));
663
669
  }
@@ -815,7 +821,7 @@ export function useSimilarProducts(product: Product) {
815
821
  result.facets,
816
822
  resultsWithResetSorter.facets
817
823
  ),
818
- sorters: resultsWithResetSorter.sorters // Reset edilmiş sorter'ları kullan
824
+ sorters: resultsWithResetSorter.sorters
819
825
  };
820
826
  updateResultsAndKey(finalResult);
821
827
  }
@@ -868,6 +874,14 @@ export function useSimilarProducts(product: Product) {
868
874
  setFileError('');
869
875
  }, []);
870
876
 
877
+ const clearResults = useCallback(() => {
878
+ setSearchResults(null);
879
+ setResultsKey(0);
880
+ setLastProductIds([]);
881
+ setAllLoadedProducts([]);
882
+ setLoadedPages(new Set([1]));
883
+ }, []);
884
+
871
885
  const clearFileInput = useCallback(
872
886
  (fileInputRef: React.RefObject<HTMLInputElement>) => {
873
887
  if (fileInputRef.current) {
@@ -934,6 +948,7 @@ export function useSimilarProducts(product: Product) {
934
948
  loadedPages: Array.from(loadedPages),
935
949
  allLoadedProducts,
936
950
  clearError,
951
+ clearResults,
937
952
  clearFileInput
938
953
  };
939
954
  }
@@ -316,20 +316,29 @@ export interface SimilarProductsSettings {
316
316
  maxPagesLoadMore?: number;
317
317
  customStyles?: {
318
318
  modal?: string;
319
+ modalContent?: string;
319
320
  filterSidebar?: string;
320
321
  resultsGrid?: string;
321
322
  imageSearchModal?: string;
322
323
 
323
324
  activeFiltersContainer?: string;
325
+ activeFiltersWrapper?: string;
324
326
  activeFilterTag?: string;
325
327
  activeFilterTagButton?: string;
326
328
 
327
329
  controlsContainer?: string;
330
+ controlsInner?: string;
331
+ controlsLeft?: string;
332
+ controlsRight?: string;
328
333
  itemCount?: string;
329
334
  sortDropdown?: string;
330
335
  filterToggleButton?: string;
331
336
 
332
337
  filterSidebarMobileHeader?: string;
338
+ filterSidebarMobileTitle?: string;
339
+ filterSidebarMobileCloseButton?: string;
340
+ filterSidebarMobileCounter?: string;
341
+ filterSidebarMobileCounterText?: string;
333
342
  filterGroup?: string;
334
343
  filterGroupTitle?: string;
335
344
  filterGroupContent?: string;
@@ -348,6 +357,19 @@ export interface SimilarProductsSettings {
348
357
  errorMessage?: string;
349
358
  cropControls?: string;
350
359
 
360
+ // Detailed crop styling
361
+ cropComponent?: string;
362
+ cropImage?: string;
363
+ cropImageActive?: string;
364
+ cropOverlay?: string;
365
+ cropSelection?: string;
366
+ cropSelectionBorder?: string;
367
+ cropImageNonCropping?: string;
368
+ cropImageContainer?: string;
369
+ cropImageWrapper?: string;
370
+ cropOverlayBackground?: string;
371
+ cropSelectionHighlight?: string;
372
+
351
373
  resultsContainer?: string;
352
374
  gridContainer?: string;
353
375
  productItem?: string;
@@ -356,6 +378,7 @@ export interface SimilarProductsSettings {
356
378
  productInfo?: string;
357
379
  productTitle?: string;
358
380
  productPrice?: string;
381
+ productPriceContainer?: string;
359
382
  productOldPrice?: string;
360
383
 
361
384
  pagination?: string;
@@ -372,12 +395,25 @@ export interface SimilarProductsSettings {
372
395
  loadingSpinner?: string;
373
396
  loadingOverlay?: string;
374
397
  emptyState?: string;
398
+ emptyStateInner?: string;
375
399
  emptyStateIcon?: string;
376
400
  emptyStateText?: string;
377
401
 
402
+ imageSearchContent?: string;
403
+ imageSearchUploadSection?: string;
404
+ imageSearchUploadTitle?: string;
405
+ imageSearchUploadArea?: string;
406
+ imageSearchUploadButton?: string;
407
+ imageSearchTipsSection?: string;
408
+ imageSearchTipsTitle?: string;
409
+ imageSearchTipsList?: string;
410
+ imageSearchTipsItem?: string;
411
+ imageSearchModalOverlay?: string;
412
+
378
413
  mobileActiveFilters?: string;
379
414
  mobileActiveFilterTag?: string;
380
415
  mobileClearAllButton?: string;
416
+ filterSidebarMobileOverlay?: string;
381
417
  };
382
418
  customRenderers?: {
383
419
  Modal?: React.ComponentType<SimilarProductsModalProps>;
@@ -0,0 +1,44 @@
1
+ export const convertWebPToJPEG = (file: File): Promise<File> => {
2
+ return new Promise((resolve, reject) => {
3
+ const canvas = document.createElement('canvas');
4
+ const ctx = canvas.getContext('2d');
5
+ const img = new Image();
6
+
7
+ img.onload = () => {
8
+ canvas.width = img.naturalWidth;
9
+ canvas.height = img.naturalHeight;
10
+
11
+ if (!ctx) {
12
+ reject(new Error('Canvas context not available'));
13
+ return;
14
+ }
15
+
16
+ ctx.drawImage(img, 0, 0);
17
+
18
+ canvas.toBlob(
19
+ (blob) => {
20
+ if (!blob) {
21
+ reject(new Error('Failed to convert image'));
22
+ return;
23
+ }
24
+
25
+ const convertedFile = new File(
26
+ [blob],
27
+ file.name.replace(/\.webp$/i, '.jpg'),
28
+ {
29
+ type: 'image/jpeg',
30
+ lastModified: Date.now()
31
+ }
32
+ );
33
+
34
+ resolve(convertedFile);
35
+ },
36
+ 'image/jpeg',
37
+ 0.9
38
+ );
39
+ };
40
+
41
+ img.onerror = () => reject(new Error('Failed to load image'));
42
+ img.src = URL.createObjectURL(file);
43
+ });
44
+ };
@@ -21,7 +21,12 @@ export const defaultSettings: Required<SimilarProductsSettings> = {
21
21
  render: {}
22
22
  },
23
23
  theme: {},
24
- cssVariables: {}
24
+ cssVariables: {},
25
+ paginationType: 'pagination',
26
+ loadMoreText: '',
27
+ loadMoreStyle: 'button',
28
+ loadMoreThreshold: 10,
29
+ maxPagesLoadMore: 20
25
30
  };
26
31
 
27
32
  export function mergeSettings(