Commit cf16a26c authored by Jaden DIEFENBAUGH's avatar Jaden DIEFENBAUGH
Browse files

add library editor and refactor for DRYness

parent 6692c9fa
......@@ -36,6 +36,7 @@ import * as Selectors from '@store/selectors.js';
import { algorithmValidator } from '@helpers/beat.js';
import type { AlgorithmValidatorObject, BeatObject } from '@helpers/beat.js';
import { changeObjFieldName } from '@helpers';
type Props = {
data: BeatObject,
......@@ -136,12 +137,6 @@ const GroupIOBlock = ({ name, ioObj, dfs, updateFunc, deleteFunc, lazyFunc }: an
</FormGroup>
);
const changeObjFieldName = (obj: {}, oldName: string, newName: string): {} => {
return Object.entries(obj)
.map(([name, val]) => [name === oldName ? newName : name, val])
.reduce((o, [name, val]) => ({...o, [name]: val}), {});
};
const builtinDfs = [
'int8',
'int16',
......@@ -166,7 +161,7 @@ const resultDfs = [
'string'
];
class AlgorithmDetail extends React.Component<Props, State> {
class AlgorithmEditor extends React.Component<Props, State> {
constructor(props: Props) {
//console.log(`Creating AlgDetail`);
super(props);
......@@ -960,7 +955,7 @@ class AlgorithmDetail extends React.Component<Props, State> {
<Button outline block
onClick={() => this.changeContentsVal('uses',
{ ...this.state.cache.contents.uses,
[`library ${ Object.keys(this.state.cache.contents.uses).length }`]: ''
[`library_${ Object.keys(this.state.cache.contents.uses).length }`]: ''
})
}
>
......@@ -1071,4 +1066,4 @@ class AlgorithmDetail extends React.Component<Props, State> {
}
}
export default connect(mapStateToProps)(AlgorithmDetail);
export default connect(mapStateToProps)(AlgorithmEditor);
......@@ -24,8 +24,6 @@ import {
import { connect } from 'react-redux';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import cn from 'classnames';
import * as Selectors from '@store/selectors.js';
import CacheInput from './CacheInput.jsx';
......@@ -34,6 +32,8 @@ import type {
BeatObject,
} from '@helpers/beat.js';
import { changeObjFieldName } from '@helpers';
type Props = {
data: BeatObject,
dfs: BeatObject[],
......@@ -44,10 +44,6 @@ type State = {
cache: any,
};
const orderedParams = (params: any): string[] => [ params.user, params.name, params.version ];
const findDf = (dfs, nameStr) => dfs.find(a => a.name === nameStr) || {};
const builtinDfs = [
'int8',
'int16',
......@@ -105,12 +101,6 @@ const DeleteInputBtn = ({ deleteFunc }: any) => (
</span>
);
const changeObjFieldName = (obj: {}, oldName: string, newName: string): {} => {
return Object.entries(obj)
.map(([name, val]) => [name === oldName ? newName : name, val])
.reduce((o, [name, val]) => ({...o, [name]: val}), {});
};
const resultDfs = [
'int32',
'float32',
......@@ -128,13 +118,6 @@ const shownType = (obj: any): string => {
return obj;
};
const lazyInputUpdate = (obj, str, updateFunc) => {
if(Object.keys(obj).includes(str))
return;
updateFunc();
};
const RecursiveObj = ({ obj, dfs, updateFunc }: {obj: any, dfs: string[], updateFunc: (any) => any}) => (
<div className='ml-3 pl-3 border border-top-0 border-right-0 border-bottom-0 my-1'>
{
......@@ -145,11 +128,13 @@ const RecursiveObj = ({ obj, dfs, updateFunc }: {obj: any, dfs: string[], update
<Row>
<Col sm='6'>
<InputGroup>
<DeleteInputBtn deleteFunc={e => {
const newObj = JSON.parse(JSON.stringify(obj));
delete newObj[name];
updateFunc(newObj);
}} />
<DeleteInputBtn
deleteFunc={e => {
const newObj = JSON.parse(JSON.stringify(obj));
delete newObj[name];
updateFunc(newObj);
}}
/>
<CacheInput
type='text'
placeholder='Name...'
......@@ -330,7 +315,7 @@ const RecursiveObj = ({ obj, dfs, updateFunc }: {obj: any, dfs: string[], update
</div>
);
class DataformatDetail extends React.Component<Props, State> {
class DataformatEditor extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
}
......@@ -362,11 +347,11 @@ class DataformatDetail extends React.Component<Props, State> {
<FormGroup tag='fieldset'>
<legend>Dataformat Settings</legend>
<FormGroup>
<Label for='algName'>Name</Label>
<Label for='dfName'>Name</Label>
<Input
type='text'
name='name'
id='algName'
id='dfName'
placeholder='New dataformat name...'
value={this.state.cache.name}
onChange={e => this.setState({ cache: {...this.state.cache, 'name': e.target.value}})}
......@@ -393,4 +378,4 @@ class DataformatDetail extends React.Component<Props, State> {
);
}
export default connect(mapStateToProps)(DataformatDetail);
export default connect(mapStateToProps)(DataformatEditor);
......@@ -27,6 +27,7 @@ import type { BeatObject } from '@helpers/beat';
import DataformatEditor from './DataformatEditor.jsx';
import AlgorithmEditor from './AlgorithmEditor.jsx';
import LibraryEditor from './LibraryEditor.jsx';
type Props = {
match: any,
......@@ -99,6 +100,7 @@ class EntityDetail extends React.Component<Props, State> {
<TabPane tabId='0'>
{ this.props.match.params.entity === 'algorithm' && <AlgorithmEditor data={this.props.getEntityObject()} /> }
{ this.props.match.params.entity === 'dataformat' && <DataformatEditor data={this.props.getEntityObject()} /> }
{ this.props.match.params.entity === 'library' && <LibraryEditor data={this.props.getEntityObject()} /> }
</TabPane>
<TabPane tabId='1'>
</TabPane>
......
// @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,
} from 'reactstrap';
import { connect } from 'react-redux';
import type { MapStateToProps, MapDispatchToProps } from 'react-redux';
import * as Selectors from '@store/selectors.js';
import CacheInput from './CacheInput.jsx';
import type {
BeatObject,
} from '@helpers/beat.js';
import { changeObjFieldName } from '@helpers';
type Props = {
data: BeatObject,
libraries: BeatObject[],
};
type State = {
cache: any,
};
const mapStateToProps: MapStateToProps<*,*,*> = (state, ownProps) => {
const obj = {
libraries: Selectors.libraryGet(state),
};
return obj;
};
const getValidObj = (data = {}) => {
const getObj = {
name: '',
contents: {},
...JSON.parse(JSON.stringify(data))
};
const obj = {
...getObj,
contents: {
'description': '',
'language': 'python',
'uses': {},
...getObj.contents,
},
};
return obj;
};
const DeleteInputBtn = ({ deleteFunc }: any) => (
<span className='input-group-btn'>
<Button
color='danger'
onClick={(e) => deleteFunc(e)}
>
X
</Button>
</span>
);
class LibraryEditor extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
}
state = {
cache: getValidObj(this.props.data),
}
componentWillReceiveProps (nextProps) {
this.setState({
cache: getValidObj(nextProps.data),
});
}
setContents = (newContents) => {
this.setState({
cache: {
...this.state.cache,
contents: {
'description': this.state.cache.contents['description'],
...newContents,
}
}
});
}
updateLibraries = (libraries) => {
this.setContents({...this.state.cache.contents, uses: libraries});
}
render () {
return (
<Form onSubmit={(e) => e.preventDefault()}>
<FormGroup tag='fieldset'>
<legend>Library Settings</legend>
<FormGroup>
<Label for='libName'>Name</Label>
<Input
type='text'
name='name'
id='libName'
placeholder='New library name...'
value={this.state.cache.name}
onChange={e => this.setState({ cache: {...this.state.cache, 'name': e.target.value}})}
valid={
!this.props.libraries.find(o => o.name === this.state.cache.name && o !== this.props.data) &&
/^\S+\/\S+\/\d+$/.test(this.state.cache.name)
}
/>
</FormGroup>
<FormGroup>
<Label for='description'>Short Description</Label>
<Input
type='text'
name='description'
placeholder='Library description...'
value={this.state.cache.contents['description']}
onChange={e => this.setContents({ ...this.state.cache.contents, 'description': e.target.value})}
/>
</FormGroup>
<FormGroup>
<Label>Language</Label>
<Input
type='select'
className='custom-select'
value={this.state.cache.contents.language}
onChange={() => {}}
>
<option value='python'>Python</option>
<option disabled value='cxx'>C++</option>
<option disabled value='matlab'>Matlab</option>
<option disabled value='r'>R</option>
</Input>
</FormGroup>
</FormGroup>
<FormGroup>
<h4>Libraries Used</h4>
<Row>
<Col sm='12'>
{
Object.entries(this.state.cache.contents.uses).map(([name, lib], i) => (
<FormGroup key={i}>
<Row>
<Col sm='6'>
<InputGroup>
<DeleteInputBtn
deleteFunc={e => {
const newLibs = this.state.cache.contents.uses;
delete newLibs[name];
this.updateLibraries(newLibs);
}}
/>
<CacheInput
type='text'
placeholder='Name...'
value={name}
validateFunc={str => !Object.keys(this.state.cache.contents.uses).includes(str)}
onChange={(e) => {
const newLibs = changeObjFieldName(this.state.cache.contents.uses, name, e.target.value);
this.updateLibraries(newLibs);
}}
/>
</InputGroup>
</Col>
<Col sm='6'>
<Input
type='select'
className='custom-select'
value={lib}
onChange={(e) => {
const newLibs = {
...this.state.cache.contents.uses,
[name]: e.target.value
};
this.updateLibraries(newLibs);
}}
>
<option disabled value=''>Library...</option>
{
this.props.libraries
.filter(l => l.name !== this.state.cache.name)
.map((l, i) => (
<option key={i} value={l.name}>{l.name}</option>
))
}
</Input>
</Col>
</Row>
</FormGroup>
))
}
</Col>
</Row>
<Button
outline
block
onClick={e => {
let newKey = '';
let v = 0;
while(true) {
const tryKey = `lib_${ v }`;
if(Object.keys(this.state.cache.contents.uses).includes(tryKey)){
v++;
continue;
}
newKey = tryKey;
break;
}
this.updateLibraries({
...this.state.cache.contents.uses,
[newKey]: ''
});
}}
>
Use Another Library
</Button>
</FormGroup>
</Form>
);
}
}
export default connect(mapStateToProps)(LibraryEditor);
......@@ -99,6 +99,10 @@ export const nameValidator = (be: BeatEntity, allNames?: string[] = []) => (name
export type ValidatorObjectField = boolean | boolean[];
export type ValidatorObject = AlgorithmValidatorObject | LibraryValidatorObject;
export type ValidatorFunc = (obj: BeatObject, allObjs: BeatObject[]) => ValidatorObject;
export type AlgorithmValidatorObject = {
// is algorithm name valid?
name: ValidatorObjectField,
......@@ -114,10 +118,6 @@ export type AlgorithmValidatorObject = {
result0Exists: ValidatorObjectField,
};
export type ValidatorObject = AlgorithmValidatorObject;
export type ValidatorFunc = (obj: BeatObject, allObjs: BeatObject[]) => ValidatorObject;
export const algorithmValidator: ValidatorFunc = (obj: BeatObject, allObjs: BeatObject[]) => {
const contentsExists = !!obj.contents;
const endpoint0Exists = contentsExists && !!obj.contents.groups[0];
......@@ -131,3 +131,17 @@ export const algorithmValidator: ValidatorFunc = (obj: BeatObject, allObjs: Beat
result0Exists: contentsExists && obj.contents.results && Object.keys(obj.contents.results).length > 0,
};
};
export type LibraryValidatorObject = {
// is library name valid?
name: ValidatorObjectField,
// is language valid?
language: ValidatorObjectField,
};
export const libraryValidator: ValidatorFunc = (obj: BeatObject, allObjs: BeatObject[]) => {
return {
name: nameValidator('library', allObjs.map(o => o.name))(obj.name),
language: obj.contents.language === 'python',
};
};
......@@ -10,3 +10,9 @@ export const objToArr = (data: any): any[] => {
.reduce((a: any[], objs: any[]) => [...a, ...objs], [])
;
};
export const changeObjFieldName = (obj: {}, oldName: string, newName: string): {} => {
return Object.entries(obj)
.map(([name, val]) => [name === oldName ? newName : name, val])
.reduce((o, [name, val]) => ({...o, [name]: val}), {});
};
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