EndpointDetailsContent.tsx 8.1 KB

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