index.jsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. // @flow
  15. import React from 'react'
  16. import { observer } from 'mobx-react'
  17. import styled, { css } from 'styled-components'
  18. import Button from '../../atoms/Button'
  19. import LoginOptions from '../../molecules/LoginOptions'
  20. import LoadingButton from '../../molecules/LoadingButton'
  21. import LoginFormField from '../../molecules/LoginFormField'
  22. import StyleProps from '../../styleUtils/StyleProps'
  23. import errorIcon from './images/error.svg'
  24. import { loginButtons } from '../../../config'
  25. import notificationStore from '../../../stores/NotificationStore'
  26. const Form = styled.form`
  27. background: rgba(221, 224, 229, 0.5);
  28. padding: 16px 32px 32px 32px;
  29. border-radius: 8px;
  30. `
  31. const FormFields = styled.div`
  32. display: flex;
  33. margin-left: -16px;
  34. ${loginButtons.length < 3 ? css`flex-direction: column;` : ''}
  35. `
  36. const LoginSeparator = styled.div`
  37. margin: 8px 0 24px;
  38. opacity: 0.5;
  39. display: flex;
  40. justify-content: center;
  41. align-items: center;
  42. `
  43. const SeparatorLine = styled.div`
  44. width: 19px;
  45. border-top: 1px solid white;
  46. `
  47. const SeparatorText = styled.div`
  48. font-size: 12px;
  49. color: white;
  50. flex-grow: 1;
  51. text-align: center;
  52. `
  53. const LoginError = styled.div`
  54. display: flex;
  55. flex-direction: column;
  56. align-items: center;
  57. margin-top: 4px;
  58. margin-bottom: 16px;
  59. `
  60. const LoginErrorIcon = styled.div`
  61. width: 26px;
  62. height: 26px;
  63. background-image: url('${errorIcon}');
  64. `
  65. const LoginErrorText = styled.div`
  66. color: white;
  67. font-size: 12px;
  68. margin-top: 4px;
  69. width: ${StyleProps.inputSizes.regular.width}px;
  70. text-align: center;
  71. `
  72. type Props = {
  73. className: string,
  74. loading: boolean,
  75. loginFailedResponse: { status: string, message?: string },
  76. onFormSubmit: (credentials: { username: string, password: string }) => void,
  77. }
  78. type State = {
  79. username: string,
  80. password: string,
  81. }
  82. @observer
  83. class LoginForm extends React.Component<Props, State> {
  84. static defaultProps = {
  85. className: '',
  86. }
  87. constructor() {
  88. super()
  89. this.state = {
  90. username: '',
  91. password: '',
  92. }
  93. }
  94. handleUsernameChange(username: string) {
  95. this.setState({ username })
  96. }
  97. handlePasswordChange(password: string) {
  98. this.setState({ password })
  99. }
  100. handleFormSubmit(e: Event) {
  101. e.preventDefault()
  102. if (this.state.username.length === 0 || this.state.password.length === 0) {
  103. notificationStore.notify('Please fill in all fields')
  104. } else {
  105. this.props.onFormSubmit({ username: this.state.username, password: this.state.password })
  106. }
  107. }
  108. renderErrorMessage() {
  109. if (!this.props.loginFailedResponse) {
  110. return null
  111. }
  112. let errorMessage = 'Request failed, there might be a problem with the connection to the server.'
  113. if (this.props.loginFailedResponse.status) {
  114. switch (this.props.loginFailedResponse.status) {
  115. case 401:
  116. errorMessage = 'The username or password did not match. Please try again.'
  117. break
  118. default:
  119. errorMessage = this.props.loginFailedResponse.message || errorMessage
  120. }
  121. }
  122. return (
  123. <LoginError>
  124. <LoginErrorIcon />
  125. <LoginErrorText data-test-id="loginForm-errorText">
  126. {errorMessage}
  127. </LoginErrorText>
  128. </LoginError>
  129. )
  130. }
  131. render() {
  132. let loginSeparator = loginButtons.length ? (
  133. <LoginSeparator>
  134. <SeparatorLine />
  135. <SeparatorText>or sign in with username</SeparatorText>
  136. <SeparatorLine />
  137. </LoginSeparator>
  138. ) : null
  139. let buttonStyle = { width: '100%', marginTop: '16px' }
  140. let button = this.props.loading ?
  141. <LoadingButton style={buttonStyle}>Please wait ... </LoadingButton>
  142. : <Button style={buttonStyle}>Login</Button>
  143. return (
  144. <Form className={this.props.className} onSubmit={(e) => { this.handleFormSubmit(e) }}>
  145. {this.renderErrorMessage()}
  146. <LoginOptions />
  147. {loginSeparator}
  148. <FormFields>
  149. <LoginFormField
  150. label="Username"
  151. value={this.state.username}
  152. name="username"
  153. onChange={e => { this.handleUsernameChange(e.target.value) }}
  154. data-test-id="loginForm-usernameField"
  155. />
  156. <LoginFormField
  157. label="Password"
  158. value={this.state.password}
  159. onChange={e => { this.handlePasswordChange(e.target.value) }}
  160. name="password"
  161. type="password"
  162. data-test-id="loginForm-passwordField"
  163. />
  164. </FormFields>
  165. {button}
  166. </Form>
  167. )
  168. }
  169. }
  170. export default LoginForm