/*
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 .
*/
// @flow
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 '../../atoms/Button'
import Modal from '../../molecules/Modal'
import Field, { Asterisk } from '../../molecules/EndpointField'
import ToggleButtonBar from '../../atoms/ToggleButtonBar'
import AutocompleteDropdown from '../../molecules/AutocompleteDropdown'
import StyleProps from '../../styleUtils/StyleProps'
import Palette from '../../styleUtils/Palette'
import userImage from './images/user.svg'
import KeyboardManager from '../../../utils/KeyboardManager'
const Wrapper = styled.div`
padding: 48px 32px 32px 32px;
display: flex;
flex-direction: column;
`
const Image = styled.div`
width: 96px;
height: 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;
> div {
margin-top: 16px;
}
`
const FieldStyled = styled(Field)`
${StyleProps.exactWidth('224px')}
`
const FormField = styled.div``
const FormLabel = styled.div`
font-size: 10px;
font-weight: ${StyleProps.fontWeights.medium};
color: ${Palette.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;
`
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,
username: string,
description: string,
email: string,
projectId: string,
password: string,
confirmPassword: string,
enabled: boolean,
highlightFieldNames: string[],
selectedRolesExisting: string[],
selectedRolesNew: string[],
}
@observer
class ProjectMemberModal extends React.Component {
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() {
let selectedRoles = this.state.isNew ? this.state.selectedRolesNew : this.state.selectedRolesExisting
let setSelectedRoles = (roles: string[]) => {
if (this.state.isNew) {
this.setState({ selectedRolesNew: roles })
} else {
this.setState({ selectedRolesExisting: roles })
}
}
let highlighFieldName = this.state.isNew ? 'rolesNew' : 'rolesExisting'
return (
{
if (selectedRoles.find(id => id === roleId)) {
setSelectedRoles(selectedRoles.filter(r => r !== roleId))
} else {
setSelectedRoles([...selectedRoles, roleId])
}
}}
selectedItems={selectedRoles}
value={null}
large
disabled={this.props.loading}
items={this.props.roles.filter(r => r.name !== 'key-manager:service-admin').map(r => { return { label: r.name, value: 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 => { return { label: p.name, value: p.id } })
const fields = [
this.renderField(
{ name: 'username', required: true },
this.state.username,
username => { this.setState({ username }) }
),
this.renderField(
{ name: 'description' },
this.state.description,
description => { this.setState({ description }) }
),
this.renderField(
{
name: 'Primary Project',
// $FlowIssue
enum: [{ label: 'Choose a project', value: null }].concat(userProjects),
},
this.state.projectId,
projectId => { this.setState({ projectId }) },
),
this.renderRolesField(),
this.renderField(
{ name: 'password', required: true },
this.state.password,
password => { this.setState({ password }) }
),
this.renderField(
{ name: 'confirm_password', required: true },
this.state.confirmPassword,
confirmPassword => { this.setState({ confirmPassword }) }
),
this.renderField(
{ name: 'Email' },
this.state.email,
email => { this.setState({ email }) }
),
this.renderField(
{ name: 'Enabled', type: 'boolean' },
this.state.enabled,
enabled => { this.setState({ enabled }) }
),
]
return (
)
}
renderExistingForm() {
const users = this.props.users.map(u => { return { label: u.name, value: u.id } })
return (
)
}
renderForm() {
if (this.state.isNew) {
return this.renderNewForm()
}
return this.renderExistingForm()
}
render() {
return (
{this.renderToggleButton()}
{this.renderForm()}
)
}
}
export default ProjectMemberModal