github.com/replicatedhq/ship@v0.55.0/web/init/src/components/shared/StepTerraform.jsx (about) 1 import React from "react"; 2 import ReactDOM from "react-dom"; 3 import PropTypes from "prop-types"; 4 import { Line } from "rc-progress"; 5 import clamp from "lodash/clamp"; 6 7 import { Utilities } from "../../utilities/utilities"; 8 import Loader from "./Loader"; 9 import StepMessage from "./StepMessage"; 10 11 export const TERRAFORM_PHASE = "terraform"; 12 13 export class StepTerraform extends React.Component { 14 static propTypes = { 15 location: PropTypes.shape({ 16 pathname: PropTypes.string, 17 }).isRequired, 18 currentRoute: PropTypes.shape({ 19 id: PropTypes.string, 20 phase: PropTypes.string, 21 }).isRequired, 22 startPoll: PropTypes.func.isRequired, 23 gotoRoute: PropTypes.func.isRequired, 24 initializeStep: PropTypes.func.isRequired, 25 status: PropTypes.shape({ 26 type: PropTypes.string, 27 detail: PropTypes.string, 28 }), 29 handleAction: PropTypes.func, 30 startPollingStep: PropTypes.func.isRequired, 31 } 32 33 componentDidMount() { 34 const { 35 startPollingStep, 36 currentRoute, 37 } = this.props; 38 39 if (currentRoute.phase === TERRAFORM_PHASE) { 40 startPollingStep(currentRoute.id); 41 } 42 } 43 44 componentDidUpdate() { 45 this.scrollToLogsBottom(); 46 } 47 48 parseStatus = () => { 49 const { status = {} } = this.props; 50 const { type, detail } = status; 51 const isJSON = type === "json"; 52 53 const parsedDetail = isJSON ? JSON.parse(detail) : {}; 54 const { 55 status: parsedDetailStatus, 56 progressDetail, 57 message, 58 actions, 59 } = parsedDetail; 60 61 if (parsedDetailStatus === "message") { 62 return { 63 actions, 64 isJSON, 65 status: parsedDetailStatus, 66 message, 67 } 68 } 69 70 if (parsedDetailStatus === "error") { 71 return { 72 isJSON, 73 status: parsedDetailStatus, 74 message, 75 } 76 } 77 78 // TODO(Robert): for now, this is a catch all for using the progress status to determine the phase 79 if (parsedDetailStatus !== "error") { 80 const percent = progressDetail ? `${Utilities.calcPercent(progressDetail.current, progressDetail.total, 0)}` : 0; 81 const clampedPercent = clamp(percent, 0, 100); 82 return { 83 isJSON, 84 status: parsedDetailStatus, 85 percent: clampedPercent, 86 progressDetail, 87 message, 88 } 89 } 90 } 91 92 handleAction = (action) => { 93 const { 94 handleAction, 95 startPoll, 96 currentRoute, 97 gotoRoute, 98 } = this.props; 99 handleAction(action, false); 100 startPoll(currentRoute.id, gotoRoute); 101 } 102 103 scrollToLogsBottom = (elm) => { 104 const { 105 status = "", 106 } = this.parseStatus(); 107 const node = ReactDOM.findDOMNode(this); 108 const child = node.querySelector('.term-container'); 109 if(child) { 110 const height = child.scrollHeight; 111 child.scrollTo({ top: height, behavior: "instant" }); 112 } 113 } 114 115 render() { 116 const { 117 isJSON, 118 percent, 119 status, 120 message, 121 progressDetail, 122 actions, 123 } = this.parseStatus(); 124 125 return ( 126 <div className="flex1 flex-column justifyContent--center"> 127 {status === "working" ? 128 <div className="flex1 flex-column u-paddingTop--30 justifyContent--center"> 129 <div className="flex justifyContent--center"> 130 <Loader size="60" /> 131 </div> 132 {isJSON ? 133 <div className="flex-column"> 134 {!progressDetail ? null : 135 <div className="flex1 flex-column"> 136 <div className="u-marginTop--20"> 137 <div className="progressBar-wrapper"> 138 <Line percent={percent} strokeWidth="1" strokeColor="#337AB7" /> 139 </div> 140 </div> 141 </div> 142 } 143 {!message ? null : 144 <StepMessage goBack={this.props.goBack} firstRoute={this.props.firstRoute} message={message} /> 145 } 146 </div> 147 : 148 <p className="u-fontSizer--larger u-color--tundora u-fontWeight--bold u-marginTop--normal u-textAlign--center">{status}</p> 149 } 150 </div>: null 151 } 152 {status === "message" ? 153 <StepMessage 154 message={message} 155 actions={actions} 156 handleAction={this.handleAction} 157 goBack={this.props.goBack} 158 firstRoute={this.props.firstRoute} 159 setLogsRef={this.setLogsRef} 160 /> 161 : null 162 } 163 {status === "error" ? 164 <div className="Error--wrapper flex-column alignItems--center"> 165 <div className="icon progress-detail-error"></div> 166 <p className="u-fontSizer--larger u-color--tundora u-lineHeight--normal u-fontWeight--bold u-marginTop--normal u-textAlign--center">{message}</p> 167 </div> 168 : null 169 } 170 {status === "success" ? 171 <React.Fragment> 172 <div className="icon progress-detail-success"></div> 173 <p className="u-fontSizer--larger u-color--tundora u-fontWeight--bold u-marginTop--normal u-textAlign--center">{message}</p> 174 </React.Fragment> : null 175 } 176 </div> 177 ); 178 } 179 }