Commit 2d07c42d authored by Jaden DIEFENBAUGH's avatar Jaden DIEFENBAUGH
Browse files

add db editor

parent 8c5c4972
// @flow
import * as React from 'react';
import {
Container,
Row,
Col,
Button,
Form,
FormGroup,
Label,
Input,
InputGroup,
FormText,
Collapse,
Card,
CardHeader,
CardBody,
TabContent, TabPane, Nav, NavItem, NavLink, CardTitle, CardText,
FormFeedback,
Alert,
InputGroupAddon,
UncontrolledDropdown, DropdownToggle, DropdownMenu, DropdownItem,
ButtonGroup,
} from 'reactstrap';
import { connect } from 'react-redux';
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 {
BeatObject,
} from '@helpers/beat.js';
import { nameValidator } from '@helpers/beat';
import { changeObjFieldName } from '@helpers';
type Props = {
data: BeatObject,
saveFunc: (BeatObject) => any,
databases: BeatObject[],
dataformats: string[],
};
type State = {
cache: any,
activeProtocol: number,
};
const mapStateToProps: MapStateToProps<*,*,*> = (state, ownProps) => {
const obj = {
databases: Selectors.databaseGet(state),
dataformats: Selectors.dataformatGet(state).map(d => d.name)/*.concat(builtinDfs)*/,
};
return obj;
};
const getValidObj = (data = {}) => {
const getObj = {
name: '',
contents: {},
...JSON.parse(JSON.stringify(data))
};
const obj = {
...getObj,
contents: {
'description': '',
'root_folder': '',
'protocols': [],
...getObj.contents,
},
};
return obj;
};
const DeleteInputBtn = ({ deleteFunc }: any) => (
<span className='input-group-btn'>
<Button
color='danger'
onClick={(e) => deleteFunc(e)}
>
X
</Button>
</span>
);
class DatabaseEditor extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
}
state = {
cache: getValidObj(this.props.data),
activeProtocol: 0,
}
componentWillReceiveProps (nextProps) {
this.setState({
cache: getValidObj(nextProps.data),
});
}
setContents = (newContents) => {
this.setState({
cache: {
...this.state.cache,
contents: {
'description': this.state.cache.contents['description'],
...newContents,
}
}
});
}
activateProtocol = (index: number) => {
this.setState({ activeProtocol: index });
}
updateProtocol = (index: number, obj) => {
this.setContents({
...this.state.cache.contents,
protocols: this.state.cache.contents.protocols.map((o, i) => i === index ? obj : o)
});
}
updateSet = (idxProtocol: number, idxSet: number, obj) => {
const protocol = this.state.cache.contents.protocols[idxProtocol];
this.updateProtocol(
idxProtocol,
{
...protocol,
sets: protocol.sets.map((s, i) => i === idxSet ? obj : s)
}
);
}
validatorFunc = nameValidator('database', this.props.databases.map(d => d.name));
nameIsValid = (str: string = this.state.cache.name) => str === this.props.data.name || this.validatorFunc(str);
renderProtocol = (index: number, protocol) => (
<Container>
<FormGroup row>
<Col sm='2'>
<Label for='protocol-name' className='col-form-label'>Name</Label>
</Col>
<Col sm='10'>
<Input
name='protocol-name'
placeholder='Protocol name...'
value={protocol.name}
onChange={(e) => {
this.updateProtocol(index, {...protocol, name: e.target.value});
}}
/>
</Col>
</FormGroup>
<FormGroup row>
<Col sm='2'>
<Label for='template' className='col-form-label'>Template Name</Label>
</Col>
<Col sm='10'>
<Input
name='template'
placeholder='Protocol Template name...'
value={protocol.template}
onChange={(e) => {
this.updateProtocol(index, {...protocol, template: e.target.value});
}}
/>
</Col>
</FormGroup>
<Row>
<Col className='d-flex align-items-center mb-2'>
<h3 className='mb-0 mr-2'>Sets</h3>
<Button outline color='success'
onClick={e => {
const newSets = [...protocol.sets, {
name: '',
template: '',
view: '',
outputs: {}
}];
this.updateProtocol(
index,
{
...protocol,
sets: newSets
}
);
}}
>
+
</Button>
</Col>
</Row>
{
protocol.sets.map((currSet, i) =>
<Row key={i}>
<Col>
<h4>
{ currSet.name }
{' '}
<ButtonGroup>
<Button outline color='danger'
onClick={e => {
this.updateProtocol(
index,
{
...protocol,
sets: protocol.sets.filter((s, j) => i !== j)
}
);
}}
>
Delete
</Button>
<Button outline color='secondary'
onClick={e => {
const newSets = [...protocol.sets];
newSets.splice(i + 1, 0, currSet);
this.updateProtocol(
index,
{
...protocol,
sets: newSets
}
);
}}
>
Clone
</Button>
</ButtonGroup>
</h4>
<FormGroup row>
<Col sm='2'>
<Label for={`set-name-${ i }`} className='col-form-label'>Name</Label>
</Col>
<Col sm='10'>
<Input
name={`set-name-${ i }`}
placeholder='Set name...'
value={currSet.name}
onChange={(e) => {
this.updateSet(index, i, {...currSet, name: e.target.value});
}}
/>
</Col>
</FormGroup>
<FormGroup row>
<Col sm='2'>
<Label for={`set-template-${ i }`} className='col-form-label'>Template Name</Label>
</Col>
<Col sm='10'>
<Input
name={`set-template-${ i }`}
placeholder='Set Template name...'
value={currSet.template}
onChange={(e) => {
this.updateSet(index, i, {...currSet, template: e.target.value});
}}
/>
</Col>
</FormGroup>
<FormGroup row>
<Col sm='2'>
<Label for={`set-view-${ i }`} className='col-form-label'>View Name</Label>
</Col>
<Col sm='10'>
<Input
name={`set-view-${ i }`}
placeholder='Set View name...'
value={currSet.view}
onChange={(e) => {
this.updateSet(index, i, {...currSet, view: e.target.value});
}}
/>
</Col>
</FormGroup>
<Container>
<Row>
<Col sm='6'>
<h6>Output Name</h6>
</Col>
<Col sm='6'>
<h6>Output Type</h6>
</Col>
</Row>
{
Object.entries(currSet.outputs).map(([outputName, df], j) =>
<FormGroup row
key={j}
>
<Col sm='6'>
<CacheInput
value={outputName}
validateFunc={(str) => !Object.keys(currSet.outputs).includes(str)}
onChange={e => {
const newOutputs = changeObjFieldName(
currSet.outputs,
outputName,
e.target.value
);
this.updateSet(index, i,
{
...currSet,
outputs: newOutputs
}
);
}}
/>
</Col>
<Col sm='6'>
<Input
type='select'
className='custom-select'
value={df}
onChange={e => {
this.updateSet(index, i,
{
...currSet,
outputs: {
...currSet.outputs,
[outputName]: e.target.value
}
}
);
}}
>
<option disabled value=''>Type...</option>
{
this.props.dataformats
.filter(d => !['object', 'array'].includes(d))
.map((d, i) => (
<option key={i} value={d}>{d}</option>
))
}
</Input>
</Col>
</FormGroup>
)
}
<FormGroup row>
<Button outline block
onClick={e => {
let newKey = '';
let v = 0;
while(true) {
const tryKey = `Output ${ v }`;
if(Object.keys(currSet.outputs).includes(tryKey)){
v++;
continue;
}
newKey = tryKey;
break;
}
this.updateSet(index, i, {
...currSet,
outputs: {
...currSet.outputs,
[newKey]: 'string'
}
});
}}
>
New Output
</Button>
</FormGroup>
</Container>
</Col>
</Row>
)
}
</Container>
);
render = () => (
<div>
<div className='d-flex'>
<Button
className='mx-auto'
outline
color='secondary'
onClick={() => this.props.saveFunc(this.state.cache)}
>
Save Changes (Changes are <ValidSchemaBadge entity='database' obj={this.state.cache} />)
</Button>
</div>
<Form onSubmit={(e) => e.preventDefault()}>
<FormGroup tag='fieldset'>
<FormGroup>
<Label for='dbName'>Name</Label>
<Input
type='text'
name='name'
id='dbName'
placeholder='New database name...'
value={this.state.cache.name}
onChange={e => this.setState({ cache: {...this.state.cache, 'name': e.target.value}})}
valid={this.nameIsValid()}
/>
</FormGroup>
<FormGroup>
<Label for='description'>Short Description</Label>
<Input
type='text'
name='description'
placeholder='Database description...'
value={this.state.cache.contents['description']}
onChange={e => this.setContents({ ...this.state.cache.contents, 'description': e.target.value})}
/>
</FormGroup>
</FormGroup>
<div className='d-flex align-items-center mb-3'>
<h3 className='mr-2 mb-0'>Protocols</h3>
{
this.state.cache.contents.protocols.length > 0 &&
<UncontrolledDropdown className='mr-3'>
<DropdownToggle outline color='secondary' caret className=''>
Active Protocol: { this.state.cache.contents.protocols[this.state.activeProtocol].name }
</DropdownToggle>
<DropdownMenu>
{
this.state.cache.contents.protocols
.map((p, i) =>
<DropdownItem
key={i}
onClick={() => this.activateProtocol(i)}
>
{ this.state.cache.contents.protocols[i].name }{' '}
({ this.state.cache.contents.protocols[i].template })
</DropdownItem>
)
}
</DropdownMenu>
</UncontrolledDropdown>
}
<ButtonGroup>
<Button outline color='danger'
onClick={e => {
const active = this.state.activeProtocol;
const protocols = this.state.cache.contents.protocols
.filter((p, i) => i !== active);
this.setContents({
...this.state.cache.contents,
protocols
});
this.activateProtocol(active === 0 ? 0 : active - 1);
}}
>
Delete
</Button>
<Button outline color='secondary'
onClick={e => {
const protocols = [...this.state.cache.contents.protocols];
protocols.splice(
this.state.activeProtocol + 1,
0,
protocols[this.state.activeProtocol]
);
this.setContents({
...this.state.cache.contents,
protocols
});
this.activateProtocol(this.state.activeProtocol + 1);
}}
>
Clone
</Button>
<Button outline color='success'
onClick={e => {
const protocols = [...this.state.cache.contents.protocols, {
name: '',
template: '',
sets: [],
}];
this.setContents({
...this.state.cache.contents,
protocols
});
this.activateProtocol(protocols.length - 1);
}}
>
New
</Button>
</ButtonGroup>
</div>
{ this.state.cache.contents.protocols.length > 0 && this.renderProtocol(
this.state.activeProtocol,
this.state.cache.contents.protocols[this.state.activeProtocol]
)}
</Form>
</div>
);
}
export default connect(mapStateToProps)(DatabaseEditor);
......@@ -30,6 +30,7 @@ import ValidSchemaBadge from './ValidSchemaBadge.jsx';
import DataformatEditor from './DataformatEditor.jsx';
import AlgorithmEditor from './AlgorithmEditor.jsx';
import LibraryEditor from './LibraryEditor.jsx';
import DatabaseEditor from './DatabaseEditor.jsx';
type Props = {
match: any,
......@@ -148,6 +149,13 @@ class EntityDetail extends React.Component<Props, State> {
saveFunc={this.saveChanges}
/>
}
{
this.props.match.params.entity === 'database' &&
<DatabaseEditor
data={this.props.getEntityObject()}
saveFunc={this.saveChanges}
/>
}
</TabPane>
<TabPane tabId='1'>
<Row>
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment