@pagamio/frontend-commons-lib 0.8.279 → 0.8.280

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.
@@ -226,10 +226,21 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
226
226
  };
227
227
  const passwordTabLabel = phoneOtpConfig?.passwordTabLabel ?? 'Email & Password';
228
228
  const phoneTabLabel = phoneOtpConfig?.tabLabel ?? 'Phone + OTP';
229
- return (_jsx(AuthPageLayout, { appLabel: appLabel, title: text.welcomeTitle, subtitle: text.welcomeSubtitle, errorMessage: activeTab === 'password' ? (error ?? authError?.message) : undefined, logo: logo, className: className, horizontal: false, sideContent: features && features.length > 0 ? _jsx(FeatureCarousel, { features: features }) : undefined, sideContentClass: sideContentClass, footer: footer, children: _jsxs("div", { className: "mt-8", children: [phoneOtpConfig && (_jsxs("div", { className: "mb-6 flex rounded-lg border border-border overflow-hidden", children: [_jsx("button", { type: "button", onClick: () => setActiveTab('password'), className: `flex-1 py-2 text-sm font-medium transition-colors ${activeTab === 'password' ? 'bg-primary text-primary-foreground' : 'bg-background text-foreground/70 hover:bg-muted'}`, children: passwordTabLabel }), _jsx("button", { type: "button", onClick: () => { setActiveTab('phone'); setPhoneStep('enterPhone'); setPhoneError(null); }, className: `flex-1 py-2 text-sm font-medium transition-colors ${activeTab === 'phone' ? 'bg-primary text-primary-foreground' : 'bg-background text-foreground/70 hover:bg-muted'}`, children: phoneTabLabel })] })), activeTab === 'password' && (_jsxs(_Fragment, { children: [_jsx(FormEngine, { fields: loginFields, onSubmit: handleSubmit, layout: "vertical", className: "mb-0 px-0", submitButtonClass: "w-full", submitButtonText: isLoading ? text.loadingButtonLabel : text.loginButtonLabel, onCancel: () => { }, showCancelButton: false, showSubmittingText: false, formRef: formRef, initialValues: {
229
+ return (_jsx(AuthPageLayout, { appLabel: appLabel, title: text.welcomeTitle, subtitle: text.welcomeSubtitle, errorMessage: activeTab === 'password' ? (error ?? authError?.message) : undefined, logo: logo, className: className, horizontal: false, sideContent: features && features.length > 0 ? _jsx(FeatureCarousel, { features: features }) : undefined, sideContentClass: sideContentClass, footer: footer, children: _jsxs("div", { className: "mt-8", children: [phoneOtpConfig && (_jsxs("div", { className: "mb-6 flex rounded-lg border border-border overflow-hidden", children: [_jsx("button", { type: "button", onClick: () => setActiveTab('password'), className: `flex-1 py-2 text-sm font-medium transition-colors ${activeTab === 'password' ? 'bg-primary text-primary-foreground' : 'bg-background text-foreground/70 hover:bg-muted'}`, children: passwordTabLabel }), _jsx("button", { type: "button", onClick: () => {
230
+ setActiveTab('phone');
231
+ setPhoneStep('enterPhone');
232
+ setPhoneError(null);
233
+ }, className: `flex-1 py-2 text-sm font-medium transition-colors ${activeTab === 'phone' ? 'bg-primary text-primary-foreground' : 'bg-background text-foreground/70 hover:bg-muted'}`, children: phoneTabLabel })] })), activeTab === 'password' && (_jsxs(_Fragment, { children: [_jsx(FormEngine, { fields: loginFields, onSubmit: handleSubmit, layout: "vertical", className: "mb-0 px-0", submitButtonClass: "w-full", submitButtonText: isLoading ? text.loadingButtonLabel : text.loginButtonLabel, onCancel: () => { }, showCancelButton: false, showSubmittingText: false, formRef: formRef, initialValues: {
230
234
  ...(loginFieldType === 'email' ? { email: '' } : { username: '' }),
231
235
  password: '',
232
236
  rememberMe: false,
233
- } }), _jsxs("div", { className: "flex items-center justify-center gap-x-4 mt-4", children: [onForgotPassword && (_jsx(Button, { type: "button", variant: "link", onClick: onForgotPassword, className: "text-sm text-primary hover:underline", children: text.forgotPasswordLabel })), hasCreateAccount && (_jsxs(_Fragment, { children: [onForgotPassword && _jsx("span", { className: "text-muted-foreground", children: "|" }), _jsx(Button, { type: "button", variant: "link", onClick: handleCreateAccount, className: "text-sm text-primary hover:underline", children: text.createAccountLabel })] }))] })] })), activeTab === 'phone' && phoneOtpConfig && (_jsxs("div", { className: "space-y-4", children: [phoneError && (_jsx("div", { className: "rounded-lg bg-red-50 p-4 text-sm text-red-600", children: phoneError })), phoneStep === 'enterPhone' && (_jsxs(_Fragment, { children: [_jsx(FormEngine, { fields: phoneNumberField, onSubmit: handlePhoneFormSubmit, layout: "vertical", className: "mb-0 px-0", submitButtonClass: "w-full", submitButtonText: isSendingOtp ? 'Sending code...' : 'Send code', onCancel: () => { }, showCancelButton: false, showSubmittingText: false, initialValues: { phoneNumber: '' } }), hasCreateAccount && (_jsx("div", { className: "text-center mt-2", children: _jsx(Button, { type: "button", variant: "link", onClick: handleCreateAccount, className: "text-sm text-primary hover:underline", children: text.createAccountLabel }) }))] })), phoneStep === 'enterOtp' && (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-sm text-muted-foreground text-center", children: ["Enter the 6-digit code sent to ", _jsx("span", { className: "font-medium text-foreground", children: phoneNumber })] }), _jsxs("div", { children: [_jsx("p", { className: "mb-2 block text-sm font-medium text-foreground text-center", children: "Verification Code" }), _jsx(OtpInput, { length: 6, value: phoneOtpValue, onChange: (v) => { setPhoneOtpValue(v); setPhoneError(null); }, disabled: isVerifyingOtp })] }), _jsx(Button, { type: "button", onClick: handleVerifyPhoneOtp, disabled: phoneOtpValue.length !== 6 || isVerifyingOtp, className: "w-full", size: "lg", children: isVerifyingOtp ? 'Verifying...' : 'Verify & Sign In' }), _jsx("div", { className: "text-center text-sm text-muted-foreground", children: canResendPhone ? (_jsx(Button, { type: "button", variant: "ghost", onClick: handleResendPhoneOtp, disabled: isResendingOtp, className: "text-sm font-medium text-primary hover:text-primary/90", children: isResendingOtp ? 'Sending...' : 'Resend code' })) : (_jsxs("span", { children: ["Resend code in ", _jsxs("span", { className: "font-medium text-foreground", children: [phoneCountdown, "s"] })] })) }), _jsx(Button, { type: "button", variant: "ghost", onClick: () => { setPhoneStep('enterPhone'); setPhoneOtpValue(''); setPhoneError(null); }, className: "w-full text-sm text-foreground/70", children: "\u2190 Change number" })] }))] }))] }) }));
237
+ } }), _jsxs("div", { className: "flex items-center justify-center gap-x-4 mt-4", children: [onForgotPassword && (_jsx(Button, { type: "button", variant: "link", onClick: onForgotPassword, className: "text-sm text-primary hover:underline", children: text.forgotPasswordLabel })), hasCreateAccount && (_jsxs(_Fragment, { children: [onForgotPassword && _jsx("span", { className: "text-muted-foreground", children: "|" }), _jsx(Button, { type: "button", variant: "link", onClick: handleCreateAccount, className: "text-sm text-primary hover:underline", children: text.createAccountLabel })] }))] })] })), activeTab === 'phone' && phoneOtpConfig && (_jsxs("div", { className: "space-y-4", children: [phoneError && _jsx("div", { className: "rounded-lg bg-red-50 p-4 text-sm text-red-600", children: phoneError }), phoneStep === 'enterPhone' && (_jsxs(_Fragment, { children: [_jsx(FormEngine, { fields: phoneNumberField, onSubmit: handlePhoneFormSubmit, layout: "vertical", className: "mb-0 px-0", submitButtonClass: "w-full", submitButtonText: isSendingOtp ? 'Sending code...' : 'Send code', onCancel: () => { }, showCancelButton: false, showSubmittingText: false, initialValues: { phoneNumber: '' } }), hasCreateAccount && (_jsx("div", { className: "text-center mt-2", children: _jsx(Button, { type: "button", variant: "link", onClick: handleCreateAccount, className: "text-sm text-primary hover:underline", children: text.createAccountLabel }) }))] })), phoneStep === 'enterOtp' && (_jsxs(_Fragment, { children: [_jsxs("p", { className: "text-sm text-muted-foreground text-center", children: ["Enter the 6-digit code sent to ", _jsx("span", { className: "font-medium text-foreground", children: phoneNumber })] }), _jsxs("div", { children: [_jsx("p", { className: "mb-2 block text-sm font-medium text-foreground text-center", children: "Verification Code" }), _jsx(OtpInput, { length: 6, value: phoneOtpValue, onChange: (v) => {
238
+ setPhoneOtpValue(v);
239
+ setPhoneError(null);
240
+ }, disabled: isVerifyingOtp })] }), _jsx(Button, { type: "button", onClick: handleVerifyPhoneOtp, disabled: phoneOtpValue.length !== 6 || isVerifyingOtp, className: "w-full", size: "lg", children: isVerifyingOtp ? 'Verifying...' : 'Verify & Sign In' }), _jsx("div", { className: "text-center text-sm text-muted-foreground", children: canResendPhone ? (_jsx(Button, { type: "button", variant: "ghost", onClick: handleResendPhoneOtp, disabled: isResendingOtp, className: "text-sm font-medium text-primary hover:text-primary/90", children: isResendingOtp ? 'Sending...' : 'Resend code' })) : (_jsxs("span", { children: ["Resend code in ", _jsxs("span", { className: "font-medium text-foreground", children: [phoneCountdown, "s"] })] })) }), _jsx(Button, { type: "button", variant: "ghost", onClick: () => {
241
+ setPhoneStep('enterPhone');
242
+ setPhoneOtpValue('');
243
+ setPhoneError(null);
244
+ }, className: "w-full text-sm text-foreground/70", children: "\u2190 Change number" })] }))] }))] }) }));
234
245
  }
235
246
  export default PagamioLoginPage;
@@ -147,9 +147,7 @@ export const OtpVerificationPage = ({ email, phoneNumber, onVerifySuccess, onBac
147
147
  setIsResending(false);
148
148
  }
149
149
  };
150
- return (_jsx("div", { className: `flex min-h-screen items-center justify-center bg-muted px-4 py-12 ${classNames.container ?? ''}`, children: _jsxs("div", { className: "w-full max-w-md", children: [logo && (_jsxs("div", { className: "mb-8 flex justify-center", children: [_jsx("img", { src: logo.src, alt: logo.alt, width: logo.width, height: logo.height, className: `h-auto max-h-16${logo.darkSrc ? ' dark:hidden' : ''}` }), logo.darkSrc && (_jsx("img", { src: logo.darkSrc, alt: logo.alt, width: logo.width, height: logo.height, className: "h-auto max-h-16 hidden dark:block" }))] })), _jsxs("div", { className: `rounded-2xl border border-border bg-background p-8 shadow-sm ${classNames.card ?? ''}`, children: [_jsxs("div", { className: `mb-6 text-center ${classNames.header ?? ''}`, children: [_jsx("div", { className: `mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-primary/15 ${classNames.icon ?? ''}`, children: isPhoneMode
151
- ? _jsx(HiOutlinePhone, { className: "h-8 w-8 text-primary" })
152
- : _jsx(HiOutlineMail, { className: "h-8 w-8 text-primary" }) }), _jsx("h1", { className: "text-2xl font-bold text-foreground", children: text.title }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: text.subtitle }), _jsx("p", { className: "mt-1 font-medium text-foreground", children: isPhoneMode ? phoneNumber : email })] }), error && _jsx("div", { className: "mb-4 rounded-lg bg-red-50 p-4 text-sm text-red-600", children: error }), _jsxs("div", { className: "mb-6", children: [_jsx("p", { className: "mb-2 block text-sm font-medium text-foreground", children: text.codeLabel }), _jsx(OtpInput, { length: otpLength, value: otpValue, onChange: (value) => {
150
+ return (_jsx("div", { className: `flex min-h-screen items-center justify-center bg-muted px-4 py-12 ${classNames.container ?? ''}`, children: _jsxs("div", { className: "w-full max-w-md", children: [logo && (_jsxs("div", { className: "mb-8 flex justify-center", children: [_jsx("img", { src: logo.src, alt: logo.alt, width: logo.width, height: logo.height, className: `h-auto max-h-16${logo.darkSrc ? ' dark:hidden' : ''}` }), logo.darkSrc && (_jsx("img", { src: logo.darkSrc, alt: logo.alt, width: logo.width, height: logo.height, className: "h-auto max-h-16 hidden dark:block" }))] })), _jsxs("div", { className: `rounded-2xl border border-border bg-background p-8 shadow-sm ${classNames.card ?? ''}`, children: [_jsxs("div", { className: `mb-6 text-center ${classNames.header ?? ''}`, children: [_jsx("div", { className: `mx-auto mb-4 flex h-16 w-16 items-center justify-center rounded-full bg-primary/15 ${classNames.icon ?? ''}`, children: isPhoneMode ? (_jsx(HiOutlinePhone, { className: "h-8 w-8 text-primary" })) : (_jsx(HiOutlineMail, { className: "h-8 w-8 text-primary" })) }), _jsx("h1", { className: "text-2xl font-bold text-foreground", children: text.title }), _jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: text.subtitle }), _jsx("p", { className: "mt-1 font-medium text-foreground", children: isPhoneMode ? phoneNumber : email })] }), error && _jsx("div", { className: "mb-4 rounded-lg bg-red-50 p-4 text-sm text-red-600", children: error }), _jsxs("div", { className: "mb-6", children: [_jsx("p", { className: "mb-2 block text-sm font-medium text-foreground", children: text.codeLabel }), _jsx(OtpInput, { length: otpLength, value: otpValue, onChange: (value) => {
153
151
  setOtpValue(value);
154
152
  clearError();
155
153
  }, disabled: isVerifying })] }), _jsx(Button, { onClick: handleVerify, disabled: otpValue.length !== otpLength || isVerifying, className: "w-full", size: "lg", children: isVerifying ? (_jsxs(_Fragment, { children: [_jsx(Loader, { size: "sm", className: "mr-2" }), text.verifyingButton] })) : (text.verifyButton) }), _jsx("div", { className: "mt-6 text-center", children: canResend ? (_jsx(Button, { type: "button", variant: "ghost", onClick: handleResend, disabled: isResending, className: "text-sm font-medium text-primary hover:text-primary/90 disabled:text-muted-foreground", children: isResending ? text.resendingButton : text.resendButton })) : (_jsxs("p", { className: "text-sm text-muted-foreground", children: [text.resendCountdownText, " ", _jsxs("span", { className: "font-medium text-foreground", children: [countdown, "s"] })] })) }), _jsx("div", { className: "mt-6 border-t border-border pt-6", children: _jsxs(Button, { type: "button", variant: "ghost", onClick: onBack, className: "flex w-full items-center justify-center gap-2 text-sm text-foreground/70 hover:text-foreground", children: [_jsx(HiOutlineArrowLeft, { className: "h-4 w-4" }), text.backButton] }) })] })] }) }));
@@ -45,11 +45,18 @@ const FieldWrapper = ({ field, control, errors, layout, onFieldUpdate, onFieldCh
45
45
  }
46
46
  };
47
47
  if (field.name === 'email') {
48
+ const emailPattern = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
49
+ const existingValidate = field.validation?.validate;
48
50
  field.validation = {
49
51
  ...field.validation,
50
- pattern: {
51
- value: /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/,
52
- message: 'Please enter a valid email address',
52
+ // Use validate instead of pattern so empty values are allowed (field is not required unless
53
+ // the required rule is explicitly set). pattern always runs even on empty strings.
54
+ validate: (value) => {
55
+ if (!value)
56
+ return true;
57
+ if (!emailPattern.test(value))
58
+ return 'Please enter a valid email address';
59
+ return existingValidate ? existingValidate(value) : true;
53
60
  },
54
61
  };
55
62
  }
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.279",
4
+ "version": "0.8.280",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "provenance": false