ConnectionsList.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. /*
  2. Copyright (C) 2017 Cloudbase Solutions SRL
  3. This program is free software: you can redistribute it and/or modify
  4. it under the terms of the GNU Affero General Public License as
  5. published by the Free Software Foundation, either version 3 of the
  6. License, or (at your option) any later version.
  7. This program is distributed in the hope that it will be useful,
  8. but WITHOUT ANY WARRANTY; without even the implied warranty of
  9. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  10. GNU Affero General Public License for more details.
  11. You should have received a copy of the GNU Affero General Public License
  12. along with this program. If not, see <http://www.gnu.org/licenses/>.
  13. */
  14. import React, { PropTypes } from 'react';
  15. import Reflux from 'reflux';
  16. import withStyles from 'isomorphic-style-loader/lib/withStyles';
  17. import Location from '../../core/Location';
  18. import Dropdown from '../NewDropdown';
  19. import SearchBox from '../SearchBox';
  20. import Moment from 'react-moment';
  21. import s from './ConnectionsList.scss';
  22. import AddCloudConnection from '../AddCloudConnection';
  23. import Modal from 'react-modal';
  24. import ConnectionsStore from '../../stores/ConnectionsStore';
  25. import ConnectionsActions from '../../actions/ConnectionsActions';
  26. import TextTruncate from 'react-text-truncate';
  27. import UserIcon from '../UserIcon';
  28. import FilteredTable from '../FilteredTable';
  29. import EndpointUsage from '../EndpointUsage';
  30. import NotificationIcon from '../NotificationIcon';
  31. import ConfirmationDialog from '../ConfirmationDialog'
  32. import ProjectsDropdown from '../ProjectsDropdown';
  33. const title = 'Cloud Endpoints';
  34. const connectionTypes = [
  35. { label: "All", type: "all" },
  36. { label: "Oracle Cloud", type: "opc" },
  37. { label: "Oracle VM Server", type: "oracle_vm" },
  38. { label: "Openstack", type: "openstack" },
  39. { label: "VMware", type: "vmware_vsphere" }
  40. ]
  41. const connectionActions = [
  42. { label: "Delete", value: "delete" }
  43. ]
  44. class ConnectionsList extends Reflux.Component {
  45. constructor(props) {
  46. super(props)
  47. this.store = ConnectionsStore
  48. this.state = {
  49. showModal: false,
  50. queryText: '',
  51. filterType: 'all',
  52. selectedAll: false,
  53. searchMin: true,
  54. connections: null,
  55. confirmationDialog: {
  56. visible: false,
  57. message: "Are you sure?",
  58. onConfirm: null,
  59. onCancel: null
  60. }
  61. }
  62. }
  63. static contextTypes = {
  64. onSetTitle: PropTypes.func.isRequired,
  65. };
  66. componentWillMount() {
  67. super.componentWillMount.call(this)
  68. this.context.onSetTitle(title);
  69. if (this.state.connections == null) {
  70. ConnectionsActions.loadConnections()
  71. }
  72. }
  73. connectionsSelected() {
  74. let count = this.connectionsSelectedCount(),
  75. total = 0
  76. if (this.state.connections) {
  77. total = this.state.connections.length
  78. }
  79. return `${count} of ${total} connection(s) selected`;
  80. }
  81. connectionsSelectedCount() {
  82. let count = 0
  83. if (this.state.connections) {
  84. this.state.connections.forEach((item) => {
  85. if (item.selected) count++
  86. })
  87. }
  88. return count
  89. }
  90. connectionDetail(e, item) {
  91. Location.push('/cloud-endpoints/' + item.id + "/")
  92. }
  93. checkItem(e, itemRef) {
  94. let items = this.state.connections
  95. items.forEach((item) => {
  96. if (item == itemRef) {
  97. item.selected = !item.selected
  98. }
  99. })
  100. this.setState({ connections: items, selectedAll: false })
  101. }
  102. checkAll() {
  103. let items = this.state.connections
  104. let selectedAll = this.state.selectedAll
  105. items.forEach((item) => {
  106. item.selected = !selectedAll
  107. })
  108. this.setState({ connections: items, selectedAll: !selectedAll })
  109. }
  110. filterFn(item, queryText, filterType) {
  111. return (
  112. item.name.toLowerCase().indexOf(queryText.toLowerCase()) != -1 &&
  113. (filterType == "all" || filterType == item.type)
  114. )
  115. }
  116. searchItem(queryText) {
  117. this.setState({ queryText: queryText })
  118. }
  119. filterType(e, type) {
  120. this.setState({ filterType: type })
  121. }
  122. closeModal() {
  123. this.setState({ showModal: false })
  124. }
  125. bulkActions(action) {
  126. switch (action.value) {
  127. case "delete":
  128. this.setState({
  129. confirmationDialog: {
  130. visible: true,
  131. onConfirm: () => {
  132. this.setState({ confirmationDialog: { visible: false }})
  133. let selectedConnections = this.state.connections.filter((connection) => connection.selected)
  134. selectedConnections.forEach(connection => {
  135. ConnectionsActions.deleteConnection(connection)
  136. })
  137. },
  138. onCancel: () => {
  139. this.setState({ confirmationDialog: { visible: false }})
  140. }
  141. }
  142. })
  143. break;
  144. }
  145. }
  146. renderSearch(items) {
  147. let output = null
  148. if (items && items.length) {
  149. output = items.map((item, index) => (
  150. <div className={"item " + (item.selected ? " selected" : "")} key={"vm_" + index}>
  151. <div className="checkbox-container">
  152. <input
  153. id={"vm_check_" + index}
  154. type="checkbox"
  155. checked={item.selected}
  156. onChange={(e) => this.checkItem(e, item)}
  157. className="checkbox-normal"
  158. />
  159. <label htmlFor={"vm_check_" + index}></label>
  160. </div>
  161. <span className="cell cell-icon" onClick={(e) => this.connectionDetail(e, item)}>
  162. <div className={"icon endpoint"}></div>
  163. <span className="details">
  164. {/*{item.name ? item.name : "N/A"}*/}
  165. <TextTruncate line={1} truncateText="..." text={item.name} />
  166. <span className={s.description}>{item.description == "" ? "N/A" : item.description}</span>
  167. </span>
  168. </span>
  169. <span className="cell">
  170. <div className={s.cloudImage + " icon small-cloud " + item.type}></div>
  171. </span>
  172. <span className={"cell " + s.composite}>
  173. <span className={s.label}>Created</span>
  174. <span className={s.value}>
  175. <Moment fromNow ago date={item.created_at}/> ago
  176. </span>
  177. </span>
  178. <span className={"cell " + s.composite}>
  179. <span className={s.label}>Usage</span>
  180. <span className={s.value}>
  181. <EndpointUsage connectionId={item.id} />
  182. </span>
  183. </span>
  184. </div>
  185. ), this)
  186. }
  187. return output
  188. }
  189. currentInstance(migration) {
  190. let instance = "N/A"
  191. migration.vms.forEach((item) => {
  192. if (item.selected) {
  193. instance = item.name
  194. }
  195. })
  196. return instance
  197. }
  198. showNewConnectionModal() {
  199. this.setState({ showModal: true })
  200. }
  201. render() {
  202. let itemStates = connectionTypes.map((state, index) => (
  203. <a
  204. className={this.state.filterType == state.type || (this.state.filterType == null && state.type == "all") ?
  205. "selected" : ""}
  206. onClick={(e) => this.filterType(e, state.type)} key={"status_" + index}
  207. >{state.label}</a>
  208. ), this)
  209. let modalStyle = {
  210. content: {
  211. padding: "0px",
  212. borderRadius: "4px",
  213. bottom: "auto",
  214. width: "576px",
  215. height: "auto",
  216. left: "50%",
  217. top: "70px",
  218. marginLeft: "-288px"
  219. }
  220. }
  221. return (
  222. <div className={s.root}>
  223. <div className={s.container}>
  224. <div className={s.pageHeader}>
  225. <div className={s.top}>
  226. <h1>{title}</h1>
  227. <div className={s.topActions}>
  228. <ProjectsDropdown />
  229. <button onClick={(e) => this.showNewConnectionModal(e)}>New</button>
  230. <UserIcon />
  231. <NotificationIcon />
  232. </div>
  233. </div>
  234. <div className="filters">
  235. <div className="checkbox-container">
  236. <input
  237. id={"vm_check_all"}
  238. type="checkbox"
  239. checked={this.state.selectedAll}
  240. onChange={(e) => this.checkAll()}
  241. className="checkbox-normal"
  242. />
  243. <label htmlFor={"vm_check_all"}></label>
  244. </div>
  245. <div className="category-filter">
  246. {itemStates}
  247. </div>
  248. <div className="name-filter">
  249. <SearchBox
  250. placeholder="Search"
  251. value={this.state.queryText}
  252. onChange={(e) => this.searchItem(e)}
  253. minimize={true} // eslint-disable-line react/jsx-boolean-value
  254. onClick={(e) => this.toggleSearch(e)}
  255. className={"searchBox " + (this.state.searchMin ? "minimize" : "")}
  256. />
  257. </div>
  258. <div className={s.bulkActions + (this.connectionsSelectedCount() === 0 ? " invisible": "")}>
  259. <div className={s.connectionsCount}>
  260. {this.connectionsSelected()}
  261. </div>
  262. <Dropdown
  263. options={connectionActions}
  264. placeholder="More Actions"
  265. onChange={(e) => this.bulkActions(e)}
  266. />
  267. </div>
  268. </div>
  269. </div>
  270. <div className={s.pageContent}>
  271. <FilteredTable
  272. items={this.state.connections}
  273. filterFn={this.filterFn}
  274. queryText={this.state.queryText}
  275. filterType={this.state.filterType}
  276. renderSearch={(e) => this.renderSearch(e)}
  277. ></FilteredTable>
  278. </div>
  279. <div className={s.pageFooter}>
  280. </div>
  281. </div>
  282. <Modal
  283. isOpen={this.state.showModal}
  284. contentLabel="Add new cloud connection"
  285. style={modalStyle}
  286. >
  287. <AddCloudConnection
  288. closeHandle={(e) => this.closeModal(e)}
  289. addHandle={(e) => this.closeModal(e)}
  290. />
  291. </Modal>
  292. <ConfirmationDialog
  293. visible={this.state.confirmationDialog.visible}
  294. message={this.state.confirmationDialog.message}
  295. onConfirm={(e) => this.state.confirmationDialog.onConfirm(e)}
  296. onCancel={(e) => this.state.confirmationDialog.onCancel(e)}
  297. />
  298. </div>
  299. );
  300. }
  301. }
  302. export default withStyles(ConnectionsList, s);