Advertisement
Guest User

Untitled

a guest
Mar 20th, 2019
81
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 23.83 KB | None | 0 0
  1. import * as React from 'react';
  2. import { Component } from 'react';
  3. import { RouteComponentProps } from 'react-router';
  4. import { Dispatch } from 'redux';
  5. import { connect } from 'react-redux';
  6. import { Link } from 'react-router-dom';
  7. import { replace } from 'react-router-redux';
  8. import * as _ from 'lodash';
  9.  
  10. import AddDataSourceToolbar from '../../components/admin/AddDataSourceToolbar';
  11. import Loading from '../../components/common/Loading';
  12. import { RootState, IStorybook } from '../../types';
  13. import { SharedModels } from '../../types/Models';
  14. import {
  15. createStorybook,
  16. updateStorybook,
  17. StorybookPostParams,
  18. generateSecurityKey,
  19. getStorybooksSettings
  20. } from '../../state/storybooks/StorybooksActions';
  21. import { getStorybookIdFromPath, getStorybookFromId, getStorybook } from '../../state/storybook/StorybookSelector';
  22. import { refreshToken } from '../../state/session/SessionActions';
  23. import { SharedEnums } from '../../types/Enums';
  24. import { revealTestIsSuccessStateSelector, revealTestIsFetchingStateSelector } from '../../state/reveal/RevealSelector';
  25. import { fetchDataConnections } from '../../state/databaseServer/databaseServerActions';
  26. import { getServerConnections } from '../../state/databaseServer/databaseServerSelector';
  27. import { fetchRevealApiTest, clearRevealApiTest, fetchStorybook } from '../../state/actions';
  28.  
  29. interface RouteMatchProps {
  30. storybookId: number | null;
  31. }
  32.  
  33. interface AddStorybookProps {
  34. storybookId: number;
  35. storybook: IStorybook | null;
  36. storybooks: IStorybook[];
  37. connections: SharedModels.DatabaseServerModelRequest[];
  38. isCreating: boolean;
  39. isUpdating: boolean;
  40. actionError: string | null;
  41. settings: SharedModels.StoryBookCommonSettings | null;
  42. isTestSuccess: boolean | null;
  43. isTestFetching: boolean;
  44. }
  45.  
  46. interface AddStorybookDispatchProps {
  47. // tslint:disable-next-line:no-any
  48. createStorybook: (data: StorybookPostParams) => Promise<any>;
  49. // tslint:disable-next-line: no-any
  50. updateStorybook: (storybook: IStorybook) => Promise<any>;
  51. fetchStorybook: (storybookId: number, cosmicGroupId?: number) => void;
  52. fetchDataConnections: () => void;
  53. getStorybooksSettings: () => void;
  54. redirect: (destination: string) => void;
  55. refreshAuthenticationToken: () => void;
  56. // tslint:disable-next-line:no-any
  57. generateSecurityKey: () => Promise<any>;
  58. testRevealApi: (url: string) => void;
  59. clearRevealApiTest: () => void;
  60. }
  61.  
  62. interface AddStorybookState {
  63. storybookName: string;
  64. databaseName: string;
  65. serverId: number;
  66. externalIntegrationType: string;
  67. autoGenerate: boolean;
  68. supportDashboard: boolean;
  69. defaultLayoutId: number | null;
  70. key1: string;
  71. key2: string;
  72. externalKey: string;
  73. externalUrl: string;
  74. lastUpdatedStorybook: IStorybook | null;
  75. createSuccess: boolean | null;
  76. updateSuccess: boolean | null;
  77. revealFieldsIsRequired: boolean;
  78. validationError: string | null;
  79. }
  80.  
  81. class AddStorybook extends Component<AddStorybookProps & RouteComponentProps<RouteMatchProps> & AddStorybookDispatchProps, AddStorybookState> {
  82. private titleContainer: HTMLHeadingElement | null;
  83.  
  84. constructor(props: AddStorybookProps & RouteComponentProps<RouteMatchProps> & AddStorybookDispatchProps) {
  85. super(props);
  86.  
  87. const externalIntegrationType = props.storybook && props.storybook.externalIntegrationType
  88. ? props.storybook.externalIntegrationType : SharedEnums.ExternalIntegrationType.NONE;
  89. const revealFieldsIsRequired = externalIntegrationType === SharedEnums.ExternalIntegrationType.REVEAL;
  90.  
  91. this.state = {
  92. storybookName: props.storybook ? props.storybook.name : '',
  93. databaseName: '',
  94. serverId: -1,
  95. externalIntegrationType,
  96. autoGenerate: false,
  97. supportDashboard: props.storybook ? !!props.storybook.supportDashboard : false,
  98. defaultLayoutId: props.storybook ? props.storybook.defaultLayoutId : 0,
  99. key1: props.storybook ? props.storybook.key1 : '',
  100. key2: props.storybook ? props.storybook.key2 : '',
  101. lastUpdatedStorybook: null,
  102. createSuccess: null,
  103. updateSuccess: null,
  104. validationError: null,
  105. revealFieldsIsRequired: revealFieldsIsRequired,
  106. externalUrl: props.storybook && props.storybook.externalUrl ? props.storybook.externalUrl : '',
  107. externalKey: props.storybook && props.storybook.externalKey ? props.storybook.externalKey : '',
  108. };
  109. }
  110.  
  111. componentDidMount() {
  112. this.props.fetchDataConnections();
  113. this.props.getStorybooksSettings();
  114. }
  115.  
  116. componentWillUnmount() {
  117. this.props.clearRevealApiTest();
  118. }
  119.  
  120. componentDidUpdate(prevProps: AddStorybookProps & AddStorybookDispatchProps) {
  121. const { isCreating, isUpdating, storybooks, storybook, storybookId } = this.props;
  122. if (prevProps.isCreating && !isCreating) {
  123. if (!_.isEqual(storybooks, prevProps.storybooks) && storybooks.length > 0) {
  124. this.setState({ createSuccess: true, lastUpdatedStorybook: _.last(storybooks) || null });
  125. } else {
  126. this.setState({ createSuccess: false });
  127. }
  128. }
  129. if (prevProps.isUpdating && !isUpdating) {
  130. if (!_.isEqual(storybooks, prevProps.storybooks) && storybooks.length > 0) {
  131. this.setState({
  132. updateSuccess: true,
  133. lastUpdatedStorybook: (storybookId === -1 ? _.last(storybooks) : storybooks.find(s => s.id === storybookId)) || null,
  134. });
  135. } else {
  136. this.setState({ updateSuccess: false });
  137. }
  138. }
  139. if (storybookId !== -1 && !prevProps.storybook && storybook) {
  140. const externalIntegrationType = storybook.externalIntegrationType
  141. ? storybook.externalIntegrationType : SharedEnums.ExternalIntegrationType.NONE;
  142.  
  143. this.setState({
  144. storybookName: storybook.name,
  145. supportDashboard: !!storybook.supportDashboard,
  146. defaultLayoutId: storybook.defaultLayoutId || 0,
  147. key1: storybook.key1,
  148. key2: storybook.key2,
  149. externalIntegrationType,
  150. revealFieldsIsRequired: externalIntegrationType === SharedEnums.ExternalIntegrationType.REVEAL,
  151. externalKey: storybook.externalKey,
  152. externalUrl: storybook.externalUrl
  153. });
  154. }
  155. }
  156.  
  157. onCreateNewKey = (keyName: 'key1' | 'key2') => {
  158. this.props.generateSecurityKey()
  159. .then(res => (keyName === 'key1' ? this.setState({ key1: res.key }) : this.setState({ key2: res.key })));
  160. }
  161.  
  162. onSave = () => {
  163. const { storybookId, storybook } = this.props;
  164. const { storybookName, databaseName, serverId, externalIntegrationType, supportDashboard, defaultLayoutId, key1, key2, lastUpdatedStorybook, autoGenerate,
  165. revealFieldsIsRequired, externalKey, externalUrl } = this.state;
  166. let errored = false;
  167. if (storybookId !== -1) {
  168. if (storybookName && !_.isNil(defaultLayoutId) && !_.isNaN(defaultLayoutId) && key1 && key2 && (!revealFieldsIsRequired || externalKey)
  169. && (!revealFieldsIsRequired || externalUrl)) {
  170. this.setState({ updateSuccess: null, validationError: null });
  171. this.props.updateStorybook({
  172. ...(lastUpdatedStorybook || storybook)!,
  173. name: storybookName,
  174. supportDashboard,
  175. defaultLayoutId,
  176. key1,
  177. key2,
  178. externalIntegrationType,
  179. externalKey,
  180. externalUrl
  181. }).then(() => this.props.fetchStorybook(storybookId));
  182. } else {
  183. errored = true;
  184. }
  185. } else {
  186. if (storybookName && (autoGenerate || databaseName) && serverId !== -1) {
  187. const server = this.props.connections.find(c => c.id === serverId)!;
  188. this.setState({ validationError: null, createSuccess: null, updateSuccess: null });
  189. const data = {
  190. name: storybookName,
  191. databaseServerId: serverId,
  192. databaseServer: server.name,
  193. outputDatabaseName: databaseName,
  194. };
  195. if (lastUpdatedStorybook) {
  196. this.props.updateStorybook({ ...lastUpdatedStorybook!, ...data });
  197. } else {
  198. this.props.createStorybook(data).then((res) => {
  199. if (res) {
  200. if (res.storybook && res.storybook.outputDatabaseName) {
  201. this.setState({
  202. databaseName: res.storybook.outputDatabaseName
  203. });
  204. }
  205. this.props.refreshAuthenticationToken();
  206. }
  207.  
  208. });
  209. }
  210. } else {
  211. errored = true;
  212. }
  213. }
  214. if (errored) {
  215. this.setState({ validationError: 'Please enter values for required fields (*)' });
  216. if (this.titleContainer) {
  217. this.titleContainer.scrollIntoView({ behavior: 'smooth' });
  218. }
  219. }
  220. }
  221.  
  222. render() {
  223. const { connections, isCreating, isUpdating, storybookId, actionError, isTestSuccess, isTestFetching } = this.props;
  224. const {
  225. storybookName, databaseName, serverId, externalIntegrationType, autoGenerate, supportDashboard, defaultLayoutId,
  226. key1, key2, lastUpdatedStorybook, createSuccess, updateSuccess, validationError, externalKey, revealFieldsIsRequired,
  227. externalUrl
  228. } = this.state;
  229. const isAdd = storybookId === -1;
  230. return (
  231. <div className="admin-primary admin-add-storybook">
  232. {(isCreating || isUpdating) && <div style={{ float: 'right' }}><Loading large={true} /></div>}
  233. {!_.isNil(createSuccess) && !isCreating && !validationError &&
  234. <div className={`result-alert ${createSuccess ? 'success' : 'fail'}`}>
  235. {createSuccess ? 'Storybook created successfully.' : actionError}
  236. </div>
  237. }
  238. {!_.isNil(updateSuccess) && !isUpdating && !validationError &&
  239. <div className={`result-alert ${updateSuccess ? 'success' : 'fail'}`}>
  240. {updateSuccess ? 'Storybook updated successfully.' : actionError}
  241. </div>
  242. }
  243. {validationError &&
  244. <div className="result-alert fail">
  245. {validationError}
  246. </div>
  247. }
  248.  
  249. <h2 ref={e => this.titleContainer = e}>{isAdd ? 'Add' : 'Edit'} storybook</h2>
  250.  
  251. <p>*required</p>
  252.  
  253. <div className="form-row">
  254. <div className="form-label">Name<span title="required">*</span></div>
  255. <div className={`form-field${validationError && !storybookName ? ' has-error' : ''}`}>
  256. <input
  257. className="form-control"
  258. type="text"
  259. value={storybookName}
  260. onChange={e => this.setState({
  261. storybookName: e.target.value,
  262. databaseName: !autoGenerate ? `NexLP_${e.target.value.replace(/\s/g, '_')}` : '',
  263. })}
  264. />
  265. </div>
  266. </div>
  267. {isAdd ? <>
  268. <div className="form-row">
  269. <div className="form-label">Storybook database name<span title="required">*</span></div>
  270. <div className={`form-field${validationError && (!autoGenerate && !databaseName) ? ' has-error' : ''}`}>
  271. <input
  272. className="form-control"
  273. type="text"
  274. value={databaseName}
  275. disabled={autoGenerate}
  276. onChange={e => this.setState({ databaseName: e.target.value })}
  277. />
  278. </div>
  279.  
  280. <label>
  281. <span
  282. onClick={() => this.setState({ autoGenerate: !autoGenerate, databaseName: !autoGenerate ? '' : `NexLP_${storybookName.replace(/\s/, '_')}`})}
  283. className="checkbox-option"
  284. >
  285. <i className={`far fa-fw ${autoGenerate ? 'fa-check-square' : 'fa-square'}`}/>
  286. &nbsp;Autogenerate
  287. </span>
  288. </label>
  289. </div>
  290.  
  291. <div className="form-row">
  292. <div className="form-label">Storybook database server<span title="required">*</span></div>
  293. <div className={`form-field${validationError && serverId === -1 ? ' has-error' : ''}`}>
  294. <select className="form-control" onChange={e => this.setState({ serverId: parseInt(e.target.value, 10) })} value={serverId}>
  295. <option value={-1}>Choose server...</option>
  296. {connections.map(connection => <option key={`connection-${connection.id}`} value={connection.id}>{connection.name}</option>)}
  297. </select>
  298. </div>
  299. </div>
  300. </> : <>
  301. <div className="form-row">
  302. <div className="form-label">External Integration Type</div>
  303. <div className={`form-field${validationError && !externalIntegrationType ? ' has-error' : ''}`}>
  304. <select
  305. className="form-control"
  306. onChange={e => this.setState({
  307. externalIntegrationType: e.target.value,
  308. revealFieldsIsRequired: e.target.value === SharedEnums.ExternalIntegrationType.REVEAL
  309. })}
  310. value={externalIntegrationType}
  311. >
  312. <option value={SharedEnums.ExternalIntegrationType.NONE}>{SharedEnums.ExternalIntegrationType.NONE}</option>
  313. <option value={SharedEnums.ExternalIntegrationType.RELATIVITY}>{SharedEnums.ExternalIntegrationType.RELATIVITY}</option>
  314. <option value={SharedEnums.ExternalIntegrationType.REVEAL}>{SharedEnums.ExternalIntegrationType.REVEAL}</option>
  315. </select>
  316. </div>
  317. </div>
  318. <div className="form-row">
  319. <div className="form-label">External Key{revealFieldsIsRequired ? <span title="required">*</span> : null}</div>
  320. <div className={`form-field${validationError && !externalKey && revealFieldsIsRequired ? ' has-error' : ''}`}>
  321. <input
  322. className="form-control"
  323. type="text"
  324. value={externalKey}
  325. onChange={e => this.setState({
  326. externalKey: e.target.value
  327. })}
  328. />
  329. </div>
  330. </div>
  331. {revealFieldsIsRequired &&
  332. <>
  333. <div className="form-row">
  334. <div className="form-label">External Url<span title="required">*</span></div>
  335. <div className={`form-field${validationError && !externalUrl ? ' has-error' : ''}`}>
  336. <input
  337. className="form-control"
  338. type="text"
  339. value={externalUrl}
  340. onChange={e => this.setState({
  341. externalUrl: e.target.value
  342. })}
  343. />
  344. </div>
  345. </div>
  346. <div className="form-row">
  347. <div className="form-label" />
  348. <div className="form-field">
  349. <div className="test-connection-result">
  350. {isTestSuccess !== null && <>
  351. {isTestSuccess ? <>
  352. <span className="icon-color-yes">
  353. <i className="fas fa-check-circle" /> &nbsp;
  354. Success
  355. </span>
  356. </> : <>
  357. <span className="icon-color-no">
  358. <i className="fas fa-times-circle" /> &nbsp;
  359. Connection Error
  360. </span>
  361. </>}
  362. </>}
  363. </div>
  364.  
  365. <button className="btn btn-default pull-right" disabled={isTestFetching} onClick={() => this.props.testRevealApi(this.state.externalUrl)}>
  366. {isTestFetching && <Loading />} Test Connection
  367. </button>
  368. </div>
  369. </div>
  370.  
  371. </>}
  372. {this.props.settings && this.props.settings.dashboardSupported &&
  373. <><div className="form-row">
  374. <div className="form-label">Dashboard<span title="required">*</span></div>
  375. <div className="form-field">
  376. <label>
  377. <input type="checkbox" checked={supportDashboard} onChange={e => this.setState({ supportDashboard: e.target.checked })} />&nbsp;
  378. Enable dashboard
  379. </label>
  380. </div>
  381. </div>
  382. <div className="form-row">
  383. <div className="form-label">Dashboard layout ID<span title="required">*</span></div>
  384. <div className={`form-field${validationError && (_.isNil(defaultLayoutId) || _.isNaN(defaultLayoutId)) ? ' has-error' : ''}`}>
  385. <input
  386. className="form-control"
  387. type="text"
  388. value={(_.isNil(defaultLayoutId) || _.isNaN(defaultLayoutId)) ? '' : `${defaultLayoutId}`}
  389. onChange={e => this.setState({ defaultLayoutId: e.target.value ? parseInt(e.target.value, 10) : null })}
  390. />
  391. </div>
  392. </div></>}
  393. <div className="form-row keys-row">
  394. <div className="form-label">Security keys</div>
  395. <div className="form-field">
  396. <p>
  397. {key1}&nbsp;&nbsp;
  398. <a className="btn-link" onClick={() => this.onCreateNewKey('key1')}>Generate new key</a>
  399. </p>
  400. <p>
  401. {key2}&nbsp;&nbsp;
  402. <a className="btn-link" onClick={() => this.onCreateNewKey('key2')}>Generate new key</a>
  403. </p>
  404. </div>
  405. </div>
  406. </>
  407. }
  408. <div className="form-row">
  409. <div className="form-label" />
  410. <div className="form-field">
  411. <button className="btn btn-primary" onClick={this.onSave}>Save</button>&nbsp;
  412. <Link to="/settings/storybooks" className="btn btn-link">Cancel</Link>
  413. </div>
  414. </div>
  415.  
  416. {isAdd && lastUpdatedStorybook &&
  417. <>
  418. <hr />
  419. <AddDataSourceToolbar
  420. title="Add a data source"
  421. subtitle="Save this storybook and add a data source now or you can add one later."
  422. storybookId={lastUpdatedStorybook.id}
  423. />
  424. </>
  425. }
  426. </div>
  427. );
  428. }
  429. }
  430.  
  431. const mapStateToProps = (state: RootState, ownProps: RouteComponentProps<RouteMatchProps>) => {
  432. const storybookId = getStorybookIdFromPath(ownProps) || -1;
  433. return {
  434. storybookId,
  435. storybook: storybookId !== -1 ? getStorybookFromId(state)(storybookId) || getStorybook(state) : null,
  436. storybooks: state.storybooks.storybooks,
  437. connections: getServerConnections(state),
  438. isCreating: state.storybooks.isCreating,
  439. isUpdating: state.storybooks.isUpdating,
  440. actionError: state.storybooks.actionError,
  441. settings: state.storybooks.setttings,
  442. isTestSuccess: revealTestIsSuccessStateSelector(state),
  443. isTestFetching: revealTestIsFetchingStateSelector(state)
  444. };
  445. };
  446.  
  447. const mapDispatchToProps = (dispatch: Dispatch<RootState>) => {
  448. return {
  449. createStorybook: (data: StorybookPostParams) => dispatch(createStorybook(data)),
  450. updateStorybook: (storybook: IStorybook) => dispatch(updateStorybook(storybook)),
  451. fetchDataConnections: () => dispatch(fetchDataConnections()),
  452. redirect: (destination: string) => dispatch(replace(destination)),
  453. refreshAuthenticationToken: () => {
  454. dispatch(refreshToken);
  455. },
  456. generateSecurityKey: () => dispatch(generateSecurityKey()),
  457. getStorybooksSettings: () => dispatch(getStorybooksSettings()),
  458. testRevealApi: (url: string) => dispatch(fetchRevealApiTest(url)),
  459. clearRevealApiTest: () => dispatch(clearRevealApiTest()),
  460. fetchStorybook: (storybookId: number, cosmicGroupId?: number) => dispatch(fetchStorybook(storybookId, cosmicGroupId))
  461. };
  462. };
  463.  
  464. export default connect<AddStorybookProps, AddStorybookDispatchProps>(mapStateToProps, mapDispatchToProps)(AddStorybook);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement