UserModal.tsx 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  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. import React from 'react'
  15. import { observer } from 'mobx-react'
  16. import styled from 'styled-components'
  17. import type { User } from '@src/@types/User'
  18. import type { Project } from '@src/@types/Project'
  19. import type { Field as FieldType } from '@src/@types/Field'
  20. import Button from '@src/components/ui/Button'
  21. import Modal from '@src/components/ui/Modal'
  22. import FieldInput from '@src/components/ui/FieldInput'
  23. import LabelDictionary from '@src/utils/LabelDictionary'
  24. import KeyboardManager from '@src/utils/KeyboardManager'
  25. import { ThemeProps } from '@src/components/Theme'
  26. import userImage from './images/user.svg'
  27. const Wrapper = styled.div<any>`
  28. padding: 48px 0 32px 0;
  29. display: flex;
  30. flex-direction: column;
  31. min-height: 0;
  32. `
  33. const Image = styled.div<any>`
  34. width: 96px;
  35. height: 96px;
  36. background: url('${userImage}') center no-repeat;
  37. margin: 0 auto;
  38. `
  39. const Form = styled.div<any>`
  40. display: flex;
  41. justify-content: space-between;
  42. flex-wrap: wrap;
  43. margin-top: 64px;
  44. overflow: auto;
  45. padding: 0 32px;
  46. > div {
  47. margin-top: 16px;
  48. }
  49. `
  50. const Buttons = styled.div<any>`
  51. margin-top: 32px;
  52. display: flex;
  53. justify-content: space-between;
  54. padding: 0 32px;
  55. `
  56. type Props = {
  57. user?: User,
  58. isLoggedUser?: boolean,
  59. loading: boolean,
  60. isNewUser?: boolean,
  61. projects: Project[],
  62. editPassword?: boolean,
  63. onRequestClose: () => void,
  64. onUpdateClick: (user: User) => void,
  65. }
  66. type State = {
  67. name: string,
  68. email: string,
  69. enabled?: boolean,
  70. projectId?: string,
  71. highlightFieldNames: string[],
  72. password: string,
  73. confirmPassword: string,
  74. description?: string,
  75. }
  76. const testName = 'userModal'
  77. @observer
  78. class UserModal extends React.Component<Props, State> {
  79. UNSAFE_componentWillMount() {
  80. this.setState({
  81. name: this.props.user ? this.props.user.name : '',
  82. email: this.props.user ? this.props.user.email : '',
  83. description: this.props.user ? this.props.user.description : '',
  84. projectId: this.props.user ? this.props.user.project_id : '',
  85. enabled: this.props.user ? this.props.user.enabled : true,
  86. highlightFieldNames: [],
  87. password: '',
  88. confirmPassword: '',
  89. })
  90. }
  91. componentDidMount() {
  92. KeyboardManager.onEnter('editUserModal', () => {
  93. this.handleUpdateClick()
  94. }, 2)
  95. }
  96. componentWillUnmount() {
  97. KeyboardManager.removeKeyDown('editUserModal')
  98. }
  99. handleUpdateClick() {
  100. if (
  101. (this.props.isNewUser && this.highlightAllFields())
  102. || (!this.props.isNewUser && !this.props.editPassword && this.highlightDetailsFields())
  103. || (!this.props.isNewUser && this.props.editPassword && this.highlightPasswordFields())
  104. ) {
  105. return
  106. }
  107. this.props.onUpdateClick({
  108. id: '',
  109. project: { id: '', name: '' },
  110. project_id: this.state.projectId,
  111. name: this.state.name,
  112. email: this.state.email,
  113. description: this.state.description,
  114. password: this.state.password,
  115. enabled: this.state.enabled,
  116. })
  117. }
  118. highlightAllFields() {
  119. const highlightFieldNames = []
  120. if (!this.state.name) {
  121. highlightFieldNames.push('username')
  122. }
  123. if (!this.state.password) {
  124. highlightFieldNames.push('new_password')
  125. }
  126. if (this.state.password && this.state.password !== this.state.confirmPassword) {
  127. highlightFieldNames.push('confirm_password')
  128. }
  129. if (highlightFieldNames.length > 0) {
  130. this.setState({ highlightFieldNames })
  131. return true
  132. }
  133. this.setState({ highlightFieldNames: [] })
  134. return false
  135. }
  136. highlightDetailsFields(): boolean {
  137. if (!this.state.name) {
  138. this.setState({ highlightFieldNames: ['username'] })
  139. return true
  140. }
  141. this.setState({ highlightFieldNames: [] })
  142. return false
  143. }
  144. highlightPasswordFields(): boolean {
  145. if (!this.state.password) {
  146. this.setState({ highlightFieldNames: ['new_password'] })
  147. return true
  148. }
  149. if (this.state.password !== this.state.confirmPassword) {
  150. this.setState({ highlightFieldNames: ['confirm_password'] })
  151. return true
  152. }
  153. this.setState({ highlightFieldNames: [] })
  154. return false
  155. }
  156. renderField(field: FieldType, value: any, onChange: (value: any) => void) {
  157. const disabled = this.props.loading || (this.props.isLoggedUser && field.name === 'enabled')
  158. return (
  159. <FieldInput
  160. layout="modal"
  161. data-test-id={`${testName}-field-${field.name}`}
  162. key={field.name}
  163. name={field.name}
  164. label={LabelDictionary.get(field.name)}
  165. type={field.type || 'string'}
  166. value={value}
  167. onChange={onChange}
  168. width={ThemeProps.inputSizes.large.width}
  169. disabled={disabled}
  170. enum={field.enum}
  171. password={field.name === 'new_password' || field.name === 'confirm_password'}
  172. required={field.required}
  173. highlight={Boolean(this.state.highlightFieldNames.find(n => n === field.name))}
  174. noSelectionMessage="Choose a project"
  175. />
  176. )
  177. }
  178. renderForm() {
  179. let fields
  180. const passwordFields = [
  181. this.renderField(
  182. { name: 'new_password', required: true },
  183. this.state.password,
  184. password => { this.setState({ password }) },
  185. ),
  186. this.renderField(
  187. { name: 'confirm_password', required: true },
  188. this.state.confirmPassword,
  189. confirmPassword => { this.setState({ confirmPassword }) },
  190. ),
  191. ]
  192. const detailsFields = [
  193. this.renderField(
  194. { name: 'username', required: true },
  195. this.state.name,
  196. name => { this.setState({ name }) },
  197. ),
  198. this.renderField(
  199. { name: 'description' },
  200. this.state.description,
  201. description => { this.setState({ description }) },
  202. ),
  203. this.renderField(
  204. { name: 'Email' },
  205. this.state.email,
  206. email => { this.setState({ email }) },
  207. ),
  208. this.renderField(
  209. {
  210. name: 'Primary Project',
  211. enum: [({ name: 'Choose a project', id: null } as any)].concat(this.props.projects.concat([])),
  212. },
  213. this.state.projectId,
  214. projectId => { this.setState({ projectId }) },
  215. ),
  216. ]
  217. const enabledField = this.renderField(
  218. { name: 'enabled', type: 'boolean' },
  219. this.state.enabled,
  220. enabled => { this.setState({ enabled }) },
  221. )
  222. if (this.props.isNewUser) {
  223. fields = detailsFields.concat(passwordFields).concat(enabledField)
  224. } else if (this.props.editPassword) {
  225. fields = passwordFields
  226. } else {
  227. fields = detailsFields.concat(enabledField)
  228. }
  229. return (
  230. <Form>
  231. {fields}
  232. </Form>
  233. )
  234. }
  235. render() {
  236. const label = this.props.isNewUser ? 'New User' : this.props.editPassword ? 'Change Password' : 'Update User'
  237. return (
  238. <Modal
  239. isOpen
  240. title={label}
  241. onRequestClose={this.props.onRequestClose}
  242. >
  243. <Wrapper>
  244. <Image />
  245. {this.renderForm()}
  246. <Buttons>
  247. <Button
  248. secondary
  249. large
  250. onClick={this.props.onRequestClose}
  251. >Cancel
  252. </Button>
  253. <Button
  254. data-test-id={`${testName}-updateButton`}
  255. large
  256. disabled={this.props.loading}
  257. onClick={() => { this.handleUpdateClick() }}
  258. >{label}
  259. </Button>
  260. </Buttons>
  261. </Wrapper>
  262. </Modal>
  263. )
  264. }
  265. }
  266. export default UserModal