@pagamio/frontend-commons-lib 0.8.211 → 0.8.213
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,11 +1,17 @@
|
|
|
1
|
+
import type { Field } from '../../form-engine';
|
|
1
2
|
import type { AuthenticatorType } from '../authenticators';
|
|
2
3
|
import type { AuthResponse, BaseAuthCredentials, CustomAuthConfig } from '../types';
|
|
3
4
|
import type { BaseAuthPageProps } from './AuthFormUtils';
|
|
5
|
+
/**
|
|
6
|
+
* Login field type - determines whether the login form uses username or email
|
|
7
|
+
*/
|
|
8
|
+
export type LoginFieldType = 'username' | 'email';
|
|
4
9
|
/**
|
|
5
10
|
* Base login credentials interface that can be extended for specific implementations
|
|
6
11
|
*/
|
|
7
12
|
interface PagamioLoginCredentials {
|
|
8
|
-
username
|
|
13
|
+
username?: string;
|
|
14
|
+
email?: string;
|
|
9
15
|
password: string;
|
|
10
16
|
rememberMe?: boolean;
|
|
11
17
|
}
|
|
@@ -19,6 +25,7 @@ interface PagamioLoginPageProps<T extends CustomAuthConfig> extends BaseAuthPage
|
|
|
19
25
|
welcomeTitle: string;
|
|
20
26
|
welcomeSubtitle: string;
|
|
21
27
|
usernameLabel: string;
|
|
28
|
+
emailLabel: string;
|
|
22
29
|
passwordLabel: string;
|
|
23
30
|
rememberMeLabel: string;
|
|
24
31
|
loginButtonLabel: string;
|
|
@@ -26,6 +33,16 @@ interface PagamioLoginPageProps<T extends CustomAuthConfig> extends BaseAuthPage
|
|
|
26
33
|
forgotPasswordLabel: string;
|
|
27
34
|
createAccountLabel: string;
|
|
28
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Type of login field to display - 'username' or 'email'
|
|
38
|
+
* Defaults to 'username' for backward compatibility
|
|
39
|
+
*/
|
|
40
|
+
loginFieldType?: LoginFieldType;
|
|
41
|
+
/**
|
|
42
|
+
* Custom login fields to use instead of the default fields
|
|
43
|
+
* When provided, this will override the default login fields entirely
|
|
44
|
+
*/
|
|
45
|
+
customLoginFields?: Field[];
|
|
29
46
|
/** Callback handlers */
|
|
30
47
|
onForgotPassword?: () => void;
|
|
31
48
|
onLoginSuccess?: (authResponse?: AuthResponse<T>) => void;
|
|
@@ -57,6 +74,7 @@ export declare const loginPageDefaultText: {
|
|
|
57
74
|
welcomeTitle: string;
|
|
58
75
|
welcomeSubtitle: string;
|
|
59
76
|
usernameLabel: string;
|
|
77
|
+
emailLabel: string;
|
|
60
78
|
passwordLabel: string;
|
|
61
79
|
rememberMeLabel: string;
|
|
62
80
|
loginButtonLabel: string;
|
|
@@ -68,6 +86,6 @@ export declare const loginPageDefaultText: {
|
|
|
68
86
|
* Generic Login Page component
|
|
69
87
|
* @template T - Authentication configuration type
|
|
70
88
|
*/
|
|
71
|
-
export declare function PagamioLoginPage<T extends CustomAuthConfig>({ logo, text, appLabel, onForgotPassword, onLoginSuccess, onLoginError, hasCreateAccount, createAccountRoute, onCreateAccount, transformLoginData, authenticatorType, className, }: Readonly<PagamioLoginPageProps<T>>): import("react/jsx-runtime").JSX.Element;
|
|
89
|
+
export declare function PagamioLoginPage<T extends CustomAuthConfig>({ logo, text, appLabel, onForgotPassword, onLoginSuccess, onLoginError, hasCreateAccount, createAccountRoute, onCreateAccount, transformLoginData, authenticatorType, loginFieldType, customLoginFields, className, }: Readonly<PagamioLoginPageProps<T>>): import("react/jsx-runtime").JSX.Element;
|
|
72
90
|
export default PagamioLoginPage;
|
|
73
91
|
export type { PagamioLoginCredentials, PagamioLoginPageProps };
|
|
@@ -32,6 +32,7 @@ export const loginPageDefaultText = {
|
|
|
32
32
|
welcomeTitle: 'Welcome Back!',
|
|
33
33
|
welcomeSubtitle: 'Sign in to your account to continue',
|
|
34
34
|
usernameLabel: 'Username',
|
|
35
|
+
emailLabel: 'Email',
|
|
35
36
|
passwordLabel: 'Password',
|
|
36
37
|
rememberMeLabel: 'Remember me',
|
|
37
38
|
loginButtonLabel: 'Login',
|
|
@@ -43,14 +44,29 @@ export const loginPageDefaultText = {
|
|
|
43
44
|
* Generic Login Page component
|
|
44
45
|
* @template T - Authentication configuration type
|
|
45
46
|
*/
|
|
46
|
-
export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel, onForgotPassword, onLoginSuccess, onLoginError, hasCreateAccount = false, createAccountRoute, onCreateAccount, transformLoginData, authenticatorType, className = '', }) {
|
|
47
|
+
export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel, onForgotPassword, onLoginSuccess, onLoginError, hasCreateAccount = false, createAccountRoute, onCreateAccount, transformLoginData, authenticatorType, loginFieldType = 'username', customLoginFields, className = '', }) {
|
|
47
48
|
const { login, error: authError } = useAuth();
|
|
48
49
|
const { addToast } = useToast();
|
|
49
50
|
const [isLoading, setIsLoading] = useState(false);
|
|
50
51
|
const [error, setError] = useState(null);
|
|
51
52
|
const formRef = useRef();
|
|
52
|
-
|
|
53
|
-
|
|
53
|
+
// Generate the identifier field based on loginFieldType
|
|
54
|
+
const identifierField = loginFieldType === 'email'
|
|
55
|
+
? {
|
|
56
|
+
name: 'email',
|
|
57
|
+
label: text.emailLabel,
|
|
58
|
+
type: 'email',
|
|
59
|
+
placeholder: 'Enter email',
|
|
60
|
+
gridSpan: 12,
|
|
61
|
+
validation: {
|
|
62
|
+
required: 'Email is required',
|
|
63
|
+
pattern: {
|
|
64
|
+
value: /\S+@\S+\.\S+/,
|
|
65
|
+
message: 'Please enter a valid email address',
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
: {
|
|
54
70
|
name: 'username',
|
|
55
71
|
label: text.usernameLabel,
|
|
56
72
|
type: 'text',
|
|
@@ -60,7 +76,9 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
|
|
|
60
76
|
required: 'Username is required',
|
|
61
77
|
validate: (value) => !/\s/.test(value) || 'Username should not contain spaces',
|
|
62
78
|
},
|
|
63
|
-
}
|
|
79
|
+
};
|
|
80
|
+
const defaultLoginFields = [
|
|
81
|
+
identifierField,
|
|
64
82
|
{
|
|
65
83
|
name: 'password',
|
|
66
84
|
label: text.passwordLabel,
|
|
@@ -78,6 +96,7 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
|
|
|
78
96
|
gridSpan: 12,
|
|
79
97
|
},
|
|
80
98
|
];
|
|
99
|
+
const loginFields = customLoginFields ?? defaultLoginFields;
|
|
81
100
|
const handleSubmit = createFormSubmissionHandler({
|
|
82
101
|
setIsLoading,
|
|
83
102
|
setError,
|
|
@@ -95,9 +114,14 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
|
|
|
95
114
|
credentials = transformLoginData(data);
|
|
96
115
|
}
|
|
97
116
|
else {
|
|
98
|
-
// Default behavior - extract username and password
|
|
99
|
-
const { username, password } = data;
|
|
100
|
-
|
|
117
|
+
// Default behavior - extract identifier (username or email) and password based on loginFieldType
|
|
118
|
+
const { username, email, password } = data;
|
|
119
|
+
if (loginFieldType === 'email') {
|
|
120
|
+
credentials = { email: email ?? username, password };
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
credentials = { username: username ?? email, password };
|
|
124
|
+
}
|
|
101
125
|
}
|
|
102
126
|
// Process the login with the selected authenticator
|
|
103
127
|
return authenticator.processLogin(credentials, data.rememberMe ?? false, login);
|
|
@@ -111,7 +135,7 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
|
|
|
111
135
|
}
|
|
112
136
|
};
|
|
113
137
|
return (_jsx(AuthPageLayout, { appLabel: appLabel, title: text.welcomeTitle, subtitle: text.welcomeSubtitle, errorMessage: error ?? authError?.message, logo: logo, className: className, horizontal: false, children: _jsxs("div", { className: "mt-8", 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: {
|
|
114
|
-
username: '',
|
|
138
|
+
...(loginFieldType === 'email' ? { email: '' } : { username: '' }),
|
|
115
139
|
password: '',
|
|
116
140
|
rememberMe: false,
|
|
117
141
|
} }), _jsxs("div", { className: "flex items-center justify-center gap-x-4 mt-4", children: [onForgotPassword && (_jsx("button", { type: "button", onClick: onForgotPassword, className: "text-sm text-primary-500 hover:underline dark:text-primary-500", children: text.forgotPasswordLabel })), hasCreateAccount && (_jsxs(_Fragment, { children: [onForgotPassword && _jsx("span", { className: "text-gray-500", children: "|" }), _jsx("button", { type: "button", onClick: handleCreateAccount, className: "text-sm text-primary-500 hover:underline dark:text-primary-500", children: text.createAccountLabel })] }))] })] }) }));
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { default as LogoutButton } from './LogoutButton';
|
|
2
|
-
export { default as PagamioLoginPage, loginPageDefaultText, type PagamioLoginCredentials, type PagamioLoginPageProps, } from './LoginPage';
|
|
2
|
+
export { default as PagamioLoginPage, loginPageDefaultText, type LoginFieldType, type PagamioLoginCredentials, type PagamioLoginPageProps, } from './LoginPage';
|
|
3
3
|
export { default as ChangePasswordPage, type ChangePasswordPageProps } from './ChangePasswordPage';
|
|
4
4
|
export { type PostDataProps } from './hooks/useChangeUserPassword';
|
|
5
5
|
export { default as PagamioCustomerRegistrationPage, customerRegistrationPageDefaultText, type PagamioCustomerRegistrationPageProps, } from './CustomerRegistrationPage';
|
|
@@ -13,7 +13,7 @@ const AppSidebarMenu = () => {
|
|
|
13
13
|
// Default behavior all items in one ItemGroup
|
|
14
14
|
return (_jsx(Sidebar.Items, { children: _jsx(Sidebar.ItemGroup, { children: pages.map((item) => (_jsx(AppSidebarItem, { ...item }, item.label))) }) }));
|
|
15
15
|
};
|
|
16
|
-
const AppSidebarItem = ({ href, target, icon, label, items, badge, forceDropdown }) => {
|
|
16
|
+
const AppSidebarItem = ({ href, target, icon, label, items, badge, forceDropdown, collapsible = true, showSeparator = false, }) => {
|
|
17
17
|
const { pathname, linkComponent: Link } = useAppSidebarContext();
|
|
18
18
|
const { t } = useTranslation();
|
|
19
19
|
// Check if current path matches this item or any of its descendants
|
|
@@ -38,6 +38,17 @@ const AppSidebarItem = ({ href, target, icon, label, items, badge, forceDropdown
|
|
|
38
38
|
return (_jsx(Sidebar.Item, { as: Link, href: singleItem.href, target: target, icon: icon, label: badge && t(badge), className: twMerge('text-gray-600 hover:text-primary-700 hover:bg-primary-50/70', 'dark:text-gray-400 dark:hover:text-primary-300 dark:hover:bg-primary-900/30', pathname === singleItem.href &&
|
|
39
39
|
'text-primary-700 bg-primary-100/80 hover:bg-primary-100/80 dark:text-primary-300 dark:bg-primary-900/40 dark:hover:bg-primary-900/40'), children: t(label) }));
|
|
40
40
|
}
|
|
41
|
+
// Handle non-collapsible sections - render as section header with always-visible links
|
|
42
|
+
if (items?.length && collapsible === false) {
|
|
43
|
+
return (_jsxs("div", { className: "space-y-1", children: [showSeparator && _jsx("hr", { className: "my-3 border-t border-gray-200 dark:border-gray-700" }), _jsxs("div", { className: twMerge('flex items-center gap-3 px-3 py-2 text-sm font-semibold uppercase tracking-wider', 'text-gray-500 dark:text-gray-400'), children: [icon && React.createElement(icon, { className: 'h-5 w-5' }), _jsx("span", { children: t(label) })] }), _jsx("div", { className: "space-y-1 pl-2", children: items.map((child) => (_jsx(React.Fragment, { children: child.items?.length ? (
|
|
44
|
+
// Recursively render nested items
|
|
45
|
+
_jsx("div", { className: "pl-3", children: _jsx(AppSidebarItem, { ...child }) })) : child.href ? (
|
|
46
|
+
// Render leaf item with link
|
|
47
|
+
_jsx(Sidebar.Item, { as: Link, href: child.href, target: child.target, icon: child.icon, className: twMerge('justify-center [&>*]:font-normal', 'text-gray-600 hover:text-primary-700 hover:bg-primary-50/70', 'dark:text-gray-400 dark:hover:text-primary-300 dark:hover:bg-primary-900/30', pathname === child.href &&
|
|
48
|
+
'text-primary-700 bg-primary-100/80 hover:bg-primary-100/80 dark:text-primary-300 dark:bg-primary-900/40 dark:hover:bg-primary-900/40'), children: t(child.label) })) : (
|
|
49
|
+
// Render leaf item without link
|
|
50
|
+
_jsx(Sidebar.Item, { icon: child.icon, className: twMerge('justify-center [&>*]:font-normal', 'text-gray-600 cursor-default', 'dark:text-gray-400'), children: t(child.label) })) }, child.label))) })] }));
|
|
51
|
+
}
|
|
41
52
|
if (items?.length) {
|
|
42
53
|
const isOpen = isParentActive || hasActiveChild(items);
|
|
43
54
|
return (_jsx(Sidebar.Collapse, { icon: icon, label: t(label), open: isOpen, className: twMerge('text-gray-600 hover:text-primary-700 hover:bg-primary-50/70', 'dark:text-gray-400 dark:hover:text-primary-300 dark:hover:bg-primary-900/30',
|
|
@@ -9,6 +9,16 @@ interface AppSidebarPageItem {
|
|
|
9
9
|
badge?: string;
|
|
10
10
|
roles?: string[];
|
|
11
11
|
forceDropdown?: boolean;
|
|
12
|
+
/**
|
|
13
|
+
* When false, the section will not be collapsible - it will display as a
|
|
14
|
+
* section header with always-visible links below it. Defaults to true.
|
|
15
|
+
*/
|
|
16
|
+
collapsible?: boolean;
|
|
17
|
+
/**
|
|
18
|
+
* When true, displays a horizontal separator line above the section.
|
|
19
|
+
* Useful for visually separating non-collapsible sections. Defaults to false.
|
|
20
|
+
*/
|
|
21
|
+
showSeparator?: boolean;
|
|
12
22
|
}
|
|
13
23
|
/**
|
|
14
24
|
* Props for the AppSidebarContext
|
package/lib/styles.css
CHANGED
|
@@ -978,6 +978,9 @@ input[type="range"]::-ms-fill-lower {
|
|
|
978
978
|
.pointer-events-none {
|
|
979
979
|
pointer-events: none;
|
|
980
980
|
}
|
|
981
|
+
.visible {
|
|
982
|
+
visibility: visible;
|
|
983
|
+
}
|
|
981
984
|
.invisible {
|
|
982
985
|
visibility: hidden;
|
|
983
986
|
}
|
|
@@ -1237,6 +1240,10 @@ input[type="range"]::-ms-fill-lower {
|
|
|
1237
1240
|
margin-top: 0.25rem;
|
|
1238
1241
|
margin-bottom: 0.25rem;
|
|
1239
1242
|
}
|
|
1243
|
+
.my-3 {
|
|
1244
|
+
margin-top: 0.75rem;
|
|
1245
|
+
margin-bottom: 0.75rem;
|
|
1246
|
+
}
|
|
1240
1247
|
.my-4 {
|
|
1241
1248
|
margin-top: 1rem;
|
|
1242
1249
|
margin-bottom: 1rem;
|
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.
|
|
4
|
+
"version": "0.8.213",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public",
|
|
7
7
|
"provenance": false
|