@griddo/ax 10.6.21 → 10.7.0

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/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@griddo/ax",
3
3
  "description": "Griddo Author Experience",
4
- "version": "10.6.21",
4
+ "version": "10.7.0",
5
5
  "authors": [
6
6
  "Álvaro Sánchez' <alvaro.sanches@secuoyas.com>",
7
7
  "Carlos Torres <carlos.torres@secuoyas.com>",
@@ -233,5 +233,5 @@
233
233
  "publishConfig": {
234
234
  "access": "public"
235
235
  },
236
- "gitHead": "52124754ba1a1030fdf5495dcec6e4a6281ac84d"
236
+ "gitHead": "0c56d9f6d66dea9fd92f811630b97808227aa693"
237
237
  }
@@ -5,7 +5,7 @@ import { render, cleanup, screen, fireEvent, waitFor, act } from "@testing-libra
5
5
  import "@testing-library/jest-dom";
6
6
 
7
7
  import { parseTheme } from "@ax/helpers";
8
- import RecoveryModal, { IRecoveryProps } from "@ax/components/Login/RecoveryModal";
8
+ import RecoveryModal, { IRecoveryProps } from "@ax/components/Login/LoginForm/RecoveryModal";
9
9
  import globalTheme from "@ax/themes/theme.json";
10
10
 
11
11
  afterEach(cleanup);
@@ -34,9 +34,8 @@ const SERVICES: { [key: string]: IServiceConfig } = {
34
34
  },
35
35
  };
36
36
 
37
- const login = async (username: string, password: string) => {
38
- // FIXME set email as param instead of username
39
- return sendRequest(SERVICES.LOGIN, { username, password });
37
+ const login = async (data?: { username?: string; password?: string; petitionId?: string }) => {
38
+ return sendRequest(SERVICES.LOGIN, data);
40
39
  };
41
40
 
42
41
  const getSettings = () => {
@@ -12,7 +12,7 @@ enum buttonStyles {
12
12
  }
13
13
 
14
14
  const Button = (props: IButtonProps): JSX.Element => {
15
- const { children, type, disabled, icon, buttonStyle, onClick, className, loader, backIcon } = props;
15
+ const { children, type, disabled, icon, buttonStyle, onClick, className, loader, backIcon, iconFill = true } = props;
16
16
 
17
17
  const handleOnClick = (e: React.MouseEvent<HTMLButtonElement>) => {
18
18
  if (onClick !== undefined) {
@@ -23,8 +23,8 @@ const Button = (props: IButtonProps): JSX.Element => {
23
23
 
24
24
  const iconSize = buttonStyle === buttonStyles.MINIMAL ? "16" : "24";
25
25
 
26
- let iconWrapper = icon ? <Icon name={icon} size={iconSize} /> : loader ? <Loader name={loader} /> : <></>;
27
- let iconBackWrapper = backIcon ? <Icon name={backIcon} /> : <></>;
26
+ const iconWrapper = icon ? <Icon name={icon} size={iconSize} /> : loader ? <Loader name={loader} /> : <></>;
27
+ const iconBackWrapper = backIcon ? <Icon name={backIcon} /> : <></>;
28
28
 
29
29
  const buttonContent = (
30
30
  <S.Label icon={icon || loader} backIcon={backIcon}>
@@ -83,6 +83,7 @@ const Button = (props: IButtonProps): JSX.Element => {
83
83
  type={type}
84
84
  disabled={disabled}
85
85
  onClick={handleOnClick}
86
+ iconFill={iconFill}
86
87
  >
87
88
  {buttonContent}
88
89
  </S.Button>
@@ -100,6 +101,7 @@ export interface IButtonProps {
100
101
  buttonStyle?: "solid" | "text" | "line" | "lineInverse" | "minimal";
101
102
  className?: string;
102
103
  backIcon?: string;
104
+ iconFill?: boolean;
103
105
  }
104
106
 
105
107
  export default Button;
@@ -1,7 +1,7 @@
1
1
  import React from "react";
2
2
  import styled from "styled-components";
3
3
 
4
- const Button = styled.button`
4
+ const Button = styled.button<{ iconFill: boolean }>`
5
5
  ${(p) => p.theme.textStyle.uiButton};
6
6
  background-color: ${(p) => p.theme.color.interactive01};
7
7
  border-radius: 4px;
@@ -55,7 +55,7 @@ const Button = styled.button`
55
55
  width: ${(p) => p.theme.spacing.s};
56
56
  height: ${(p) => p.theme.spacing.s};
57
57
  path {
58
- fill: #ffffff;
58
+ fill: ${(p) => (p.iconFill ? "#ffffff" : "")};
59
59
  }
60
60
  circle {
61
61
  stroke: #ffffff;
@@ -0,0 +1,10 @@
1
+ import * as React from "react";
2
+ const SvgMicrosoft = (props) => (
3
+ <svg xmlns="http://www.w3.org/2000/svg" width={17} height={16} fill="none" {...props}>
4
+ <path fill="#F35325" d="M1.196.695h6.956v6.957H1.196V.695Z" />
5
+ <path fill="#81BC06" d="M8.848.695h6.956v6.957H8.848V.695Z" />
6
+ <path fill="#05A6F0" d="M1.196 8.348h6.956v6.956H1.196V8.348Z" />
7
+ <path fill="#FFBA08" d="M8.848 8.348h6.956v6.956H8.848V8.348Z" />
8
+ </svg>
9
+ );
10
+ export default SvgMicrosoft;
@@ -20,6 +20,8 @@ const Icon = (props: IProps) => {
20
20
  const size = props.size ? props.size : "22";
21
21
  const fill = props.fill ? props.fill : "black";
22
22
 
23
+ const viewBox = props.viewBox ? `0 0 ${props.viewBox} ${props.viewBox}` : "0 0 24 24";
24
+
23
25
  const Svg = getImage(name);
24
26
 
25
27
  if (Svg) {
@@ -28,7 +30,7 @@ const Icon = (props: IProps) => {
28
30
  data-testid={`icon-component-${name.toLowerCase()}`}
29
31
  height={size}
30
32
  width={size}
31
- viewBox="0 0 24 24"
33
+ viewBox={viewBox}
32
34
  fill={fill}
33
35
  />
34
36
  );
@@ -41,6 +43,7 @@ export interface IStateProps {
41
43
  name: string;
42
44
  size?: string;
43
45
  fill?: string;
46
+ viewBox?: number;
44
47
  }
45
48
 
46
49
  type IProps = IStateProps;
@@ -0,0 +1,6 @@
1
+ <svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
2
+ <path d="M1.19565 0.695312H8.15217V7.65183H1.19565V0.695312Z" fill="#F35325"/>
3
+ <path d="M8.84782 0.695312H15.8043V7.65183H8.84782V0.695312Z" fill="#81BC06"/>
4
+ <path d="M1.19565 8.34766H8.15217V15.3042H1.19565V8.34766Z" fill="#05A6F0"/>
5
+ <path d="M8.84782 8.34766H15.8043V15.3042H8.84782V8.34766Z" fill="#FFBA08"/>
6
+ </svg>
@@ -0,0 +1,13 @@
1
+ import styled from "styled-components";
2
+
3
+ const ModalContent = styled.div`
4
+ padding: ${(p) => p.theme.spacing.m};
5
+ p {
6
+ margin-bottom: ${(p) => p.theme.spacing.m};
7
+ }
8
+ div {
9
+ margin-bottom: 0;
10
+ }
11
+ `;
12
+
13
+ export { ModalContent };
@@ -0,0 +1,135 @@
1
+ import React, { useState } from "react";
2
+
3
+ import { IGlobalSettings } from "@ax/containers/App/reducer";
4
+ import { useModal } from "@ax/hooks";
5
+ import { Button, FieldsBehavior, ErrorToast } from "@ax/components";
6
+ import RecoveryModal from "./RecoveryModal";
7
+
8
+ import * as S from "./style";
9
+
10
+ declare global {
11
+ interface Window {
12
+ handleErrorClick: () => void;
13
+ }
14
+ }
15
+
16
+ const LoginForm = (props: ILoginFormProps): JSX.Element => {
17
+ const {
18
+ handleSubmit,
19
+ email,
20
+ handleEmail,
21
+ password,
22
+ handlePassword,
23
+ isLoggingIn,
24
+ settings,
25
+ rememberMe,
26
+ handleRememberMe,
27
+ } = props;
28
+
29
+ const [viewPass, setViewPass] = useState(false);
30
+ const { isOpen, toggleModal } = useModal();
31
+
32
+ window.handleErrorClick = toggleModal;
33
+
34
+ const _handleEmail = (e: string) => handleEmail(e);
35
+
36
+ const _handlePwd = (e: string) => handlePassword(e);
37
+
38
+ const _togglePassword = () => setViewPass(!viewPass);
39
+
40
+ const btnText = isLoggingIn ? "Sending" : "Login";
41
+
42
+ const inputType = viewPass ? "text" : "password";
43
+ const icon = viewPass ? "hide" : "view";
44
+
45
+ const textName = settings.welcomeText2 ? settings.welcomeText2 : "Griddo";
46
+ const nameLogo =
47
+ textName === "Griddo" ? <img src="/img/logos/logoGriddoExtended@3x.svg" alt="Griddo Logo" /> : textName;
48
+
49
+ return (
50
+ <S.Wrapper>
51
+ <S.Main>
52
+ <S.Header>
53
+ <S.Title>
54
+ <div>Welcome to</div>
55
+ <div>{nameLogo}</div>
56
+ </S.Title>
57
+ <S.Subtitle>To start using {textName}, introduce your email and password.</S.Subtitle>
58
+ </S.Header>
59
+ <ErrorToast />
60
+ <S.Form onSubmit={handleSubmit}>
61
+ <FieldsBehavior
62
+ fieldType="TextField"
63
+ title="Email"
64
+ autoComplete="email"
65
+ value={email}
66
+ onChange={_handleEmail}
67
+ name="email"
68
+ inversed={true}
69
+ placeholder="Type your email"
70
+ />
71
+ <FieldsBehavior
72
+ fieldType="TextField"
73
+ title="Password"
74
+ inputType={inputType}
75
+ value={password}
76
+ onChange={_handlePwd}
77
+ autoComplete="current-password"
78
+ icon={icon}
79
+ onClickIcon={_togglePassword}
80
+ iconPosition="in"
81
+ name="password"
82
+ inversed={true}
83
+ />
84
+ <S.Actions>
85
+ <FieldsBehavior
86
+ fieldType="UniqueCheck"
87
+ title=""
88
+ value={rememberMe}
89
+ onChange={handleRememberMe}
90
+ options={[{ title: "Remember me" }]}
91
+ name="rememberMe"
92
+ inversed={true}
93
+ />
94
+ <S.Password>
95
+ <span
96
+ onClick={toggleModal}
97
+ onKeyDown={toggleModal}
98
+ role="checkbox"
99
+ aria-checked="false"
100
+ tabIndex={0}
101
+ data-testid="forgot-button"
102
+ >
103
+ Lost your password?
104
+ </span>
105
+ </S.Password>
106
+ </S.Actions>
107
+ <Button type="submit" disabled={isLoggingIn ? true : false}>
108
+ {btnText}
109
+ </Button>
110
+ <RecoveryModal isOpen={isOpen} toggleModal={toggleModal} />
111
+ </S.Form>
112
+ </S.Main>
113
+ <S.Secuoyas>
114
+ Made with care at{" "}
115
+ <a href="https://www.secuoyas.com/" target="_blank" rel="noopener noreferrer">
116
+ <img src="/img/logos/logoSQY.svg" alt="Secuoyas logo" />
117
+ </a>
118
+ </S.Secuoyas>
119
+ </S.Wrapper>
120
+ );
121
+ };
122
+
123
+ export interface ILoginFormProps {
124
+ handleSubmit: (e: React.FormEvent<HTMLFormElement>) => void;
125
+ email: string;
126
+ handleEmail: (email: string) => void;
127
+ password: string;
128
+ handlePassword: (password: string) => void;
129
+ isLoggingIn: boolean;
130
+ settings: IGlobalSettings;
131
+ rememberMe: boolean;
132
+ handleRememberMe: () => void;
133
+ }
134
+
135
+ export default LoginForm;
@@ -0,0 +1,78 @@
1
+ import styled from "styled-components";
2
+
3
+ const Wrapper = styled.div`
4
+ width: 368px;
5
+ position: relative;
6
+ height: 100%;
7
+ display: flex;
8
+ align-items: center;
9
+ `;
10
+
11
+ const Main = styled.div`
12
+ width: 100%;
13
+ `;
14
+
15
+ const Header = styled.header``;
16
+
17
+ const Subtitle = styled.p`
18
+ ${(p) => p.theme.textStyle.uiM};
19
+ color: ${(p) => p.theme.color.textMediumEmphasisInverse};
20
+ font-weight: normal;
21
+ margin-bottom: ${(p) => p.theme.spacing.xs};
22
+ width: 280px;
23
+ `;
24
+
25
+ const Title = styled.div`
26
+ ${(p) => p.theme.textStyle.headingM};
27
+ color: ${(p) => p.theme.color.textHighEmphasisInverse};
28
+ display: flex;
29
+ margin-bottom: ${(p) => p.theme.spacing.xs};
30
+ align-items: center;
31
+ div:last-child {
32
+ margin-left: ${(p) => p.theme.spacing.xxs};
33
+ }
34
+ img {
35
+ height: 24px;
36
+ margin-top: ${(p) => p.theme.spacing.xxs};
37
+ }
38
+ `;
39
+
40
+ const Form = styled.form`
41
+ display: flex;
42
+ flex-direction: column;
43
+ margin-top: ${(p) => p.theme.spacing.m};
44
+ `;
45
+
46
+ const Password = styled.div`
47
+ ${(p) => p.theme.textStyle.uiS};
48
+ color: ${(p) => p.theme.color.interactiveInverse};
49
+ width: 100%;
50
+ text-align: right;
51
+
52
+ span {
53
+ cursor: pointer;
54
+ }
55
+ `;
56
+
57
+ const Actions = styled.div`
58
+ display: flex;
59
+ `;
60
+
61
+ const Secuoyas = styled.div`
62
+ ${(p) => p.theme.textStyle.uiXS};
63
+ position: absolute;
64
+ display: flex;
65
+ color: ${(p) => p.theme.color.textMediumEmphasisInverse};
66
+ justify-content: center;
67
+ align-items: center;
68
+ bottom: ${(p) => p.theme.spacing.m};
69
+ padding-bottom: ${(p) => p.theme.spacing.xs};
70
+ width: 100%;
71
+ img {
72
+ width: 20px;
73
+ height: 20px;
74
+ margin-left: ${(p) => p.theme.spacing.xs};
75
+ }
76
+ `;
77
+
78
+ export { Wrapper, Main, Header, Subtitle, Title, Form, Password, Actions, Secuoyas };
@@ -0,0 +1,67 @@
1
+ import React from "react";
2
+ import { Icon, ErrorToast } from "@ax/components";
3
+
4
+ import * as S from "./style";
5
+
6
+ const LoginSSO = (props: ILoginSSOProps): JSX.Element => {
7
+ const { handleSSO, errorSSO } = props;
8
+
9
+ const handleClick = () => handleSSO();
10
+
11
+ return (
12
+ <S.Wrapper>
13
+ <S.Main>
14
+ <S.Logo>
15
+ <img src="/img/logos/logoGriddoExtended@3x.svg" alt="Griddo Logo" />
16
+ </S.Logo>
17
+ <S.Welcome>
18
+ Wel
19
+ <br />
20
+ come
21
+ </S.Welcome>
22
+ <S.StartWrapper>
23
+ <ErrorToast />
24
+ {errorSSO ? (
25
+ <>
26
+ <S.StyledNotification text={errorSSO} type="error" closeButton={false} />
27
+ <S.StyledButton type="button" iconFill={false} onClick={handleClick}>
28
+ <S.ContentButton>
29
+ <S.TextButton>Try again</S.TextButton>
30
+ </S.ContentButton>
31
+ </S.StyledButton>
32
+ </>
33
+ ) : (
34
+ <>
35
+ <S.Text>
36
+ To start using Griddo, login with your
37
+ <br />
38
+ autentication platform
39
+ </S.Text>
40
+ <S.StyledButton type="button" iconFill={false} onClick={handleClick}>
41
+ <S.ContentButton>
42
+ <S.IconButton>
43
+ <Icon name="microsoft" size="16" viewBox={16} />
44
+ </S.IconButton>
45
+ <S.TextButton>Microsoft ID</S.TextButton>
46
+ </S.ContentButton>
47
+ </S.StyledButton>
48
+ </>
49
+ )}
50
+ </S.StartWrapper>
51
+ </S.Main>
52
+ <S.Secuoyas>
53
+ Made with care at{" "}
54
+ <a href="https://www.secuoyas.com/" target="_blank" rel="noopener noreferrer">
55
+ <img src="/img/logos/logoSQY.svg" alt="Secuoyas logo" />
56
+ </a>
57
+ </S.Secuoyas>
58
+ </S.Wrapper>
59
+ );
60
+ };
61
+
62
+ interface ILoginSSOProps {
63
+ handleSSO: () => void;
64
+ errorSSO: string | null;
65
+ }
66
+
67
+ export default LoginSSO;
@@ -0,0 +1,99 @@
1
+ import React from "react";
2
+ import styled from "styled-components";
3
+ import { Button, Notification } from "@ax/components";
4
+
5
+ const Wrapper = styled.div`
6
+ position: relative;
7
+ height: 100%;
8
+ display: flex;
9
+ align-items: strech;
10
+ `;
11
+
12
+ const Main = styled.div`
13
+ display: flex;
14
+ flex-direction: column;
15
+ `;
16
+
17
+ const Logo = styled.div`
18
+ margin-top: 15%;
19
+ margin-bottom: ${(p) => p.theme.spacing.m};
20
+ img {
21
+ height: 40px;
22
+ width: auto;
23
+ }
24
+ `;
25
+
26
+ const Welcome = styled.div`
27
+ color: #ffffff;
28
+ font-size: 181px;
29
+ line-height: 149.32px;
30
+ word-break: break-word;
31
+ margin-bottom: ${(p) => p.theme.spacing.l};
32
+ `;
33
+
34
+ const StartWrapper = styled.div`
35
+ max-width: 416px;
36
+ `;
37
+
38
+ const Text = styled.div`
39
+ ${(p) => p.theme.textStyle.uiM};
40
+ color: ${(p) => p.theme.color.textMediumEmphasisInverse};
41
+ margin-bottom: ${(p) => p.theme.spacing.m};
42
+ `;
43
+
44
+ const StyledButton = styled((props) => <Button {...props} />)`
45
+ display: flex;
46
+ justify-content: center;
47
+ align-items: center;
48
+ height: 40px;
49
+ width: 288px;
50
+ `;
51
+
52
+ const ContentButton = styled.div`
53
+ display: flex;
54
+ align-items: center;
55
+ `;
56
+
57
+ const IconButton = styled.div`
58
+ width: ${(p) => p.theme.spacing.s};
59
+ height: ${(p) => p.theme.spacing.s};
60
+ margin-right: ${(p) => p.theme.spacing.xs};
61
+ `;
62
+
63
+ const TextButton = styled.div``;
64
+
65
+ const Secuoyas = styled.div`
66
+ ${(p) => p.theme.textStyle.uiXS};
67
+ position: absolute;
68
+ display: flex;
69
+ color: ${(p) => p.theme.color.textMediumEmphasisInverse};
70
+ justify-content: center;
71
+ align-items: center;
72
+ left: 0;
73
+ bottom: ${(p) => p.theme.spacing.m};
74
+ padding-bottom: ${(p) => p.theme.spacing.xs};
75
+ img {
76
+ width: 20px;
77
+ height: 20px;
78
+ margin-left: ${(p) => p.theme.spacing.xs};
79
+ }
80
+ `;
81
+
82
+ const StyledNotification = styled((props) => <Notification {...props} />)`
83
+ margin-bottom: ${(p) => p.theme.spacing.s};
84
+ `;
85
+
86
+ export {
87
+ Wrapper,
88
+ Main,
89
+ Logo,
90
+ Welcome,
91
+ StartWrapper,
92
+ Text,
93
+ StyledButton,
94
+ ContentButton,
95
+ IconButton,
96
+ TextButton,
97
+ Secuoyas,
98
+ StyledNotification,
99
+ };
@@ -14,10 +14,10 @@ const LoginSlider = (props: ILoginSliderProps): JSX.Element => {
14
14
  const nextSlideElement = customSlider.current.innerSlider.list.querySelector(`[data-index="${next}"]`);
15
15
 
16
16
  setTimeout(() => {
17
- prevSlideElement.classList.add('prev-slide-anim');
18
- nextSlideElement.classList.remove('prev-slide-anim');
19
- },100);
20
- }
17
+ prevSlideElement.classList.add("prev-slide-anim");
18
+ nextSlideElement.classList.remove("prev-slide-anim");
19
+ }, 100);
20
+ };
21
21
 
22
22
  const settings = {
23
23
  dots: false,
@@ -1,9 +1,9 @@
1
1
  import React, { useEffect, useRef, useState } from "react";
2
2
 
3
3
  import { IGlobalSettings } from "@ax/containers/App/reducer";
4
- import { useModal } from "@ax/hooks";
5
- import { Button, FieldsBehavior, ErrorToast, Circle } from "@ax/components";
6
- import RecoveryModal from "./RecoveryModal";
4
+ import { Circle } from "@ax/components";
5
+ import LoginForm from "./LoginForm";
6
+ import LoginSSO from "./LoginSSO";
7
7
 
8
8
  import * as S from "./style";
9
9
 
@@ -26,11 +26,11 @@ const Login = (props: ILoginProps): JSX.Element => {
26
26
  handleRememberMe,
27
27
  isLoginSuccess,
28
28
  handleLoginSuccess,
29
+ handleSSO,
30
+ errorSSO,
29
31
  } = props;
30
32
 
31
- const [viewPass, setViewPass] = useState(false);
32
33
  const [wrapperWidth, setWrapperWidth] = useState<number>(0);
33
- const { isOpen, toggleModal } = useModal();
34
34
  const wrapperRef = useRef<HTMLDivElement>(null);
35
35
 
36
36
  useEffect(() => {
@@ -41,99 +41,29 @@ const Login = (props: ILoginProps): JSX.Element => {
41
41
  if (wrapperRef.current) {
42
42
  setWrapperWidth(wrapperRef.current.clientWidth);
43
43
  }
44
- }, [wrapperRef.current]);
45
-
46
- window.handleErrorClick = toggleModal;
47
-
48
- const _handleEmail = (e: string) => handleEmail(e);
49
-
50
- const _handlePwd = (e: string) => handlePassword(e);
51
-
52
- const _togglePassword = () => setViewPass(!viewPass);
44
+ }, [wrapperRef]);
53
45
 
54
46
  const _handleAnimationEnd = () => handleLoginSuccess();
55
47
 
56
- const btnText = isLoggingIn ? "Sending" : "Login";
57
-
58
- const inputType = viewPass ? "text" : "password";
59
- const icon = viewPass ? "hide" : "view";
60
-
61
- const textName = settings.welcomeText2 ? settings.welcomeText2 : "Griddo";
62
- const nameLogo =
63
- textName === "Griddo" ? <img src="/img/logos/logoGriddoExtended@3x.svg" alt="Griddo Logo" /> : textName;
64
-
65
48
  return (
66
49
  <S.Wrapper data-testid="login-wrapper">
67
50
  <S.LeftWrapper className={isLoginSuccess ? "animate" : ""} onAnimationEnd={_handleAnimationEnd} ref={wrapperRef}>
68
51
  <S.LoginWrapper className={isLoginSuccess ? "animate" : ""} width={wrapperWidth}>
69
- <S.Main>
70
- <S.Header>
71
- <S.Title>
72
- <div>Welcome to</div>
73
- <div>{nameLogo}</div>
74
- </S.Title>
75
- <S.Subtitle>To start using {textName}, introduce your email and password.</S.Subtitle>
76
- </S.Header>
77
- <ErrorToast />
78
- <S.Form onSubmit={handleSubmit}>
79
- <FieldsBehavior
80
- fieldType="TextField"
81
- title="Email"
82
- autoComplete="email"
83
- value={email}
84
- onChange={_handleEmail}
85
- name="email"
86
- inversed={true}
87
- placeholder="Type your email"
88
- />
89
- <FieldsBehavior
90
- fieldType="TextField"
91
- title="Password"
92
- inputType={inputType}
93
- value={password}
94
- onChange={_handlePwd}
95
- autoComplete="current-password"
96
- icon={icon}
97
- onClickIcon={_togglePassword}
98
- iconPosition="in"
99
- name="password"
100
- inversed={true}
101
- />
102
- <S.Actions>
103
- <FieldsBehavior
104
- fieldType="UniqueCheck"
105
- title=""
106
- value={rememberMe}
107
- onChange={handleRememberMe}
108
- options={[{ title: "Remember me" }]}
109
- name="rememberMe"
110
- inversed={true}
111
- />
112
- <S.Password>
113
- <span
114
- onClick={toggleModal}
115
- onKeyDown={toggleModal}
116
- role="checkbox"
117
- aria-checked="false"
118
- tabIndex={0}
119
- data-testid="forgot-button"
120
- >
121
- Lost your password?
122
- </span>
123
- </S.Password>
124
- </S.Actions>
125
- <Button type="submit" disabled={isLoggingIn ? true : false}>
126
- {btnText}
127
- </Button>
128
- <RecoveryModal isOpen={isOpen} toggleModal={toggleModal} />
129
- </S.Form>
130
- </S.Main>
131
- <S.Secuoyas width={wrapperWidth}>
132
- Made with care at{" "}
133
- <a href="https://www.secuoyas.com/" target="_blank">
134
- <img src="/img/logos/logoSQY.svg" alt="Secuoyas logo" />
135
- </a>
136
- </S.Secuoyas>
52
+ {settings.SSOActivated ? (
53
+ <LoginSSO handleSSO={handleSSO} errorSSO={errorSSO} />
54
+ ) : (
55
+ <LoginForm
56
+ handleSubmit={handleSubmit}
57
+ email={email}
58
+ handleEmail={handleEmail}
59
+ password={password}
60
+ handlePassword={handlePassword}
61
+ isLoggingIn={isLoggingIn}
62
+ settings={settings}
63
+ rememberMe={rememberMe}
64
+ handleRememberMe={handleRememberMe}
65
+ />
66
+ )}
137
67
  </S.LoginWrapper>
138
68
  </S.LeftWrapper>
139
69
  <S.RightWrapper>
@@ -157,6 +87,8 @@ export interface ILoginProps {
157
87
  handleRememberMe: () => void;
158
88
  isLoginSuccess: boolean;
159
89
  handleLoginSuccess: () => void;
90
+ handleSSO: () => void;
91
+ errorSSO: string | null;
160
92
  }
161
93
 
162
94
  export default Login;
@@ -71,56 +71,6 @@ const AnimatedLoginSlider = styled(LoginSlider)`
71
71
  }
72
72
  `;
73
73
 
74
- const Main = styled.div`
75
- width: 368px;
76
- `;
77
-
78
- const Header = styled.header``;
79
-
80
- const Subtitle = styled.p`
81
- ${(p) => p.theme.textStyle.uiM};
82
- color: ${(p) => p.theme.color.textMediumEmphasisInverse};
83
- font-weight: normal;
84
- margin-bottom: ${(p) => p.theme.spacing.xs};
85
- width: 280px;
86
- `;
87
-
88
- const Title = styled.div`
89
- ${(p) => p.theme.textStyle.headingM};
90
- color: ${(p) => p.theme.color.textHighEmphasisInverse};
91
- display: flex;
92
- margin-bottom: ${(p) => p.theme.spacing.xs};
93
- align-items: center;
94
- div:last-child {
95
- margin-left: ${(p) => p.theme.spacing.xxs};
96
- }
97
- img {
98
- height: 24px;
99
- margin-top: ${(p) => p.theme.spacing.xxs};
100
- }
101
- `;
102
-
103
- const Form = styled.form`
104
- display: flex;
105
- flex-direction: column;
106
- margin-top: ${(p) => p.theme.spacing.m};
107
- `;
108
-
109
- const Password = styled.div`
110
- ${(p) => p.theme.textStyle.uiS};
111
- color: ${(p) => p.theme.color.interactiveInverse};
112
- width: 100%;
113
- text-align: right;
114
-
115
- span {
116
- cursor: pointer;
117
- }
118
- `;
119
-
120
- const Actions = styled.div`
121
- display: flex;
122
- `;
123
-
124
74
  const Secuoyas = styled.div<{ width: number }>`
125
75
  ${(p) => p.theme.textStyle.uiXS};
126
76
  position: absolute;
@@ -139,18 +89,4 @@ const Secuoyas = styled.div<{ width: number }>`
139
89
  }
140
90
  `;
141
91
 
142
- export {
143
- Wrapper,
144
- Main,
145
- Header,
146
- Subtitle,
147
- Title,
148
- Form,
149
- Password,
150
- Actions,
151
- Secuoyas,
152
- LeftWrapper,
153
- RightWrapper,
154
- LoginWrapper,
155
- AnimatedLoginSlider,
156
- };
92
+ export { Wrapper, LeftWrapper, RightWrapper, LoginWrapper, AnimatedLoginSlider, Secuoyas };
@@ -5,7 +5,7 @@ import SubNotification from "./SubNotification";
5
5
  import * as S from "./style";
6
6
 
7
7
  const Notification = (props: INotificationProps): JSX.Element => {
8
- const { text, type, btnText, resetError, onClick, closeButton = true, actionsBelow, subErrors } = props;
8
+ const { text, type, btnText, resetError, onClick, closeButton = true, actionsBelow, subErrors, className } = props;
9
9
 
10
10
  const [isVisible, setIsVisible] = useState(true);
11
11
  const [isOpen, setIsOpen] = useState(false);
@@ -60,7 +60,7 @@ const Notification = (props: INotificationProps): JSX.Element => {
60
60
  const showText = isOpen ? "Hide errors" : "Show errors";
61
61
 
62
62
  return (
63
- <S.Wrapper isVisible={isVisible} className={type} data-testid="notification-wrapper">
63
+ <S.Wrapper isVisible={isVisible} className={`${type} ${className}`} data-testid="notification-wrapper">
64
64
  <S.NotificationWrapper>
65
65
  <S.Row>
66
66
  {getNotificationIcon(type)}
@@ -98,6 +98,7 @@ export interface INotificationProps {
98
98
  closeButton?: boolean;
99
99
  actionsBelow?: boolean;
100
100
  subErrors?: { id: number; error: string }[];
101
+ className?: string;
101
102
  }
102
103
 
103
104
  export default Notification;
@@ -147,10 +147,15 @@ function handleError(response: any, isMultiple = false, msg?: string): (dispatch
147
147
  };
148
148
  }
149
149
 
150
- function login(email: string, password: string, rememberMe: boolean): (dispatch: Dispatch) => Promise<boolean> {
150
+ function login(
151
+ email?: string,
152
+ password?: string,
153
+ rememberMe?: boolean,
154
+ petitionId?: string
155
+ ): (dispatch: Dispatch) => Promise<boolean> {
151
156
  return async (dispatch) => {
152
157
  dispatch(setIsLogging(true));
153
- const loginResponse: any = await global.login(email, password);
158
+ const loginResponse: any = await global.login({ username: email, password, petitionId });
154
159
  dispatch(setIsLogging(false));
155
160
  switch (loginResponse?.status) {
156
161
  case 200: {
@@ -194,6 +199,23 @@ function login(email: string, password: string, rememberMe: boolean): (dispatch:
194
199
  };
195
200
  }
196
201
 
202
+ function loginSSO(): (dispatch: Dispatch) => Promise<string | null> {
203
+ return async (dispatch) => {
204
+ try {
205
+ dispatch(setIsLogging(true));
206
+ const response: { status: number; data: any } = await global.login();
207
+
208
+ if (isReqOk(response?.status)) {
209
+ return response.data;
210
+ } else {
211
+ return null;
212
+ }
213
+ } catch (e) {
214
+ return null;
215
+ }
216
+ };
217
+ }
218
+
197
219
  function getGlobalSettings(): (dispatch: Dispatch) => Promise<void> {
198
220
  return async (dispatch) => {
199
221
  try {
@@ -244,4 +266,5 @@ export {
244
266
  setUser,
245
267
  checkUserSession,
246
268
  setHasAnimation,
269
+ loginSSO,
247
270
  };
@@ -52,6 +52,7 @@ export interface IGlobalSettings {
52
52
  skipReviewOnPublish?: boolean;
53
53
  autoSummary: boolean;
54
54
  autoTranslation: boolean;
55
+ SSOActivated: boolean;
55
56
  }
56
57
 
57
58
  export const initialState = {
@@ -82,6 +83,7 @@ export const initialState = {
82
83
  welcomeText2: "",
83
84
  autoSummary: false,
84
85
  autoTranslation: false,
86
+ SSOActivated: false,
85
87
  },
86
88
  sessionStartedAt: null,
87
89
  hasAnimation: false,
@@ -0,0 +1,7 @@
1
+ const errorText: Record<string, string> = {
2
+ noemail: "No email found in token",
3
+ noregistered:
4
+ "It looks like your email isn't registered in Griddo. Please contact your administrator to request access.",
5
+ };
6
+
7
+ export { errorText };
@@ -1,13 +1,19 @@
1
1
  import React, { useEffect, useState } from "react";
2
2
  import { connect } from "react-redux";
3
- import { IRootState } from "@ax/types";
3
+ import { useParams } from "react-router-dom";
4
4
 
5
+ import { IRootState } from "@ax/types";
5
6
  import { appActions } from "@ax/containers/App";
6
7
  import { Login } from "@ax/components";
7
8
  import { IGlobalSettings } from "@ax/containers/App/reducer";
9
+ import { errorText } from "./constants";
8
10
 
9
11
  const LoginModule = (props: IProps) => {
10
- const { isLoggingIn, globalSettings, login, resetError, getGlobalSettings, setHistoryPush } = props;
12
+ const { isLoggingIn, globalSettings, login, loginSSO, resetError, getGlobalSettings, setHistoryPush } = props;
13
+
14
+ const { petitionId } = useParams<{ petitionId?: string }>();
15
+
16
+ const errorSSO = petitionId && Object.keys(errorText).includes(petitionId) ? errorText[petitionId] : null;
11
17
 
12
18
  const initState = {
13
19
  email: "",
@@ -18,6 +24,20 @@ const LoginModule = (props: IProps) => {
18
24
  const [state, setState] = useState(initState);
19
25
  const [isSuccess, setIsSuccess] = useState(false);
20
26
 
27
+ useEffect(() => {
28
+ const handleLogin = async () => {
29
+ const isLogged = await login(undefined, undefined, undefined, petitionId);
30
+ if (isLogged) {
31
+ setIsSuccess(true);
32
+ }
33
+ };
34
+
35
+ if (petitionId && !errorSSO) {
36
+ handleLogin();
37
+ }
38
+ // eslint-disable-next-line react-hooks/exhaustive-deps
39
+ }, [petitionId, errorSSO]);
40
+
21
41
  useEffect(() => {
22
42
  const prefix = process.env.REACT_APP_SITE_TITLE ? process.env.REACT_APP_SITE_TITLE : "";
23
43
  document.title = `${prefix} ${globalSettings.welcomeText2}`;
@@ -30,11 +50,18 @@ const LoginModule = (props: IProps) => {
30
50
  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
31
51
  e.preventDefault();
32
52
  const isLogged = await login(state.email, state.password, state.rememberMe);
33
- if(isLogged){
53
+ if (isLogged) {
34
54
  setIsSuccess(true);
35
55
  }
36
56
  };
37
57
 
58
+ const handleClickSSO = async () => {
59
+ const urlSSO = await loginSSO();
60
+ if (urlSSO) {
61
+ window.open(urlSSO, "_self");
62
+ }
63
+ };
64
+
38
65
  const _handleEmail = (email: string) => {
39
66
  resetError();
40
67
  setState({ ...state, email });
@@ -48,7 +75,7 @@ const LoginModule = (props: IProps) => {
48
75
  const handleLoginSuccess = () => {
49
76
  const welcomePageURI = "/sites";
50
77
  setHistoryPush(welcomePageURI);
51
- }
78
+ };
52
79
 
53
80
  const _handleRememberMe = () => setState({ ...state, rememberMe: !state.rememberMe });
54
81
 
@@ -65,23 +92,26 @@ const LoginModule = (props: IProps) => {
65
92
  handleRememberMe={_handleRememberMe}
66
93
  isLoginSuccess={isSuccess}
67
94
  handleLoginSuccess={handleLoginSuccess}
95
+ handleSSO={handleClickSSO}
96
+ errorSSO={errorSSO}
68
97
  />
69
98
  );
70
99
  };
71
100
 
72
101
  interface IProps {
73
- token: string;
74
102
  isLoggingIn: boolean;
75
103
  children: any;
76
104
  globalSettings: IGlobalSettings;
77
- login(email: string, password: string, rememberMe: boolean): Promise<boolean>;
105
+ login(email?: string, password?: string, rememberMe?: boolean, petitionId?: string): Promise<boolean>;
106
+ loginSSO(): Promise<string | null>;
78
107
  resetError(): void;
79
108
  getGlobalSettings(): void;
80
- setHistoryPush(path: string): Promise<void>
109
+ setHistoryPush(path: string): Promise<void>;
81
110
  }
82
111
 
83
112
  const mapDispatchToProps = {
84
113
  login: appActions.login,
114
+ loginSSO: appActions.loginSSO,
85
115
  resetError: appActions.resetError,
86
116
  getGlobalSettings: appActions.getGlobalSettings,
87
117
  setHistoryPush: appActions.setHistoryPush,
@@ -4,6 +4,7 @@ import Login from "./../modules/Login";
4
4
  import PublicPreview from "./../modules/PublicPreview";
5
5
 
6
6
  const publicRoutes = [
7
+ { path: "/login/:petitionId", component: Login, name: "Login", hideNav: true },
7
8
  { path: "/login", component: Login, name: "Login", hideNav: true },
8
9
  { path: "/new-password/:id/:token", component: ResetPass, name: "Reset Password", hideNav: true },
9
10
  { path: "/set-password/:id/:token", component: CreatePass, name: "Create Password", hideNav: true },
@@ -1,11 +0,0 @@
1
- import styled from "styled-components";
2
-
3
- export const ModalContent = styled.div`
4
- padding: ${p => p.theme.spacing.m};
5
- p {
6
- margin-bottom: ${p => p.theme.spacing.m};
7
- }
8
- div {
9
- margin-bottom: 0;
10
- }
11
- `;