EntityDetail.jsx 7.17 KB
Newer Older
Jaden's avatar
Jaden committed
1
// @flow
2
import * as React from 'react';
Jaden's avatar
Jaden committed
3
4
5
6
import {
	Container,
	Row,
	Col,
7
8
9
10
11
	TabContent,
	TabPane,
	Nav,
	NavItem,
	NavLink,
12
13
14
	Input,
	InputGroupAddon,
	InputGroup,
15
	Button,
Jaden's avatar
Jaden committed
16
17
} from 'reactstrap';

18
19
20
21
import { connect } from 'react-redux';

import cn from 'classnames';

22
import * as Actions from '@store/actions.js';
23
import * as Selectors from '@store/selectors.js';
Jaden's avatar
Jaden committed
24
25
26
27
28
29

import {
	Route,
	Link
} from 'react-router-dom';

30
import type { BeatObject, BeatEntity } from '@helpers/beat';
31
import { getDefaultEntityObject, pluralize } from '@helpers/beat';
32
import { genModuleApiFuncs } from '@helpers/api';
33
import ValidSchemaBadge from './ValidSchemaBadge.jsx';
34

35
36
37
38
39
40
import DataformatEditorContainer from './dataformat';
import AlgorithmEditorContainer from './algorithm';
import LibraryEditorContainer from './library';
import DatabaseEditorContainer from './database';
import ExperimentEditorContainer from './experiment';
import ToolchainEditorContainer from './toolchain';
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
41
import PlotterEditorContainer from './plotter';
42
import PlotterparameterEditorContainer from './plotterparameter';
43

Jaden's avatar
Jaden committed
44
type Props = {
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
45
46
	// the match object from react-router
	// used to find which object to render
47
	match: any,
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
48
49
	// the history object from react-router
	// used to push new history to
50
	history: any,
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
51
	// gets the object based on the current URL
52
	getEntityObject: () => BeatObject,
53
54
	// gets the object's index # in the Redux array based on the current URL
	getEntityIndex: () => number,
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
55
	// updates the current object
56
	updateFunc: (BeatObject) => any,
57
58
	// the current BEAT entity being show
	entity: BeatEntity,
59
60
	// the absolute path to the user's prefix folder
	prefix: string,
Jaden's avatar
Jaden committed
61
62
};

Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
63
// 3 tabs so far: editor, docs, raw json
64
type Tab = '0' | '1' | '2';
Jaden's avatar
Jaden committed
65

66
type State = {
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
67
	// which tab is active
68
69
	activeTab: Tab,
};
70

Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
71
72
// wrapper for editors
// doesnt really do much now besides the raw json tab
73
export class EntityDetail extends React.Component<Props, State> {
74
75
	constructor(props: Props){
		super(props);
76
77
	}

78
79
80
	state = {
		activeTab: '0',
	}
81

82
83
84
85
86
87
	switchToTab = (tab: Tab) => {
		this.setState({
			activeTab: tab
		});
	}

88
89
	saveChanges = (newObj: BeatObject) => {
		this.props.updateFunc(newObj);
90
91
		const put = genModuleApiFuncs(this.props.entity).put;
		return put([newObj]);
92
93
	}

94
	render () {
95
		const name = this.props.match.params.name;
96
97
		const obj = this.props.getEntityObject();
		const index = this.props.getEntityIndex();
98
99
100
101
102
103
		let expName;
		if(this.props.entity === 'experiment'){
			const segs = name.split('/');
			const usern = segs.shift();
			const expn = segs.pop();
			const tcn = segs.join('/');
104
			expName = <span>{ usern }/<Link to={`/toolchain/${ tcn }`}>{ tcn }</Link>/{ expn }</span>;
105
		}
106
		return (
107
			<Container>
108
				<Row className='mb-1'>
109
					<Col>
110
111
						{/* title line (name & validation info) */}
						<h4 className='text-center'>
112
							<span style={{'textTransform': 'capitalize'}}>
113
								{ this.props.entity }
114
115
116
							</span>
							{' '}
							<pre style={{display: 'inline'}}>
117
								{ expName || name }
118
							</pre>
119
							<ValidSchemaBadge entity={this.props.entity} obj={obj} />
120
						</h4>
121
122
123
124
						{/* path line */}
						<InputGroup>
							<InputGroupAddon addonType='prepend'>Path:</InputGroupAddon>
							<Input
125
								id='objectPath'
126
127
128
129
								readOnly
								width='80'
								value={`${ this.props.prefix }/${ pluralize(this.props.entity) }/${ name }`}
							/>
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
							<InputGroupAddon addonType='append'>
								<Button
									outline
									color='secondary'
									title={`Copy the object's path to the clipboard`}
									onClick={(e) => {
										const toCopy = document.querySelector('#objectPath');
										toCopy.select();
										document.execCommand('copy');
									}}
								>
									{/*UTF8 char for the clipboard: 📋*/}
									Copy Path
								</Button>
							</InputGroupAddon>
145
						</InputGroup>
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
					</Col>
				</Row>
				<Row>
					<Col sm='12'>
						<Nav tabs className='nav-fill'>
							<NavItem>
								<NavLink
									className={cn({ active: this.state.activeTab === '0' })}
									onClick={() => this.switchToTab('0')}
								>
									Editor
								</NavLink>
							</NavItem>
							<NavItem>
								<NavLink
									className={cn({ active: this.state.activeTab === '1' })}
									onClick={() => this.switchToTab('1')}
								>
									Raw JSON
								</NavLink>
							</NavItem>
						</Nav>
168
						<TabContent activeTab={this.state.activeTab} className='mt-2'>
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
169
							{/* Uses the appropriate editor, could probably be done nicer */}
170
171
							<TabPane tabId='0'>
								{
172
									this.props.entity === 'algorithm' &&
173
										<AlgorithmEditorContainer
174
											saveFunc={this.saveChanges}
175
											index={index}
176
177
178
										/>
								}
								{
179
									this.props.entity === 'dataformat' &&
180
										<DataformatEditorContainer
181
											saveFunc={this.saveChanges}
182
											index={index}
183
184
185
										/>
								}
								{
186
									this.props.entity === 'library' &&
187
										<LibraryEditorContainer
188
											saveFunc={this.saveChanges}
189
											index={index}
190
191
										/>
								}
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
192
								{
193
									this.props.entity === 'database' &&
194
										<DatabaseEditorContainer
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
195
											saveFunc={this.saveChanges}
196
											index={index}
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
197
198
										/>
								}
199
								{
200
									this.props.entity === 'experiment' &&
201
202
										<ExperimentEditorContainer
											saveFunc={this.saveChanges}
203
											index={index}
204
205
										/>
								}
206
								{
207
									this.props.entity === 'toolchain' &&
208
209
										<ToolchainEditorContainer
											saveFunc={this.saveChanges}
210
											index={index}
211
212
										/>
								}
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
213
								{
214
									this.props.entity === 'plotter' &&
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
215
216
										<PlotterEditorContainer
											saveFunc={this.saveChanges}
217
											index={index}
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
218
219
										/>
								}
220
								{
221
									this.props.entity === 'plotterparameter' &&
222
223
										<PlotterparameterEditorContainer
											saveFunc={this.saveChanges}
224
											index={index}
225
226
										/>
								}
227
228
							</TabPane>
							<TabPane tabId='1'>
229
								<pre>{ JSON.stringify(obj, null, 4) }</pre>
230
231
232
233
234
							</TabPane>
						</TabContent>
					</Col>
				</Row>
			</Container>
235
236
237
238
		);
	}
}

239
const mapStateToProps = (state, ownProps) => {
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
240
	// which beat entity is currently active: 'algorithm', 'plotter', 'database', etc.
241
	const entity = ownProps.match.params.entity;
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
242
	// the name of the object ("atnt/1", "plot/isoroc/1", "user/tc/test/1/exp", etc.)
243
244
	const name = ownProps.match.params.name;
	const obj = {
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
245
246
		// uses a selector based off the entity and finds the obj given the name
		// if the obj doesnt exist (huge edge case) just return a default obj to not break everything
247
		getEntityObject: (): BeatObject => Selectors[`${ entity }Get`](state).find(o => o.name === name) || getDefaultEntityObject(),
248
		getEntityIndex: (): number => Selectors[`${ entity }Get`](state).findIndex(o => o.name === name),
249
		entity,
250
		prefix: Selectors.settingsGet(state).prefix,
251
252
253
254
255
	};

	return obj;
};

256
const mapDispatchToProps = (dispatch, ownProps) => ({
Jaden DIEFENBAUGH's avatar
Jaden DIEFENBAUGH committed
257
	// replace the obj in the Redux store with the new object
258
259
260
261
262
263
	updateFunc: (obj) => {
		dispatch(Actions[`${ ownProps.match.params.entity }Update`](ownProps.match.params.name, obj));
		ownProps.history.push(`/${ ownProps.match.params.entity }/${ obj.name }`);
	},
});

264
export default connect(mapStateToProps, mapDispatchToProps)(EntityDetail);