PageHeader.tsx 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  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 from 'react'
  15. import styled from 'styled-components'
  16. import { observer } from 'mobx-react'
  17. import type { User } from '@src/@types/User'
  18. import type { Project } from '@src/@types/Project'
  19. import type { Endpoint as EndpointType } from '@src/@types/Endpoint'
  20. import Dropdown from '@src/components/ui/Dropdowns/Dropdown'
  21. import NewItemDropdown from '@src/components/ui/Dropdowns/NewItemDropdown'
  22. import NotificationDropdown from '@src/components/ui/Dropdowns/NotificationDropdown'
  23. import UserDropdown from '@src/components/ui/Dropdowns/UserDropdown'
  24. import Modal from '@src/components/ui/Modal'
  25. import ChooseProvider from '@src/components/modules/EndpointModule/ChooseProvider'
  26. import EndpointModal from '@src/components/modules/EndpointModule/EndpointModal'
  27. import UserModal from '@src/components/modules/UserModule/UserModal'
  28. import ProjectModal from '@src/components/modules/ProjectModule/ProjectModal'
  29. import projectStore from '@src/stores/ProjectStore'
  30. import userStore from '@src/stores/UserStore'
  31. import endpointStore from '@src/stores/EndpointStore'
  32. import notificationStore from '@src/stores/NotificationStore'
  33. import providerStore from '@src/stores/ProviderStore'
  34. import { ThemePalette, ThemeProps } from '@src/components/Theme'
  35. import { ProviderTypes } from '@src/@types/Providers'
  36. import MinionEndpointModal from '@src/components/modules/MinionModule/MinionEndpointModal'
  37. import MinionPoolModal from '@src/components/modules/MinionModule/MinionPoolModal'
  38. import ObjectUtils from '@src/utils/ObjectUtils'
  39. import regionStore from '@src/stores/RegionStore'
  40. import AboutModal from '@src/components/smart/AboutModal'
  41. import Config from '@src/utils/Config'
  42. const Wrapper = styled.div<any>`
  43. display: flex;
  44. margin: 32px 0 48px 0;
  45. align-items: center;
  46. flex-wrap: wrap;
  47. `
  48. const Title = styled.div<any>`
  49. color: ${ThemePalette.black};
  50. font-size: 32px;
  51. font-weight: ${ThemeProps.fontWeights.light};
  52. flex-grow: 1;
  53. overflow: hidden;
  54. white-space: nowrap;
  55. text-overflow: ellipsis;
  56. margin-top: 16px;
  57. margin-right: 16px;
  58. `
  59. const Controls = styled.div<any>`
  60. display: flex;
  61. margin-top: 16px;
  62. margin-left: -16px;
  63. & > div {
  64. margin-left: 16px;
  65. }
  66. `
  67. type Props = {
  68. title: string,
  69. onProjectChange?: (project: Project) => void,
  70. onModalOpen?: () => void,
  71. onModalClose?: () => void,
  72. componentRef?: (ref: any) => void
  73. }
  74. type State = {
  75. showChooseProviderModal: boolean,
  76. showEndpointModal: boolean,
  77. showChooseMinionEndpointModal: boolean,
  78. showMinionPoolModal: boolean,
  79. selectedMinionPoolEndpoint: EndpointType | null
  80. showUserModal: boolean,
  81. showProjectModal: boolean,
  82. showAddLicenceModal: boolean,
  83. showAboutModal: boolean,
  84. providerType: ProviderTypes | null,
  85. uploadedEndpoint: EndpointType | null,
  86. multiValidating: boolean,
  87. selectedMinionPoolPlatform: 'source' | 'destination'
  88. addingProject: boolean
  89. }
  90. @observer
  91. class PageHeader extends React.Component<Props, State> {
  92. state: State = {
  93. showChooseProviderModal: false,
  94. showEndpointModal: false,
  95. showChooseMinionEndpointModal: false,
  96. selectedMinionPoolEndpoint: null,
  97. showMinionPoolModal: false,
  98. showUserModal: false,
  99. showProjectModal: false,
  100. providerType: null,
  101. uploadedEndpoint: null,
  102. showAboutModal: false,
  103. showAddLicenceModal: false,
  104. multiValidating: false,
  105. selectedMinionPoolPlatform: 'source',
  106. addingProject: false,
  107. }
  108. pollTimeout!: number
  109. stopPolling: boolean = false
  110. UNSAFE_componentWillMount() {
  111. this.stopPolling = false
  112. this.pollData(true)
  113. if (this.props.componentRef) {
  114. this.props.componentRef(this)
  115. }
  116. }
  117. componentWillUnmount() {
  118. clearTimeout(this.pollTimeout)
  119. this.stopPolling = true
  120. }
  121. getCurrentProject() {
  122. const project = userStore.loggedUser && userStore.loggedUser.project
  123. ? userStore.loggedUser.project : null
  124. if (project) {
  125. return projectStore.projects.find(p => p.id === project.id)
  126. }
  127. return null
  128. }
  129. handleUserItemClick(item: { value: string }) {
  130. switch (item.value) {
  131. case 'about':
  132. this.setState({ showAboutModal: true })
  133. if (this.props.onModalOpen) {
  134. this.props.onModalOpen()
  135. }
  136. return
  137. case 'signout':
  138. userStore.logout()
  139. break
  140. default:
  141. }
  142. }
  143. handleNewItem(item: string | null | undefined) {
  144. switch (item) {
  145. case 'endpoint':
  146. providerStore.loadProviders()
  147. regionStore.getRegions()
  148. if (this.props.onModalOpen) {
  149. this.props.onModalOpen()
  150. }
  151. this.setState({ showChooseProviderModal: true })
  152. break
  153. case 'minionPool':
  154. providerStore.loadProviders()
  155. endpointStore.getEndpoints({ showLoading: true })
  156. if (this.props.onModalOpen) {
  157. this.props.onModalOpen()
  158. }
  159. this.setState({ showChooseMinionEndpointModal: true })
  160. break
  161. case 'user':
  162. projectStore.getProjects()
  163. if (this.props.onModalOpen) {
  164. this.props.onModalOpen()
  165. }
  166. this.setState({ showUserModal: true })
  167. break
  168. case 'project':
  169. if (this.props.onModalOpen) {
  170. this.props.onModalOpen()
  171. }
  172. endpointStore.getEndpoints()
  173. this.setState({ showProjectModal: true })
  174. break
  175. case 'licence':
  176. if (this.props.onModalOpen) {
  177. this.props.onModalOpen()
  178. }
  179. this.setState({ showAddLicenceModal: true })
  180. break
  181. default:
  182. }
  183. }
  184. handleNotificationsClose() {
  185. notificationStore.saveSeen()
  186. }
  187. handleCloseChooseProviderModal() {
  188. if (this.props.onModalClose) {
  189. this.props.onModalClose()
  190. }
  191. this.setState({ showChooseProviderModal: false }, () => { this.pollData() })
  192. }
  193. handleCloseChooseMinionPoolEndpointModal() {
  194. if (this.props.onModalClose) {
  195. this.props.onModalClose()
  196. }
  197. this.setState({ showChooseMinionEndpointModal: false }, () => { this.pollData() })
  198. }
  199. handleBackMinionPoolModal() {
  200. this.setState({ showChooseMinionEndpointModal: true, showMinionPoolModal: false })
  201. }
  202. handleCloseMinionPoolModalRequest() {
  203. if (this.props.onModalClose) {
  204. this.props.onModalClose()
  205. }
  206. this.setState({ showMinionPoolModal: false }, () => { this.pollData() })
  207. }
  208. handleChooseMinionPoolSelectEndpoint(selectedMinionPoolEndpoint: EndpointType, platform: 'source' | 'destination') {
  209. this.setState({
  210. showChooseMinionEndpointModal: false,
  211. showMinionPoolModal: true,
  212. selectedMinionPoolEndpoint,
  213. selectedMinionPoolPlatform: platform,
  214. })
  215. }
  216. handleProviderClick(providerType: ProviderTypes) {
  217. this.setState({
  218. showChooseProviderModal: false,
  219. showEndpointModal: true,
  220. uploadedEndpoint: null,
  221. providerType,
  222. })
  223. }
  224. handleUploadEndpoint(endpoint: EndpointType) {
  225. endpointStore.setConnectionInfo(endpoint.connection_info)
  226. this.setState({
  227. showChooseProviderModal: false,
  228. showEndpointModal: true,
  229. providerType: endpoint.type,
  230. uploadedEndpoint: endpoint,
  231. })
  232. }
  233. handleRemoveEndpoint(endpoint: EndpointType) {
  234. endpointStore.delete(endpoint)
  235. }
  236. async handleValidateMultipleEndpoints(endpoints: EndpointType[]) {
  237. this.setState({ multiValidating: true })
  238. const addedEndpoints = await endpointStore.addMultiple(endpoints)
  239. await endpointStore.validateMultiple(addedEndpoints)
  240. this.setState({ multiValidating: false })
  241. }
  242. handleResetValidation() {
  243. endpointStore.resetMultiValidation()
  244. }
  245. handleCloseEndpointModal() {
  246. if (this.props.onModalClose) {
  247. this.props.onModalClose()
  248. }
  249. this.setState({ showEndpointModal: false }, () => { this.pollData() })
  250. }
  251. handleBackEndpointModal(options?: { autoClose?: boolean }) {
  252. const showChooseProviderModal = !options || !options.autoClose
  253. this.setState({ showChooseProviderModal, showEndpointModal: false }, () => {
  254. if (!showChooseProviderModal) {
  255. this.pollData()
  256. }
  257. })
  258. }
  259. async handleProjectChange(project: Project) {
  260. await userStore.switchProject(project.id)
  261. await projectStore.getProjects()
  262. notificationStore.loadData(true)
  263. if (this.props.onProjectChange) {
  264. this.props.onProjectChange(project)
  265. }
  266. }
  267. handleUserModalClose() {
  268. if (this.props.onModalClose) {
  269. this.props.onModalClose()
  270. }
  271. this.setState({ showUserModal: false }, () => { this.pollData() })
  272. }
  273. async handleUserUpdateClick(user: User) {
  274. await userStore.add(user)
  275. if (this.props.onModalClose) {
  276. this.props.onModalClose()
  277. }
  278. this.setState({ showUserModal: false }, () => { this.pollData() })
  279. }
  280. handleProjectModalClose() {
  281. if (this.props.onModalClose) {
  282. this.props.onModalClose()
  283. }
  284. this.setState({ showProjectModal: false }, () => { this.pollData() })
  285. }
  286. async handleProjectModalUpdateClick(project: Project) {
  287. try {
  288. this.setState({ addingProject: true })
  289. const newProject = await projectStore.add(project)
  290. const bareMetalEndpoint = endpointStore.endpoints.find(e => e.name === Config.config.bareMetalEndpointName)
  291. if (bareMetalEndpoint) {
  292. await endpointStore.duplicate({
  293. shouldSwitchProject: true,
  294. onSwitchProject: () => userStore.switchProject(newProject.id),
  295. endpoints: [bareMetalEndpoint],
  296. })
  297. }
  298. } finally {
  299. if (this.props.onModalClose) {
  300. this.props.onModalClose()
  301. }
  302. this.setState({ showProjectModal: false, addingProject: false }, () => {
  303. this.pollData()
  304. })
  305. }
  306. }
  307. async pollData(showLoading?: boolean) {
  308. if (
  309. this.stopPolling
  310. || this.state.showChooseProviderModal
  311. || this.state.showEndpointModal
  312. || this.state.showChooseMinionEndpointModal
  313. || this.state.showMinionPoolModal
  314. || this.state.showProjectModal
  315. || this.state.showUserModal
  316. || this.state.showAboutModal
  317. || this.state.showAddLicenceModal
  318. ) {
  319. return
  320. }
  321. await notificationStore.loadData(showLoading)
  322. this.pollTimeout = window.setTimeout(() => { this.pollData() }, 15000)
  323. }
  324. render() {
  325. return (
  326. <Wrapper>
  327. <Title>{this.props.title}</Title>
  328. <Controls>
  329. <Dropdown
  330. selectedItem={this.getCurrentProject()}
  331. items={projectStore.projects}
  332. onChange={project => { this.handleProjectChange(project) }}
  333. noItemsMessage="Loading..."
  334. labelField="name"
  335. />
  336. <NewItemDropdown onChange={item => { this.handleNewItem(item.value) }} />
  337. <NotificationDropdown
  338. items={notificationStore.notificationItems}
  339. onClose={() => this.handleNotificationsClose()}
  340. />
  341. <UserDropdown
  342. user={userStore.loggedUser}
  343. onItemClick={item => { this.handleUserItemClick(item) }}
  344. />
  345. </Controls>
  346. <Modal
  347. isOpen={this.state.showChooseProviderModal}
  348. title="New Cloud Endpoint"
  349. onRequestClose={() => { this.handleCloseChooseProviderModal() }}
  350. >
  351. <ChooseProvider
  352. onCancelClick={() => { this.handleCloseChooseProviderModal() }}
  353. providers={providerStore.providerNames}
  354. loading={providerStore.providersLoading || regionStore.loading}
  355. regions={regionStore.regions}
  356. onProviderClick={providerName => { this.handleProviderClick(providerName) }}
  357. onUploadEndpoint={endpoint => { this.handleUploadEndpoint(endpoint) }}
  358. multiValidating={this.state.multiValidating}
  359. onValidateMultipleEndpoints={endpoints => {
  360. this.handleValidateMultipleEndpoints(endpoints)
  361. }}
  362. multiValidation={endpointStore.multiValidation}
  363. onRemoveEndpoint={e => { this.handleRemoveEndpoint(e) }}
  364. onResetValidation={() => { this.handleResetValidation() }}
  365. />
  366. </Modal>
  367. {this.state.showChooseMinionEndpointModal ? (
  368. <MinionEndpointModal
  369. providers={providerStore.providers}
  370. endpoints={endpointStore.endpoints}
  371. loading={providerStore.providersLoading || endpointStore.loading}
  372. onRequestClose={() => { this.handleCloseChooseMinionPoolEndpointModal() }}
  373. onSelectEndpoint={(endpoint, platform) => {
  374. this.handleChooseMinionPoolSelectEndpoint(endpoint, platform)
  375. }}
  376. />
  377. ) : null}
  378. {this.state.showMinionPoolModal ? (
  379. <Modal
  380. isOpen
  381. title={`New ${ObjectUtils.capitalizeFirstLetter(this.state.selectedMinionPoolPlatform)} Minion Pool`}
  382. onRequestClose={() => { this.handleCloseMinionPoolModalRequest() }}
  383. >
  384. <MinionPoolModal
  385. cancelButtonText="Back"
  386. platform={this.state.selectedMinionPoolPlatform}
  387. endpoint={this.state.selectedMinionPoolEndpoint!}
  388. onCancelClick={() => { this.handleBackMinionPoolModal() }}
  389. onRequestClose={() => { this.handleCloseMinionPoolModalRequest() }}
  390. />
  391. </Modal>
  392. ) : null}
  393. {this.state.showEndpointModal && this.state.providerType ? (
  394. <Modal
  395. isOpen
  396. title="New Cloud Endpoint"
  397. onRequestClose={() => { this.handleCloseEndpointModal() }}
  398. >
  399. <EndpointModal
  400. type={this.state.providerType}
  401. cancelButtonText="Back"
  402. onCancelClick={options => { this.handleBackEndpointModal(options) }}
  403. endpoint={this.state.uploadedEndpoint}
  404. isNewEndpoint={Boolean(this.state.uploadedEndpoint)}
  405. />
  406. </Modal>
  407. ) : null}
  408. {this.state.showUserModal ? (
  409. <UserModal
  410. isNewUser
  411. loading={userStore.updating}
  412. projects={projectStore.projects}
  413. onRequestClose={() => { this.handleUserModalClose() }}
  414. onUpdateClick={user => { this.handleUserUpdateClick(user) }}
  415. />
  416. ) : null}
  417. {this.state.showProjectModal ? (
  418. <ProjectModal
  419. isNewProject
  420. loading={this.state.addingProject}
  421. onRequestClose={() => { this.handleProjectModalClose() }}
  422. onUpdateClick={project => { this.handleProjectModalUpdateClick(project) }}
  423. />
  424. ) : null}
  425. {this.state.showAboutModal || this.state.showAddLicenceModal ? (
  426. <AboutModal
  427. licenceAddMode={this.state.showAddLicenceModal}
  428. onRequestClose={() => {
  429. this.setState({ showAboutModal: false, showAddLicenceModal: false })
  430. if (this.props.onModalClose) {
  431. this.props.onModalClose()
  432. }
  433. }}
  434. />
  435. ) : null}
  436. </Wrapper>
  437. )
  438. }
  439. }
  440. export default PageHeader