Skip to content
Snippets Groups Projects
Commit c583b204 authored by piotr.kupczyk@id.ethz.ch's avatar piotr.kupczyk@id.ethz.ch
Browse files

SSDM-7569 NEW openBIS UI - General Template/Infrastructure for Forms -...

SSDM-7569 NEW openBIS UI - General Template/Infrastructure for Forms - Visualisation/Creation/Edit - object type form with property type choice and field preview
parent 512c1912
No related branches found
No related tags found
No related merge requests found
...@@ -39,41 +39,58 @@ class ObjectType extends React.Component { ...@@ -39,41 +39,58 @@ class ObjectType extends React.Component {
} }
componentDidMount(){ componentDidMount(){
let id = new dto.EntityTypePermId(this.props.objectId) Promise.all([
this.loadObjectType(this.props.objectId),
this.loadPropertyTypes()
]).then(([objectType, propertyTypes]) => {
this.setState(() => ({
loaded: true,
objectType: objectType,
propertyTypes: propertyTypes
}))
})
}
loadObjectType(objectTypeId){
let id = new dto.EntityTypePermId(objectTypeId)
let fo = new dto.SampleTypeFetchOptions() let fo = new dto.SampleTypeFetchOptions()
fo.withPropertyAssignments().withPropertyType() fo.withPropertyAssignments().withPropertyType()
fo.withPropertyAssignments().sortBy().code() fo.withPropertyAssignments().sortBy().code()
facade.getSampleTypes([id], fo).then(map => { return facade.getSampleTypes([id], fo).then(map => {
let objectType = map[this.props.objectId] let objectType = map[objectTypeId]
if(objectType){ if(objectType){
this.setState(() => { return {
return { code: objectType.code,
loaded: true, properties: objectType.propertyAssignments.map(assignment => ({
object: { permId: assignment.permId,
code: objectType.code, propertyType: assignment.propertyType,
properties: objectType.propertyAssignments.map(assignment => ({ ordinal: assignment.ordinal,
permId: assignment.permId, mandatory: assignment.mandatory,
code: assignment.propertyType.code, selected: false,
label: assignment.propertyType.label, errors: {}
description: assignment.propertyType.description, }))
dataType: assignment.propertyType.dataType, }
ordinal: assignment.ordinal, }else{
mandatory: assignment.mandatory, return null
selected: false,
errors: {}
}))
}
}
})
} }
}) })
} }
handleChange(index, key, value){ loadPropertyTypes(){
let criteria = new dto.PropertyTypeSearchCriteria()
let fo = new dto.PropertyTypeFetchOptions()
fo.withVocabulary().withTerms()
return facade.searchPropertyTypes(criteria, fo).then(result => {
return result.objects
})
}
handleChange(ordinal, key, value){
this.setState((prevState) => { this.setState((prevState) => {
let newProperties = prevState.object.properties.map((property, i) => { let newProperties = prevState.objectType.properties.map((property) => {
if(i === index){ if(property.ordinal === ordinal){
return { return {
...property, ...property,
[key]: value [key]: value
...@@ -84,8 +101,8 @@ class ObjectType extends React.Component { ...@@ -84,8 +101,8 @@ class ObjectType extends React.Component {
}) })
return { return {
...prevState, ...prevState,
object: { objectType: {
...prevState.object, ...prevState.objectType,
properties: newProperties properties: newProperties
} }
} }
...@@ -98,7 +115,7 @@ class ObjectType extends React.Component { ...@@ -98,7 +115,7 @@ class ObjectType extends React.Component {
handleRemove(){ handleRemove(){
this.setState((prevState) => { this.setState((prevState) => {
let newProperties = prevState.object.properties.reduce((array, property) => { let newProperties = prevState.objectType.properties.reduce((array, property) => {
if(!property.selected){ if(!property.selected){
array.push(property) array.push(property)
} }
...@@ -107,8 +124,8 @@ class ObjectType extends React.Component { ...@@ -107,8 +124,8 @@ class ObjectType extends React.Component {
return { return {
...prevState, ...prevState,
object: { objectType: {
...prevState.object, ...prevState.objectType,
properties: newProperties properties: newProperties
} }
} }
...@@ -117,39 +134,44 @@ class ObjectType extends React.Component { ...@@ -117,39 +134,44 @@ class ObjectType extends React.Component {
handleAdd(){ handleAdd(){
this.setState((prevState) => { this.setState((prevState) => {
let newProperties = prevState.object.properties.map(property => ({ let newOrdinal = 0
...property, let newProperties = prevState.objectType.properties.map(property => {
selected: false if(newOrdinal <= property.ordinal){
})) newOrdinal = property.ordinal + 1
}
return {
...property,
selected: false
}
})
newProperties.push({ newProperties.push({
code: 'PROPERTY_' + prevState.object.properties.length, permId: null,
label: '', propertyType: null,
description: '',
dataType: '',
mandatory: false, mandatory: false,
selected: true, selected: true,
ordinal: newOrdinal,
errors: {} errors: {}
}) })
return { return {
...prevState, ...prevState,
object: { objectType: {
...prevState.object, ...prevState.objectType,
properties: newProperties properties: newProperties
} }
} }
}) })
} }
handleSelect(propertyCode){ handleSelect(ordinal){
this.setState((prevState) => ({ this.setState((prevState) => ({
...prevState, ...prevState,
object: { objectType: {
...prevState.object, ...prevState.objectType,
properties: prevState.object.properties.map(property => { properties: prevState.objectType.properties.map(property => {
return { return {
...property, ...property,
selected: property.code === propertyCode ? !property.selected : false selected: property.ordinal === ordinal ? !property.selected : false
} }
}) })
} }
...@@ -157,7 +179,7 @@ class ObjectType extends React.Component { ...@@ -157,7 +179,7 @@ class ObjectType extends React.Component {
} }
handleReorder(oldPropertyIndex, newPropertyIndex){ handleReorder(oldPropertyIndex, newPropertyIndex){
let oldProperties = this.state.object.properties let oldProperties = this.state.objectType.properties
let newProperties = oldProperties.map(property => { let newProperties = oldProperties.map(property => {
if(property.selected){ if(property.selected){
return { return {
...@@ -177,8 +199,8 @@ class ObjectType extends React.Component { ...@@ -177,8 +199,8 @@ class ObjectType extends React.Component {
this.setState((prevState) => ({ this.setState((prevState) => ({
...prevState, ...prevState,
object: { objectType: {
...prevState.object, ...prevState.objectType,
properties: newProperties properties: newProperties
} }
})) }))
...@@ -187,20 +209,11 @@ class ObjectType extends React.Component { ...@@ -187,20 +209,11 @@ class ObjectType extends React.Component {
validate(){ validate(){
let valid = true let valid = true
let newProperties = this.state.object.properties.map(property => { let newProperties = this.state.objectType.properties.map(property => {
let errors = {} let errors = {}
if(!_.trim(property.code)){ if(!_.trim(property.type)){
errors['code'] = 'Cannot be empty' errors['propertyType'] = 'Cannot be empty'
}
if(!_.trim(property.label)){
errors['label'] = 'Cannot be empty'
}
if(!_.trim(property.description)){
errors['description'] = 'Cannot be empty'
}
if(!_.trim(property.dataType)){
errors['dataType'] = 'Cannot be empty'
} }
if(_.size(errors) > 0){ if(_.size(errors) > 0){
...@@ -216,8 +229,8 @@ class ObjectType extends React.Component { ...@@ -216,8 +229,8 @@ class ObjectType extends React.Component {
this.setState((prevState) => ({ this.setState((prevState) => ({
...prevState, ...prevState,
validated: true, validated: true,
object: { objectType: {
...prevState.object, ...prevState.objectType,
properties: newProperties properties: newProperties
} }
})) }))
...@@ -246,7 +259,8 @@ class ObjectType extends React.Component { ...@@ -246,7 +259,8 @@ class ObjectType extends React.Component {
<div className={classes.container}> <div className={classes.container}>
<div className={classes.form}> <div className={classes.form}>
<ObjectTypeForm <ObjectTypeForm
object={this.state.object} objectType={this.state.objectType}
propertyTypes={this.state.propertyTypes}
onSelect={this.handleSelect} onSelect={this.handleSelect}
onReorder={this.handleReorder} onReorder={this.handleReorder}
onChange={this.handleChange} onChange={this.handleChange}
......
...@@ -13,7 +13,8 @@ class ObjectTypeForm extends React.Component { ...@@ -13,7 +13,8 @@ class ObjectTypeForm extends React.Component {
render() { render() {
logger.log(logger.DEBUG, 'ObjectTypeForm.render') logger.log(logger.DEBUG, 'ObjectTypeForm.render')
let { code, properties } = this.props.object let { objectType, propertyTypes } = this.props
let { code, properties } = objectType
return ( return (
<div> <div>
...@@ -23,7 +24,8 @@ class ObjectTypeForm extends React.Component { ...@@ -23,7 +24,8 @@ class ObjectTypeForm extends React.Component {
<form> <form>
{properties && properties.length > 0 && {properties && properties.length > 0 &&
<ObjectTypeTable <ObjectTypeTable
properties={properties} objectType={objectType}
propertyTypes={propertyTypes}
onSelect={this.props.onSelect} onSelect={this.props.onSelect}
onReorder={this.props.onReorder} onReorder={this.props.onReorder}
onChange={this.props.onChange} onChange={this.props.onChange}
......
import _ from 'lodash'
import React from 'react'
import FormControl from '@material-ui/core/FormControl'
import FormControlLabel from '@material-ui/core/FormControlLabel'
import FormHelperText from '@material-ui/core/FormHelperText'
import TextField from '@material-ui/core/TextField'
import Select from '@material-ui/core/Select'
import MenuItem from '@material-ui/core/MenuItem'
import Checkbox from '@material-ui/core/Checkbox'
import {withStyles} from '@material-ui/core/styles'
import logger from '../../../common/logger.js'
const styles = () => ({
})
class ObjectTypePropertyPreview extends React.Component {
constructor(props){
super(props)
this.state = {
values: {
vocabulary: '',
}
}
}
handleChange(name){
return (event) => {
this.setState((prevState) => ({
...prevState,
values: {
...prevState.values,
[name]: _.has(event.target, 'checked') ? event.target.checked : event.target.value
}
}))
}
}
render(){
logger.log(logger.DEBUG, 'ObjectTypePropertyPreview.render')
const {propertyType} = this.props
switch(propertyType.dataType){
case 'BOOLEAN':
return this.renderBoolean()
case 'VARCHAR':
return this.renderVarchar()
case 'MULTILINE_VARCHAR':
return this.renderMultilineVarchar()
case 'INTEGER':
case 'REAL':
return this.renderNumber()
case 'CONTROLLEDVOCABULARY':
return this.renderVocabulary()
default:
return this.renderUnsupported()
}
}
renderBoolean(){
return (
<FormControl>
<FormControlLabel
control={<Checkbox />}
label={this.getLabel()}
/>
<FormHelperText>{this.getDescription()}</FormHelperText>
</FormControl>
)
}
renderVarchar(){
return (
<TextField
label={this.getLabel()}
helperText={this.getDescription()}
variant="filled"
/>
)
}
renderMultilineVarchar(){
return (
<TextField
label={this.getLabel()}
helperText={this.getDescription()}
multiline={true}
variant="filled"
/>
)
}
renderNumber(){
return (
<TextField
label={this.getLabel()}
helperText={this.getDescription()}
type="number"
variant="filled"
/>
)
}
renderVocabulary(){
return (
<FormControl>
<FormControlLabel
control={
<Select value={this.getValue('vocabulary')} onChange={this.handleChange('vocabulary')}>
<MenuItem value=""></MenuItem>
{this.getTerms().map(term => (
<MenuItem key={term.code} value={term.code}>{term.label || term.code}</MenuItem>
))}
</Select>
}
label={this.getLabel()}
labelPlacement="top"
/>
<FormHelperText>{this.getDescription()}</FormHelperText>
</FormControl>
)
}
renderUnsupported(){
return (<div>unsupported</div>)
}
getValue(field){
return this.state.values[field]
}
getLabel(){
let mandatory = this.props.property.mandatory
let label = this.props.propertyType.label
return mandatory ? label + '*' : label
}
getDescription(){
return this.props.propertyType.description
}
getTerms(){
return this.props.propertyType.vocabulary.terms
}
}
export default _.flow(
withStyles(styles)
)(ObjectTypePropertyPreview)
...@@ -8,10 +8,10 @@ import TableRow from '@material-ui/core/TableRow' ...@@ -8,10 +8,10 @@ import TableRow from '@material-ui/core/TableRow'
import FormControl from '@material-ui/core/FormControl' import FormControl from '@material-ui/core/FormControl'
import FormHelperText from '@material-ui/core/FormHelperText' import FormHelperText from '@material-ui/core/FormHelperText'
import Select from '@material-ui/core/Select' import Select from '@material-ui/core/Select'
import TextField from '@material-ui/core/TextField'
import Checkbox from '@material-ui/core/Checkbox' import Checkbox from '@material-ui/core/Checkbox'
import DragHandleIcon from '@material-ui/icons/DragHandle' import DragHandleIcon from '@material-ui/icons/DragHandle'
import RootRef from '@material-ui/core/RootRef' import RootRef from '@material-ui/core/RootRef'
import ObjectTypePropertyPreview from './ObjectTypePropertyPreview.jsx'
import {withStyles} from '@material-ui/core/styles' import {withStyles} from '@material-ui/core/styles'
import logger from '../../../common/logger.js' import logger from '../../../common/logger.js'
...@@ -72,6 +72,9 @@ class ObjectTypeTableRow extends React.Component { ...@@ -72,6 +72,9 @@ class ObjectTypeTableRow extends React.Component {
super(props) super(props)
this.handleRef = React.createRef() this.handleRef = React.createRef()
this.rowRef = React.createRef() this.rowRef = React.createRef()
this.handleSelect = this.handleSelect.bind(this)
this.handleChangePropertyType = this.handleChangePropertyType.bind(this)
this.handleChangeMandatory = this.handleChangeMandatory.bind(this)
} }
componentDidMount(){ componentDidMount(){
...@@ -80,80 +83,100 @@ class ObjectTypeTableRow extends React.Component { ...@@ -80,80 +83,100 @@ class ObjectTypeTableRow extends React.Component {
this.props.connectDropTarget(this.rowRef.current) this.props.connectDropTarget(this.rowRef.current)
} }
handleSelect(property){ handleSelect(){
return () => { this.props.onSelect(this.props.property.ordinal)
this.props.onSelect(property)
}
} }
handleChange(index, key){ handleChangePropertyType(event){
return event => { event.stopPropagation()
let value = _.has(event.target, 'checked') ? event.target.checked : event.target.value let propertyType = _.find(this.props.propertyTypes, propertyType => {
this.props.onChange(index, key, value) return propertyType.code === event.target.value
} })
this.props.onChange(this.props.property.ordinal, 'propertyType', propertyType)
}
handleChangeMandatory(event){
event.stopPropagation()
this.props.onChange(this.props.property.ordinal, 'mandatory', event.target.checked)
} }
render(){ render(){
logger.log(logger.DEBUG, 'ObjectTypeTableRow.render') logger.log(logger.DEBUG, 'ObjectTypePropertyRow.render')
const {classes, property, index} = this.props const {classes, property} = this.props
return ( return (
<RootRef rootRef={this.rowRef}> <RootRef rootRef={this.rowRef}>
<TableRow key={property.code} classes={{ root: classes.row, selected: classes.selected }} selected={property.selected} onClick={this.handleSelect(property.code)}> <TableRow
classes={{ root: classes.row, selected: classes.selected }}
selected={property.selected}
onClick={this.handleSelect}
>
<RootRef rootRef={this.handleRef}> <RootRef rootRef={this.handleRef}>
<TableCell classes={{ root: classes.drag }}> <TableCell classes={{ root: classes.drag }}>
<DragHandleIcon /> <DragHandleIcon />
</TableCell> </TableCell>
</RootRef> </RootRef>
<TableCell> <TableCell>
<TextField {this.renderPreview()}
value={property.code}
error={property.errors['code'] ? true : false}
helperText={property.errors['code']}
onChange={this.handleChange(index, 'code')}
/>
</TableCell>
<TableCell>
<TextField
value={property.label}
error={property.errors['label'] ? true : false}
helperText={property.errors['label']}
onChange={this.handleChange(index, 'label')}
/>
</TableCell> </TableCell>
<TableCell> <TableCell>
<TextField {this.renderPropertyType()}
value={property.description}
error={property.errors['description'] ? true : false}
helperText={property.errors['description']}
onChange={this.handleChange(index, 'description')}
/>
</TableCell> </TableCell>
<TableCell> <TableCell>
<FormControl error={property.errors['dataType'] ? true : false}> {this.renderMandatory()}
<Select
value={property.dataType ? property.dataType : ''}
onChange={this.handleChange(index, 'dataType')}
>
<MenuItem value=""></MenuItem>
<MenuItem value={'VARCHAR'}>VARCHAR</MenuItem>
<MenuItem value={'INTEGER'}>INTEGER</MenuItem>
<MenuItem value={'REAL'}>REAL</MenuItem>
<MenuItem value={'BOOLEAN'}>BOOLEAN</MenuItem>
</Select>
{ property.errors['dataType'] &&
<FormHelperText>{property.errors['dataType']}</FormHelperText>
}
</FormControl>
</TableCell>
<TableCell>
<Checkbox checked={property.mandatory} value='mandatory' onChange={this.handleChange(index, 'mandatory')} />
</TableCell> </TableCell>
</TableRow> </TableRow>
</RootRef> </RootRef>
) )
} }
renderPreview(){
const {property, propertyTypes} = this.props
const propertyType = _.find(propertyTypes, propertyType => {
return propertyType.code === property.propertyType.code
})
return (
<ObjectTypePropertyPreview property={property} propertyType={propertyType} />
)
}
renderPropertyType(){
const {property, propertyTypes} = this.props
return (
<FormControl error={property.errors['propertyType'] ? true : false}>
<Select
value={property.propertyType ? property.propertyType.code : ''}
onClick={event => {event.stopPropagation()}}
onChange={this.handleChangePropertyType}
>
<MenuItem value=""></MenuItem>
{propertyTypes && propertyTypes.map(propertyType => (
<MenuItem key={propertyType.code} value={propertyType.code}>{propertyType.code}</MenuItem>
))}
</Select>
{property.errors['type'] &&
<FormHelperText>{property.errors['type']}</FormHelperText>
}
</FormControl>
)
}
renderMandatory(){
const {property} = this.props
return (
<Checkbox
checked={property.mandatory}
value='mandatory'
onClick={event => {event.stopPropagation()}}
onChange={this.handleChangeMandatory}
/>
)
}
} }
export default _.flow( export default _.flow(
......
...@@ -5,11 +5,10 @@ import TableBody from '@material-ui/core/TableBody' ...@@ -5,11 +5,10 @@ import TableBody from '@material-ui/core/TableBody'
import TableHead from '@material-ui/core/TableHead' import TableHead from '@material-ui/core/TableHead'
import TableCell from '@material-ui/core/TableCell' import TableCell from '@material-ui/core/TableCell'
import TableRow from '@material-ui/core/TableRow' import TableRow from '@material-ui/core/TableRow'
import ObjectTypeTableRow from './ObjectTypeTableRow.jsx' import ObjectTypePropertyRow from './ObjectTypePropertyRow.jsx'
import {withStyles} from '@material-ui/core/styles' import {withStyles} from '@material-ui/core/styles'
import logger from '../../../common/logger.js' import logger from '../../../common/logger.js'
const styles = () => ({ const styles = () => ({
table: { table: {
width: '100%' width: '100%'
...@@ -18,29 +17,34 @@ const styles = () => ({ ...@@ -18,29 +17,34 @@ const styles = () => ({
class ObjectTypeTable extends React.Component { class ObjectTypeTable extends React.Component {
constructor(props){
super(props)
this.state = {}
}
render() { render() {
logger.log(logger.DEBUG, 'ObjectTypeTable.render') logger.log(logger.DEBUG, 'ObjectTypeTable.render')
const { classes, properties } = this.props const { classes, objectType, propertyTypes } = this.props
const { properties } = objectType
return ( return (
<Table className={classes.table}> <Table className={classes.table}>
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell></TableCell> <TableCell></TableCell>
<TableCell>Code*</TableCell> <TableCell>Preview</TableCell>
<TableCell>Label*</TableCell> <TableCell>Property Type*</TableCell>
<TableCell>Description*</TableCell>
<TableCell>Data Type*</TableCell>
<TableCell>Mandatory</TableCell> <TableCell>Mandatory</TableCell>
</TableRow> </TableRow>
</TableHead> </TableHead>
<TableBody> <TableBody>
{properties.map((property, index) => ( {properties.map((property, index) => (
<React.Fragment key={property.code}> <React.Fragment key={property.ordinal}>
<ObjectTypeTableRow <ObjectTypePropertyRow
index={index} index={index}
property={property} property={property}
propertyTypes={propertyTypes}
onChange={this.props.onChange} onChange={this.props.onChange}
onSelect={this.props.onSelect} onSelect={this.props.onSelect}
onReorder={this.props.onReorder} onReorder={this.props.onReorder}
......
...@@ -13,7 +13,10 @@ const CLASS_FULL_NAMES = [ ...@@ -13,7 +13,10 @@ const CLASS_FULL_NAMES = [
'as/dto/dataset/search/DataSetTypeSearchCriteria', 'as/dto/dataset/search/DataSetTypeSearchCriteria',
'as/dto/dataset/fetchoptions/DataSetTypeFetchOptions', 'as/dto/dataset/fetchoptions/DataSetTypeFetchOptions',
'as/dto/material/search/MaterialTypeSearchCriteria', 'as/dto/material/search/MaterialTypeSearchCriteria',
'as/dto/material/fetchoptions/MaterialTypeFetchOptions' 'as/dto/material/fetchoptions/MaterialTypeFetchOptions',
'as/dto/property/PropertyType',
'as/dto/property/search/PropertyTypeSearchCriteria',
'as/dto/property/fetchoptions/PropertyTypeFetchOptions'
] ]
class Dto { class Dto {
......
...@@ -32,6 +32,10 @@ export class Facade { ...@@ -32,6 +32,10 @@ export class Facade {
return this.v3.logout() return this.v3.logout()
} }
searchPropertyTypes(criteria, fo) {
return this.v3.searchPropertyTypes(criteria, fo)
}
searchPersons(criteria, fo) { searchPersons(criteria, fo) {
return this.v3.searchPersons(criteria, fo) return this.v3.searchPersons(criteria, fo)
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment