/* Copyright (C) 2017 Cloudbase Solutions SRL This program is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with this program. If not, see . */ import React from 'react' import { observer } from 'mobx-react' import styled from 'styled-components' import type { Field as FieldType } from '../../../../@types/Field' import type { User } from '../../../../@types/User' import type { Project, Role } from '../../../../@types/Project' import Button from '../../../ui/Button/Button' import Modal from '../../../ui/Modal/Modal' import FieldInput from '../../../ui/FieldInput/FieldInput' import ToggleButtonBar from '../../../ui/ToggleButtonBar/ToggleButtonBar' import AutocompleteDropdown from '../../../ui/Dropdowns/AutocompleteDropdown/AutocompleteDropdown' import { ThemePalette, ThemeProps } from '../../../Theme' import KeyboardManager from '../../../../utils/KeyboardManager' import userImage from './images/user.svg' const Wrapper = styled.div` padding: 48px 0 32px 0; display: flex; flex-direction: column; min-height: 0; ` const Image = styled.div` ${ThemeProps.exactSize('96px')} background: url('${userImage}') center no-repeat; margin: 0 auto; ` const ToggleButtonBarStyled = styled(ToggleButtonBar)` margin-top: 48px; ` const Form = styled.div` display: flex; justify-content: space-between; flex-wrap: wrap; margin-top: 32px; overflow: auto; padding: 0 32px; > div { margin-top: 16px; } ` const FieldStyled = styled(FieldInput)` ${ThemeProps.exactWidth(`${ThemeProps.inputSizes.large.width}px`)} ` const FormField = styled.div`` const FormLabel = styled.div` font-size: 10px; font-weight: ${ThemeProps.fontWeights.medium}; color: ${ThemePalette.grayscale[3]}; text-transform: uppercase; margin-bottom: 2px; display: flex; align-items: center; ` const Buttons = styled.div` margin-top: 32px; display: flex; justify-content: space-between; padding: 0 32px; ` type Props = { loading: boolean, users: User[], projects: Project[], onRequestClose: () => void, onAddClick: (user: User, isNew: boolean, roles: Role[]) => void, roles: Role[], } type State = { isNew: boolean, selectedUser?: User | null, username: string, description: string, email: string, projectId: string, password: string, confirmPassword: string, enabled: boolean, highlightFieldNames: string[], selectedRolesExisting: string[], selectedRolesNew: string[], } const testName = 'pmModal' @observer class ProjectMemberModal extends React.Component { state: State = { isNew: false, selectedUser: null, username: '', description: '', email: '', projectId: '', password: '', confirmPassword: '', enabled: true, highlightFieldNames: [], selectedRolesExisting: [], selectedRolesNew: [], } componentDidMount() { KeyboardManager.onEnter('projectMemberModal', () => { this.handleAddClick() }) } componentWillUnmount() { KeyboardManager.removeKeyDown('projectMemberModal') } handleAddClick() { if (this.highlightFields()) { return } let user: User let roles = [] if (this.state.isNew) { user = { id: '', project: { id: '', name: '' }, project_id: this.state.projectId, email: this.state.email, name: this.state.username, description: this.state.description, password: this.state.password, enabled: this.state.enabled, } roles = this.state.selectedRolesNew } else if (this.state.selectedUser) { user = this.state.selectedUser roles = this.state.selectedRolesExisting } else { return } roles = roles.map(id => this.props.roles.find(r => r.id === id) || { id: 'undefined', name: '' }) this.props.onAddClick(user, this.state.isNew, roles) } highlightFields(): boolean { const highlightFieldNames = [] if (!this.state.isNew) { if (!this.state.selectedUser) { highlightFieldNames.push('selectedUser') } if (this.state.selectedRolesExisting.length === 0) { highlightFieldNames.push('rolesExisting') } if (highlightFieldNames.length > 0) { this.setState({ highlightFieldNames }) return true } this.setState({ highlightFieldNames: [] }) return false } if (!this.state.username) { highlightFieldNames.push('username') } if (!this.state.password) { highlightFieldNames.push('password') } if (this.state.password && this.state.password !== this.state.confirmPassword) { highlightFieldNames.push('confirm_password') } if (this.state.selectedRolesNew.length === 0) { highlightFieldNames.push('rolesNew') } if (highlightFieldNames.length > 0) { this.setState({ highlightFieldNames }) return true } this.setState({ highlightFieldNames: [] }) return false } renderToggleButton() { const items = [{ value: 'existing', label: 'Existing', }, { value: 'new', label: 'New', }] return ( { this.setState({ isNew: item.value === 'new' }) }} /> ) } renderRolesField() { const selectedRoles = this.state.isNew ? this.state.selectedRolesNew : this.state.selectedRolesExisting const setSelectedRoles = (roles: string[]) => { if (this.state.isNew) { this.setState({ selectedRolesNew: roles }) } else { this.setState({ selectedRolesExisting: roles }) } } const highlighFieldName = this.state.isNew ? 'rolesNew' : 'rolesExisting' return ( { if (selectedRoles.find(id => id === roleId)) { setSelectedRoles(selectedRoles.filter(r => r !== roleId)) } else { setSelectedRoles([...selectedRoles, roleId]) } }} value={selectedRoles} width={ThemeProps.inputSizes.large.width} layout="modal" disabled={this.props.loading} enum={this.props.roles.filter(r => r.name !== 'key-manager:service-admin').map(r => ({ name: r.name, id: r.id }))} required highlight={Boolean(this.state.highlightFieldNames.find(n => n === highlighFieldName))} noSelectionMessage="Choose role(s)" noItemsMessage="No available roles" /> ) } renderField(field: FieldType, value: any, onChange: (value: any) => void) { return ( n === field.name))} noSelectionMessage="Choose a project" noItemsMessage="No available members" /> ) } renderNewForm() { const userProjects = this.props.projects.map(p => ({ name: p.name, id: p.id })) const chooseProject: { name: string, id: string | null } = { name: 'Choose a project', id: null } const fields = [ this.renderField( { name: 'username', label: 'Username', required: true }, this.state.username, username => { this.setState({ username }) }, ), this.renderField( { name: 'description', label: 'Description' }, this.state.description, description => { this.setState({ description }) }, ), this.renderField( { name: 'Primary Project', label: 'Primary Project', enum: [chooseProject].concat(userProjects), }, this.state.projectId, projectId => { this.setState({ projectId }) }, ), this.renderRolesField(), this.renderField( { name: 'password', label: 'Password', required: true }, this.state.password, password => { this.setState({ password }) }, ), this.renderField( { name: 'confirm_password', label: 'Confirm Password', required: true }, this.state.confirmPassword, confirmPassword => { this.setState({ confirmPassword }) }, ), this.renderField( { name: 'Email', label: 'Email' }, this.state.email, email => { this.setState({ email }) }, ), this.renderField( { name: 'Enabled', label: 'Enabled', type: 'boolean' }, this.state.enabled, enabled => { this.setState({ enabled }) }, ), ] return (
{fields}
) } renderExistingForm() { const users = this.props.users.map(u => ({ label: u.name, value: u.id })) return (
Username n === 'selectedUser'))} onChange={item => { this.setState({ selectedUser: this.props.users.find(u => u.id === item.value) }) }} required /> {this.renderRolesField()}
) } renderForm() { if (this.state.isNew) { return this.renderNewForm() } return this.renderExistingForm() } render() { return ( {this.renderToggleButton()} {this.renderForm()} ) } } export default ProjectMemberModal