@akinon/pz-virtual-try-on 1.119.0-rc.3 → 2.0.0-beta.13

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.
@@ -1,6 +1,5 @@
1
1
  import { useState, useRef, useCallback } from 'react';
2
2
  import 'react-image-crop/dist/ReactCrop.css';
3
- import { validateAspectRatioFromDimensions } from '../utils';
4
3
 
5
4
  type CropType = {
6
5
  unit: 'px' | '%';
@@ -10,16 +9,10 @@ type CropType = {
10
9
  height: number;
11
10
  };
12
11
 
13
- export interface CropResult {
14
- success: boolean;
15
- error?: string;
16
- }
17
-
18
12
  export function useImageCropper(
19
13
  setIsLoading: (loading: boolean) => void,
20
14
  processImage: (base64: string) => void,
21
- clearError?: () => void,
22
- onAspectRatioError?: (error: string) => void
15
+ clearError?: () => void
23
16
  ) {
24
17
  const [isCropping, setIsCropping] = useState(false);
25
18
  const [crop, setCrop] = useState<CropType>({
@@ -71,12 +64,10 @@ export function useImageCropper(
71
64
  }
72
65
  };
73
66
 
74
- const processCompletedCropImmediate = async (
75
- crop: any
76
- ): Promise<CropResult> => {
67
+ const processCompletedCropImmediate = async (crop: any) => {
77
68
  if (!imageRef.current || !crop?.width || !crop?.height) {
78
69
  setIsLoading(false);
79
- return { success: false, error: 'Invalid crop dimensions' };
70
+ return;
80
71
  }
81
72
 
82
73
  if (clearError) {
@@ -91,28 +82,8 @@ export function useImageCropper(
91
82
  const scaleX = image.naturalWidth / image.width;
92
83
  const scaleY = image.naturalHeight / image.height;
93
84
 
94
- const croppedWidth = crop.width * scaleX;
95
- const croppedHeight = crop.height * scaleY;
96
-
97
- // Validate aspect ratio before processing
98
- const aspectRatioValidation = validateAspectRatioFromDimensions(
99
- croppedWidth,
100
- croppedHeight
101
- );
102
-
103
- if (!aspectRatioValidation.isValid) {
104
- setIsLoading(false);
105
- if (onAspectRatioError && aspectRatioValidation.error) {
106
- onAspectRatioError(aspectRatioValidation.error);
107
- }
108
- return {
109
- success: false,
110
- error: aspectRatioValidation.error
111
- };
112
- }
113
-
114
- canvas.width = croppedWidth;
115
- canvas.height = croppedHeight;
85
+ canvas.width = crop.width * scaleX;
86
+ canvas.height = crop.height * scaleY;
116
87
 
117
88
  const ctx = canvas.getContext('2d');
118
89
 
@@ -123,8 +94,8 @@ export function useImageCropper(
123
94
 
124
95
  const sourceX = crop.x * scaleX;
125
96
  const sourceY = crop.y * scaleY;
126
- const sourceWidth = croppedWidth;
127
- const sourceHeight = croppedHeight;
97
+ const sourceWidth = crop.width * scaleX;
98
+ const sourceHeight = crop.height * scaleY;
128
99
 
129
100
  ctx.drawImage(
130
101
  image,
@@ -144,11 +115,9 @@ export function useImageCropper(
144
115
  setCropProcessed(true);
145
116
  setLastSuccessfulCrop(crop);
146
117
  setIsLoading(false);
147
- return { success: true };
148
118
  } catch (error) {
149
119
  console.error('❌ processCompletedCrop failed:', error);
150
120
  setIsLoading(false);
151
- return { success: false, error: 'Failed to process crop' };
152
121
  }
153
122
  };
154
123
 
@@ -187,28 +156,10 @@ export function useImageCropper(
187
156
  });
188
157
  };
189
158
 
190
- const closeCrop = () => {
191
- if (debounceTimeoutRef.current) {
192
- clearTimeout(debounceTimeoutRef.current);
193
- }
194
-
195
- setIsCropping(false);
196
- setCompletedCrop(null);
197
- setCrop(undefined as any);
198
- };
199
-
200
159
  const confirmCrop = useCallback(async () => {
201
160
  if (completedCrop) {
202
161
  await processManualCrop(completedCrop);
203
162
  setIsCropping(false);
204
- setCompletedCrop(null);
205
- setCrop({
206
- unit: '%',
207
- x: 25,
208
- y: 25,
209
- width: 50,
210
- height: 50
211
- });
212
163
  }
213
164
  }, [completedCrop, processManualCrop]);
214
165
 
@@ -224,7 +175,6 @@ export function useImageCropper(
224
175
  processCompletedCrop,
225
176
  processManualCrop,
226
177
  confirmCrop,
227
- resetCrop,
228
- closeCrop
178
+ resetCrop
229
179
  };
230
180
  }
@@ -353,7 +353,6 @@ export function useVirtualTryOnAsync(
353
353
  }, [
354
354
  uploadedImage,
355
355
  productsArray,
356
- categoryMapping,
357
356
  startAsyncTryOn,
358
357
  getJobStatus,
359
358
  abortController
@@ -15,8 +15,7 @@ import {
15
15
  isVirtualTryOnEnabled,
16
16
  VirtualTryOnCache,
17
17
  compressImageToUnder1MB,
18
- convertWebPToJPEG,
19
- validateImageAspectRatio
18
+ convertWebPToJPEG
20
19
  } from '../utils';
21
20
  import { useImageCropper } from './use-image-cropper';
22
21
  import type { VirtualTryOnResponse } from '../types';
@@ -113,10 +112,7 @@ export function useVirtualTryOn(product: Product) {
113
112
  (croppedBase64) => {
114
113
  setUploadedImage(croppedBase64);
115
114
  },
116
- () => setFileError(''),
117
- (aspectRatioError) => {
118
- setFileError(aspectRatioError);
119
- }
115
+ () => setFileError('')
120
116
  );
121
117
 
122
118
  useEffect(() => {
@@ -182,14 +178,6 @@ export function useVirtualTryOn(product: Product) {
182
178
  return;
183
179
  }
184
180
 
185
- const aspectRatioValidation = await validateImageAspectRatio(
186
- processedFile
187
- );
188
- if (!aspectRatioValidation.isValid) {
189
- setFileError(aspectRatioValidation.error || 'Invalid aspect ratio');
190
- return;
191
- }
192
-
193
181
  const base64Image = await fileToBase64(processedFile);
194
182
  setUploadedImage(base64Image);
195
183
  setOriginalImage(base64Image);
@@ -365,7 +353,6 @@ export function useVirtualTryOn(product: Product) {
365
353
  data.error || data.result?.error || 'Processing failed';
366
354
  setError(errorMsg);
367
355
  setCurrentStep('processing');
368
- cropperHook.closeCrop();
369
356
  return;
370
357
  }
371
358
  } catch (pollError: any) {
@@ -383,7 +370,6 @@ export function useVirtualTryOn(product: Product) {
383
370
  error?.message === 'Request aborted'
384
371
  ) {
385
372
  setCurrentStep('upload');
386
- cropperHook.closeCrop();
387
373
  return;
388
374
  }
389
375
 
@@ -392,23 +378,11 @@ export function useVirtualTryOn(product: Product) {
392
378
  error?.message ||
393
379
  'Virtual try-on processing failed'
394
380
  );
395
- setError(
396
- error?.data?.message ||
397
- error?.message ||
398
- 'Virtual try-on processing failed'
399
- );
400
- setCurrentStep('processing');
401
- cropperHook.closeCrop();
381
+ setCurrentStep('upload');
402
382
  } finally {
403
383
  setAbortController(null);
404
384
  }
405
- }, [
406
- uploadedImage,
407
- product,
408
- processVirtualTryOn,
409
- abortController,
410
- cropperHook
411
- ]);
385
+ }, [uploadedImage, product, processVirtualTryOn, abortController]);
412
386
 
413
387
  const acceptLegalConsent = useCallback(() => {
414
388
  setLegalConsentAccepted();
package/src/index.ts CHANGED
@@ -9,16 +9,10 @@ export { VirtualTryOnProductSelector } from './views/virtual-try-on-product-sele
9
9
  export { VirtualTryOnAsyncModal } from './views/virtual-try-on-async-modal';
10
10
  export { BasketAsyncModal } from './views/basket-async-modal';
11
11
 
12
- export { BarcodeScannerPlugin } from './views/barcode-scanner-plugin';
13
- export { BarcodeScannerModal } from './views/barcode-scanner-modal';
14
- export { BarcodeScannerButton } from './views/barcode-scanner-button';
15
- export { BarcodeScanner } from './components/barcode-scanner';
16
-
17
12
  export { ProcessingSpinner } from './components/processing-spinner';
18
13
 
19
14
  export { useVirtualTryOn } from './hooks/use-virtual-try-on';
20
15
  export { useImageCropper } from './hooks/use-image-cropper';
21
- export { useBarcodeSearch } from './hooks/use-barcode-search';
22
16
 
23
17
  export { useVirtualTryOnAsync } from './hooks/use-virtual-try-on-async';
24
18
 
@@ -30,11 +24,6 @@ export {
30
24
  useSubmitVirtualTryOnFeedbackMutation
31
25
  } from './data/endpoints';
32
26
 
33
- export {
34
- useSearchProductByBarcodeQuery,
35
- useLazySearchProductByBarcodeQuery
36
- } from './data/barcode-endpoints';
37
-
38
27
  export type {
39
28
  VirtualTryOnResponse,
40
29
  VirtualTryOnModalProps,
@@ -49,21 +38,7 @@ export type {
49
38
  VirtualTryOnMultipleResult,
50
39
  VirtualTryOnCompatibilityError,
51
40
  BasketProduct,
52
- VirtualTryOnMultipleModalProps,
53
- BarcodeFormat,
54
- BarcodeScanResult,
55
- BarcodeSearchResponse,
56
- BarcodeSearchRequest,
57
- BarcodeScannerState,
58
- ScannedProductState,
59
- BarcodeScannerButtonProps,
60
- BarcodeScannerModalProps,
61
- BarcodeScannerProps,
62
- ScannedProductCardProps,
63
- BarcodeScannerSettings,
64
- BarcodeScannerTheme,
65
- BarcodeScannerCustomStyles,
66
- BarcodeScannerCustomRenderers
41
+ VirtualTryOnMultipleModalProps
67
42
  } from './types';
68
43
 
69
44
  export * from './utils';
@@ -2,9 +2,6 @@ import React from 'react';
2
2
  import { Product } from '@akinon/next/types';
3
3
  import { useVirtualTryOn } from '../hooks/use-virtual-try-on';
4
4
 
5
- // Re-export barcode types
6
- export * from './barcode';
7
-
8
5
  export interface VirtualTryOnResponse {
9
6
  reference: string;
10
7
  generated: string;
@@ -564,32 +561,6 @@ export interface CustomRendererProps {
564
561
  };
565
562
  }
566
563
 
567
- export interface VirtualTryOnModalImages {
568
- ruleGoodExample?: string;
569
- ruleBadExample1?: string;
570
- ruleBadExample2?: string;
571
- ruleBadExample3?: string;
572
- ruleBadExample4?: string;
573
- uploadIcon?: string;
574
- }
575
-
576
- export interface VirtualTryOnModalTexts {
577
- title?: string;
578
- editPhotoTitle?: string;
579
- uploadPrompt?: string;
580
- uploadRequirements?: string;
581
- uploadInfo?: string;
582
- rule1?: string;
583
- rule2?: string;
584
- rule3?: string;
585
- rule4?: string;
586
- rule5?: string;
587
- rule6?: string;
588
- processingTitle?: string;
589
- processingMessage?: string;
590
- retryUpload?: string;
591
- }
592
-
593
564
  export interface VirtualTryOnPluginSettings {
594
565
  maxFileSize?: number;
595
566
  allowedFormats?: string[];
@@ -600,7 +571,6 @@ export interface VirtualTryOnPluginSettings {
600
571
  buttonSize?: 'sm' | 'md' | 'lg';
601
572
  legal_text?: string;
602
573
  instructions?: string;
603
-
604
574
  customStyles?: {
605
575
  plugin?: string;
606
576
  button?: string;
@@ -639,8 +609,6 @@ export interface VirtualTryOnPluginSettings {
639
609
  uploadRequirements?: string;
640
610
  uploadRequirementsError?: string;
641
611
  uploadButton?: string;
642
- uploadModalImages?: VirtualTryOnModalImages;
643
- uploadModalTexts?: VirtualTryOnModalTexts;
644
612
  fileInput?: string;
645
613
 
646
614
  rulesInfoText?: string;
@@ -11,9 +11,7 @@ export const ERROR_MESSAGES: Record<string, string> = {
11
11
  background: 'Görüntü arka planı uygun değil.',
12
12
  political: 'Siyasi içerik tespit edildi.',
13
13
  others: 'Görüntü uygun değil.',
14
- empty: 'Görüntüde insan tespit edilemedi.',
15
- aspect_ratio_too_tall: `Görsel çok uzun. Dikey görseller için izin verilen maksimum oran 9:16'dır. Lütfen kırpın veya farklı bir görsel seçin.`,
16
- aspect_ratio_too_wide: `Görsel çok geniş. Yatay görseller için izin verilen maksimum oran 21:9'dur. Lütfen kırpın veya farklı bir görsel seçin.`
14
+ empty: 'Görüntüde insan tespit edilemedi.'
17
15
  };
18
16
 
19
17
  export function getErrorMessage(
@@ -1,120 +1,5 @@
1
1
  export { parseVirtualTryOnError } from './error-parser';
2
2
 
3
- export const ASPECT_RATIO_LIMITS = {
4
- MIN_RATIO: 9 / 16,
5
- MAX_RATIO: 21 / 9
6
- } as const;
7
-
8
- export interface AspectRatioValidation {
9
- isValid: boolean;
10
- error?: string;
11
- ratio?: number;
12
- width?: number;
13
- height?: number;
14
- }
15
-
16
- export const getImageDimensions = (
17
- source: string | File
18
- ): Promise<{ width: number; height: number }> => {
19
- return new Promise((resolve, reject) => {
20
- const img = new Image();
21
-
22
- img.onload = () => {
23
- resolve({
24
- width: img.naturalWidth,
25
- height: img.naturalHeight
26
- });
27
- URL.revokeObjectURL(img.src);
28
- };
29
-
30
- img.onerror = () => {
31
- reject(new Error('Failed to load image for dimension check'));
32
- URL.revokeObjectURL(img.src);
33
- };
34
-
35
- if (typeof source === 'string') {
36
- img.src = source;
37
- } else {
38
- img.src = URL.createObjectURL(source);
39
- }
40
- });
41
- };
42
-
43
- export const validateImageAspectRatio = async (
44
- source: string | File
45
- ): Promise<AspectRatioValidation> => {
46
- try {
47
- const { width, height } = await getImageDimensions(source);
48
- const ratio = width / height;
49
-
50
- if (ratio < ASPECT_RATIO_LIMITS.MIN_RATIO) {
51
- return {
52
- isValid: false,
53
- error: 'aspect_ratio_too_tall',
54
- ratio,
55
- width,
56
- height
57
- };
58
- }
59
-
60
- if (ratio > ASPECT_RATIO_LIMITS.MAX_RATIO) {
61
- return {
62
- isValid: false,
63
- error: 'aspect_ratio_too_wide',
64
- ratio,
65
- width,
66
- height
67
- };
68
- }
69
-
70
- return {
71
- isValid: true,
72
- ratio,
73
- width,
74
- height
75
- };
76
- } catch (error) {
77
- return {
78
- isValid: false,
79
- error: 'Failed to validate image dimensions'
80
- };
81
- }
82
- };
83
-
84
- export const validateAspectRatioFromDimensions = (
85
- width: number,
86
- height: number
87
- ): AspectRatioValidation => {
88
- const ratio = width / height;
89
-
90
- if (ratio < ASPECT_RATIO_LIMITS.MIN_RATIO) {
91
- return {
92
- isValid: false,
93
- error: 'aspect_ratio_too_tall',
94
- ratio,
95
- width,
96
- height
97
- };
98
- }
99
-
100
- if (ratio > ASPECT_RATIO_LIMITS.MAX_RATIO) {
101
- return {
102
- isValid: false,
103
- error: 'aspect_ratio_too_wide',
104
- ratio,
105
- width,
106
- height
107
- };
108
- }
109
-
110
- return {
111
- isValid: true,
112
- ratio,
113
- width,
114
- height
115
- };
116
- };
117
-
118
3
  export const validateImageFile = (
119
4
  file: File
120
5
  ): { isValid: boolean; error?: string } => {
@@ -17,8 +17,6 @@ export interface BasketAsyncModalProps {
17
17
  hookData: ReturnType<typeof useVirtualTryOnAsync>;
18
18
  onRetryUpload?: () => void;
19
19
  settings?: VirtualTryOnPluginSettings;
20
- /** Source of the modal - affects button text */
21
- source?: 'basket' | 'barcode';
22
20
  }
23
21
 
24
22
  type ModalStep = 'processing' | 'results' | 'detail';
@@ -30,8 +28,7 @@ export function BasketAsyncModal({
30
28
  onComplete,
31
29
  hookData,
32
30
  onRetryUpload,
33
- settings,
34
- source = 'basket'
31
+ settings
35
32
  }: BasketAsyncModalProps) {
36
33
  const { t } = useLocalization();
37
34
  const [currentStep, setCurrentStep] = useState<ModalStep>('processing');
@@ -519,9 +516,7 @@ export function BasketAsyncModal({
519
516
  settings?.customStyles?.basketAsyncModalBackButton
520
517
  )}
521
518
  >
522
- {source === 'barcode'
523
- ? t('product.virtual_try_on.close_button')
524
- : t('product.virtual_try_on.back_to_basket')}
519
+ {t('product.virtual_try_on.back_to_basket')}
525
520
  </button>
526
521
  </div>
527
522
  </div>
@@ -1,6 +1,6 @@
1
1
  'use client';
2
2
 
3
- import React, { useState, useMemo, useEffect } from 'react';
3
+ import React, { useState, useMemo } from 'react';
4
4
  import { Product } from '@akinon/next/types';
5
5
  import { twMerge } from 'tailwind-merge';
6
6
  import { VirtualTryOnModal } from './virtual-try-on-modal';
@@ -30,18 +30,6 @@ export function VirtualTryOnPlugin({
30
30
  const [isModalLoading, setIsModalLoading] = useState(false);
31
31
  const { isEnabled } = useVirtualTryOn(product);
32
32
 
33
- useEffect(() => {
34
- return () => {
35
- document.documentElement.style.overflow = '';
36
- };
37
- }, []);
38
-
39
- useEffect(() => {
40
- if (!isModalOpen && !isModalLoading) {
41
- document.documentElement.style.overflow = '';
42
- }
43
- }, [isModalOpen, isModalLoading]);
44
-
45
33
  const cssVariables = useMemo(() => {
46
34
  if (!settings?.cssVariables && !settings?.theme) return {};
47
35
 
@@ -110,12 +98,10 @@ export function VirtualTryOnPlugin({
110
98
  setTimeout(() => {
111
99
  setIsModalLoading(false);
112
100
  setIsModalOpen(true);
113
- document.documentElement.style.overflow = 'hidden';
114
101
  }, 500);
115
102
  };
116
103
 
117
104
  const handleCloseModal = () => {
118
- document.documentElement.style.overflow = '';
119
105
  setIsModalOpen(false);
120
106
  setIsModalLoading(false);
121
107
  };