Commit 36e99c46 authored by Jaden DIEFENBAUGH's avatar Jaden DIEFENBAUGH
Browse files

added schema validation to details & editor, added save func for editors

parent cf16a26c
......@@ -38,11 +38,14 @@ import { algorithmValidator } from '@helpers/beat.js';
import type { AlgorithmValidatorObject, BeatObject } from '@helpers/beat.js';
import { changeObjFieldName } from '@helpers';
import ValidSchemaBadge from './ValidSchemaBadge.jsx';
type Props = {
data: BeatObject,
algorithms: BeatObject[],
libraries: BeatObject[],
dataformats: BeatObject[],
saveFunc: (BeatObject) => any,
};
type State = {
......@@ -791,279 +794,291 @@ class AlgorithmEditor extends React.Component<Props, State> {
);
}
render () {
return (
<div>
<Form>
<FormGroup tag='fieldset'>
<legend>Algorithm Settings</legend>
<FormGroup>
<Label for='algName'>Name</Label>
<Input
type='text'
name='name'
id='algName'
placeholder='New algorithm name...'
valid={this.getValidity().name}
value={this.state.cache.name}
onChange={(e) => this.updateName(e.target.value)}
/>
{
this.getValidity().name ? '' :
<Alert color='danger'>
Please provide a <strong>unique</strong> name in the form{' '}
<pre style={{display: 'inline'}}>
&lt;user&gt;/&lt;algorithm name&gt;/&lt;version number&gt;
</pre>
</Alert>
}
</FormGroup>
<FormGroup>
<Label for='algDesc'>Short Description</Label>
<Input
type='text'
name='desc'
id='algDesc'
placeholder='Algorithm description...'
value={this.state.cache.contents[this.descField()]}
onChange={(e) => this.changeContentsVal(this.descField(), e.target.value)}
/>
</FormGroup>
<FormGroup check>
<Label check>
renderLibraries = () => (
<TabPane tabId='2'>
<Row>
<Col sm='12'>
{ Object.entries(this.state.cache.contents.uses).map(([name, lib], i) => (
<GroupIOBlock
key={i}
name={name}
ioObj={lib}
dfs={this.props.libraries}
updateFunc={(oldName, newName, obj) => {
const libs = this.state.cache.contents.uses;
delete libs[oldName];
libs[newName] = obj;
this.changeContentsVal('uses', libs);
}}
deleteFunc={(name) => {
const libs = this.state.cache.contents.uses;
delete libs[name];
this.changeContentsVal('uses', libs);
}}
lazyFunc={(str, updateFunc) => this.lazyInputUpdate(
this.state.cache.contents.uses,
str,
updateFunc
)}
/>
))
}
</Col>
</Row>
<Button outline block
onClick={() => this.changeContentsVal('uses',
{ ...this.state.cache.contents.uses,
[`library_${ Object.keys(this.state.cache.contents.uses).length }`]: ''
})
}
>
New Library
</Button>
</TabPane>
);
renderResults = () => (
<TabPane tabId='3'>
{
Object.entries(this.state.cache.contents.results)
.map(([name, result], i) => (
<FormGroup row key={i}>
<Col sm='4'>
<InputGroup>
<DeleteInputBtn
deleteFunc={
() => {
delete this.state.cache.contents.results[name];
this.changeContentsVal(
'results',
this.state.cache.contents.results
);
}
}
/>
<Input
type='checkbox'
checked={this.isAnalyzer()}
onChange={e => e.target.checked ? this.changeToAnalyzer() : this.changeToNormal()}
/>{' '}
Analyser
</Label>
</FormGroup>
<FormGroup check>
type='text'
placeholder='Result name...'
value={name}
onChange={(e) => this.lazyInputUpdate(
this.state.cache.contents.results,
e.target.value,
() => this.changeContentsVal(
'results',
changeObjFieldName(
this.state.cache.contents.results,
name,
e.target.value
)
)
)}
/>
</InputGroup>
</Col>
<Col sm='4'>
<Input
type='select'
className='custom-select'
value={result.type}
onChange={(e) => this.updateResult(name,
{
...result,
type: e.target.value
})
}
>
<option disabled value=''>Type...</option>
{
this.state.resultDfs.map((d, i) => (
<option key={i} value={d}>{d}</option>
))
}
</Input>
</Col>
<Col>
<Label check>
<Input
type='checkbox'
checked={this.state.cache.contents.splittable ? true : false}
onChange={e => this.changeContentsVal('splittable', e.target.checked)}
/>{' '}
Splittable
value={result.display ? true : false}
onChange={(e) => this.changeContentsVal(
'results',
{ ...this.state.cache.contents.results,
[name] : {
...result,
display: e.target.checked
}
})
}
/>
{' '}
<span>Display By Default</span>
</Label>
</FormGroup>
</Col>
</FormGroup>
{
(!this.isAnalyzer() || this.getValidity().result0Exists) ||
<Alert color='danger'>
You need at least 1 result from an Analyzer.
</Alert>
}
{
this.getValidity().endpoint0Exists ||
<Alert color='danger'>
You need at least 1 endpoint group.
</Alert>
}
{
this.getValidity().endpointNamesUnique ||
<Alert color='danger'>
Each endpoint group should have a different name.
</Alert>
}
{
(this.isAnalyzer() || this.getValidity().endpoint0OutputExists) ||
<Alert color='danger'>
You must have at least 1 output
in the first endpoint group.
</Alert>
}
{
this.getValidity().endpoint0InputExists ||
<Alert color='danger'>
You need at least 1 input in the first endpoint group.
</Alert>
))
}
<Button outline block
onClick={() => this.changeContentsVal(
'results',
{...this.state.cache.contents.results,
[`result ${ Object.keys(this.state.cache.contents.results).length }`]: {
display: false,
type: ''
}
}
<div className='mt-3 mb-3'>
<Nav tabs>
<NavItem>
<NavLink
className={cn({ active: this.state.activeTab === '0' })}
onClick={() => this.tabTo('0')}
>
Endpoints
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={cn({ active: this.state.activeTab === '1' })}
onClick={() => this.tabTo('1')}
>
Parameters
</NavLink>
</NavItem>
)}
>
New Result
</Button>
</TabPane>
);
render = () => (
<div className='mt-1'>
<div className='d-flex'>
<Button
className='mx-auto'
outline
color='secondary'
onClick={() => this.props.saveFunc(this.state.cache)}
>
Save Changes (Cache is <ValidSchemaBadge entity='algorithm' obj={this.state.cache} />)
</Button>
</div>
<Form>
<FormGroup tag='fieldset'>
<FormGroup>
<Label for='algName'>Name</Label>
<Input
type='text'
name='name'
id='algName'
placeholder='New algorithm name...'
valid={this.getValidity().name}
value={this.state.cache.name}
onChange={(e) => this.updateName(e.target.value)}
/>
{
this.getValidity().name ? '' :
<Alert color='danger'>
Please provide a <strong>unique</strong> name in the form{' '}
<pre style={{display: 'inline'}}>
&lt;user&gt;/&lt;algorithm name&gt;/&lt;version number&gt;
</pre>
</Alert>
}
</FormGroup>
<FormGroup>
<Label for='algDesc'>Short Description</Label>
<Input
type='text'
name='desc'
id='algDesc'
placeholder='Algorithm description...'
value={this.state.cache.contents[this.descField()]}
onChange={(e) => this.changeContentsVal(this.descField(), e.target.value)}
/>
</FormGroup>
<FormGroup check>
<Label check>
<Input
type='checkbox'
checked={this.isAnalyzer()}
onChange={e => e.target.checked ? this.changeToAnalyzer() : this.changeToNormal()}
/>{' '}
Analyser
</Label>
</FormGroup>
<FormGroup check>
<Label check>
<Input
type='checkbox'
checked={this.state.cache.contents.splittable ? true : false}
onChange={e => this.changeContentsVal('splittable', e.target.checked)}
/>{' '}
Splittable
</Label>
</FormGroup>
</FormGroup>
{
(!this.isAnalyzer() || this.getValidity().result0Exists) ||
<Alert color='danger'>
You need at least 1 result from an Analyzer.
</Alert>
}
{
this.getValidity().endpoint0Exists ||
<Alert color='danger'>
You need at least 1 endpoint group.
</Alert>
}
{
this.getValidity().endpointNamesUnique ||
<Alert color='danger'>
Each endpoint group should have a different name.
</Alert>
}
{
(this.isAnalyzer() || this.getValidity().endpoint0OutputExists) ||
<Alert color='danger'>
You must have at least 1 output
in the first endpoint group.
</Alert>
}
{
this.getValidity().endpoint0InputExists ||
<Alert color='danger'>
You need at least 1 input in the first endpoint group.
</Alert>
}
<div className='mt-3 mb-3'>
<Nav tabs>
<NavItem>
<NavLink
className={cn({ active: this.state.activeTab === '0' })}
onClick={() => this.tabTo('0')}
>
Endpoints
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={cn({ active: this.state.activeTab === '1' })}
onClick={() => this.tabTo('1')}
>
Parameters
</NavLink>
</NavItem>
<NavItem>
<NavLink
className={cn({ active: this.state.activeTab === '2' })}
onClick={() => this.tabTo('2')}
>
Libraries
</NavLink>
</NavItem>
{ this.state.cache.contents.results ? (
<NavItem>
<NavLink
className={cn({ active: this.state.activeTab === '2' })}
onClick={() => this.tabTo('2')}
className={cn({ active: this.state.activeTab === '3' })}
onClick={() => this.tabTo('3')}
>
Libraries
Results
</NavLink>
</NavItem>
{ this.state.cache.contents.results ? (
<NavItem>
<NavLink
className={cn({ active: this.state.activeTab === '3' })}
onClick={() => this.tabTo('3')}
>
Results
</NavLink>
</NavItem>
) : ''
}
</Nav>
<TabContent className='mt-3' activeTab={this.state.activeTab}>
{ this.renderEndpoints() }
{ this.renderParameters() }
<TabPane tabId='2'>
<Row>
<Col sm='12'>
{ Object.entries(this.state.cache.contents.uses).map(([name, lib], i) => (
<GroupIOBlock
key={i}
name={name}
ioObj={lib}
dfs={this.props.libraries}
updateFunc={(oldName, newName, obj) => {
const libs = this.state.cache.contents.uses;
delete libs[oldName];
libs[newName] = obj;
this.changeContentsVal('uses', libs);
}}
deleteFunc={(name) => {
const libs = this.state.cache.contents.uses;
delete libs[name];
this.changeContentsVal('uses', libs);
}}
lazyFunc={(str, updateFunc) => this.lazyInputUpdate(
this.state.cache.contents.uses,
str,
updateFunc
)}
/>
))
}
</Col>
</Row>
<Button outline block
onClick={() => this.changeContentsVal('uses',
{ ...this.state.cache.contents.uses,
[`library_${ Object.keys(this.state.cache.contents.uses).length }`]: ''
})
}
>
New Library
</Button>
</TabPane>
{ this.state.cache.contents.results ? (
<TabPane tabId='3'>
{
Object.entries(this.state.cache.contents.results)
.map(([name, result], i) => (
<FormGroup row key={i}>
<Col sm='4'>
<InputGroup>
<DeleteInputBtn
deleteFunc={
() => {
delete this.state.cache.contents.results[name];
this.changeContentsVal(
'results',
this.state.cache.contents.results
);
}
}
/>
<Input
type='text'
placeholder='Result name...'
value={name}
onChange={(e) => this.lazyInputUpdate(
this.state.cache.contents.results,
e.target.value,
() => this.changeContentsVal(
'results',
changeObjFieldName(
this.state.cache.contents.results,
name,
e.target.value
)
)
)}
/>
</InputGroup>
</Col>
<Col sm='4'>
<Input
type='select'
className='custom-select'
value={result.type}
onChange={(e) => this.updateResult(name,
{
...result,
type: e.target.value
})
}
>
<option disabled value=''>Type...</option>
{
this.state.resultDfs.map((d, i) => (
<option key={i} value={d}>{d}</option>
))
}
</Input>
</Col>
<Col>
<Label check>
<Input
type='checkbox'
value={result.display ? true : false}
onChange={(e) => this.changeContentsVal(
'results',
{ ...this.state.cache.contents.results,
[name] : {
...result,
display: e.target.checked
}
})
}
/>
{' '}
<span>Display By Default</span>
</Label>
</Col>
</FormGroup>
))
}
<Button outline block
onClick={() => this.changeContentsVal(
'results',
{...this.state.cache.contents.results,
[`result ${ Object.keys(this.state.cache.contents.results).length }`]: {
display: false,
type: ''
}
}
)}
>
New Result
</Button>
</TabPane>
) : ''
}
</TabContent>
</div>
</Form>
</div>
);
}
) : ''
}
</Nav>
<TabContent className='mt-3' activeTab={this.state.activeTab}>
{ this.renderEndpoints() }
{ this.renderParameters() }
{ this.renderLibraries() }
{ this.state.cache.contents.results && this.renderResults() }
</TabContent>
</div>
</Form>
</div>
);
}
export default connect(mapStateToProps)(AlgorithmEditor);
......@@ -26,6 +26,7 @@ import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import * as Selectors from '@store/selectors.js';
import ValidSchemaBadge from './ValidSchemaBadge.jsx';
import CacheInput from './CacheInput.jsx';
import type {
......@@ -38,6 +39,7 @@ type Props = {
data: BeatObject,
dfs: BeatObject[],
dataformats: string[],
saveFunc: (BeatObject) => any,
};
type State = {
......@@ -343,38 +345,49 @@ class DataformatEditor extends React.Component<Props, State> {
}
render = () => (
<Form onSubmit={(e) => e.preventDefault()}>
<FormGroup tag='fieldset'>
<legend>Dataformat Settings</legend>
<FormGroup>
<Label for='dfName'>Name</Label>
<Input
type='text'
name='name'
id='dfName'
placeholder='New dataformat name...'
value={this.state.cache.name}
onChange={e => this.setState({ cache: {...this.state.cache, 'name': e.target.value}})}