@pagamio/frontend-commons-lib 0.8.341 → 0.8.343

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.
@@ -66,9 +66,10 @@ export interface PhonePinLoginConfig {
66
66
  }
67
67
  /**
68
68
  * Configuration for the username + PIN login tab.
69
- * When provided, a "Login Number + PIN" tab is displayed alongside the other
70
- * tabs. Login numbers are admin-assigned 10-digit numeric identifiers (there
71
- * is no self-service signup for this method).
69
+ * When provided, an "Operator ID + PIN" tab is displayed alongside the other
70
+ * tabs. Operator IDs are admin-assigned 10-digit numeric identifiers (the
71
+ * underlying API field stays `loginNumber`; only the user-facing copy has
72
+ * been renamed to align with the mobile Welcome Back screen).
72
73
  */
73
74
  export interface UsernamePinLoginConfig {
74
75
  /** Login with login number and PIN. Should throw on error. */
@@ -77,7 +78,7 @@ export interface UsernamePinLoginConfig {
77
78
  * Optional callback called instead of onLoginSuccess when mustChangePIN is true.
78
79
  */
79
80
  onMustChangePIN?: (loginNumber: string, response: any) => void;
80
- /** Optional label for the username-PIN tab (default: 'Login Number + PIN') */
81
+ /** Optional label for the username-PIN tab (default: 'Operator ID') */
81
82
  tabLabel?: string;
82
83
  /** Optional label for the password tab (default: 'Email & Password') */
83
84
  passwordTabLabel?: string;
@@ -164,8 +165,9 @@ interface PagamioLoginPageProps<T extends CustomAuthConfig> extends BaseAuthPage
164
165
  */
165
166
  phonePinConfig?: PhonePinLoginConfig;
166
167
  /**
167
- * When provided, adds a "Login Number + PIN" tab alongside the standard login
168
- * form. The tab handles the 2-step login-number + PIN flow internally.
168
+ * When provided, adds an "Operator ID" tab alongside the standard login
169
+ * form. The tab handles the 2-step Operator ID + PIN flow internally.
170
+ * (Underlying API field name stays `loginNumber`.)
169
171
  */
170
172
  usernamePinConfig?: UsernamePinLoginConfig;
171
173
  }
@@ -53,7 +53,18 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
53
53
  const [isLoading, setIsLoading] = useState(false);
54
54
  const [error, setError] = useState(null);
55
55
  const formRef = useRef(null);
56
- const [activeTab, setActiveTab] = useState('password');
56
+ // Default-active tab follows the "phone-first" hierarchy from the
57
+ // commerce-os mobile Welcome Back screen: Phone → Operator ID → Email.
58
+ // Falls back to whichever tab is actually configured when the others
59
+ // aren't enabled, so single-tab consumers keep working unchanged.
60
+ const showPhoneTabInitial = Boolean(phoneOtpConfig || phonePinConfig);
61
+ const showUsernameTabInitial = Boolean(usernamePinConfig);
62
+ const defaultActiveTab = showPhoneTabInitial
63
+ ? 'phone'
64
+ : showUsernameTabInitial
65
+ ? 'username'
66
+ : 'password';
67
+ const [activeTab, setActiveTab] = useState(defaultActiveTab);
57
68
  const [phoneStep, setPhoneStep] = useState('enterPhone');
58
69
  const [phoneNumber, setPhoneNumber] = useState('');
59
70
  const [phoneOtpValue, setPhoneOtpValue] = useState('');
@@ -98,16 +109,19 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
98
109
  ];
99
110
  const loginNumberField = [
100
111
  {
112
+ // Internal field name stays `loginNumber` to match the auth API
113
+ // payload; only the user-facing strings change to "Operator ID" to
114
+ // align with the mobile Welcome Back screen.
101
115
  name: 'loginNumber',
102
- label: 'Login Number',
116
+ label: 'Operator ID',
103
117
  type: 'text',
104
- placeholder: '1230000001',
118
+ placeholder: 'Enter Operator ID',
105
119
  gridSpan: 12,
106
120
  validation: {
107
- required: 'Login number is required',
121
+ required: 'Operator ID is required',
108
122
  pattern: {
109
123
  value: /^\d{10}$/,
110
- message: 'Login number must be exactly 10 digits',
124
+ message: 'Operator ID must be exactly 10 digits',
111
125
  },
112
126
  },
113
127
  },
@@ -308,10 +322,10 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
308
322
  usernamePinConfig?.passwordTabLabel ??
309
323
  'Email';
310
324
  const phoneTabLabel = phoneOtpConfig?.tabLabel ?? phonePinConfig?.tabLabel ?? 'Phone';
311
- const usernameTabLabel = usernamePinConfig?.tabLabel ?? 'Login Number';
312
- 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: [showTabs && (_jsxs("div", { className: "mb-6 flex divide-x divide-border overflow-hidden rounded-lg border border-border", role: "tablist", "aria-label": "Login method", children: [_jsx("button", { type: "button", role: "tab", "aria-selected": activeTab === 'password', onClick: () => setActiveTab('password'), className: `flex-1 whitespace-nowrap px-3 py-2.5 text-center text-sm font-medium transition-colors ${activeTab === 'password'
313
- ? 'bg-primary text-primary-foreground'
314
- : 'bg-background text-foreground/70 hover:bg-muted'}`, children: passwordTabLabel }), showPhoneTab && (_jsx("button", { type: "button", role: "tab", "aria-selected": activeTab === 'phone', onClick: () => {
325
+ // "Operator ID" aligns the web tab label with the mobile Welcome Back
326
+ // screen. Consumers can still override via usernamePinConfig.tabLabel.
327
+ const usernameTabLabel = usernamePinConfig?.tabLabel ?? 'Operator ID';
328
+ 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: [showTabs && (_jsxs("div", { className: "mb-6 flex divide-x divide-border overflow-hidden rounded-lg border border-border", role: "tablist", "aria-label": "Login method", children: [showPhoneTab && (_jsx("button", { type: "button", role: "tab", "aria-selected": activeTab === 'phone', onClick: () => {
315
329
  setActiveTab('phone');
316
330
  setPhoneStep('enterPhone');
317
331
  setPhoneOtpValue('');
@@ -326,7 +340,9 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
326
340
  setUsernameError(null);
327
341
  }, className: `flex-1 whitespace-nowrap px-3 py-2.5 text-center text-sm font-medium transition-colors ${activeTab === 'username'
328
342
  ? 'bg-primary text-primary-foreground'
329
- : 'bg-background text-foreground/70 hover:bg-muted'}`, children: usernameTabLabel }))] })), 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: {
343
+ : 'bg-background text-foreground/70 hover:bg-muted'}`, children: usernameTabLabel })), _jsx("button", { type: "button", role: "tab", "aria-selected": activeTab === 'password', onClick: () => setActiveTab('password'), className: `flex-1 whitespace-nowrap px-3 py-2.5 text-center text-sm font-medium transition-colors ${activeTab === 'password'
344
+ ? 'bg-primary text-primary-foreground'
345
+ : 'bg-background text-foreground/70 hover:bg-muted'}`, children: passwordTabLabel })] })), 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: {
330
346
  ...(loginFieldType === 'email' ? { email: '' } : { username: '' }),
331
347
  password: '',
332
348
  rememberMe: false,
@@ -351,6 +367,6 @@ export function PagamioLoginPage({ logo, text = loginPageDefaultText, appLabel,
351
367
  setUsernameStep('enterLoginNumber');
352
368
  setLoginNumberPinValue('');
353
369
  setUsernameError(null);
354
- }, className: "w-full text-sm text-foreground/70", children: "\u2190 Change login number" })] }))] }))] }) }));
370
+ }, className: "w-full text-sm text-foreground/70", children: "\u2190 Change Operator ID" })] }))] }))] }) }));
355
371
  }
356
372
  export default PagamioLoginPage;
@@ -22,6 +22,12 @@ interface DrawerContentProps {
22
22
  processingDependencyRef?: React.MutableRefObject<boolean>;
23
23
  pendingUpdatesRef?: React.MutableRefObject<Map<string, unknown>>;
24
24
  persistenceKey?: string;
25
+ /**
26
+ * When true, persisted form data is wiped whenever the drawer transitions
27
+ * from open → closed. Use for one-shot create/register flows where the
28
+ * next open should start blank.
29
+ */
30
+ clearOnClose?: boolean;
25
31
  /** Ref updated every render with current dirty state — read by FormEngineDrawer for X-button check. */
26
32
  isDirtyRef?: React.MutableRefObject<boolean>;
27
33
  /** Ref updated every render with handleCancel — call from outside (e.g. X button) to show the same unsaved-changes modal. */
@@ -5,7 +5,7 @@ import { Button, NotificationModal } from '../../components';
5
5
  import { useToast } from '../../context';
6
6
  import { FieldWrapper } from '../../form-engine';
7
7
  import { useFormPersistence } from '../../form-engine/hooks/useFormPersistence';
8
- const DrawerContent = ({ fields, onSubmit, isOpen, initialValues, submitButtonText = 'Submit', cancelButtonText = 'Cancel', showForm = true, showDrawerButtons = true, handleCloseDrawer, onFieldUpdate, onFieldChange, children, updateFieldOptions, updateFieldLabel, setFieldHidden, addField, updateFieldValidation, processInitialFieldUpdates, processingDependencyRef, pendingUpdatesRef, persistenceKey, isDirtyRef, cancelRef, }) => {
8
+ const DrawerContent = ({ fields, onSubmit, isOpen, initialValues, submitButtonText = 'Submit', cancelButtonText = 'Cancel', showForm = true, showDrawerButtons = true, handleCloseDrawer, onFieldUpdate, onFieldChange, children, updateFieldOptions, updateFieldLabel, setFieldHidden, addField, updateFieldValidation, processInitialFieldUpdates, processingDependencyRef, pendingUpdatesRef, persistenceKey, clearOnClose = false, isDirtyRef, cancelRef, }) => {
9
9
  const { saveFormData, restoreFormData, clearPersistedData, hasPersistedData } = useFormPersistence(persistenceKey);
10
10
  const { addToast } = useToast();
11
11
  // Determine initial values: initialValues take precedence over persisted data
@@ -39,6 +39,21 @@ const DrawerContent = ({ fields, onSubmit, isOpen, initialValues, submitButtonTe
39
39
  mountInitialValuesRef.current = initialValues || {};
40
40
  }
41
41
  wasOpenRef.current = !!isOpen;
42
+ // Clear persisted data on every open → close transition when the consumer
43
+ // opts in. Covers cancel, X, ESC, programmatic close, and post-submit close
44
+ // — all routes the user can take out of the drawer.
45
+ const clearOnCloseRef = useRef(clearOnClose);
46
+ clearOnCloseRef.current = clearOnClose;
47
+ const clearPersistedDataRef = useRef(clearPersistedData);
48
+ clearPersistedDataRef.current = clearPersistedData;
49
+ const previousIsOpenRef = useRef(!!isOpen);
50
+ useEffect(() => {
51
+ const wasOpen = previousIsOpenRef.current;
52
+ previousIsOpenRef.current = !!isOpen;
53
+ if (wasOpen && !isOpen && clearOnCloseRef.current) {
54
+ clearPersistedDataRef.current();
55
+ }
56
+ }, [isOpen]);
42
57
  const password = watch('password');
43
58
  const allFields = watch();
44
59
  // Function to check if there are unsaved changes
@@ -16,6 +16,14 @@ interface FormEngineDrawerProps {
16
16
  onFieldUpdate?: Record<string, DependentFieldUpdate[]>;
17
17
  onFieldChange?: (fieldName: string, value: unknown) => void;
18
18
  persistenceKey?: string;
19
+ /**
20
+ * When true, persisted form data is wiped whenever the drawer transitions
21
+ * from open → closed (cancel, X, ESC, programmatic close, successful submit).
22
+ * Default `false` preserves the original "resume a half-filled form" behaviour.
23
+ * Set to `true` for one-shot create/register flows where the next open should
24
+ * start blank — e.g. a "register device" or "deploy terminal" drawer.
25
+ */
26
+ clearOnClose?: boolean;
19
27
  }
20
28
  declare const FormEngineDrawer: React.FC<FormEngineDrawerProps>;
21
29
  export default FormEngineDrawer;
@@ -2,7 +2,7 @@ import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { useEffect, useRef, useState } from 'react';
3
3
  import BaseDrawer from './components/BaseDrawer';
4
4
  import DrawerContent from './components/DrawerContent';
5
- const FormEngineDrawer = ({ title, cancelButtonText = 'Cancel', submitButtonText = 'Submit', showForm = true, showDrawerButtons = true, isOpen, fields: initialFields, initialValues, children, marginTop = '0px', onClose, onSubmit, onFieldUpdate, onFieldChange, persistenceKey, }) => {
5
+ const FormEngineDrawer = ({ title, cancelButtonText = 'Cancel', submitButtonText = 'Submit', showForm = true, showDrawerButtons = true, isOpen, fields: initialFields, initialValues, children, marginTop = '0px', onClose, onSubmit, onFieldUpdate, onFieldChange, persistenceKey, clearOnClose = false, }) => {
6
6
  const [fields, setFields] = useState(initialFields);
7
7
  const initializedRef = useRef(false);
8
8
  const processingDependencyRef = useRef(false);
@@ -133,6 +133,6 @@ const FormEngineDrawer = ({ title, cancelButtonText = 'Cancel', submitButtonText
133
133
  processingDependencyRef.current = false;
134
134
  }
135
135
  };
136
- return (_jsx(BaseDrawer, { title: title, isOpen: isOpen, marginTop: marginTop, onClose: onClose, onCloseAttempt: () => cancelRef.current(), children: _jsx(DrawerContent, { isOpen: isOpen, fields: fields, onSubmit: onSubmit, handleCloseDrawer: onClose, initialValues: initialValues, showForm: showForm, showDrawerButtons: showDrawerButtons, cancelButtonText: cancelButtonText, submitButtonText: submitButtonText, onFieldUpdate: onFieldUpdate, onFieldChange: onFieldChange, updateFieldOptions: updateFieldOptions, updateFieldLabel: updateFieldLabel, setFieldHidden: setFieldHidden, addField: addField, updateFieldValidation: updateFieldValidation, processInitialFieldUpdates: processInitialFieldUpdates, processingDependencyRef: processingDependencyRef, pendingUpdatesRef: pendingUpdatesRef, persistenceKey: persistenceKey, cancelRef: cancelRef, children: children }, persistenceKey || 'drawer-content') }));
136
+ return (_jsx(BaseDrawer, { title: title, isOpen: isOpen, marginTop: marginTop, onClose: onClose, onCloseAttempt: () => cancelRef.current(), children: _jsx(DrawerContent, { isOpen: isOpen, fields: fields, onSubmit: onSubmit, handleCloseDrawer: onClose, initialValues: initialValues, showForm: showForm, showDrawerButtons: showDrawerButtons, cancelButtonText: cancelButtonText, submitButtonText: submitButtonText, onFieldUpdate: onFieldUpdate, onFieldChange: onFieldChange, updateFieldOptions: updateFieldOptions, updateFieldLabel: updateFieldLabel, setFieldHidden: setFieldHidden, addField: addField, updateFieldValidation: updateFieldValidation, processInitialFieldUpdates: processInitialFieldUpdates, processingDependencyRef: processingDependencyRef, pendingUpdatesRef: pendingUpdatesRef, persistenceKey: persistenceKey, clearOnClose: clearOnClose, cancelRef: cancelRef, children: children }, persistenceKey || 'drawer-content') }));
137
137
  };
138
138
  export default FormEngineDrawer;
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.341",
4
+ "version": "0.8.343",
5
5
  "publishConfig": {
6
6
  "access": "public",
7
7
  "provenance": false