2 * STANDARD_RIFT_IO_COPYRIGHT
5 import React from 'react';
6 import ReactDOM from 'react-dom';
7 import AppHeader from 'widgets/header/header.jsx';
8 import RedundancyStore from './redundancyStore.js';
9 import SkyquakeComponent from 'widgets/skyquake_container/skyquakeComponent.jsx';
10 import SkyquakeRBAC from 'widgets/skyquake_rbac/skyquakeRBAC.jsx';
11 import 'style/layout.scss';
12 import {Panel, PanelWrapper} from 'widgets/panel/panel';
13 import {InputCollection, FormSection} from 'widgets/form_controls/formControls.jsx';
15 import TextInput from 'widgets/form_controls/textInput.jsx';
16 import Input from 'widgets/form_controls/input.jsx';
17 import Button, {ButtonGroup} from 'widgets/button/sq-button.jsx';
18 import SelectOption from 'widgets/form_controls/selectOption.jsx';
19 import 'widgets/form_controls/formControls.scss';
20 import imgAdd from '../../node_modules/open-iconic/svg/plus.svg'
21 import imgRemove from '../../node_modules/open-iconic/svg/trash.svg'
22 import _ from 'lodash';
23 import ROLES from 'utils/roleConstants.js';
25 import './redundancy.scss';
26 const PLATFORM = ROLES.PLATFORM;
28 class SiteManagementDashboard extends React.Component {
31 this.Store = this.props.flux.stores.hasOwnProperty('RedundancyStore') ? this.props.flux.stores.RedundancyStore : this.props.flux.createStore(RedundancyStore, 'RedundancyStore');
32 this.state = this.Store.getState();
33 this.actions = this.state.actions;
35 componentDidUpdate() {
37 ReactDOM.findDOMNode(this.siteList).addEventListener('transitionend', this.onTransitionEnd, false);
38 setTimeout(function() {
39 let element = self[`site-ref-${self.state.activeIndex}`]
40 element && !isElementInView(element) && element.scrollIntoView({block: 'end', behavior: 'smooth'});
43 componentWillMount() {
44 this.state = this.Store.getState();
45 this.Store.getRedundancy();
46 this.Store.listen(this.updateState);
48 componentWillUnmount() {
49 this.Store.unlisten(this.updateState);
51 updateState = (state) => {
54 updateInput = (key, e) => {
56 let siteData = this.state.siteData;
57 siteData[property] = e.target.value;
58 this.actions.handleUpdateInput({
62 updateServiceTargetInput = (serviceName, key, e) => {
63 let state = this.state;
64 let siteData = this.state.siteData;
65 let value = e.target.value;
66 let index = _.findIndex(state.siteData['target-endpoint'], function(o) {
67 return o.name == serviceName
69 if((index == undefined) || index == -1) {
70 index = siteData['target-endpoint'].push({name: serviceName}) - 1
72 siteData['target-endpoint'][index]['name'] = serviceName;
73 if(value.trim() == '') {
74 delete siteData['target-endpoint'][index][key];
76 siteData['target-endpoint'][index][key] = value
78 this.actions.handleUpdateInput({
82 updateInstanceInput = (index, key, e) => {
83 let siteData = this.state.siteData;
84 siteData['rw-instances'][index][key] = e.target.value;
85 this.actions.handleUpdateInput({
89 updateInstanceInputEndpoint = (index, serviceName, key, e) => {
90 let siteData = this.state.siteData;
91 let state = this.state;
92 let value = e.target.value;
93 let listIndex = _.findIndex(siteData['rw-instances'][index].endpoint, function(o) {
94 return o.name == serviceName
96 if(!siteData['rw-instances'][index].endpoint) {
97 siteData['rw-instances'][index].endpoint = []
99 if(listIndex == undefined || listIndex == -1) {
100 listIndex = siteData['rw-instances'][index].endpoint.push({name: serviceName}) - 1;
102 if(value.trim() == '') {
103 delete siteData['rw-instances'][index].endpoint[listIndex][key];
105 siteData['rw-instances'][index].endpoint[listIndex][key] = value
107 siteData['rw-instances'][index].endpoint[listIndex]['name'] = serviceName;
108 this.actions.handleUpdateInput({
113 this.actions.handleAddSite();
115 viewSite = (un, index) => {
116 this.actions.viewSite(un, index, true);
119 this.actions.editSite(false);
121 cancelEditSite = () => {
122 this.actions.editSite(true)
125 this.actions.handleCloseSitePanel();
128 deleteSite = (e) => {
131 if (confirm('Are you sure you want to delete this site?')) {
132 this.Store.deleteSite({
133 'site-name': this.state.siteData['site-name']
137 createSite = (e) => {
141 let siteData = self.state.siteData;
142 if (this.validateFields(self, siteData)) {
143 this.Store.createSite(siteData);
146 updateSite = (e) => {
150 let siteData = self.state.siteData;
151 if (this.validateFields(self, siteData)) {
152 this.Store.updateSite(siteData);
155 validateFields(self, siteData) {
156 if (!siteData['site-name'] || siteData['site-name'].trim() == '') {
157 self.props.flux.actions.global.showNotification("Please enter a site name");
160 let instanceInvalid = false;
161 siteData['rw-instances'] && siteData['rw-instances'].map(function(rw) {
162 if (!rw['rwinstance-id'] || rw['rwinstance-id'].trim() == '' ) {
163 instanceInvalid = true;;
166 if (instanceInvalid) {
167 self.props.flux.actions.global.showNotification("One or more of your RIFT.WARE Instances is missing it's FQDN/IP Address");
172 evaluateSubmit = (e) => {
173 if (e.keyCode == 13) {
174 if (this.props.isEdit) {
183 onTransitionEnd = (e) => {
184 this.actions.handleHideColumns(e);
185 console.log('transition end')
190 let props = this.props;
191 let state = this.state;
192 let passwordSectionHTML = null;
193 let formButtonsHTML = (
194 <ButtonGroup className="buttonGroup">
195 <Button label="EDIT" type="submit" onClick={this.editSite} />
198 if(!this.state.isReadOnly) {
202 <ButtonGroup className="buttonGroup">
203 <Button label="Update" type="submit" onClick={this.updateSite} />
204 <Button label="Delete" onClick={this.deleteSite} />
205 <Button label="Cancel" onClick={this.cancelEditSite} />
209 <ButtonGroup className="buttonGroup">
210 <Button label="Create" type="submit" onClick={this.createSite} />
217 <PanelWrapper column>
218 <AppHeader nav={[{ name: 'SITES' }, { name: 'CONFIG', onClick: this.context.router.push.bind(this, { pathname: '/config' }) }, { name: 'STATUS', onClick: this.context.router.push.bind(this, { pathname: '/status' }) }]} />
219 <PanelWrapper className={`row siteManagement ${!this.state.siteOpen ? 'siteList-open' : ''}`} style={{'alignContent': 'center', 'flexDirection': 'row'}} >
221 <PanelWrapper ref={(div) => { this.siteList = div}} className={`column siteList expanded ${this.state.siteOpen ? 'collapsed ' : ' '} ${this.state.hideColumns ? 'hideColumns ' : ' '}`}>
222 <Panel title="Sites" style={{marginBottom: 0}} no-corners>
223 <div className="tableRow tableRow--header">
224 <div className="siteName">
231 {state.sites && state.sites.map((u, k) => {
233 <div onClick={self.viewSite.bind(null, u, k)} ref={(el) => this[`site-ref-${k}`] = el} className={`tableRow tableRow--data ${((self.state.activeIndex == k) && self.state.siteOpen) ? 'tableRow--data-active' : ''}`} key={k}>
235 className={`siteName siteName-header ${((self.state.activeIndex == k) && self.state.siteOpen) ? 'activeSite' : ''}`}
240 {u['rw-instances'] && u['rw-instances'].length}
247 <SkyquakeRBAC className="rbacButtonGroup">
248 <ButtonGroup className="buttonGroup">
249 <Button label="Add Site" onClick={this.addSite} />
253 <PanelWrapper onKeyUp={this.evaluateSubmit}
254 className={`SiteAdmin column`}>
256 title={state.isEdit ? state.siteData['site-name'] : 'Create Site'}
257 style={{marginBottom: 0}}
258 hasCloseButton={this.closePanel}
260 <FormSection title="SITE INFO">
262 (state.isEditSite || state.isReadOnly) ?
263 <Input readonly={state.isReadOnly || this.state.isEdit} required label="Name" value={state.siteData['site-name']} onChange={this.updateInput.bind(null, 'site-name')} />
266 <TextInput readonly={state.isReadOnly}
267 label='FQDN/IP Address'
268 pattern={state.siteIdPattern}
269 value={state.siteData['site-id']}
270 onChange={this.updateInput.bind(null, 'site-id')}
274 <FormSection className="subSection" title="Service Endpoints">
275 <TextInput type="text" readonly={state.isReadOnly} onChange={self.updateServiceTargetInput.bind(self, 'ui-service', 'port')} label="UI Port" value={state.siteData['target-endpoint'] && state.siteData['target-endpoint'][ _.findIndex(state.siteData['target-endpoint'], function(o) { return o.name == 'ui-service' })] && state.siteData['target-endpoint'][ _.findIndex(state.siteData['target-endpoint'], function(o) { return o.name == 'ui-service' })].port} />
276 <TextInput type="text" readonly={state.isReadOnly} onChange={self.updateServiceTargetInput.bind(self, 'rest-service', 'port')} label="REST Port" value={state.siteData['target-endpoint'] && state.siteData['target-endpoint'][ _.findIndex(state.siteData['target-endpoint'], function(o) { return o.name == 'rest-service' })] && state.siteData['target-endpoint'][ _.findIndex(state.siteData['target-endpoint'], function(o) { return o.name == 'rest-service' })].port} />
280 <FormSection title="RIFT.WARE INSTANCES">
282 state.siteData['rw-instances'] && state.siteData['rw-instances'].map(function(t, i) {
283 return <div key={i} className="rwInstance">
285 <span className="title">INSTANCE</span>
287 (state.isReadOnly) ? null :
289 onClick={self.actions.handleRemoveInstance.bind(null, {index: i})}
290 className="removeInput">
293 style={{marginBottom: '0px'}}/>
298 <TextInput type="text"
299 label="FQDN/IP Address"
301 pattern={state.siteIdPattern}
302 readonly={!(t.isNew && (!state.isReadOnly))}
303 onChange={self.updateInstanceInput.bind(self, i, 'rwinstance-id')}
304 value={state.siteData['rw-instances'][i]['rwinstance-id']} />
305 <TextInput type="text"
307 readonly={state.isReadOnly}
308 onChange={self.updateInstanceInput.bind(self, i, 'floating-ip')}
309 value={state.siteData['rw-instances'][i]['floating-ip']} />
310 <FormSection className="subSection" title="Service Endpoints">
311 <TextInput type="text"
313 readonly={state.isReadOnly}
314 onChange={self.updateInstanceInputEndpoint.bind(self, i, 'ui-service', 'port')}
315 value={state.siteData['rw-instances'] && state.siteData['rw-instances'][i] && state.siteData['rw-instances'][i].endpoint && state.siteData['rw-instances'][i].endpoint[ _.findIndex(state.siteData['rw-instances'][i].endpoint, function(o) { return o.name == 'ui-service' })
316 ] && state.siteData['rw-instances'][i].endpoint[ _.findIndex(state.siteData['rw-instances'][i].endpoint, function(o) { return o.name == 'ui-service' })
319 <TextInput type="text"
321 readonly={state.isReadOnly}
322 onChange={self.updateInstanceInputEndpoint.bind(self, i,'rest-service', 'port')}
323 value={state.siteData['rw-instances'] && state.siteData['rw-instances'][i] && state.siteData['rw-instances'][i].endpoint && state.siteData['rw-instances'][i].endpoint[
324 _.findIndex(state.siteData['rw-instances'][i].endpoint, function(o) { return o.name == 'rest-service' })
325 ] && state.siteData['rw-instances'][i].endpoint[ _.findIndex(state.siteData['rw-instances'][i].endpoint, function(o) { return o.name == 'rest-service' })
333 (state.isReadOnly) ? null :
334 <span onClick={self.actions.handleAddInstance} className="addInput" ><img src={imgAdd} />Add Instance</span>
341 <SkyquakeRBAC allow={[PLATFORM.SUPER, PLATFORM.ADMIN]} site={this.state.name} className="rbacButtonGroup">
351 // onClick={this.Store.update.bind(null, Account)}
352 SiteManagementDashboard.contextTypes = {
353 router: React.PropTypes.object,
354 userProfile: React.PropTypes.object
357 SiteManagementDashboard.defaultProps = {
362 export default SkyquakeComponent(SiteManagementDashboard);
365 function isElementInView(el) {
366 var rect = el && el.getBoundingClientRect() || {};
371 rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) && /*or $(window).height() */
372 rect.right <= (window.innerWidth || document.documentElement.clientWidth) /*or $(window).width() */
377 // isReadOnly={state.isReadOnly} disabled={state.disabled} onChange={this.disableChange}
379 class isDisabled extends React.Component {
384 let props = this.props;
389 function showInput(e){
390 let target = e.target;
391 if(target.parentElement.classList.contains("addInput")) {
392 target = target.parentElement;
394 target.style.display = 'none';
395 target.parentElement.nextElementSibling.style.display = 'flex';
396 // e.target.parentElement.nextElementSibling.children[1].style.display = 'initial';
398 function hideInput(e){
399 let target = e.target;
400 if(target.parentElement.classList.contains("removeInput")) {
401 target = target.parentElement;
403 target.parentElement.style.display = 'none';
404 target.parentElement.previousElementSibling.children[1].style.display = 'inline';
405 target.previousSibling.value = '';