ReplicaDetailsContent.tsx 7.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271
  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 { observer } from "mobx-react";
  15. import React from "react";
  16. import styled from "styled-components";
  17. import { ReplicaItemDetails } from "@src/@types/MainItem";
  18. import { MinionPool } from "@src/@types/MinionPool";
  19. import DetailsNavigation from "@src/components/modules/NavigationModule/DetailsNavigation";
  20. import Executions from "@src/components/modules/TransferModule/Executions";
  21. import MainDetails from "@src/components/modules/TransferModule/MainDetails";
  22. import Schedule from "@src/components/modules/TransferModule/Schedule";
  23. import { ThemeProps } from "@src/components/Theme";
  24. import Button from "@src/components/ui/Button";
  25. import scheduleStore from "@src/stores/ScheduleStore";
  26. import configLoader from "@src/utils/Config";
  27. import type { Instance } from "@src/@types/Instance";
  28. import type { Endpoint, StorageBackend } from "@src/@types/Endpoint";
  29. import type { Execution, ExecutionTasks } from "@src/@types/Execution";
  30. import type { Network } from "@src/@types/Network";
  31. import type { Field } from "@src/@types/Field";
  32. import type { Schedule as ScheduleType } from "@src/@types/Schedule";
  33. const Wrapper = styled.div<any>`
  34. display: flex;
  35. justify-content: center;
  36. `;
  37. const Buttons = styled.div<any>`
  38. display: flex;
  39. justify-content: space-between;
  40. margin-top: 24px;
  41. `;
  42. const ButtonColumn = styled.div<any>`
  43. display: flex;
  44. flex-direction: column;
  45. button {
  46. margin-top: 16px;
  47. &:first-child {
  48. margin-top: 0;
  49. }
  50. }
  51. `;
  52. const DetailsBody = styled.div<any>`
  53. ${ThemeProps.exactWidth(ThemeProps.contentWidth)}
  54. `;
  55. const NavigationItems = [
  56. {
  57. label: "Replica",
  58. value: "",
  59. },
  60. {
  61. label: "Executions",
  62. value: "executions",
  63. },
  64. {
  65. label: "Schedule",
  66. value: "schedule",
  67. },
  68. ];
  69. type TimezoneValue = "utc" | "local";
  70. type Props = {
  71. item?: ReplicaItemDetails | null;
  72. itemId: string;
  73. endpoints: Endpoint[];
  74. sourceSchema: Field[];
  75. sourceSchemaLoading: boolean;
  76. destinationSchema: Field[];
  77. destinationSchemaLoading: boolean;
  78. networks: Network[];
  79. instancesDetails: Instance[];
  80. instancesDetailsLoading: boolean;
  81. scheduleStore: typeof scheduleStore;
  82. page: string;
  83. detailsLoading: boolean;
  84. executions: Execution[];
  85. executionsLoading: boolean;
  86. executionsTasksLoading: boolean;
  87. executionsTasks: ExecutionTasks[];
  88. minionPools: MinionPool[];
  89. storageBackends: StorageBackend[];
  90. onExecutionChange: (executionId: string) => void;
  91. onCancelExecutionClick: (
  92. execution: Execution | null,
  93. force?: boolean
  94. ) => void;
  95. onDeleteExecutionClick: (execution: Execution | null) => void;
  96. onExecuteClick: () => void;
  97. onCreateDeploymentClick: () => void;
  98. onDeleteReplicaClick: () => void;
  99. onAddScheduleClick: (schedule: ScheduleType) => void;
  100. onScheduleChange: (
  101. scheduleId: string | null,
  102. data: ScheduleType,
  103. forceSave?: boolean
  104. ) => void;
  105. onScheduleRemove: (scheduleId: string | null) => void;
  106. onScheduleSave: (schedule: ScheduleType) => void;
  107. };
  108. type State = {
  109. timezone: TimezoneValue;
  110. };
  111. @observer
  112. class ReplicaDetailsContent extends React.Component<Props, State> {
  113. state: State = {
  114. timezone: "local",
  115. };
  116. getLastExecution() {
  117. return this.props.item?.executions?.length
  118. ? this.props.item.executions[this.props.item.executions.length - 1]
  119. : null;
  120. }
  121. getStatus() {
  122. return this.getLastExecution()?.status;
  123. }
  124. isEndpointMissing() {
  125. return (
  126. this.props.endpoints.filter(
  127. e =>
  128. e.id === this.props.item?.origin_endpoint_id ||
  129. e.id === this.props.item?.destination_endpoint_id
  130. ).length < 2
  131. );
  132. }
  133. handleTimezoneChange(timezone: TimezoneValue) {
  134. this.setState({ timezone });
  135. }
  136. renderBottomControls() {
  137. return (
  138. <Buttons>
  139. <ButtonColumn>
  140. <Button
  141. secondary
  142. disabled={this.getStatus() === "RUNNING"}
  143. onClick={this.props.onExecuteClick}
  144. >
  145. Execute Replica
  146. </Button>
  147. <Button
  148. primary
  149. disabled={this.isEndpointMissing()}
  150. onClick={this.props.onCreateDeploymentClick}
  151. >
  152. Create Migration
  153. </Button>
  154. </ButtonColumn>
  155. <ButtonColumn>
  156. <Button alert hollow onClick={this.props.onDeleteReplicaClick}>
  157. Delete Replica
  158. </Button>
  159. </ButtonColumn>
  160. </Buttons>
  161. );
  162. }
  163. renderMainDetails() {
  164. if (this.props.page !== "") {
  165. return null;
  166. }
  167. return (
  168. <MainDetails
  169. item={this.props.item}
  170. storageBackends={this.props.storageBackends}
  171. minionPools={this.props.minionPools}
  172. sourceSchema={this.props.sourceSchema}
  173. sourceSchemaLoading={this.props.sourceSchemaLoading}
  174. destinationSchema={this.props.destinationSchema}
  175. destinationSchemaLoading={this.props.destinationSchemaLoading}
  176. instancesDetails={this.props.instancesDetails}
  177. instancesDetailsLoading={this.props.instancesDetailsLoading}
  178. loading={this.props.detailsLoading}
  179. endpoints={this.props.endpoints}
  180. networks={this.props.networks}
  181. bottomControls={this.renderBottomControls()}
  182. />
  183. );
  184. }
  185. renderExecutions() {
  186. if (this.props.page !== "executions") {
  187. return null;
  188. }
  189. return (
  190. <Executions
  191. executions={this.props.executions}
  192. executionsTasks={this.props.executionsTasks}
  193. onCancelExecutionClick={this.props.onCancelExecutionClick}
  194. onDeleteExecutionClick={this.props.onDeleteExecutionClick}
  195. onExecuteClick={this.props.onExecuteClick}
  196. loading={this.props.executionsLoading || this.props.detailsLoading}
  197. onChange={this.props.onExecutionChange}
  198. tasksLoading={this.props.executionsTasksLoading}
  199. instancesDetails={this.props.instancesDetails}
  200. />
  201. );
  202. }
  203. renderSchedule() {
  204. if (this.props.page !== "schedule") {
  205. return null;
  206. }
  207. return (
  208. <Schedule
  209. disableExecutionOptions={configLoader.config.providersDisabledExecuteOptions.some(
  210. p =>
  211. p ===
  212. this.props.endpoints.find(
  213. e => e.id === this.props.item?.origin_endpoint_id
  214. )?.type
  215. )}
  216. schedules={this.props.scheduleStore.schedules}
  217. unsavedSchedules={this.props.scheduleStore.unsavedSchedules}
  218. adding={this.props.scheduleStore.adding}
  219. loading={this.props.scheduleStore.loading}
  220. onAddScheduleClick={this.props.onAddScheduleClick}
  221. onChange={this.props.onScheduleChange}
  222. onRemove={this.props.onScheduleRemove}
  223. onSaveSchedule={this.props.onScheduleSave}
  224. timezone={this.state.timezone}
  225. onTimezoneChange={timezone => {
  226. this.handleTimezoneChange(timezone);
  227. }}
  228. savingIds={this.props.scheduleStore.savingIds}
  229. enablingIds={this.props.scheduleStore.enablingIds}
  230. deletingIds={this.props.scheduleStore.deletingIds}
  231. />
  232. );
  233. }
  234. render() {
  235. return (
  236. <Wrapper>
  237. <DetailsNavigation
  238. items={NavigationItems}
  239. selectedValue={this.props.page}
  240. itemId={this.props.itemId}
  241. itemType="replica"
  242. />
  243. <DetailsBody>
  244. {this.renderMainDetails()}
  245. {this.renderExecutions()}
  246. {this.renderSchedule()}
  247. </DetailsBody>
  248. </Wrapper>
  249. );
  250. }
  251. }
  252. export default ReplicaDetailsContent;