EndpointDetailsContent.tsx 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  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 * as React from 'react'
  15. import { Link } from 'react-router-dom'
  16. import { observer } from 'mobx-react'
  17. import styled from 'styled-components'
  18. import EndpointLogos from '../EndpointLogos/EndpointLogos'
  19. import PasswordValue from '../../../ui/PasswordValue/PasswordValue'
  20. import Button from '../../../ui/Button/Button'
  21. import CopyValue from '../../../ui/CopyValue/CopyValue'
  22. import CopyMultilineValue from '../../../ui/CopyMultilineValue/CopyMultilineValue'
  23. import StatusImage from '../../../ui/StatusComponents/StatusImage/StatusImage'
  24. import type { Endpoint } from '../../../../@types/Endpoint'
  25. import StyleProps from '../../../styleUtils/StyleProps'
  26. import Palette from '../../../styleUtils/Palette'
  27. import DateUtils from '../../../../utils/DateUtils'
  28. import LabelDictionary from '../../../../utils/LabelDictionary'
  29. import configLoader from '../../../../utils/Config'
  30. import { Region } from '../../../../@types/Region'
  31. import {
  32. getTransferItemTitle, MigrationItem, ReplicaItem, TransferItem,
  33. } from '../../../../@types/MainItem'
  34. import { Field as FieldType } from '../../../../@types/Field'
  35. import DomUtils from '../../../../utils/DomUtils'
  36. const Wrapper = styled.div<any>`
  37. ${StyleProps.exactWidth(StyleProps.contentWidth)}
  38. margin: 0 auto;
  39. padding-left: 126px;
  40. `
  41. const Info = styled.div<any>`
  42. display: flex;
  43. flex-wrap: wrap;
  44. margin-top: 32px;
  45. margin-left: -32px;
  46. `
  47. const Field = styled.div<any>`
  48. ${StyleProps.exactWidth('calc(50% - 32px)')}
  49. margin-bottom: 32px;
  50. margin-left: 32px;
  51. `
  52. const Label = styled.div<any>`
  53. font-size: 10px;
  54. font-weight: ${StyleProps.fontWeights.medium};
  55. color: ${Palette.grayscale[3]};
  56. text-transform: uppercase;
  57. margin-bottom: 3px;
  58. `
  59. const Value = styled.div<any>``
  60. const Buttons = styled.div<any>`
  61. display: flex;
  62. justify-content: space-between;
  63. margin-top: 64px;
  64. `
  65. const MainButtons = styled.div<any>``
  66. const DeleteButton = styled.div<any>``
  67. const LoadingWrapper = styled.div<any>`
  68. display: flex;
  69. justify-content: center;
  70. width: 100%;
  71. margin: 32px 0 64px 0;
  72. `
  73. const LinkStyled = styled(Link)`
  74. color: ${Palette.primary};
  75. text-decoration: none;
  76. cursor: pointer;
  77. `
  78. const TransferItems = styled.div`
  79. max-height: 200px;
  80. overflow: auto;
  81. `
  82. const TransferItemWrapper = styled.div`
  83. margin-bottom: 4px;
  84. `
  85. const DownloadLink = styled.div`
  86. display: inline-block;
  87. color: ${Palette.primary};
  88. cursor: pointer;
  89. :hover {
  90. text-decoration: underline;
  91. }
  92. `
  93. type Props = {
  94. item: Endpoint | null,
  95. regions: Region[],
  96. connectionInfo: Endpoint['connection_info'] | null,
  97. loading: boolean,
  98. usage: { migrations: MigrationItem[], replicas: ReplicaItem[] },
  99. connectionInfoSchema: FieldType[],
  100. onDeleteClick: () => void,
  101. onValidateClick: () => void,
  102. }
  103. @observer
  104. class EndpointDetailsContent extends React.Component<Props> {
  105. renderedKeys!: { [prop: string]: boolean }
  106. renderDownloadValue(value: string, fieldName: string) {
  107. const endpoint = this.props.item
  108. if (!endpoint) {
  109. return null
  110. }
  111. return (
  112. <DownloadLink onClick={() => {
  113. DomUtils.download(value, fieldName)
  114. }}
  115. >Download
  116. </DownloadLink>
  117. )
  118. }
  119. renderConnectionInfoLoading() {
  120. if (!this.props.loading) {
  121. return null
  122. }
  123. return (
  124. <LoadingWrapper>
  125. <StatusImage loading data-test-id="edContent-connLoading" />
  126. </LoadingWrapper>
  127. )
  128. }
  129. renderConnectionInfo(connectionInfo: any): React.ReactNode {
  130. if (!connectionInfo) {
  131. return null
  132. }
  133. return Object.keys(connectionInfo).map(key => {
  134. let value = connectionInfo[key]
  135. if (key === 'secret_ref') {
  136. return null
  137. }
  138. if (typeof connectionInfo[key] === 'object') {
  139. return this.renderConnectionInfo(connectionInfo[key])
  140. }
  141. if (this.renderedKeys[key]) {
  142. return null
  143. }
  144. this.renderedKeys[key] = true
  145. if (value === true) {
  146. value = 'Yes'
  147. } else if (value === false) {
  148. value = 'No'
  149. } else if (!value) {
  150. value = '-'
  151. }
  152. let valueElement = null
  153. const schemaField = this.props.connectionInfoSchema.find(f => f.name === key)
  154. if (configLoader.config.passwordFields.find(fn => fn === key) || key.indexOf('password') > -1) {
  155. valueElement = <PasswordValue value={value} data-test-id="edContent-connPassword" />
  156. } else if (schemaField?.useFile) {
  157. valueElement = this.renderDownloadValue(value, key)
  158. } else {
  159. valueElement = this.renderValue(value, `connValue-${key}`)
  160. }
  161. return (
  162. <Field key={key}>
  163. <Label>{LabelDictionary.get(key)}</Label>
  164. {valueElement}
  165. </Field>
  166. )
  167. })
  168. }
  169. renderButtons() {
  170. return (
  171. <Buttons>
  172. <MainButtons>
  173. <Button onClick={this.props.onValidateClick} data-test-id="edContent-validateButton">Validate Endpoint</Button>
  174. </MainButtons>
  175. <DeleteButton>
  176. <Button hollow alert onClick={this.props.onDeleteClick} data-test-id="edContent-deleteButton">Delete Endpoint</Button>
  177. </DeleteButton>
  178. </Buttons>
  179. )
  180. }
  181. renderValue(value: string, dataTestId?: string) {
  182. return <CopyValue data-test-id={dataTestId ? `edContent-${dataTestId}` : undefined} value={value} maxWidth="90%" />
  183. }
  184. renderRegions() {
  185. return (
  186. <span>
  187. {this.props.item?.mapped_regions.map(regionId => this.props.regions.find(r => r.id === regionId)?.name).join(', ') || '-'}
  188. </span>
  189. )
  190. }
  191. renderUsage(items: TransferItem[]) {
  192. return (
  193. <TransferItems>
  194. {items.map(item => (
  195. <TransferItemWrapper>
  196. <LinkStyled
  197. key={item.id}
  198. to={`/${item.type}s/${item.id}`}
  199. >
  200. {getTransferItemTitle(item)}
  201. </LinkStyled>
  202. </TransferItemWrapper>
  203. ))}
  204. </TransferItems>
  205. )
  206. }
  207. render() {
  208. this.renderedKeys = {}
  209. const {
  210. // eslint-disable-next-line @typescript-eslint/naming-convention
  211. type, name, description, created_at, id,
  212. } = this.props.item || {}
  213. const usage: TransferItem[] = this.props.usage.replicas
  214. .concat(this.props.usage.migrations as any[])
  215. return (
  216. <Wrapper>
  217. <EndpointLogos endpoint={type} />
  218. <Info>
  219. <Field>
  220. <Label>Id</Label>
  221. {this.renderValue(id || '')}
  222. </Field>
  223. <Field>
  224. <Label>Name</Label>
  225. {this.renderValue(name || '', 'name')}
  226. </Field>
  227. <Field>
  228. <Label>Type</Label>
  229. {this.renderValue(this.props.item ? configLoader.config.providerNames[this.props.item.type] : '', 'type')}
  230. </Field>
  231. <Field>
  232. <Label>Coriolis Regions</Label>
  233. {this.renderRegions()}
  234. </Field>
  235. <Field>
  236. <Label>Description</Label>
  237. {description ? <CopyMultilineValue data-test-id="edContent-description" value={description} /> : <Value>-</Value>}
  238. </Field>
  239. <Field>
  240. <Label>Created</Label>
  241. {this.renderValue(DateUtils.getLocalTime(created_at).format('DD/MM/YYYY HH:mm'), 'created')}
  242. </Field>
  243. <Field>
  244. <Label>Used in replicas/migrations ({usage.length})</Label>
  245. {usage.length > 0 ? this.renderUsage(usage) : <Value>-</Value>}
  246. </Field>
  247. {!this.props.connectionInfo ? this.renderConnectionInfoLoading() : null}
  248. {this.renderConnectionInfo(this.props.connectionInfo)}
  249. </Info>
  250. {this.renderButtons()}
  251. </Wrapper>
  252. )
  253. }
  254. }
  255. export default EndpointDetailsContent