@opexa/portal-components 0.0.253 → 0.0.255
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.
- package/dist/components/SignIn/MobileNumberSignIn.d.ts +5 -1
- package/dist/components/SignIn/MobileNumberSignIn.js +55 -38
- package/dist/components/SignIn/NameAndPasswordSignIn.d.ts +5 -1
- package/dist/components/SignIn/NameAndPasswordSignIn.js +12 -3
- package/dist/components/SignIn/SignIn.lazy.d.ts +1 -0
- package/dist/components/SignIn/SignIn.lazy.js +1 -1
- package/dist/components/SignIn/SignInForm.d.ts +5 -1
- package/dist/components/SignIn/SignInForm.js +2 -2
- package/dist/components/SignIn/utils.d.ts +7 -0
- package/dist/components/SignIn/utils.js +22 -0
- package/dist/components/TransactionRecords/TransactionRecordsTable.js +2 -2
- package/package.json +1 -1
|
@@ -19,7 +19,8 @@ import { Checkbox } from '../../ui/Checkbox/index.js';
|
|
|
19
19
|
import { Field } from '../../ui/Field/index.js';
|
|
20
20
|
import { PinInput } from '../../ui/PinInput/index.js';
|
|
21
21
|
import { useSignInContext, useSignInPropsContext } from './SignInContext.js';
|
|
22
|
-
|
|
22
|
+
import { getFriendlyErrorMessage } from './utils.js';
|
|
23
|
+
export function MobileNumberSignIn(props) {
|
|
23
24
|
const signInProps = useSignInPropsContext();
|
|
24
25
|
const context = useSignInContext();
|
|
25
26
|
const globalStore = useGlobalStore(useShallow((ctx) => ({
|
|
@@ -27,8 +28,49 @@ export function MobileNumberSignIn() {
|
|
|
27
28
|
signUp: ctx.signUp,
|
|
28
29
|
kycReminder: ctx.kycReminder,
|
|
29
30
|
})));
|
|
30
|
-
const signInMutation = useSignInMutation(
|
|
31
|
-
|
|
31
|
+
const signInMutation = useSignInMutation({
|
|
32
|
+
onSuccess: () => {
|
|
33
|
+
context.setStep(2);
|
|
34
|
+
cooldown.start();
|
|
35
|
+
},
|
|
36
|
+
onError: (err) => {
|
|
37
|
+
const errorTitle = err.name === 'MEMBER_ACCOUNT_BLACKLISTED'
|
|
38
|
+
? 'Your Account is Blacklisted'
|
|
39
|
+
: err.name === 'MEMBER_ACCOUNT_SUSPENDED'
|
|
40
|
+
? 'Your Account is Suspended'
|
|
41
|
+
: 'Sign In Failed';
|
|
42
|
+
toaster.error({
|
|
43
|
+
title: errorTitle,
|
|
44
|
+
description: getFriendlyErrorMessage(err.name, {
|
|
45
|
+
siteName: props.siteName,
|
|
46
|
+
accountName: step1Form.getValues('mobileNumber'),
|
|
47
|
+
}),
|
|
48
|
+
});
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
const sendVerificationCodeMutation = useSendVerificationCodeMutation({
|
|
52
|
+
onSuccess: () => {
|
|
53
|
+
step1Form.reset();
|
|
54
|
+
step2Form.reset();
|
|
55
|
+
context.setStep(1);
|
|
56
|
+
globalStore.signIn.setOpen(false);
|
|
57
|
+
globalStore.kycReminder.setOpen(true);
|
|
58
|
+
},
|
|
59
|
+
onError: (err) => {
|
|
60
|
+
const errorTitle = err.name === 'MEMBER_ACCOUNT_BLACKLISTED'
|
|
61
|
+
? 'Your Account is Blacklisted'
|
|
62
|
+
: err.name === 'MEMBER_ACCOUNT_SUSPENDED'
|
|
63
|
+
? 'Your Account is Suspended'
|
|
64
|
+
: 'Sign In Failed';
|
|
65
|
+
toaster.error({
|
|
66
|
+
title: errorTitle,
|
|
67
|
+
description: getFriendlyErrorMessage(err.name, {
|
|
68
|
+
siteName: props.siteName,
|
|
69
|
+
accountName: step1Form.getValues('mobileNumber'),
|
|
70
|
+
}),
|
|
71
|
+
});
|
|
72
|
+
},
|
|
73
|
+
});
|
|
32
74
|
const localeInfo = useLocaleInfo();
|
|
33
75
|
const mobileNumberParser = useMobileNumberParser();
|
|
34
76
|
const Step1Definition = z.object({
|
|
@@ -81,22 +123,11 @@ export function MobileNumberSignIn() {
|
|
|
81
123
|
});
|
|
82
124
|
const form2Ref = useRef(null);
|
|
83
125
|
return (_jsxs(_Fragment, { children: [context.step === 1 && (_jsxs(_Fragment, { children: [_jsxs("form", { className: "mt-3xl", onSubmit: step1Form.handleSubmit(async (data) => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
});
|
|
90
|
-
context.setStep(2);
|
|
91
|
-
cooldown.start();
|
|
92
|
-
}
|
|
93
|
-
catch (e) {
|
|
94
|
-
toaster.error({
|
|
95
|
-
description: e instanceof Error
|
|
96
|
-
? e.message
|
|
97
|
-
: 'Failed to send verification code',
|
|
98
|
-
});
|
|
99
|
-
}
|
|
126
|
+
sendVerificationCodeMutation.mutate({
|
|
127
|
+
channel: 'SMS',
|
|
128
|
+
recipient: mobileNumberParser.format(data.mobileNumber),
|
|
129
|
+
strict: true,
|
|
130
|
+
});
|
|
100
131
|
}), children: [_jsxs(Field.Root, { invalid: !!step1Form.formState.errors.mobileNumber, children: [_jsx(Field.Label, { children: "Mobile Number" }), _jsxs("div", { className: "relative", children: [_jsxs("div", { className: "absolute left-3.5 top-1/2 flex shrink-0 -translate-y-1/2 items-center gap-md", children: [_jsx(localeInfo.country.flag, { className: "size-5" }), _jsx("span", { className: "text-text-placeholder", children: localeInfo.mobileNumber.areaCode })] }), _jsx(Field.Input, { style: {
|
|
101
132
|
paddingLeft: `calc(2.75rem + ${localeInfo.mobileNumber.areaCode.length}ch)`,
|
|
102
133
|
}, ...step1Form.register('mobileNumber') })] }), _jsx(Field.ErrorText, { children: step1Form.formState.errors.mobileNumber?.message })] }), _jsx(Button, { type: "submit", className: "mt-3xl", disabled: step1Form.formState.isSubmitting, children: "Log In" }), _jsx(Controller, { control: step1Form.control, name: "termsAccepted", render: (o) => (_jsxs(Field.Root, { invalid: o.fieldState.invalid, className: "mt-xl", children: [_jsxs(Checkbox.Root, { checked: o.field.value, onCheckedChange: (details) => {
|
|
@@ -109,25 +140,11 @@ export function MobileNumberSignIn() {
|
|
|
109
140
|
globalStore.signUp.setOpen(true);
|
|
110
141
|
}, children: "Sign up" })] })] })), context.step === 2 && (_jsxs(_Fragment, { children: [_jsx("h2", { className: "mt-xl text-center text-lg font-semibold", children: "Check your Phone" }), _jsxs("p", { className: "text-text-secondary-700 mt-xs text-center text-sm", children: ["We\u2019ve sent a verification code to your mobile number", ' ', _jsx("span", { className: "font-semibold", children: mobileNumberParser.format(step1Form.getValues('mobileNumber')) }), ' ', "via text"] }), _jsxs("form", { ref: form2Ref, className: "mt-5", onSubmit: step2Form.handleSubmit(async ({ verificationCode }) => {
|
|
111
142
|
const { mobileNumber } = step1Form.getValues();
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
});
|
|
118
|
-
step1Form.reset();
|
|
119
|
-
step2Form.reset();
|
|
120
|
-
context.setStep(1);
|
|
121
|
-
globalStore.signIn.setOpen(false);
|
|
122
|
-
globalStore.kycReminder.setOpen(true);
|
|
123
|
-
}
|
|
124
|
-
catch (err) {
|
|
125
|
-
toaster.error({
|
|
126
|
-
description: err instanceof Error
|
|
127
|
-
? err.message
|
|
128
|
-
: 'Invalid Verification Code',
|
|
129
|
-
});
|
|
130
|
-
}
|
|
143
|
+
signInMutation.mutate({
|
|
144
|
+
type: 'MOBILE_NUMBER',
|
|
145
|
+
mobileNumber: mobileNumberParser.format(mobileNumber),
|
|
146
|
+
verificationCode: verificationCode.join(''),
|
|
147
|
+
});
|
|
131
148
|
}), children: [_jsx(Controller, { name: "verificationCode", control: step2Form.control, render: (o) => (_jsxs(Field.Root, { invalid: o.fieldState.invalid, children: [_jsxs(PinInput.Root, { placeholder: "0", value: o.field.value, onValueChange: (details) => {
|
|
132
149
|
o.field.onChange(details.value);
|
|
133
150
|
o.field.onBlur();
|
|
@@ -18,7 +18,8 @@ import { Checkbox } from '../../ui/Checkbox/index.js';
|
|
|
18
18
|
import { Field } from '../../ui/Field/index.js';
|
|
19
19
|
import { PasswordInput } from '../../ui/PasswordInput/index.js';
|
|
20
20
|
import { useSignInContext, useSignInPropsContext } from './SignInContext.js';
|
|
21
|
-
|
|
21
|
+
import { getFriendlyErrorMessage } from './utils.js';
|
|
22
|
+
export function NameAndPasswordSignIn(props) {
|
|
22
23
|
const signInProps = useSignInPropsContext();
|
|
23
24
|
const context = useSignInContext();
|
|
24
25
|
const dialog = useDialogContext();
|
|
@@ -42,9 +43,17 @@ export function NameAndPasswordSignIn() {
|
|
|
42
43
|
}
|
|
43
44
|
},
|
|
44
45
|
onError(err) {
|
|
46
|
+
const errorTitle = err.name === 'MEMBER_ACCOUNT_BLACKLISTED'
|
|
47
|
+
? 'Your Account is Blacklisted'
|
|
48
|
+
: err.name === 'MEMBER_ACCOUNT_SUSPENDED'
|
|
49
|
+
? 'Your Account is Suspended'
|
|
50
|
+
: 'Sign In Failed';
|
|
45
51
|
toaster.error({
|
|
46
|
-
|
|
47
|
-
|
|
52
|
+
title: errorTitle,
|
|
53
|
+
description: getFriendlyErrorMessage(err.name, {
|
|
54
|
+
siteName: props.siteName,
|
|
55
|
+
accountName: form.getValues('name'),
|
|
56
|
+
}),
|
|
48
57
|
});
|
|
49
58
|
},
|
|
50
59
|
});
|
|
@@ -15,5 +15,5 @@ export function SignIn(props) {
|
|
|
15
15
|
const signInStore = useGlobalStore(useShallow((ctx) => ctx.signIn));
|
|
16
16
|
return (_jsx(SignInPropsProvider, { value: props, children: _jsx(SignInProvider, { value: signIn, children: _jsx(Dialog.Root, { open: signInStore.open, onOpenChange: (details) => {
|
|
17
17
|
signInStore.setOpen(details.open);
|
|
18
|
-
}, lazyMount: true, unmountOnExit: true, closeOnEscape: false, closeOnInteractOutside: false, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, {}), _jsx(Dialog.Positioner, { children: _jsxs(Dialog.Content, { className: twMerge('bg-bg-primary-alt mx-auto h-full w-full items-start p-3xl pb-4xl lg:h-auto lg:w-[400px] lg:rounded-xl
|
|
18
|
+
}, lazyMount: true, unmountOnExit: true, closeOnEscape: false, closeOnInteractOutside: false, children: _jsxs(Portal, { children: [_jsx(Dialog.Backdrop, {}), _jsx(Dialog.Positioner, { children: _jsxs(Dialog.Content, { className: twMerge('bg-bg-primary-alt mx-auto h-full w-full items-start overflow-y-auto p-3xl pb-4xl lg:h-auto lg:w-[400px] lg:rounded-xl', props.className), children: [_jsx(Dialog.CloseTrigger, { children: _jsx(XIcon, {}) }), _jsx(Image, { src: props.logo, alt: "", width: 200, height: 100, className: "mx-auto h-7.5 w-auto", draggable: false }), _jsx(SignInForm, { siteName: props.siteName })] }) })] }) }) }) }));
|
|
19
19
|
}
|
|
@@ -4,7 +4,7 @@ import { MobileNumberSignIn } from './MobileNumberSignIn.js';
|
|
|
4
4
|
import { NameAndPasswordSignIn } from './NameAndPasswordSignIn.js';
|
|
5
5
|
import { useSignInContext } from './SignInContext.js';
|
|
6
6
|
import { SignInMethod } from './SignInMethod.js';
|
|
7
|
-
export function SignInForm() {
|
|
7
|
+
export function SignInForm(props) {
|
|
8
8
|
const context = useSignInContext();
|
|
9
|
-
return (_jsxs(_Fragment, { children: [context.step === 1 && (_jsxs(_Fragment, { children: [_jsx("h2", { className: "mt-xl text-center text-lg font-semibold", children: "Log in to your account" }), _jsx("p", { className: "text-text-secondary-700 mt-xs text-center text-sm", children: "Welcome back! Please enter your details." })] })), _jsx(SignInMethod, {}), context.type === 'MOBILE_NUMBER' && _jsx(MobileNumberSignIn, {}), context.type === 'NAME_AND_PASSWORD' && _jsx(NameAndPasswordSignIn, {})] }));
|
|
9
|
+
return (_jsxs(_Fragment, { children: [context.step === 1 && (_jsxs(_Fragment, { children: [_jsx("h2", { className: "mt-xl text-center text-lg font-semibold", children: "Log in to your account" }), _jsx("p", { className: "text-text-secondary-700 mt-xs text-center text-sm", children: "Welcome back! Please enter your details." })] })), _jsx(SignInMethod, {}), context.type === 'MOBILE_NUMBER' && (_jsx(MobileNumberSignIn, { siteName: props.siteName })), context.type === 'NAME_AND_PASSWORD' && (_jsx(NameAndPasswordSignIn, { siteName: props.siteName }))] }));
|
|
10
10
|
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
type Options = {
|
|
2
|
+
accountName?: string;
|
|
3
|
+
siteName?: string;
|
|
4
|
+
};
|
|
5
|
+
export type ErrorCode = 'MEMBER_ACCOUNT_BLACKLISTED' | 'MEMBER_ACCOUNT_SUSPENDED' | 'INVALID_RECAPTCHA_RESPONSE' | 'MEMBER_NOT_FOUND' | 'RATE_LIMIT_REACH' | 'NOT_READY_TO_SEND_VERIFICATION_ERROR' | 'Forbidden';
|
|
6
|
+
export declare function getFriendlyErrorMessage(code: ErrorCode, options?: Options): string;
|
|
7
|
+
export {};
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export function getFriendlyErrorMessage(code, options) {
|
|
2
|
+
const accountName = options?.accountName ?? '';
|
|
3
|
+
const siteName = options?.siteName ?? '';
|
|
4
|
+
switch (code) {
|
|
5
|
+
case 'MEMBER_ACCOUNT_BLACKLISTED':
|
|
6
|
+
return `Your account ${accountName} has been blacklisted due to a serious violation of our policies. For more information or to appeal, please contact the ${siteName} Chat Support team.`;
|
|
7
|
+
case 'MEMBER_ACCOUNT_SUSPENDED':
|
|
8
|
+
return `Your account ${accountName} has been temporarily suspended. Please reach out to the ${siteName} Chat Support team to learn more and get help with resolving the issue.`;
|
|
9
|
+
case 'INVALID_RECAPTCHA_RESPONSE':
|
|
10
|
+
return 'Invalid reCAPTCHA response';
|
|
11
|
+
case 'MEMBER_NOT_FOUND':
|
|
12
|
+
return 'Account not found. Please check your username and try again.';
|
|
13
|
+
case 'RATE_LIMIT_REACH':
|
|
14
|
+
return 'Rate limit exceeded';
|
|
15
|
+
case 'NOT_READY_TO_SEND_VERIFICATION_ERROR':
|
|
16
|
+
return 'Your account is not ready to send verification requests. Please try again later.';
|
|
17
|
+
case 'Forbidden':
|
|
18
|
+
return 'Invalid username or password, please try again.';
|
|
19
|
+
default:
|
|
20
|
+
return 'Something went wrong. Please try again later.';
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -55,7 +55,7 @@ export function TransactionRecordsTable() {
|
|
|
55
55
|
transactionType: true,
|
|
56
56
|
} }), rows.length <= 0 && (_jsx(Empty, { icon: loading ? SpinnerIcon : File02Icon, title: loading ? 'Just a moment' : 'No data', message: loading
|
|
57
57
|
? 'Fetching latest transaction records...'
|
|
58
|
-
: 'No data is currently available.', className: "mt-5xl" })), rows.length >= 1 && (_jsxs("div", { className: twMerge('mt-xl', 'border-y', 'border-border-secondary', 'bleed', 'lg:not-bleed', 'lg:mt-3xl', 'lg:w-full', 'lg:rounded-xl', 'lg:border-x'), children: [_jsx("div", { className: "border-border-secondary scrollbar-thumb:bg-bg-quaternary scrollbar-track:bg-transparent scrollbar:h-2 scrollbar-thumb:rounded-full overflow-hidden overflow-x-auto rounded-none border-b lg:rounded-t-xl", children: _jsxs(Table.Root, { className: "border-0", children: [_jsx(Table.Header, { children: _jsxs(Table.Row, { children: [_jsx(Table.Heading, { children: "Transaction Type" }), _jsx(Table.Heading, { children: "Transaction Time" }), _jsx(Table.Heading, { children: "Amount" }), _jsx(Table.Heading, { children: "Current Balance" }), _jsx(Table.Heading, { children: "Reference Number" })
|
|
58
|
+
: 'No data is currently available.', className: "mt-5xl" })), rows.length >= 1 && (_jsxs("div", { className: twMerge('mt-xl', 'border-y', 'border-border-secondary', 'bleed', 'lg:not-bleed', 'lg:mt-3xl', 'lg:w-full', 'lg:rounded-xl', 'lg:border-x'), children: [_jsx("div", { className: "border-border-secondary scrollbar-thumb:bg-bg-quaternary scrollbar-track:bg-transparent scrollbar:h-2 scrollbar-thumb:rounded-full overflow-hidden overflow-x-auto rounded-none border-b lg:rounded-t-xl", children: _jsxs(Table.Root, { className: "border-0", children: [_jsx(Table.Header, { children: _jsxs(Table.Row, { children: [_jsx(Table.Heading, { children: "Transaction Type" }), _jsx(Table.Heading, { children: "Transaction Time" }), _jsx(Table.Heading, { children: "Amount" }), _jsx(Table.Heading, { children: "Current Balance" }), _jsx(Table.Heading, { children: "Reference Number" })] }) }), _jsx(Table.Body, { children: rows.map((data) => (_jsxs(Table.Row, { children: [_jsx(Table.Cell, { className: "!py-1", children: capitalize(data.type, {
|
|
59
59
|
delimiter: capitalize.delimiters.UNDERSCORE,
|
|
60
60
|
}) }), _jsxs(Table.Cell, { className: "!py-1", children: [_jsx("div", { className: "leading-tight", children: format(data.dateTimeCreated, 'MM/dd/yyyy', {
|
|
61
61
|
in: tz(localeInfo.timezone),
|
|
@@ -67,5 +67,5 @@ export function TransactionRecordsTable() {
|
|
|
67
67
|
}) }), _jsx(Table.Cell, { className: "!py-1", children: formatNumber(data.currentBalance, {
|
|
68
68
|
currency: localeInfo.currency.code,
|
|
69
69
|
minDecimalPlaces: 2,
|
|
70
|
-
}) }), _jsx(Table.Cell, { className: "!py-1", children: data.referenceNumber })
|
|
70
|
+
}) }), _jsx(Table.Cell, { className: "!py-1", children: data.referenceNumber })] }, data.id))) })] }) }), _jsxs("div", { className: "flex items-center justify-between gap-lg px-4 py-3", children: [_jsx(IconButton, { size: "xs", variant: "outline", colorScheme: "gray", disabled: !hasPrevPage || loading, onClick: () => prev(), children: _jsx(ChevronLeftIcon, { className: "size-5" }) }), _jsxs("p", { className: "text-text-secondary-700 text-sm font-medium", children: ["Page ", page, " of ", totalPages] }), _jsx(IconButton, { size: "xs", variant: "outline", colorScheme: "gray", disabled: !hasNextPage || loading, onClick: () => next(), children: _jsx(ChevronRightIcon, { className: "size-5" }) })] })] }))] }));
|
|
71
71
|
}
|