@akinon/pz-similar-products 1.92.0-rc.16

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/src/index.ts ADDED
@@ -0,0 +1,33 @@
1
+ export { SimilarProductsModal } from './views/search-modal';
2
+ export { SimilarProductsFilterSidebar } from './views/filters';
3
+ export { SimilarProductsResultsGrid } from './views/results';
4
+ export { SimilarProductsPlugin } from './views/main';
5
+ export { SimilarProductsButton } from './views/search-button';
6
+ export { ProductImageSearchFeature } from './views/product-image-search-feature';
7
+ export { ImageSearchModal } from './views/image-search';
8
+ export { ImageSearchButton } from './views/image-search-button';
9
+ export { HeaderImageSearchFeature } from './views/header-image-search-feature';
10
+
11
+ export { useSimilarProducts } from './hooks/use-similar-products';
12
+ export { useImageCropper } from './hooks/use-image-cropper';
13
+
14
+ export {
15
+ useGetSimilarProductsByUrlQuery,
16
+ useLazyGetSimilarProductsByUrlQuery,
17
+ useGetSimilarProductsByImageMutation,
18
+ useGetSimilarProductsListQuery,
19
+ useLazyGetSimilarProductsListQuery
20
+ } from './data/endpoints';
21
+
22
+ export type {
23
+ SimilarProductsListResponse,
24
+ SimilarProductsModalProps,
25
+ FilterSidebarProps,
26
+ ResultsGridProps,
27
+ SimilarProductsSettings,
28
+ SimilarProductsResponse
29
+ } from './types';
30
+
31
+ export * from './utils';
32
+
33
+ export { SimilarProductsPlugin as default } from './views/main';
@@ -0,0 +1,419 @@
1
+ import React from 'react';
2
+ import {
3
+ Product,
4
+ Facet,
5
+ SortOption,
6
+ Pagination
7
+ } from '@akinon/next/types/commerce';
8
+
9
+ export interface SimilarProductsResponse {
10
+ product_ids?: number[];
11
+ productIds?: number[];
12
+ similar_products?: Array<{ product_id: number }>;
13
+ [key: string]: any;
14
+ }
15
+
16
+ export interface SimilarProductsListResponse {
17
+ pagination: Pagination;
18
+ facets: Facet[];
19
+ sorters: SortOption[];
20
+ search_text: string | null;
21
+ products: Product[];
22
+ [key: string]: any;
23
+ }
24
+
25
+ export interface SimilarProductsModalProps {
26
+ isOpen: boolean;
27
+ onClose: () => void;
28
+ searchResults: SimilarProductsListResponse | null;
29
+ resultsKey: number;
30
+ isLoading: boolean;
31
+ isFilterMenuOpen: boolean;
32
+ setIsFilterMenuOpen: (open: boolean) => void;
33
+ handleSortChange: (value: string) => void;
34
+ handlePageChange: (page: number, sortValue?: string) => void;
35
+ handleFacetChange: (facetKey: string, choiceValue: string | number) => void;
36
+ removeFacetFilter: (facetKey: string, choiceValue: string | number) => void;
37
+ handleLoadMore?: () => void;
38
+ loadedPages?: number[];
39
+ currentImageUrl: string;
40
+ isCropping: boolean;
41
+ imageRef: React.RefObject<HTMLImageElement>;
42
+ fileInputRef: React.RefObject<HTMLInputElement>;
43
+ crop: any;
44
+ setCrop: (crop: any) => void;
45
+ completedCrop: any;
46
+ setCompletedCrop: (crop: any) => void;
47
+ handleCropComplete: (crop: any) => void;
48
+ toggleCropMode: () => void;
49
+ processCompletedCrop: (crop: any) => void;
50
+ processManualCrop: (crop: any) => void;
51
+ resetCrop: () => void;
52
+ product: Product;
53
+ activeIndex?: number;
54
+ hasUploadedImage: boolean;
55
+ handleFileUpload: (event: React.ChangeEvent<HTMLInputElement>) => void;
56
+ handleResetToOriginal: () => void;
57
+ fileError: string;
58
+ showResetButton?: boolean;
59
+ settings?: any;
60
+ className?: string;
61
+ }
62
+
63
+ export interface FilterSidebarProps {
64
+ isFilterMenuOpen: boolean;
65
+ setIsFilterMenuOpen: (open: boolean) => void;
66
+ searchResults: SimilarProductsListResponse | null;
67
+ isLoading: boolean;
68
+ handleFacetChange: (facetKey: string, choiceValue: string | number) => void;
69
+ removeFacetFilter: (facetKey: string, choiceValue: string | number) => void;
70
+ currentImageUrl: string;
71
+ isCropping: boolean;
72
+ imageRef: React.RefObject<HTMLImageElement>;
73
+ fileInputRef: React.RefObject<HTMLInputElement>;
74
+ crop: any;
75
+ setCrop: (crop: any) => void;
76
+ completedCrop: any;
77
+ setCompletedCrop: (crop: any) => void;
78
+ handleCropComplete: (crop: any) => void;
79
+ toggleCropMode: () => void;
80
+ processCompletedCrop: (crop: any) => void;
81
+ processManualCrop: (crop: any) => void;
82
+ resetCrop: () => void;
83
+ product: Product;
84
+ hasUploadedImage: boolean;
85
+ handleFileUpload: (event: React.ChangeEvent<HTMLInputElement>) => void;
86
+ handleResetToOriginal: () => void;
87
+ fileError: string;
88
+ showResetButton?: boolean;
89
+ settings?: any;
90
+ className?: string;
91
+ }
92
+
93
+ export interface ResultsGridProps {
94
+ searchResults: SimilarProductsListResponse | null;
95
+ resultsKey: number;
96
+ isLoading: boolean;
97
+ handlePageChange: (page: number) => void;
98
+ handleLoadMore?: () => void;
99
+ loadedPages?: number[];
100
+ settings?: any;
101
+ className?: string;
102
+ }
103
+
104
+ export interface CustomRendererProps {
105
+ modal?: {
106
+ renderModal?: (props: SimilarProductsModalProps) => React.ReactNode;
107
+ renderHeader?: (props: {
108
+ title: string;
109
+ onClose: () => void;
110
+ }) => React.ReactNode;
111
+ renderCloseButton?: (props: { onClose: () => void }) => React.ReactNode;
112
+ renderActiveFilters?: (props: {
113
+ filters: Array<{ key: string; value: string; label: string }>;
114
+ onRemove: (key: string, value: string) => void;
115
+ isLoading: boolean;
116
+ }) => React.ReactNode;
117
+ renderActiveFilterTag?: (props: {
118
+ filter: { key: string; value: string; label: string };
119
+ onRemove: () => void;
120
+ isLoading: boolean;
121
+ }) => React.ReactNode;
122
+ renderControls?: (props: {
123
+ totalCount: number;
124
+ sorters: SortOption[];
125
+ selectedSorter: string;
126
+ onSortChange: (value: string) => void;
127
+ onFilterMenuToggle: () => void;
128
+ isLoading: boolean;
129
+ }) => React.ReactNode;
130
+ renderSortDropdown?: (props: {
131
+ sorters: SortOption[];
132
+ selectedValue: string;
133
+ onChange: (value: string) => void;
134
+ disabled: boolean;
135
+ }) => React.ReactNode;
136
+ renderItemCount?: (props: {
137
+ count: number;
138
+ isLoading: boolean;
139
+ }) => React.ReactNode;
140
+ renderFilterToggleButton?: (props: {
141
+ onClick: () => void;
142
+ isLoading: boolean;
143
+ }) => React.ReactNode;
144
+ renderEmptyState?: () => React.ReactNode;
145
+ };
146
+ filterSidebar?: {
147
+ renderSidebar?: (props: FilterSidebarProps) => React.ReactNode;
148
+ renderMobileHeader?: (props: {
149
+ title: string;
150
+ itemCount: number;
151
+ onClose: () => void;
152
+ }) => React.ReactNode;
153
+ renderImageSection?: (props: {
154
+ currentImageUrl: string;
155
+ isCropping: boolean;
156
+ onToggleCrop: () => void;
157
+ onFileUpload: (event: React.ChangeEvent<HTMLInputElement>) => void;
158
+ onResetToOriginal: () => void;
159
+ fileError: string;
160
+ isLoading: boolean;
161
+ }) => React.ReactNode;
162
+ renderImageContainer?: (props: {
163
+ imageUrl: string;
164
+ productName: string;
165
+ isCropping: boolean;
166
+ }) => React.ReactNode;
167
+ renderCropButton?: (props: {
168
+ isCropping: boolean;
169
+ onClick: () => void;
170
+ disabled: boolean;
171
+ }) => React.ReactNode;
172
+ renderTickButton?: (props: {
173
+ onClick: () => void;
174
+ disabled: boolean;
175
+ }) => React.ReactNode;
176
+ renderUploadButton?: (props: {
177
+ onClick: () => void;
178
+ disabled: boolean;
179
+ }) => React.ReactNode;
180
+ renderResetButton?: (props: {
181
+ onClick: () => void;
182
+ disabled: boolean;
183
+ showButton: boolean;
184
+ }) => React.ReactNode;
185
+ renderErrorMessage?: (props: { error: string }) => React.ReactNode;
186
+ renderFilterGroup?: (props: {
187
+ facet: Facet;
188
+ onFacetChange: (facetKey: string, choiceValue: string | number) => void;
189
+ isLoading: boolean;
190
+ }) => React.ReactNode;
191
+ renderFilterGroupTitle?: (props: {
192
+ title: string;
193
+ isCollapsed: boolean;
194
+ onToggle: () => void;
195
+ }) => React.ReactNode;
196
+ renderFilterItem?: (props: {
197
+ choice: any;
198
+ facetKey: string;
199
+ onFacetChange: (facetKey: string, choiceValue: string | number) => void;
200
+ isLoading: boolean;
201
+ isSelected: boolean;
202
+ }) => React.ReactNode;
203
+ renderFilterItemInput?: (props: {
204
+ choice: any;
205
+ facetKey: string;
206
+ onChange: () => void;
207
+ checked: boolean;
208
+ disabled: boolean;
209
+ inputType: 'checkbox' | 'radio';
210
+ }) => React.ReactNode;
211
+ renderFilterItemLabel?: (props: {
212
+ choice: any;
213
+ facetKey: string;
214
+ }) => React.ReactNode;
215
+ renderFilterItemCount?: (props: { count: number }) => React.ReactNode;
216
+ renderMobileActiveFilters?: (props: {
217
+ filters: Array<{ key: string; value: string; label: string }>;
218
+ onRemove: (key: string, value: string) => void;
219
+ onClearAll: () => void;
220
+ isLoading: boolean;
221
+ }) => React.ReactNode;
222
+ };
223
+ resultsGrid?: {
224
+ renderGrid?: (props: ResultsGridProps) => React.ReactNode;
225
+ renderGridContainer?: (props: {
226
+ children: React.ReactNode;
227
+ resultsKey: number;
228
+ }) => React.ReactNode;
229
+ renderProductItem?: (props: {
230
+ product: Product;
231
+ index: number;
232
+ }) => React.ReactNode;
233
+ renderProductImage?: (props: {
234
+ product: Product;
235
+ imageUrl: string;
236
+ alt: string;
237
+ index: number;
238
+ }) => React.ReactNode;
239
+ renderProductInfo?: (props: {
240
+ product: Product;
241
+ index: number;
242
+ }) => React.ReactNode;
243
+ renderProductTitle?: (props: {
244
+ product: Product;
245
+ title: string;
246
+ }) => React.ReactNode;
247
+ renderProductPrice?: (props: {
248
+ product: Product;
249
+ price: string;
250
+ oldPrice?: string;
251
+ }) => React.ReactNode;
252
+ renderPagination?: (props: {
253
+ pagination: Pagination;
254
+ onPageChange: (page: number) => void;
255
+ isLoading: boolean;
256
+ }) => React.ReactNode;
257
+ renderPaginationButton?: (props: {
258
+ page: number;
259
+ isActive: boolean;
260
+ isDisabled: boolean;
261
+ onClick: () => void;
262
+ children: React.ReactNode;
263
+ }) => React.ReactNode;
264
+ renderPaginationInfo?: (props: {
265
+ currentPage: number;
266
+ totalPages: number;
267
+ totalCount: number;
268
+ }) => React.ReactNode;
269
+ renderPaginationPrevious?: (props: {
270
+ onClick: () => void;
271
+ disabled: boolean;
272
+ }) => React.ReactNode;
273
+ renderPaginationNext?: (props: {
274
+ onClick: () => void;
275
+ disabled: boolean;
276
+ }) => React.ReactNode;
277
+ renderLoadMore?: (props: {
278
+ onLoadMore: () => void;
279
+ hasMore: boolean;
280
+ isLoading: boolean;
281
+ currentPage: number;
282
+ totalPages: number;
283
+ loadedPages: number;
284
+ }) => React.ReactNode;
285
+ renderLoadMoreButton?: (props: {
286
+ onClick: () => void;
287
+ disabled: boolean;
288
+ isLoading: boolean;
289
+ text: string;
290
+ }) => React.ReactNode;
291
+ renderLoadingState?: () => React.ReactNode;
292
+ renderLoadingOverlay?: () => React.ReactNode;
293
+ renderEmptyState?: () => React.ReactNode;
294
+ };
295
+ imageSearchModal?: {
296
+ renderModal?: (props: any) => React.ReactNode;
297
+ renderUploadArea?: (props: {
298
+ onFileSelect: (file: File) => void;
299
+ acceptedFormats: string[];
300
+ maxFileSize: number;
301
+ }) => React.ReactNode;
302
+ };
303
+ }
304
+
305
+ export interface SimilarProductsSettings {
306
+ maxFileSize?: number;
307
+ imageFormats?: string[];
308
+ cropAspectRatio?: number;
309
+ resultsPerPage?: number;
310
+ enableCropping?: boolean;
311
+ enableFileUpload?: boolean;
312
+ paginationType?: 'pagination' | 'load-more' | 'infinite-scroll';
313
+ loadMoreText?: string;
314
+ loadMoreStyle?: 'button' | 'auto';
315
+ loadMoreThreshold?: number;
316
+ maxPagesLoadMore?: number;
317
+ customStyles?: {
318
+ modal?: string;
319
+ filterSidebar?: string;
320
+ resultsGrid?: string;
321
+ imageSearchModal?: string;
322
+
323
+ activeFiltersContainer?: string;
324
+ activeFilterTag?: string;
325
+ activeFilterTagButton?: string;
326
+
327
+ controlsContainer?: string;
328
+ itemCount?: string;
329
+ sortDropdown?: string;
330
+ filterToggleButton?: string;
331
+
332
+ filterSidebarMobileHeader?: string;
333
+ filterGroup?: string;
334
+ filterGroupTitle?: string;
335
+ filterGroupContent?: string;
336
+ filterItem?: string;
337
+ filterItemInput?: string;
338
+ filterItemLabel?: string;
339
+ filterItemCount?: string;
340
+
341
+ imageSection?: string;
342
+ imageContainer?: string;
343
+ imageWrapper?: string;
344
+ cropButton?: string;
345
+ tickButton?: string;
346
+ uploadButton?: string;
347
+ resetButton?: string;
348
+ errorMessage?: string;
349
+ cropControls?: string;
350
+
351
+ resultsContainer?: string;
352
+ gridContainer?: string;
353
+ productItem?: string;
354
+ productImageWrapper?: string;
355
+ productImage?: string;
356
+ productInfo?: string;
357
+ productTitle?: string;
358
+ productPrice?: string;
359
+ productOldPrice?: string;
360
+
361
+ pagination?: string;
362
+ paginationContainer?: string;
363
+ paginationInfo?: string;
364
+ paginationButton?: string;
365
+ paginationButtonActive?: string;
366
+ paginationButtonDisabled?: string;
367
+ paginationPrevious?: string;
368
+ paginationNext?: string;
369
+ loadMoreButton?: string;
370
+ loadMoreContainer?: string;
371
+
372
+ loadingSpinner?: string;
373
+ loadingOverlay?: string;
374
+ emptyState?: string;
375
+ emptyStateIcon?: string;
376
+ emptyStateText?: string;
377
+
378
+ mobileActiveFilters?: string;
379
+ mobileActiveFilterTag?: string;
380
+ mobileClearAllButton?: string;
381
+ };
382
+ customRenderers?: {
383
+ Modal?: React.ComponentType<SimilarProductsModalProps>;
384
+ FilterSidebar?: React.ComponentType<FilterSidebarProps>;
385
+ ResultsGrid?: React.ComponentType<ResultsGridProps>;
386
+ ImageSearchModal?: React.ComponentType<any>;
387
+
388
+ render?: CustomRendererProps;
389
+ };
390
+ theme?: {
391
+ colors?: {
392
+ primary?: string;
393
+ secondary?: string;
394
+ background?: string;
395
+ text?: string;
396
+ border?: string;
397
+ success?: string;
398
+ error?: string;
399
+ warning?: string;
400
+ };
401
+ spacing?: {
402
+ xs?: string;
403
+ sm?: string;
404
+ md?: string;
405
+ lg?: string;
406
+ xl?: string;
407
+ };
408
+ borderRadius?: {
409
+ sm?: string;
410
+ md?: string;
411
+ lg?: string;
412
+ };
413
+ fonts?: {
414
+ primary?: string;
415
+ secondary?: string;
416
+ };
417
+ };
418
+ cssVariables?: Record<string, string>;
419
+ }