@antscorp/antsomi-ui 1.3.7-beta.72 → 1.3.7-beta.75

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.
Files changed (32) hide show
  1. package/dist/index.js +415 -319
  2. package/es/components/organism/BasicLogin/components/AuthenticateResult/AuthenticateResult.js +1 -1
  3. package/es/components/organism/BasicLogin/components/ChooseMethod/ChooseMethod.js +37 -19
  4. package/es/components/organism/BasicLogin/components/ChooseMethod/Method.d.ts +1 -0
  5. package/es/components/organism/BasicLogin/components/ChooseMethod/Method.js +4 -5
  6. package/es/components/organism/BasicLogin/components/ChooseMethod/styled.d.ts +10 -3
  7. package/es/components/organism/BasicLogin/components/ChooseMethod/styled.js +54 -6
  8. package/es/components/organism/BasicLogin/components/LoginWithEmail/LoginWithEmail.js +2 -2
  9. package/es/components/organism/BasicLogin/components/LoginWithGG/LoginWithGG.js +3 -3
  10. package/es/components/organism/Login/components/ChooseMethod/ChooseMethod.js +36 -19
  11. package/es/components/organism/Login/components/ChooseMethod/ChooseMethod.spec.d.ts +1 -0
  12. package/es/components/organism/Login/components/ChooseMethod/ChooseMethod.spec.js +50 -0
  13. package/es/components/organism/Login/components/ChooseMethod/Method.d.ts +1 -0
  14. package/es/components/organism/Login/components/ChooseMethod/Method.js +4 -5
  15. package/es/components/organism/Login/components/ChooseMethod/styled.d.ts +10 -3
  16. package/es/components/organism/Login/components/ChooseMethod/styled.js +54 -6
  17. package/es/components/organism/Login/components/LoginSelectPortal/LoginSelectPortal.js +1 -1
  18. package/es/components/organism/Login/components/LoginWithEmail/LoginWithEmail.js +1 -1
  19. package/es/components/organism/Login/components/LoginWithGG/LoginWithGG.js +1 -1
  20. package/es/components/organism/Login/components/WidgetLayout/styled.d.ts +3 -0
  21. package/es/components/organism/Login/components/WidgetLayout/styled.js +6 -0
  22. package/es/components/organism/Login/styled.d.ts +1 -0
  23. package/es/components/organism/Login/styled.js +1 -0
  24. package/es/components/organism/TicketEditor/Content.js +74 -35
  25. package/es/components/organism/TicketEditor/util.d.ts +1 -0
  26. package/es/components/organism/TicketEditor/util.js +1 -0
  27. package/es/components/organism/TicketEditorV2/Content.js +62 -28
  28. package/es/components/organism/TicketEditorV2/util.d.ts +1 -0
  29. package/es/components/organism/TicketEditorV2/util.js +1 -0
  30. package/es/utils/uploadEmbeddedImages.d.ts +7 -0
  31. package/es/utils/uploadEmbeddedImages.js +65 -0
  32. package/package.json +1 -1
@@ -17,7 +17,7 @@ export const AuthenticateResult = props => {
17
17
  backLogin();
18
18
  }
19
19
  };
20
- return (_jsx(WidgetLayout, { header: null, isShowLogo: false, title: _jsx("div", { style: { marginBottom: 100 } }), gapContent: 40, content: _jsx(Flex, { vertical: true, gap: 50, style: { marginTop: 20 }, children: _jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: authenSuccessImg, alt: "" }), _jsx(Title, { isMobile: isMobile, children: "Authenticated!" }), _jsxs(Text, { isMobile: isMobile, children: ["Sign in as ", get(loginData, 'personal.full_name')] })] }) }), footer: _jsx(Flex, { justify: "center", children: _jsx(Button, { type: "text", onClick: handleBack, style: {
20
+ return (_jsx(WidgetLayout, { header: null, isShowLogo: false, title: _jsx("div", { style: { marginBottom: 100 } }), gapContent: 30, content: _jsx(Flex, { vertical: true, gap: 50, children: _jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: authenSuccessImg, alt: "" }), _jsx(Title, { isMobile: isMobile, children: "Authenticated!" }), _jsxs(Text, { isMobile: isMobile, children: ["Sign in as ", get(loginData, 'personal.full_name')] })] }) }), footer: _jsx(Flex, { justify: "center", children: _jsx(Button, { type: "text", onClick: handleBack, style: {
21
21
  fontSize: isMobile ? '16px' : '14px',
22
22
  }, children: "Sign in with a different account" }) }) }));
23
23
  };
@@ -5,7 +5,7 @@ import { Button, Flex } from 'antd';
5
5
  import { get } from 'lodash';
6
6
  import { useMemo } from 'react';
7
7
  import { isMobile } from 'react-device-detect';
8
- import { EmailTemplateIcon, KeyValueIcon } from '@antscorp/antsomi-ui/es/components/icons';
8
+ import { EmailTemplateIcon, PhoneIphoneIcon } from '@antscorp/antsomi-ui/es/components/icons';
9
9
  // Components
10
10
  import { WidgetLayout } from '../WidgetLayout';
11
11
  import { Method } from './Method';
@@ -13,6 +13,8 @@ import { Method } from './Method';
13
13
  import verifyImage from '../../../../../assets/images/components/Login/loginStep2.png';
14
14
  // Utils
15
15
  import { maskEmail } from './utils';
16
+ // Styled
17
+ import { SubTitle } from './styled';
16
18
  import { Title } from '../WidgetLayout/styled';
17
19
  import { Text } from '../../styled';
18
20
  import { LOGIN_METHODS } from '../../constants';
@@ -24,31 +26,47 @@ export const ChooseMethod = props => {
24
26
  }
25
27
  };
26
28
  const methods = useMemo(() => {
27
- const options = loginData?.auth_options || [];
28
- return options.map(opt => {
29
+ const options = [...(loginData?.auth_options || [])].sort((first, second) => {
30
+ if (first.provider === 'email') {
31
+ return -1;
32
+ }
33
+ if (second.provider === 'email') {
34
+ return 1;
35
+ }
36
+ return 0;
37
+ });
38
+ return options
39
+ .filter(opt => opt.provider !== 'google' || loginData.G2FA_user === 1)
40
+ .flatMap(opt => {
29
41
  switch (opt.provider) {
30
42
  case 'google':
31
- return {
32
- disable: opt.status !== 1,
33
- icon: _jsx(KeyValueIcon, { color: "#005eb8" }),
34
- name: LOGIN_METHODS.GOOGLE_AUTHENTICATOR,
35
- label: opt.status === 1
36
- ? 'Use your security code using Google Authenticator.'
37
- : 'Use your security code using Google Authenticator. Authenticator not available.',
38
- };
43
+ return [
44
+ {
45
+ disable: opt.status !== 1,
46
+ icon: _jsx(PhoneIphoneIcon, { color: "#005eb8" }),
47
+ name: LOGIN_METHODS.GOOGLE_AUTHENTICATOR,
48
+ label: 'Authenticator app',
49
+ description: opt.status === 1
50
+ ? 'Enter the code from your authenticator app.'
51
+ : 'Enter the code from your authenticator app. Authenticator not available.',
52
+ },
53
+ ];
39
54
  case 'email':
40
- return {
41
- disable: opt.status !== 1,
42
- icon: _jsx(EmailTemplateIcon, { color: "#005eb8" }),
43
- name: LOGIN_METHODS.EMAIL,
44
- label: `Get verification code at ${maskEmail(get(loginData, 'personal.email', ''))}`,
45
- };
55
+ return [
56
+ {
57
+ disable: opt.status !== 1,
58
+ icon: _jsx(EmailTemplateIcon, { color: "#005eb8" }),
59
+ name: LOGIN_METHODS.EMAIL,
60
+ label: 'Email Verification',
61
+ description: `We'll send a 6-digit verification code to ${maskEmail(get(loginData, 'personal.email', ''))}`,
62
+ },
63
+ ];
46
64
  default:
47
- return null;
65
+ return [];
48
66
  }
49
67
  });
50
68
  }, [loginData]);
51
- return (_jsx(WidgetLayout, { header: null, isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 30, content: _jsxs(Flex, { vertical: true, gap: 50, style: { marginTop: 20 }, children: [_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: verifyImage, alt: "" }), _jsx(Text, { isMobile: isMobile, color: "#595959", children: "For added security, we need to verify your identity." })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 13, children: [_jsx(Flex, { gap: 5, align: "center", justify: "center", children: _jsx(Title, { isMobile: isMobile, children: "Choose how you want to sign in:" }) }), _jsx(Flex, { vertical: true, gap: isMobile ? 20 : 11, children: methods.map(({ disable, icon, name, label }) => (_jsx(Method, { disable: disable, onClick: () => onChooseMethod(name), icon: icon, name: name, label: label }, name))) })] })] }), footer: _jsx(Flex, { justify: "center", children: _jsx(Button, { type: "text", onClick: handleBack, style: {
69
+ return (_jsx(WidgetLayout, { header: null, isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 20, content: _jsxs(Flex, { vertical: true, gap: 50, children: [_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: verifyImage, alt: "" }), _jsx(Text, { isMobile: isMobile, color: "#595959", children: "For your security, we need to verify your identity before continuing." })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 13, children: [_jsx(Flex, { gap: 5, align: "center", justify: "center", children: _jsx(SubTitle, { isMobile: isMobile, children: "How would you like to verify your identity?" }) }), _jsx(Flex, { vertical: true, gap: isMobile ? 20 : 11, children: methods.map(({ disable, icon, name, label, description }) => (_jsx(Method, { disable: disable, onClick: onChooseMethod, icon: icon, name: name, label: label, description: description }, name))) })] })] }), footer: _jsx(Flex, { justify: "center", children: _jsx(Button, { type: "text", onClick: handleBack, style: {
52
70
  fontSize: isMobile ? '16px' : '14px',
53
71
  }, children: "Sign in with a different account" }) }) }));
54
72
  };
@@ -3,6 +3,7 @@ type TMethod = {
3
3
  onClick: (name: string) => void;
4
4
  icon: React.ReactNode;
5
5
  label: React.ReactNode;
6
+ description: React.ReactNode;
6
7
  disable?: boolean;
7
8
  name: string;
8
9
  };
@@ -1,13 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { isMobile } from 'react-device-detect';
3
- import { BlockMethod } from './styled';
4
- import { Text } from '../../styled';
2
+ import { ChevronRightIcon } from '@antscorp/antsomi-ui/es/components/icons';
3
+ import { BlockMethod, MethodContent, MethodDescription, MethodIcon, MethodLabel } from './styled';
5
4
  export const Method = (props) => {
6
- const { onClick, icon, label, disable, name } = props;
5
+ const { onClick, icon, label, description, disable, name } = props;
7
6
  const onChooseMethod = () => {
8
7
  if (!disable && onClick) {
9
8
  onClick(name);
10
9
  }
11
10
  };
12
- return (_jsxs(BlockMethod, { onClick: onChooseMethod, disable: disable, "data-test": name, children: [_jsx("div", { children: icon }), _jsx(Text, { isMobile: isMobile, color: disable ? '#9C9B9B' : '#595959', children: label })] }));
11
+ return (_jsxs(BlockMethod, { type: "button", onClick: onChooseMethod, disabled: disable, "$disable": disable, "data-test": name, children: [_jsx(MethodIcon, { children: icon }), _jsxs(MethodContent, { children: [_jsx(MethodLabel, { "$disable": disable, children: label }), _jsx(MethodDescription, { "$disable": disable, children: description })] }), _jsx(ChevronRightIcon, { "aria-hidden": true, color: disable ? '#9c9b9b' : '#005eb8', width: 20, height: 20 })] }));
13
12
  };
@@ -1,10 +1,17 @@
1
- /// <reference types="react" />
2
1
  export declare const TextInfo: import("styled-components").StyledComponent<"span", any, {
3
2
  isMobile: boolean;
4
3
  }, never>;
5
4
  export declare const SubTitle: import("styled-components").StyledComponent<"span", any, {
6
5
  isMobile: boolean;
7
6
  }, never>;
8
- export declare const BlockMethod: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("antd").FlexProps<import("antd/es/_util/type").AnyObject> & import("react").RefAttributes<HTMLElement>>, any, {
9
- disable?: boolean | undefined;
7
+ export declare const BlockMethod: import("styled-components").StyledComponent<"button", any, {
8
+ $disable?: boolean | undefined;
9
+ }, never>;
10
+ export declare const MethodIcon: import("styled-components").StyledComponent<"span", any, {}, never>;
11
+ export declare const MethodContent: import("styled-components").StyledComponent<"span", any, {}, never>;
12
+ export declare const MethodLabel: import("styled-components").StyledComponent<"span", any, {
13
+ $disable?: boolean | undefined;
14
+ }, never>;
15
+ export declare const MethodDescription: import("styled-components").StyledComponent<"span", any, {
16
+ $disable?: boolean | undefined;
10
17
  }, never>;
@@ -1,4 +1,3 @@
1
- import { Flex } from 'antd';
2
1
  import styled from 'styled-components';
3
2
  export const TextInfo = styled.span `
4
3
  color: #7f7f7f;
@@ -9,16 +8,65 @@ export const SubTitle = styled.span `
9
8
  font-size: ${(props) => (props.isMobile ? ' 16px ' : '14px')};
10
9
  font-weight: 500;
11
10
  `;
12
- export const BlockMethod = styled(Flex) `
11
+ export const BlockMethod = styled.button `
12
+ display: flex;
13
13
  align-items: center;
14
14
  gap: 10px;
15
+ width: 100%;
16
+ min-height: 64px;
15
17
  padding: 10px;
16
- cursor: ${props => (props.disable ? 'not-allowed' : 'pointer')};
18
+ box-sizing: border-box;
19
+ cursor: ${props => (props.$disable ? 'not-allowed' : 'pointer')};
20
+ background-color: #ffffff;
21
+ border: 1px solid #b8cfe6;
17
22
  border-radius: 10px;
23
+ font-family: inherit;
24
+ text-align: left;
25
+ opacity: ${props => (props.$disable ? '0.5' : '1')};
18
26
 
19
- opacity: ${props => (props.disable ? '0.5' : '1')};
27
+ &:hover:not(:disabled) {
28
+ background-color: #f2f9ff;
29
+ border-color: #81bcf4;
30
+ }
31
+
32
+ &:focus-visible {
33
+ outline: 2px solid #005eb8;
34
+ outline-offset: 2px;
35
+ }
36
+ `;
37
+ export const MethodIcon = styled.span `
38
+ display: flex;
39
+ flex: 0 0 24px;
40
+ align-self: flex-start;
41
+ align-items: flex-start;
42
+ justify-content: flex-start;
43
+ margin-top: 2px;
44
+
45
+ svg {
46
+ width: 24px;
47
+ height: 24px;
48
+ }
49
+ `;
50
+ export const MethodContent = styled.span `
51
+ display: flex;
52
+ flex: 1;
53
+ min-width: 0;
54
+ flex-direction: column;
55
+ gap: 5px;
56
+ `;
57
+ export const MethodLabel = styled.span `
58
+ color: ${props => (props.$disable ? '#9c9b9b' : '#000000')};
59
+ font-size: 14px;
60
+
61
+ @media (max-width: 768px) {
62
+ font-size: 16px;
63
+ }
64
+ `;
65
+ export const MethodDescription = styled.span `
66
+ color: ${props => (props.$disable ? '#9c9b9b' : '#595959')};
67
+ font-size: 12px;
20
68
 
21
- &:hover {
22
- background-color: ${props => (props.disable ? '#f2f2f2' : '#e6f0ff')};
69
+ @media (max-width: 768px) {
70
+ font-size: 13px;
23
71
  }
24
72
  `;
@@ -186,7 +186,7 @@ export const LoginWithEmail = ({ loginData, domainLogin, codeKey = 'code', token
186
186
  const renderAlternativeSignIn = () => (_jsx(Flex, { justify: "center", children: _jsx(Button, { type: "text", onClick: handleBack, style: {
187
187
  fontSize: isMobile ? '16px' : '14px',
188
188
  }, children: "Sign in with a different account" }) }));
189
- const renderContent = () => (_jsxs(Flex, { vertical: true, gap: 50, style: { marginTop: 20 }, children: [_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: loginStep2Image, alt: "Identity Verification" }), _jsx(Text, { isMobile: isMobile, color: "#595959", children: "For added security, we need to verify your identity." })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 15, children: [renderResendSection(), _jsx(Flex, { vertical: true, gap: isMobile ? 20 : 11, children: renderCodeInput() }), _jsx(Flex, { vertical: true, gap: isMobile ? 30 : 15, style: { marginTop: isMobile ? 20 : 15 }, children: renderActionButtons() })] })] }));
189
+ const renderContent = () => (_jsxs(Flex, { vertical: true, gap: 50, children: [_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: loginStep2Image, alt: "Identity Verification" }), _jsx(Text, { isMobile: isMobile, color: "#595959", children: "For your security, we need to verify your identity before continuing." })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 15, children: [renderResendSection(), _jsx(Flex, { vertical: true, gap: isMobile ? 20 : 11, children: renderCodeInput() }), _jsx(Flex, { vertical: true, gap: isMobile ? 30 : 15, style: { marginTop: isMobile ? 20 : 15 }, children: renderActionButtons() })] })] }));
190
190
  const renderFooter = () => renderAlternativeSignIn();
191
- return (_jsx(WidgetLayout, { header: null, isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 30, content: renderContent(), footer: renderFooter() }));
191
+ return (_jsx(WidgetLayout, { header: null, isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 20, content: renderContent(), footer: renderFooter() }));
192
192
  };
@@ -99,9 +99,9 @@ export const LoginWithGG = ({ domainLogin, codeKey = 'code', token = '', onLogin
99
99
  const renderAlternativeSignIn = () => (_jsx(Flex, { justify: "center", children: _jsx(Button, { type: "text", onClick: handleBack, style: {
100
100
  fontSize: isMobile ? '16px' : '14px',
101
101
  }, children: "Sign in with a different account" }) }));
102
- const renderHeaderSection = () => (_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: loginStep2Image, alt: "Identity Verification Step" }), _jsx(Text, { isMobile: isMobile, color: "#595959", children: "For added security, we need to verify your identity." })] }));
102
+ const renderHeaderSection = () => (_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: loginStep2Image, alt: "Identity Verification Step" }), _jsx(Text, { isMobile: isMobile, color: "#595959", children: "For your security, we need to verify your identity before continuing." })] }));
103
103
  const renderInputSection = () => (_jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 15, children: [renderInstructionSection(), _jsx(Flex, { vertical: true, gap: isMobile ? 20 : 11, children: renderCodeInput() }), _jsx(Flex, { vertical: true, gap: isMobile ? 30 : 15, style: { marginTop: isMobile ? 20 : 15 }, children: renderActionButtons() })] }));
104
- const renderContent = () => (_jsxs(Flex, { vertical: true, gap: 50, style: { marginTop: 20 }, children: [renderHeaderSection(), renderInputSection()] }));
104
+ const renderContent = () => (_jsxs(Flex, { vertical: true, gap: 50, children: [renderHeaderSection(), renderInputSection()] }));
105
105
  const renderFooter = () => renderAlternativeSignIn();
106
- return (_jsx(WidgetLayout, { header: null, isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 30, content: renderContent(), footer: renderFooter() }));
106
+ return (_jsx(WidgetLayout, { header: null, isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 20, content: renderContent(), footer: renderFooter() }));
107
107
  };
@@ -12,8 +12,9 @@ import { Method } from './Method';
12
12
  // Images
13
13
  import loginStep2Image from '../../../../../assets/images/components/Login/loginStep2.png';
14
14
  import { Text } from '../../styled';
15
+ import { SubTitle } from './styled';
15
16
  import { Title } from '../WidgetLayout/styled';
16
- import { EmailTemplateIcon, KeyValueIcon } from '@antscorp/antsomi-ui/es/components/icons';
17
+ import { EmailTemplateIcon, PhoneIphoneIcon } from '@antscorp/antsomi-ui/es/components/icons';
17
18
  import { maskEmail } from './utils';
18
19
  import { LOGIN_METHODS } from '../../constants';
19
20
  export const ChooseMethod = props => {
@@ -24,31 +25,47 @@ export const ChooseMethod = props => {
24
25
  }
25
26
  };
26
27
  const methods = useMemo(() => {
27
- const options = loginData?.auth_options || [];
28
- return options.map(opt => {
28
+ const options = [...(loginData?.auth_options || [])].sort((first, second) => {
29
+ if (first.provider === 'email') {
30
+ return -1;
31
+ }
32
+ if (second.provider === 'email') {
33
+ return 1;
34
+ }
35
+ return 0;
36
+ });
37
+ return options
38
+ .filter(opt => opt.provider !== 'google' || loginData.G2FA_user === 1)
39
+ .flatMap(opt => {
29
40
  switch (opt.provider) {
30
41
  case 'google':
31
- return {
32
- disable: opt.status !== 1,
33
- icon: _jsx(KeyValueIcon, { color: "#005eb8" }),
34
- name: LOGIN_METHODS.GOOGLE_AUTHENTICATOR,
35
- label: opt.status === 1
36
- ? 'Use your security code using Google Authenticator.'
37
- : 'Use your security code using Google Authenticator. Authenticator not available.',
38
- };
42
+ return [
43
+ {
44
+ disable: opt.status !== 1,
45
+ icon: _jsx(PhoneIphoneIcon, { color: "#005eb8" }),
46
+ name: LOGIN_METHODS.GOOGLE_AUTHENTICATOR,
47
+ label: 'Authenticator app',
48
+ description: opt.status === 1
49
+ ? 'Enter the code from your authenticator app.'
50
+ : 'Enter the code from your authenticator app. Authenticator not available.',
51
+ },
52
+ ];
39
53
  case 'email':
40
- return {
41
- disable: opt.status !== 1,
42
- icon: _jsx(EmailTemplateIcon, { color: "#005eb8" }),
43
- name: LOGIN_METHODS.EMAIL,
44
- label: `Get verification code at ${maskEmail(get(loginData, 'personal.email', ''))}`,
45
- };
54
+ return [
55
+ {
56
+ disable: opt.status !== 1,
57
+ icon: _jsx(EmailTemplateIcon, { color: "#005eb8" }),
58
+ name: LOGIN_METHODS.EMAIL,
59
+ label: 'Email Verification',
60
+ description: `We'll send a 6-digit verification code to ${maskEmail(get(loginData, 'personal.email', ''))}`,
61
+ },
62
+ ];
46
63
  default:
47
- return null;
64
+ return [];
48
65
  }
49
66
  });
50
67
  }, [loginData]);
51
- return (_jsx(WidgetLayout, { header: _jsx(Avatar, { image: get(loginData, 'personal.avatar'), name: get(loginData, 'personal.full_name'), email: get(loginData, 'personal.email') }), isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 30, content: _jsxs(Flex, { vertical: true, gap: 50, style: { marginTop: 20 }, children: [_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: loginStep2Image, alt: "" }), _jsx(Text, { isMobile: isMobile, color: "#595959", children: "For added security, we need to verify your identity." })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 13, children: [_jsx(Flex, { gap: 5, align: "center", justify: "center", children: _jsx(Title, { isMobile: isMobile, children: "Choose how you want to sign in:" }) }), _jsx(Flex, { vertical: true, gap: isMobile ? 20 : 11, children: methods.map(({ disable, icon, name, label }) => (_jsx(Method, { disable: disable, onClick: onChooseMethod, icon: icon, name: name, label: label }, name))) })] })] }), footer: _jsx(Flex, { justify: "center", children: _jsx(Button, { type: "text", onClick: handleBack, style: {
68
+ return (_jsx(WidgetLayout, { header: _jsx(Avatar, { image: get(loginData, 'personal.avatar'), name: get(loginData, 'personal.full_name'), email: get(loginData, 'personal.email') }), isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 20, content: _jsxs(Flex, { vertical: true, gap: 50, children: [_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: loginStep2Image, alt: "" }), _jsx(Text, { isMobile: isMobile, color: "#595959", isCenter: true, children: "For your security, we need to verify your identity before continuing." })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 13, children: [_jsx(Flex, { gap: 5, align: "center", justify: "center", children: _jsx(SubTitle, { isMobile: isMobile, children: "How would you like to verify your identity?" }) }), _jsx(Flex, { vertical: true, gap: isMobile ? 20 : 11, children: methods.map(({ disable, icon, name, label, description }) => (_jsx(Method, { disable: disable, onClick: onChooseMethod, icon: icon, name: name, label: label, description: description }, name))) })] })] }), footer: _jsx(Flex, { justify: "center", children: _jsx(Button, { type: "text", onClick: handleBack, style: {
52
69
  fontSize: isMobile ? '16px' : '14px',
53
70
  }, children: "Sign in with a different account" }) }) }));
54
71
  };
@@ -0,0 +1,50 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { cleanup, render, screen } from '@testing-library/react';
3
+ import { ChooseMethod as LoginChooseMethod } from './ChooseMethod';
4
+ import { ChooseMethod as BasicLoginChooseMethod } from '../../../BasicLogin/components/ChooseMethod/ChooseMethod';
5
+ const loginDataWithout2FA = {
6
+ G2FA_user: 0,
7
+ personal: {
8
+ avatar: '',
9
+ email: 'user@example.com',
10
+ full_name: 'Test User',
11
+ language: 'en',
12
+ role: '',
13
+ user_id: '1',
14
+ },
15
+ auth_options: [
16
+ { provider: 'google', status: 1 },
17
+ { provider: 'email', status: 1 },
18
+ ],
19
+ };
20
+ describe.each([
21
+ ['Login', LoginChooseMethod],
22
+ ['BasicLogin', BasicLoginChooseMethod],
23
+ ])('%s ChooseMethod', (_, ChooseMethod) => {
24
+ afterEach(cleanup);
25
+ it('renders Google Authenticator only when the account has enabled 2FA', () => {
26
+ const { rerender } = render(_jsx(ChooseMethod, { backLogin: () => undefined, loginData: loginDataWithout2FA, onChooseMethod: () => undefined }));
27
+ expect(screen.queryByText('Enter the code from your authenticator app.')).toBeNull();
28
+ expect(screen.queryByRole('button', { name: /Email Verification/ })).not.toBeNull();
29
+ rerender(_jsx(ChooseMethod, { backLogin: () => undefined, loginData: { ...loginDataWithout2FA, G2FA_user: 1 }, onChooseMethod: () => undefined }));
30
+ expect(screen.queryByText('Enter the code from your authenticator app.')).not.toBeNull();
31
+ });
32
+ it('renders each verification method with a label and description', () => {
33
+ render(_jsx(ChooseMethod, { backLogin: () => undefined, loginData: { ...loginDataWithout2FA, G2FA_user: 1 }, onChooseMethod: () => undefined }));
34
+ expect(screen.queryByText('How would you like to verify your identity?')).not.toBeNull();
35
+ expect(screen.queryByRole('button', {
36
+ name: "Email Verification We'll send a 6-digit verification code to u****r@e*******m.",
37
+ })).not.toBeNull();
38
+ expect(screen.queryByRole('button', {
39
+ name: 'Authenticator app Enter the code from your authenticator app.',
40
+ })).not.toBeNull();
41
+ });
42
+ it('renders Email Verification before Authenticator app regardless of backend order', () => {
43
+ render(_jsx(ChooseMethod, { backLogin: () => undefined, loginData: { ...loginDataWithout2FA, G2FA_user: 1 }, onChooseMethod: () => undefined }));
44
+ const methodButtons = screen.getAllByRole('button', {
45
+ name: /Email Verification|Authenticator app/,
46
+ });
47
+ expect(methodButtons[0].textContent).toContain('Email Verification');
48
+ expect(methodButtons[1].textContent).toContain('Authenticator app');
49
+ });
50
+ });
@@ -3,6 +3,7 @@ type TMethod = {
3
3
  onClick: (name: string) => void;
4
4
  icon: React.ReactNode;
5
5
  label: React.ReactNode;
6
+ description: React.ReactNode;
6
7
  disable?: boolean;
7
8
  name: string;
8
9
  };
@@ -1,13 +1,12 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- import { isMobile } from 'react-device-detect';
3
- import { BlockMethod } from './styled';
4
- import { Text } from '../../styled';
2
+ import { ChevronRightIcon } from '@antscorp/antsomi-ui/es/components/icons';
3
+ import { BlockMethod, MethodContent, MethodDescription, MethodIcon, MethodLabel } from './styled';
5
4
  export const Method = (props) => {
6
- const { onClick, icon, label, disable, name } = props;
5
+ const { onClick, icon, label, description, disable, name } = props;
7
6
  const onChooseMethod = () => {
8
7
  if (!disable && onClick) {
9
8
  onClick(name);
10
9
  }
11
10
  };
12
- return (_jsxs(BlockMethod, { onClick: onChooseMethod, disable: disable, "data-test": name, children: [_jsx("div", { children: icon }), _jsx(Text, { isMobile: isMobile, color: disable ? '#9C9B9B' : '#595959', children: label })] }));
11
+ return (_jsxs(BlockMethod, { type: "button", onClick: onChooseMethod, disabled: disable, "$disable": disable, "data-test": name, children: [_jsx(MethodIcon, { children: icon }), _jsxs(MethodContent, { children: [_jsx(MethodLabel, { "$disable": disable, children: label }), _jsx(MethodDescription, { "$disable": disable, children: description })] }), _jsx(ChevronRightIcon, { "aria-hidden": true, color: disable ? '#9c9b9b' : '#005eb8', width: 20, height: 20 })] }));
13
12
  };
@@ -1,10 +1,17 @@
1
- /// <reference types="react" />
2
1
  export declare const TextInfo: import("styled-components").StyledComponent<"span", any, {
3
2
  isMobile: boolean;
4
3
  }, never>;
5
4
  export declare const SubTitle: import("styled-components").StyledComponent<"span", any, {
6
5
  isMobile: boolean;
7
6
  }, never>;
8
- export declare const BlockMethod: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("antd").FlexProps<import("antd/es/_util/type").AnyObject> & import("react").RefAttributes<HTMLElement>>, any, {
9
- disable?: boolean | undefined;
7
+ export declare const BlockMethod: import("styled-components").StyledComponent<"button", any, {
8
+ $disable?: boolean | undefined;
9
+ }, never>;
10
+ export declare const MethodIcon: import("styled-components").StyledComponent<"span", any, {}, never>;
11
+ export declare const MethodContent: import("styled-components").StyledComponent<"span", any, {}, never>;
12
+ export declare const MethodLabel: import("styled-components").StyledComponent<"span", any, {
13
+ $disable?: boolean | undefined;
14
+ }, never>;
15
+ export declare const MethodDescription: import("styled-components").StyledComponent<"span", any, {
16
+ $disable?: boolean | undefined;
10
17
  }, never>;
@@ -1,4 +1,3 @@
1
- import { Flex } from 'antd';
2
1
  import styled from 'styled-components';
3
2
  export const TextInfo = styled.span `
4
3
  color: #7f7f7f;
@@ -9,16 +8,65 @@ export const SubTitle = styled.span `
9
8
  font-size: ${(props) => (props.isMobile ? ' 16px ' : '14px')};
10
9
  font-weight: 500;
11
10
  `;
12
- export const BlockMethod = styled(Flex) `
11
+ export const BlockMethod = styled.button `
12
+ display: flex;
13
13
  align-items: center;
14
14
  gap: 10px;
15
+ width: 100%;
16
+ min-height: 64px;
15
17
  padding: 10px;
16
- cursor: ${props => (props.disable ? 'not-allowed' : 'pointer')};
18
+ box-sizing: border-box;
19
+ cursor: ${props => (props.$disable ? 'not-allowed' : 'pointer')};
20
+ background-color: #ffffff;
21
+ border: 1px solid #b8cfe6;
17
22
  border-radius: 10px;
23
+ font-family: inherit;
24
+ text-align: left;
25
+ opacity: ${props => (props.$disable ? '0.5' : '1')};
18
26
 
19
- opacity: ${props => (props.disable ? '0.5' : '1')};
27
+ &:hover:not(:disabled) {
28
+ background-color: #f2f9ff;
29
+ border-color: #81bcf4;
30
+ }
31
+
32
+ &:focus-visible {
33
+ outline: 2px solid #005eb8;
34
+ outline-offset: 2px;
35
+ }
36
+ `;
37
+ export const MethodIcon = styled.span `
38
+ display: flex;
39
+ flex: 0 0 24px;
40
+ align-self: flex-start;
41
+ align-items: flex-start;
42
+ justify-content: flex-start;
43
+ margin-top: 2px;
44
+
45
+ svg {
46
+ width: 24px;
47
+ height: 24px;
48
+ }
49
+ `;
50
+ export const MethodContent = styled.span `
51
+ display: flex;
52
+ flex: 1;
53
+ min-width: 0;
54
+ flex-direction: column;
55
+ gap: 5px;
56
+ `;
57
+ export const MethodLabel = styled.span `
58
+ color: ${props => (props.$disable ? '#9c9b9b' : '#000000')};
59
+ font-size: 14px;
60
+
61
+ @media (max-width: 768px) {
62
+ font-size: 16px;
63
+ }
64
+ `;
65
+ export const MethodDescription = styled.span `
66
+ color: ${props => (props.$disable ? '#9c9b9b' : '#595959')};
67
+ font-size: 12px;
20
68
 
21
- &:hover {
22
- background-color: ${props => (props.disable ? '#f2f2f2' : '#e6f0ff')};
69
+ @media (max-width: 768px) {
70
+ font-size: 13px;
23
71
  }
24
72
  `;
@@ -177,7 +177,7 @@ export const LoginSelectPortal = props => {
177
177
  return draftListNetwork.filter(netWork => netWork.networkName.toLocaleLowerCase().indexOf(state.portalSearch.toLocaleLowerCase()) !==
178
178
  -1);
179
179
  }, [listNetworks, state.portalSearch]);
180
- return (_jsx(WidgetLayout, { header: _jsx(Avatar, { image: get(loginData, 'personal.avatar'), name: get(loginData, 'personal.full_name'), email: get(loginData, 'personal.email') }), gapContent: 30, isShowLogo: false, title: "Select Portal", content: _jsxs(Flex, { vertical: true, gap: 30, children: [_jsxs(Flex, { vertical: true, gap: 20, children: [_jsx(StyleInput, { isMobile: isMobile, placeholder: "Search", suffix: _jsx(Icon, { type: "icon-ants-search-2", style: {
180
+ return (_jsx(WidgetLayout, { header: _jsx(Avatar, { image: get(loginData, 'personal.avatar'), name: get(loginData, 'personal.full_name'), email: get(loginData, 'personal.email') }), gapContent: 20, isShowLogo: false, title: "Select Portal", content: _jsxs(Flex, { vertical: true, gap: 30, children: [_jsxs(Flex, { vertical: true, gap: 20, children: [_jsx(StyleInput, { isMobile: isMobile, placeholder: "Search", suffix: _jsx(Icon, { type: "icon-ants-search-2", style: {
181
181
  fontSize: 24,
182
182
  }, color: "#595959" }), onChange: e => setPortalSearch(e.target.value) }), isEmpty(filterListNetworks) ? (_jsxs(Flex, { vertical: true, align: "center", justify: "center", style: { height: 514 }, gap: 15, children: [!isMobile && _jsx(Image, { src: NoData, alt: "", width: 80, height: 80 }), _jsx(Text, { style: {
183
183
  color: '#595959',
@@ -205,7 +205,7 @@ export const LoginWithEmail = ({ loginData, domainLogin, codeKey = 'code', token
205
205
  const baseText = `Please enter the 6-digit code send to ${maskedEmail} Haven't received it? `;
206
206
  return (_jsx(Flex, { gap: 5, align: "center", children: _jsxs(Text, { isMobile: isMobile, color: "#595959", children: [baseText, state.isShowResend ? (_jsx(Text, { color: state.isDisableResend ? '#a2a2a2' : '#005eb8', onClick: state.isDisableResend ? undefined : onResendCode, isMobile: isMobile, style: { cursor: state.isDisableResend ? 'not-allowed' : 'pointer' }, children: "Resend." })) : (`Resend code in ${seconds}s.`)] }) }));
207
207
  };
208
- return (_jsx(WidgetLayout, { header: _jsx(Avatar, { image: userAvatar, name: userFullName, email: userEmail }), isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 30, content: _jsxs(Flex, { vertical: true, gap: 50, style: { marginTop: 20 }, children: [_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: loginStep2Image, alt: "Identity Verification" }), _jsx(Text, { isMobile: isMobile, color: "#595959", children: "For added security, we need to verify your identity." })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 15, children: [renderResendSection(), _jsxs(Flex, { vertical: true, gap: isMobile ? 20 : 11, children: [_jsxs(Flex, { vertical: true, gap: 10, children: [_jsx(StyleInput, { isMobile: isMobile, maxLength: CODE_LENGTH, placeholder: "Input your code", value: state.code, onChange: handleCodeChange, status: errorRef.current && state.codeError ? 'error' : undefined, disabled: state.isTooManyMistakes }), renderErrorMessages()] }), _jsx(Checkbox, { checked: state.isTrust, onChange: handleTrustChange, disabled: state.isTooManyMistakes, children: _jsx(TextInfo, { isMobile: isMobile, children: "Remember this browser for future logins" }) })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 30 : 15, style: { marginTop: isMobile ? 20 : 15 }, children: [_jsx(StyleButton, { isMobile: isMobile, type: "primary", onClick: signIn, loading: state.isLoading, disabled: state.isTooManyMistakes, children: "Verify" }), _jsx(StyleButton, { isMobile: isMobile, onClick: handleBack, type: "text", children: "Back to login" })] })] })] }), footer: _jsx(Flex, { vertical: true, gap: 115, children: _jsx(Flex, { justify: "center", children: _jsx(Button, { type: "text", onClick: handleBack, style: {
208
+ return (_jsx(WidgetLayout, { header: _jsx(Avatar, { image: userAvatar, name: userFullName, email: userEmail }), isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 20, content: _jsxs(Flex, { vertical: true, gap: 50, children: [_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: loginStep2Image, alt: "Identity Verification" }), _jsx(Text, { isMobile: isMobile, color: "#595959", isCenter: true, children: "For your security, we need to verify your identity before continuing." })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 15, children: [renderResendSection(), _jsxs(Flex, { vertical: true, gap: isMobile ? 20 : 11, children: [_jsxs(Flex, { vertical: true, gap: 10, children: [_jsx(StyleInput, { isMobile: isMobile, maxLength: CODE_LENGTH, placeholder: "Input your code", value: state.code, onChange: handleCodeChange, status: errorRef.current && state.codeError ? 'error' : undefined, disabled: state.isTooManyMistakes }), renderErrorMessages()] }), _jsx(Checkbox, { checked: state.isTrust, onChange: handleTrustChange, disabled: state.isTooManyMistakes, children: _jsx(TextInfo, { isMobile: isMobile, children: "Remember this browser for future logins" }) })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 30 : 15, style: { marginTop: isMobile ? 20 : 15 }, children: [_jsx(StyleButton, { isMobile: isMobile, type: "primary", onClick: signIn, loading: state.isLoading, disabled: state.isTooManyMistakes, children: "Verify" }), _jsx(StyleButton, { isMobile: isMobile, onClick: handleBack, type: "text", children: "Back to login" })] })] })] }), footer: _jsx(Flex, { vertical: true, gap: 115, children: _jsx(Flex, { justify: "center", children: _jsx(Button, { type: "text", onClick: handleBack, style: {
209
209
  fontSize: isMobile ? '16px' : '14px',
210
210
  }, children: "Sign in with a different account" }) }) }) }));
211
211
  };
@@ -98,7 +98,7 @@ export const LoginWithGG = props => {
98
98
  });
99
99
  }
100
100
  }, [state.code]);
101
- return (_jsx(WidgetLayout, { header: _jsx(Avatar, { image: get(loginData, 'personal.avatar'), name: get(loginData, 'personal.full_name'), email: get(loginData, 'personal.email') }), isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 30, content: _jsxs(Flex, { vertical: true, gap: 50, style: { marginTop: 20 }, children: [_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: loginStep2Image, alt: "" }), _jsx(Text, { isMobile: isMobile, color: "#595959", children: "For added security, we need to verify your identity." })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 15, children: [_jsxs(Flex, { gap: 5, align: "center", children: [_jsx(Text, { isMobile: isMobile, color: "#595959", children: "Please enter the 6-digit code registered to your device." }), _jsx(IconInfo, { image: step2, title: _jsx(TextAntsomiUI, { style: {
101
+ return (_jsx(WidgetLayout, { header: _jsx(Avatar, { image: get(loginData, 'personal.avatar'), name: get(loginData, 'personal.full_name'), email: get(loginData, 'personal.email') }), isShowLogo: false, title: _jsx(Title, { isMobile: isMobile, style: { marginTop: 20 }, children: "Identity Verification" }), gapContent: 20, content: _jsxs(Flex, { vertical: true, gap: 50, children: [_jsxs(Flex, { vertical: true, gap: 20, align: "center", children: [_jsx("img", { src: loginStep2Image, alt: "" }), _jsx(Text, { isMobile: isMobile, color: "#595959", isCenter: true, children: "For your security, we need to verify your identity before continuing." })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 10 : 15, children: [_jsxs(Flex, { gap: 5, align: "center", children: [_jsx(Text, { isMobile: isMobile, color: "#595959", children: "Please enter the 6-digit code registered to your device." }), _jsx(IconInfo, { image: step2, title: _jsx(TextAntsomiUI, { style: {
102
102
  marginTop: 20,
103
103
  textAlign: 'center',
104
104
  }, children: _jsxs(Text, { isMobile: isMobile, children: ["Open Google Authenticator and enter the code from ", _jsx("b", { children: "antsomi.com" })] }) }) })] }), _jsxs(Flex, { vertical: true, gap: isMobile ? 20 : 11, children: [_jsxs(Flex, { vertical: true, gap: 10, children: [_jsx(StyleInput, { isMobile: isMobile, maxLength: 6, placeholder: "Input your code", value: state.code, onChange: e => {
@@ -2,4 +2,7 @@
2
2
  export declare const Title: import("styled-components").StyledComponent<"div", any, {
3
3
  isMobile: boolean;
4
4
  }, never>;
5
+ export declare const Label: import("styled-components").StyledComponent<"div", any, {
6
+ isMobile: boolean;
7
+ }, never>;
5
8
  export declare const WidgetLayoutMain: import("styled-components").StyledComponent<import("react").ForwardRefExoticComponent<import("antd").FlexProps<import("antd/es/_util/type").AnyObject> & import("react").RefAttributes<HTMLElement>>, any, {}, never>;
@@ -6,6 +6,12 @@ export const Title = styled.div `
6
6
  color: #005eb8;
7
7
  text-align: center;
8
8
  `;
9
+ export const Label = styled.div `
10
+ font-size: ${props => (props.isMobile ? '16px' : '14px')};
11
+ font-weight: 500;
12
+ color: #005eb8;
13
+ text-align: center;
14
+ `;
9
15
  export const WidgetLayoutMain = styled(Flex) `
10
16
  position: relative;
11
17
 
@@ -48,4 +48,5 @@ export declare const Text: import("styled-components").StyledComponent<"div", an
48
48
  color?: string | undefined;
49
49
  isBold?: boolean | undefined;
50
50
  isMobile?: boolean | undefined;
51
+ isCenter?: boolean | undefined;
51
52
  }, never>;
@@ -126,4 +126,5 @@ export const Text = styled.div `
126
126
  color: ${props => (props.color ? props.color : ' #000000')};
127
127
  font-size: ${props => (props.isMobile ? ' 14px !important' : '12px')};
128
128
  font-weight: ${props => (props.isBold ? 'bold' : 'normal')};
129
+ text-align: ${props => (props.isCenter ? 'center' : 'left')};
129
130
  `;