@pagamio/frontend-commons-lib 0.8.348 → 0.8.350

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.
@@ -82,6 +82,11 @@ export interface UsernamePinLoginConfig {
82
82
  tabLabel?: string;
83
83
  /** Optional label for the password tab (default: 'Email & Password') */
84
84
  passwordTabLabel?: string;
85
+ /**
86
+ * Optional handler for the "Forgot PIN?" CTA shown beneath the
87
+ * Operator ID step. When omitted, the CTA is hidden.
88
+ */
89
+ onForgotPin?: () => void;
85
90
  }
86
91
  /**
87
92
  * Base login credentials interface that can be extended for specific implementations
@@ -360,7 +360,7 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
360
360
  setPhoneStep('enterPhone');
361
361
  setPhonePinValue('');
362
362
  setPhoneError(null);
363
- }, className: "w-full text-sm text-foreground/70", children: "\u2190 Change number" })] }))] })), activeTab === 'username' && showUsernameTab && (_jsxs("div", { className: "space-y-4", children: [usernameError && _jsx("div", { className: "rounded-lg bg-red-50 p-4 text-sm text-red-600", children: usernameError }), usernameStep === 'enterLoginNumber' && (_jsx(FormEngine, { fields: loginNumberField, onSubmit: handleLoginNumberFormSubmit, layout: "vertical", className: "mb-0 px-0", submitButtonClass: "w-full", submitButtonText: "Continue", onCancel: () => { }, showCancelButton: false, showSubmittingText: false, initialValues: { loginNumber: '' } })), usernameStep === 'enterPin' && (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-sm text-muted-foreground text-center", children: ["Enter your 4-digit PIN for ", _jsx("span", { className: "font-medium text-foreground", children: loginNumber })] }), _jsxs("div", { children: [_jsx("p", { className: "mb-2 block text-sm font-medium text-foreground text-center", children: "PIN" }), _jsx(OtpInput, { length: 4, value: loginNumberPinValue, onChange: (v) => {
363
+ }, className: "w-full text-sm text-foreground/70", children: "\u2190 Change number" })] }))] })), activeTab === 'username' && showUsernameTab && (_jsxs("div", { className: "space-y-4", children: [usernameError && _jsx("div", { className: "rounded-lg bg-red-50 p-4 text-sm text-red-600", children: usernameError }), usernameStep === 'enterLoginNumber' && (_jsxs(_Fragment, { children: [_jsx(FormEngine, { fields: loginNumberField, onSubmit: handleLoginNumberFormSubmit, layout: "vertical", className: "mb-0 px-0", submitButtonClass: "w-full", submitButtonText: "Continue", onCancel: () => { }, showCancelButton: false, showSubmittingText: false, initialValues: { loginNumber: '' } }), (hasCreateAccount || usernamePinConfig?.onForgotPin) && (_jsxs("div", { className: "flex items-center justify-center gap-4 mt-2", children: [usernamePinConfig?.onForgotPin && (_jsx(Button, { type: "button", variant: "link", onClick: usernamePinConfig.onForgotPin, className: "text-sm text-primary hover:underline", children: "Forgot PIN?" })), hasCreateAccount && usernamePinConfig?.onForgotPin && (_jsx("span", { className: "text-muted-foreground select-none", children: "|" })), hasCreateAccount && (_jsx(Button, { type: "button", variant: "link", onClick: handleCreateAccount, className: "text-sm text-primary hover:underline", children: text.createAccountLabel }))] }))] })), usernameStep === 'enterPin' && (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-sm text-muted-foreground text-center", children: ["Enter your 4-digit PIN for ", _jsx("span", { className: "font-medium text-foreground", children: loginNumber })] }), _jsxs("div", { children: [_jsx("p", { className: "mb-2 block text-sm font-medium text-foreground text-center", children: "PIN" }), _jsx(OtpInput, { length: 4, value: loginNumberPinValue, onChange: (v) => {
364
364
  setLoginNumberPinValue(v);
365
365
  setUsernameError(null);
366
366
  }, disabled: isLoggingInWithUsername })] }), _jsx(Button, { type: "button", onClick: handleLoginWithUsername, disabled: loginNumberPinValue.length !== 4 || isLoggingInWithUsername, className: "w-full", size: "lg", children: isLoggingInWithUsername ? 'Signing in...' : 'Sign In' }), _jsx(Button, { type: "button", variant: "ghost", onClick: () => {
@@ -13,6 +13,12 @@ export interface ImageUploaderProps {
13
13
  showPreview?: boolean;
14
14
  value?: string;
15
15
  onChange?: (url: string | null) => void;
16
+ /**
17
+ * When true (default) the image is resized to 1920×1920 and converted
18
+ * to WebP before upload — correct for photos. Set false for assets that
19
+ * must keep their exact bytes/dimensions
20
+ */
21
+ processImage?: boolean;
16
22
  }
17
23
  declare const ImageUploader: React.FC<ImageUploaderProps>;
18
24
  export default ImageUploader;
@@ -1,18 +1,21 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Image as ImageIcon, Loader2, Upload, X } from 'lucide-react';
3
3
  import { useDropzone } from 'react-dropzone';
4
- import { useCallback, useState } from 'react';
4
+ import { useCallback, useMemo, useState } from 'react';
5
5
  import { Button, cn, useToast } from '../..';
6
6
  import { useImageUpload } from '../../shared/hooks/useImageUpload';
7
7
  import { Progress } from './Progress';
8
+ const PHOTO_FILE_TYPES = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'];
9
+ const RAW_FILE_TYPES = ['image/png', 'image/svg+xml', 'image/webp', 'image/x-icon', 'image/vnd.microsoft.icon'];
8
10
  const ImageUploader = ({ project, env, endpoint, onUploadSuccess, onError, className, disabled = false, maxFileSize = 5 * 1024 * 1024, // 5MB default
9
- acceptedFileTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], placeholder = 'Click to upload or drag and drop an image', showPreview = true, value, onChange, }) => {
11
+ acceptedFileTypes, placeholder = 'Click to upload or drag and drop an image', showPreview = true, value, onChange, processImage = true, }) => {
12
+ const resolvedFileTypes = useMemo(() => acceptedFileTypes ?? (processImage ? PHOTO_FILE_TYPES : RAW_FILE_TYPES), [acceptedFileTypes, processImage]);
10
13
  const [previewUrl, setPreviewUrl] = useState(value || null);
11
14
  const [isUploading, setIsUploading] = useState(false);
12
15
  const [uploadProgress, setUploadProgress] = useState(0);
13
16
  const [error, setError] = useState(null);
14
17
  const { addToast } = useToast();
15
- const { uploadFile } = useImageUpload({ project, env, endpoint });
18
+ const { uploadFile } = useImageUpload({ project, env, endpoint, processImage });
16
19
  const handleFileUpload = async (file) => {
17
20
  if (disabled)
18
21
  return;
@@ -21,8 +24,8 @@ acceptedFileTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], plac
21
24
  setUploadProgress(0);
22
25
  try {
23
26
  // Validate file type
24
- if (!acceptedFileTypes.includes(file.type)) {
25
- throw new Error(`File type not supported. Accepted types: ${acceptedFileTypes.join(', ')}`);
27
+ if (!resolvedFileTypes.includes(file.type)) {
28
+ throw new Error(`File type not supported. Accepted types: ${resolvedFileTypes.join(', ')}`);
26
29
  }
27
30
  // Validate file size
28
31
  if (file.size > maxFileSize) {
@@ -76,10 +79,10 @@ acceptedFileTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], plac
76
79
  if (file) {
77
80
  handleFileUpload(file);
78
81
  }
79
- }, [disabled, acceptedFileTypes, maxFileSize]);
82
+ }, [disabled, resolvedFileTypes, maxFileSize]);
80
83
  const { getRootProps, getInputProps, isDragActive } = useDropzone({
81
84
  onDrop,
82
- accept: acceptedFileTypes.reduce((acc, type) => {
85
+ accept: resolvedFileTypes.reduce((acc, type) => {
83
86
  acc[type] = [];
84
87
  return acc;
85
88
  }, {}),
@@ -91,6 +94,6 @@ acceptedFileTypes = ['image/jpeg', 'image/png', 'image/gif', 'image/webp'], plac
91
94
  setError(null);
92
95
  onChange?.(null);
93
96
  };
94
- return (_jsxs("div", { className: cn('w-full', className), children: [previewUrl && showPreview ? (_jsxs("div", { className: "relative mb-4", children: [_jsx("img", { src: previewUrl, alt: "Preview", className: "max-h-64 w-full rounded-lg object-contain border border-border" }), !isUploading && (_jsx(Button, { type: "button", variant: "destructive", size: "sm", className: "absolute top-2 right-2", onClick: handleRemove, children: _jsx(X, { className: "h-4 w-4" }) }))] })) : (_jsxs("div", { ...getRootProps(), className: cn('border-2 border-dashed border-input rounded-lg p-8 text-center cursor-pointer transition-colors hover:border-muted-foreground', isDragActive && 'border-primary bg-primary/10', disabled && 'cursor-not-allowed opacity-50', error && 'border-red-500 bg-red-50'), children: [_jsx("input", { ...getInputProps() }), isUploading ? (_jsxs("div", { className: "flex flex-col items-center space-y-2", children: [_jsx(Loader2, { className: "h-8 w-8 animate-spin text-primary" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Uploading image..." }), _jsx(Progress, { value: uploadProgress, className: "w-full max-w-xs" })] })) : (_jsxs("div", { className: "flex flex-col items-center space-y-2", children: [isDragActive ? (_jsx(Upload, { className: "h-8 w-8 text-primary" })) : (_jsx(ImageIcon, { className: "h-8 w-8 text-muted-foreground" })), _jsx("p", { className: "text-sm text-muted-foreground", children: isDragActive ? 'Drop the image here' : placeholder }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Supported formats: ", acceptedFileTypes.map((type) => type.split('/')[1]).join(', ')] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Max size: ", Math.round(maxFileSize / 1024 / 1024), "MB"] })] }))] })), error && _jsx("p", { className: "mt-2 text-sm text-red-600", children: error })] }));
97
+ return (_jsxs("div", { className: cn('w-full', className), children: [previewUrl && showPreview ? (_jsxs("div", { className: "relative mb-4", children: [_jsx("img", { src: previewUrl, alt: "Preview", className: "max-h-64 w-full rounded-lg object-contain border border-border" }), !isUploading && (_jsx(Button, { type: "button", variant: "destructive", size: "sm", className: "absolute top-2 right-2", onClick: handleRemove, children: _jsx(X, { className: "h-4 w-4" }) }))] })) : (_jsxs("div", { ...getRootProps(), className: cn('border-2 border-dashed border-input rounded-lg p-8 text-center cursor-pointer transition-colors hover:border-muted-foreground', isDragActive && 'border-primary bg-primary/10', disabled && 'cursor-not-allowed opacity-50', error && 'border-red-500 bg-red-50'), children: [_jsx("input", { ...getInputProps() }), isUploading ? (_jsxs("div", { className: "flex flex-col items-center space-y-2", children: [_jsx(Loader2, { className: "h-8 w-8 animate-spin text-primary" }), _jsx("p", { className: "text-sm text-muted-foreground", children: "Uploading image..." }), _jsx(Progress, { value: uploadProgress, className: "w-full max-w-xs" })] })) : (_jsxs("div", { className: "flex flex-col items-center space-y-2", children: [isDragActive ? (_jsx(Upload, { className: "h-8 w-8 text-primary" })) : (_jsx(ImageIcon, { className: "h-8 w-8 text-muted-foreground" })), _jsx("p", { className: "text-sm text-muted-foreground", children: isDragActive ? 'Drop the image here' : placeholder }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Supported formats: ", resolvedFileTypes.map((type) => type.split('/')[1]).join(', ')] }), _jsxs("p", { className: "text-xs text-muted-foreground", children: ["Max size: ", Math.round(maxFileSize / 1024 / 1024), "MB"] })] }))] })), error && _jsx("p", { className: "mt-2 text-sm text-red-600", children: error })] }));
95
98
  };
96
99
  export default ImageUploader;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@pagamio/frontend-commons-lib",
3
3
  "description": "Pagamio library for Frontend reusable components like the form engine and table container",
4
- "version": "0.8.348",
4
+ "version": "0.8.350",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "provenance": false