daily pastebin goal
40%
SHARE
TWEET

Untitled

a guest Mar 20th, 2019 66 Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  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);
RAW Paste Data
We use cookies for various purposes including analytics. By continuing to use Pastebin, you agree to our use of cookies as described in the Cookies Policy. OK, I Understand
 
Top