@akinon/pz-similar-products 1.92.0-rc.20 β 1.92.0-rc.21
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 -0
- package/README.md +286 -20
- package/package.json +1 -1
- package/src/hooks/use-similar-products.ts +18 -12
- package/src/types/index.ts +23 -0
- package/src/utils/image-conversion.ts +44 -0
- package/src/utils/index.ts +6 -1
- package/src/views/filters.tsx +696 -628
- package/src/views/header-image-search-feature.tsx +16 -4
- package/src/views/image-search.tsx +136 -84
- package/src/views/main.tsx +1 -0
- package/src/views/product-image-search-feature.tsx +12 -3
- package/src/views/results.tsx +12 -2
- package/src/views/search-modal.tsx +38 -7
package/CHANGELOG.md
CHANGED
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 (
|
|
51
|
-
- [Available Render Functions (
|
|
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**:
|
|
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,147 @@ 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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
|
|
193
|
-
|
|
194
|
-
imageSection?: string;
|
|
250
|
+
|
|
251
|
+
// Loading & empty states
|
|
195
252
|
loadingSpinner?: string;
|
|
196
|
-
|
|
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;
|
|
197
275
|
};
|
|
198
276
|
|
|
199
277
|
// 25+ render functions for granular control
|
|
200
278
|
customRenderers?: {
|
|
279
|
+
// Component-level renderers (full control)
|
|
280
|
+
Modal?: React.ComponentType<SimilarProductsModalProps>;
|
|
281
|
+
FilterSidebar?: React.ComponentType<FilterSidebarProps>;
|
|
282
|
+
ResultsGrid?: React.ComponentType<ResultsGridProps>;
|
|
283
|
+
ImageSearchModal?: React.ComponentType<any>;
|
|
284
|
+
|
|
285
|
+
// Granular render functions (30+ options)
|
|
201
286
|
render?: {
|
|
202
287
|
modal?: {
|
|
288
|
+
renderModal?: (props) => React.ReactNode; // Full modal override
|
|
203
289
|
renderHeader?: (props) => React.ReactNode;
|
|
204
290
|
renderActiveFilters?: (props) => React.ReactNode;
|
|
205
291
|
renderControls?: (props) => React.ReactNode;
|
|
206
292
|
renderItemCount?: (props) => React.ReactNode;
|
|
293
|
+
renderSortDropdown?: (props) => React.ReactNode;
|
|
294
|
+
renderFilterToggleButton?: (props) => React.ReactNode;
|
|
295
|
+
// ... see examples for complete list
|
|
296
|
+
};
|
|
297
|
+
filterSidebar?: {
|
|
298
|
+
renderSidebar?: (props) => React.ReactNode; // Full sidebar override
|
|
299
|
+
renderImageSection?: (props) => React.ReactNode;
|
|
300
|
+
renderFilterGroup?: (props) => React.ReactNode;
|
|
301
|
+
renderCropButton?: (props) => React.ReactNode;
|
|
302
|
+
renderTickButton?: (props) => React.ReactNode;
|
|
303
|
+
renderUploadButton?: (props) => React.ReactNode;
|
|
304
|
+
renderResetButton?: (props) => React.ReactNode;
|
|
207
305
|
// ... see examples for complete list
|
|
208
306
|
};
|
|
209
307
|
resultsGrid?: {
|
|
308
|
+
renderGrid?: (props) => React.ReactNode; // Full grid override
|
|
210
309
|
renderProductItem?: (props) => React.ReactNode;
|
|
211
310
|
renderPagination?: (props) => React.ReactNode;
|
|
212
311
|
renderGridContainer?: (props) => React.ReactNode;
|
|
312
|
+
renderLoadMore?: (props) => React.ReactNode;
|
|
313
|
+
renderEmptyState?: (props) => React.ReactNode;
|
|
213
314
|
// ... see examples for complete list
|
|
214
315
|
};
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
renderCropButton?: (props) => React.ReactNode;
|
|
219
|
-
// ... see examples for complete list
|
|
316
|
+
imageSearchModal?: {
|
|
317
|
+
renderModal?: (props) => React.ReactNode; // Full image search modal override
|
|
318
|
+
renderUploadArea?: (props) => React.ReactNode;
|
|
220
319
|
};
|
|
221
320
|
};
|
|
222
321
|
};
|
|
@@ -817,7 +916,8 @@ export function SiteHeader() {
|
|
|
817
916
|
settings: {
|
|
818
917
|
maxFileSize: 5,
|
|
819
918
|
customStyles: {
|
|
820
|
-
imageSearchModal: 'max-w-lg rounded-xl'
|
|
919
|
+
imageSearchModal: 'max-w-lg rounded-xl',
|
|
920
|
+
imageSearchModalOverlay: 'bg-black/70 backdrop-blur-sm' // Custom overlay
|
|
821
921
|
}
|
|
822
922
|
}
|
|
823
923
|
}}
|
|
@@ -1226,9 +1326,160 @@ const hybridSettings = {
|
|
|
1226
1326
|
};
|
|
1227
1327
|
```
|
|
1228
1328
|
|
|
1329
|
+
### Advanced Component Customization
|
|
1330
|
+
|
|
1331
|
+
The plugin now supports complete component overrides and granular element styling for maximum flexibility:
|
|
1332
|
+
|
|
1333
|
+
#### Full Component Override Example
|
|
1334
|
+
|
|
1335
|
+
```tsx
|
|
1336
|
+
// Custom Image Search Modal Component
|
|
1337
|
+
const CustomImageSearchModal = ({
|
|
1338
|
+
isOpen,
|
|
1339
|
+
setIsOpen,
|
|
1340
|
+
fileInputRef,
|
|
1341
|
+
handleImageFileChange,
|
|
1342
|
+
settings
|
|
1343
|
+
}) => {
|
|
1344
|
+
return (
|
|
1345
|
+
<Modal open={isOpen} setOpen={setIsOpen} className="custom-search-modal">
|
|
1346
|
+
<div className="custom-grid p-8">
|
|
1347
|
+
<div className="upload-section">
|
|
1348
|
+
<h3 className="text-2xl font-bold mb-4">Upload Your Image</h3>
|
|
1349
|
+
<div className="drag-drop-area border-2 border-dashed border-purple-300 rounded-xl p-12">
|
|
1350
|
+
<input
|
|
1351
|
+
type="file"
|
|
1352
|
+
ref={fileInputRef}
|
|
1353
|
+
onChange={handleImageFileChange}
|
|
1354
|
+
accept="image/*"
|
|
1355
|
+
className="hidden"
|
|
1356
|
+
/>
|
|
1357
|
+
<button
|
|
1358
|
+
onClick={() => fileInputRef.current?.click()}
|
|
1359
|
+
className="w-full py-6 px-8 bg-gradient-to-r from-purple-600 to-pink-600 text-white rounded-xl"
|
|
1360
|
+
>
|
|
1361
|
+
Choose Image
|
|
1362
|
+
</button>
|
|
1363
|
+
</div>
|
|
1364
|
+
</div>
|
|
1365
|
+
</div>
|
|
1366
|
+
</Modal>
|
|
1367
|
+
);
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
// Using component override
|
|
1371
|
+
const advancedComponentSettings = {
|
|
1372
|
+
customRenderers: {
|
|
1373
|
+
// Complete component replacement
|
|
1374
|
+
ImageSearchModal: CustomImageSearchModal,
|
|
1375
|
+
|
|
1376
|
+
// Or use render functions for granular control
|
|
1377
|
+
render: {
|
|
1378
|
+
modal: {
|
|
1379
|
+
renderModal: (props) => (
|
|
1380
|
+
<CustomModal {...props} className="advanced-modal" />
|
|
1381
|
+
)
|
|
1382
|
+
},
|
|
1383
|
+
filterSidebar: {
|
|
1384
|
+
renderSidebar: (props) => (
|
|
1385
|
+
<CustomFilterSidebar {...props} className="advanced-sidebar" />
|
|
1386
|
+
)
|
|
1387
|
+
},
|
|
1388
|
+
resultsGrid: {
|
|
1389
|
+
renderGrid: (props) => (
|
|
1390
|
+
<CustomResultsGrid {...props} className="advanced-grid" />
|
|
1391
|
+
)
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
}
|
|
1395
|
+
};
|
|
1396
|
+
```
|
|
1397
|
+
|
|
1398
|
+
#### New Granular Style Targets
|
|
1399
|
+
|
|
1400
|
+
```tsx
|
|
1401
|
+
const advancedStylingSettings = {
|
|
1402
|
+
customStyles: {
|
|
1403
|
+
// NEW: Modal structure
|
|
1404
|
+
modalContent: 'md:grid-cols-3 gap-8',
|
|
1405
|
+
resultsContainer: 'p-8 md:p-12',
|
|
1406
|
+
|
|
1407
|
+
// NEW: Control sections
|
|
1408
|
+
controlsInner: 'border-none bg-gray-50 rounded-lg p-6',
|
|
1409
|
+
controlsLeft: 'gap-6',
|
|
1410
|
+
controlsRight: 'relative z-20',
|
|
1411
|
+
activeFiltersWrapper: 'mb-6',
|
|
1412
|
+
|
|
1413
|
+
// NEW: Mobile filter sidebar
|
|
1414
|
+
filterSidebarMobileTitle: 'text-3xl font-bold text-purple-600',
|
|
1415
|
+
filterSidebarMobileCloseButton: 'w-10 h-10 bg-red-100 hover:bg-red-200',
|
|
1416
|
+
filterSidebarMobileCounter: 'bg-blue-50 rounded-lg p-4',
|
|
1417
|
+
filterSidebarMobileCounterText: 'text-blue-800 font-semibold',
|
|
1418
|
+
|
|
1419
|
+
// NEW: Product grid enhancements
|
|
1420
|
+
productPriceContainer: 'flex flex-col space-y-2',
|
|
1421
|
+
emptyStateInner: 'py-24',
|
|
1422
|
+
|
|
1423
|
+
// NEW: Image search modal sections
|
|
1424
|
+
imageSearchContent: 'gap-12 md:grid-cols-1',
|
|
1425
|
+
imageSearchUploadSection:
|
|
1426
|
+
'bg-gradient-to-br from-blue-50 to-purple-50 rounded-xl p-8',
|
|
1427
|
+
imageSearchUploadTitle: 'text-2xl font-bold text-gray-800',
|
|
1428
|
+
imageSearchUploadArea:
|
|
1429
|
+
'border-4 border-dashed border-purple-300 hover:border-purple-500 transition-colors',
|
|
1430
|
+
imageSearchUploadButton:
|
|
1431
|
+
'bg-gradient-to-r from-blue-600 to-purple-600 hover:from-blue-700 hover:to-purple-700',
|
|
1432
|
+
imageSearchTipsSection: 'bg-yellow-50 border-l-4 border-yellow-400',
|
|
1433
|
+
imageSearchTipsTitle: 'text-yellow-800 font-bold',
|
|
1434
|
+
imageSearchTipsList: 'text-yellow-700',
|
|
1435
|
+
imageSearchTipsItem: 'hover:text-yellow-900 transition-colors',
|
|
1436
|
+
imageSearchModalOverlay: 'bg-black/60 backdrop-blur-md' // NEW: Custom overlay
|
|
1437
|
+
}
|
|
1438
|
+
};
|
|
1439
|
+
```
|
|
1440
|
+
|
|
1441
|
+
#### Combined Approach Example
|
|
1442
|
+
|
|
1443
|
+
```tsx
|
|
1444
|
+
// Mix component overrides with granular styling
|
|
1445
|
+
const hybridAdvancedSettings = {
|
|
1446
|
+
customRenderers: {
|
|
1447
|
+
// Override only specific components
|
|
1448
|
+
ImageSearchModal: CustomImageSearchModal,
|
|
1449
|
+
|
|
1450
|
+
render: {
|
|
1451
|
+
modal: {
|
|
1452
|
+
renderHeader: ({ title, onClose }) => (
|
|
1453
|
+
<div className="flex items-center justify-between bg-gradient-to-r from-blue-600 to-purple-600 text-white p-6 rounded-t-xl">
|
|
1454
|
+
<h2 className="text-2xl font-bold">{title}</h2>
|
|
1455
|
+
<button
|
|
1456
|
+
onClick={onClose}
|
|
1457
|
+
className="w-10 h-10 bg-white/20 hover:bg-white/30 rounded-full flex items-center justify-center transition-colors"
|
|
1458
|
+
>
|
|
1459
|
+
β
|
|
1460
|
+
</button>
|
|
1461
|
+
</div>
|
|
1462
|
+
)
|
|
1463
|
+
}
|
|
1464
|
+
}
|
|
1465
|
+
},
|
|
1466
|
+
|
|
1467
|
+
customStyles: {
|
|
1468
|
+
// Fine-tune specific elements
|
|
1469
|
+
modalContent: 'md:grid-cols-4 gap-6',
|
|
1470
|
+
resultsContainer: 'bg-gray-50 rounded-xl p-8',
|
|
1471
|
+
productItem:
|
|
1472
|
+
'bg-white rounded-lg shadow-sm hover:shadow-xl transition-all duration-300',
|
|
1473
|
+
productPriceContainer:
|
|
1474
|
+
'bg-gradient-to-r from-green-50 to-blue-50 rounded-lg p-3',
|
|
1475
|
+
controlsInner: 'bg-white rounded-lg shadow-sm border p-6'
|
|
1476
|
+
}
|
|
1477
|
+
};
|
|
1478
|
+
```
|
|
1479
|
+
|
|
1229
1480
|
## Complete Styling Reference
|
|
1230
1481
|
|
|
1231
|
-
### Available Style Targets (
|
|
1482
|
+
### Available Style Targets (50+ options)
|
|
1232
1483
|
|
|
1233
1484
|
**Modal & Layout:**
|
|
1234
1485
|
|
|
@@ -1258,26 +1509,41 @@ const hybridSettings = {
|
|
|
1258
1509
|
|
|
1259
1510
|
- `loadingSpinner`, `loadingOverlay`, `emptyState`, `emptyStateIcon`, `emptyStateText`
|
|
1260
1511
|
|
|
1261
|
-
### Available Render Functions (
|
|
1512
|
+
### Available Render Functions (30+ options)
|
|
1513
|
+
|
|
1514
|
+
**Component-Level Renderers (Full Control):**
|
|
1515
|
+
|
|
1516
|
+
- `Modal` - Complete modal component override
|
|
1517
|
+
- `FilterSidebar` - Complete sidebar component override
|
|
1518
|
+
- `ResultsGrid` - Complete grid component override
|
|
1519
|
+
- `ImageSearchModal` - Complete image search modal override
|
|
1262
1520
|
|
|
1263
1521
|
**Modal Renderers:**
|
|
1264
1522
|
|
|
1265
|
-
- `renderModal
|
|
1523
|
+
- `renderModal` - Full modal override
|
|
1524
|
+
- `renderHeader`, `renderCloseButton`, `renderActiveFilters`, `renderActiveFilterTag`
|
|
1266
1525
|
- `renderControls`, `renderSortDropdown`, `renderItemCount`, `renderFilterToggleButton`, `renderEmptyState`
|
|
1267
1526
|
|
|
1268
1527
|
**Filter Sidebar Renderers:**
|
|
1269
1528
|
|
|
1270
|
-
- `renderSidebar
|
|
1529
|
+
- `renderSidebar` - Full sidebar override
|
|
1530
|
+
- `renderMobileHeader`, `renderImageSection`, `renderImageContainer`
|
|
1271
1531
|
- `renderCropButton`, `renderTickButton`, `renderUploadButton`, `renderResetButton`, `renderErrorMessage`
|
|
1272
1532
|
- `renderFilterGroup`, `renderFilterGroupTitle`, `renderFilterItem`, `renderFilterItemInput`, `renderFilterItemLabel`, `renderFilterItemCount`
|
|
1273
1533
|
- `renderMobileActiveFilters`
|
|
1274
1534
|
|
|
1275
1535
|
**Results Grid Renderers:**
|
|
1276
1536
|
|
|
1277
|
-
- `renderGrid
|
|
1537
|
+
- `renderGrid` - Full grid override
|
|
1538
|
+
- `renderGridContainer`, `renderProductItem`, `renderProductImage`, `renderProductInfo`, `renderProductTitle`, `renderProductPrice`
|
|
1278
1539
|
- `renderPagination`, `renderPaginationButton`, `renderPaginationInfo`, `renderPaginationPrevious`, `renderPaginationNext`, `renderLoadMore`, `renderLoadMoreButton`
|
|
1279
1540
|
- `renderLoadingState`, `renderLoadingOverlay`, `renderEmptyState`
|
|
1280
1541
|
|
|
1542
|
+
**Image Search Modal Renderers:**
|
|
1543
|
+
|
|
1544
|
+
- `renderModal` - Full image search modal override
|
|
1545
|
+
- `renderUploadArea` - Upload section customization
|
|
1546
|
+
|
|
1281
1547
|
## Best Practices
|
|
1282
1548
|
|
|
1283
1549
|
### Performance Optimization
|
package/package.json
CHANGED
|
@@ -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:
|
|
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
|
-
|
|
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(
|
|
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
|
|
824
|
+
sorters: resultsWithResetSorter.sorters
|
|
819
825
|
};
|
|
820
826
|
updateResultsAndKey(finalResult);
|
|
821
827
|
}
|
package/src/types/index.ts
CHANGED
|
@@ -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;
|
|
@@ -356,6 +365,7 @@ export interface SimilarProductsSettings {
|
|
|
356
365
|
productInfo?: string;
|
|
357
366
|
productTitle?: string;
|
|
358
367
|
productPrice?: string;
|
|
368
|
+
productPriceContainer?: string;
|
|
359
369
|
productOldPrice?: string;
|
|
360
370
|
|
|
361
371
|
pagination?: string;
|
|
@@ -372,12 +382,25 @@ export interface SimilarProductsSettings {
|
|
|
372
382
|
loadingSpinner?: string;
|
|
373
383
|
loadingOverlay?: string;
|
|
374
384
|
emptyState?: string;
|
|
385
|
+
emptyStateInner?: string;
|
|
375
386
|
emptyStateIcon?: string;
|
|
376
387
|
emptyStateText?: string;
|
|
377
388
|
|
|
389
|
+
imageSearchContent?: string;
|
|
390
|
+
imageSearchUploadSection?: string;
|
|
391
|
+
imageSearchUploadTitle?: string;
|
|
392
|
+
imageSearchUploadArea?: string;
|
|
393
|
+
imageSearchUploadButton?: string;
|
|
394
|
+
imageSearchTipsSection?: string;
|
|
395
|
+
imageSearchTipsTitle?: string;
|
|
396
|
+
imageSearchTipsList?: string;
|
|
397
|
+
imageSearchTipsItem?: string;
|
|
398
|
+
imageSearchModalOverlay?: string;
|
|
399
|
+
|
|
378
400
|
mobileActiveFilters?: string;
|
|
379
401
|
mobileActiveFilterTag?: string;
|
|
380
402
|
mobileClearAllButton?: string;
|
|
403
|
+
filterSidebarMobileOverlay?: string;
|
|
381
404
|
};
|
|
382
405
|
customRenderers?: {
|
|
383
406
|
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
|
+
};
|
package/src/utils/index.ts
CHANGED
|
@@ -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(
|