github.com/replicatedhq/ship@v0.55.0/web/init/src/components/application_settings/ApplicationSettings.jsx (about) 1 import React from "react"; 2 3 import ErrorBoundary from "../../ErrorBoundary"; 4 5 import isEmpty from "lodash/isEmpty"; 6 7 import ConfigRender from "../config_render/ConfigRender"; 8 import Toast from "../shared/Toast"; 9 10 // var EDITABLE_ITEM_TYPES = [ 11 // "select", "text", "textarea", "password", "file", "bool", "select_many", "select_one", 12 // ]; 13 14 export default class ApplicationSettings extends React.Component { 15 16 constructor(props) { 17 super(props); 18 this.state = { 19 toastDetails: { 20 opts: {} 21 } 22 }; 23 } 24 25 componentDidUpdate(lastProps) { 26 const { location, settingsFields } = this.props; 27 if (this.props.settingsFields !== lastProps.settingsFields && !isEmpty(settingsFields)) { 28 // this.setState({ groups: settingsFields }); 29 if (location.hash) { 30 const el = document.getElementById(location.hash.replace("#", "")); 31 if (!el) return; 32 el.scrollIntoView({ behavior: "smooth", block: "start" }); 33 } 34 } 35 } 36 37 componentDidMount() { 38 if (!this.props.settingsFieldsList.length) { 39 this.props.getApplicationSettings({item_values: null}); 40 } 41 } 42 43 // mergeItemData(groups, data) { 44 // const nextGroups = map(groups, function(group) { 45 // group.items = map(group.items, function(item) { 46 // if (item.name === data.name) { 47 // item.value = data.value; 48 // item.multi_value = data.multi_value; 49 // if (has(data, 'data')) { 50 // item.data = data.data; 51 // } 52 // if (has(data, 'multi_data')) { 53 // item.multi_data = data.multi_data; 54 // } 55 // } else { 56 // item.items = map(item.items, function(childItem) { 57 // if (childItem.name === data.name) { 58 // childItem.value = data.value; 59 // childItem.multi_value = data.multi_value; 60 // if (has(data, 'data')) { 61 // childItem.data = data.data; 62 // } 63 // if (has(data, 'multi_data')) { 64 // childItem.multi_data = data.multi_data; 65 // } 66 // } 67 // return childItem; 68 // }); 69 // } 70 // return item; 71 // }); 72 // return group; 73 // }); 74 // return nextGroups; 75 // } 76 77 // mergeGroupItemData(groups, currentGroups) { 78 // const getItemData = (item) => { 79 // let data = { 80 // name: item.name, 81 // value: item.value, 82 // multi_value: item.multi_value, 83 // }; 84 // if (has(item, 'data')) { 85 // data.data = item.data; 86 // } 87 // if (has(item, 'multi_data')) { 88 // data.multi_data = item.multi_data; 89 // } 90 // return data; 91 // }; 92 93 // let nextGroups = _.cloneDeep(groups); 94 // forEach(currentGroups, (group) => { 95 // forEach(group.items, (item) => { 96 // if (includes(EDITABLE_ITEM_TYPES, item.type) && !item.readonly) { 97 // nextGroups = this.mergeItemData(nextGroups, getItemData(item)); 98 // } 99 // forEach(item.items, (childItem) => { 100 // if (includes(EDITABLE_ITEM_TYPES, childItem.type) && !childItem.readonly) { 101 // nextGroups = this.mergeItemData(nextGroups, getItemData(childItem)); 102 // } 103 // }); 104 // }); 105 // }); 106 // return nextGroups; 107 // } 108 109 cancelToast = () => { 110 let nextState = {}; 111 nextState.toastDetails = { 112 showToast: false, 113 title: "", 114 subText: "", 115 type: "default", 116 opts: {} 117 }; 118 this.setState(nextState) 119 } 120 121 onConfigSaved = (data) => { 122 let nextState = {}; 123 if (data.RunStatusText === "Starting") { 124 nextState.toastDetails = { 125 showToast: true, 126 title: "Settings Saved", 127 subText: "The app is starting.", 128 type: "success", 129 opts: { 130 showCancelButton: true, 131 cancelButtonText: "Close", 132 confirmButtonText: "Take me to the Dashboard", 133 confirmAction: () => { false ? () => { return; } : window.location.href = "/dashboard"; } //TODO: pass isConfirm and then should should check !isConfirm instead of false 134 } 135 } 136 } else if (data.NextAction.State === "") { 137 nextState.toastDetails = { 138 showToast: true, 139 title: "Settings Saved", 140 subText: "You can restart the app at any time to apply these changes.", 141 type: "success", 142 } 143 } else { 144 let alertMsg = ( 145 `You can ${data.NextAction.Title.toLowerCase()} the app at any time to apply these changes.` 146 ); 147 if (false) { //TODO: Should check Config.applyEnabled 148 alertMsg = ( 149 <p> 150 {alertMsg}<br /> 151 <i>NOTE: Applying changes may result in the application services being restarted.</i> 152 </p> 153 ); 154 } 155 nextState.toastDetails = { 156 showToast: true, 157 title: "Settings Saved", 158 type: "success", 159 subText: alertMsg, 160 opts: { 161 showCancelButton: true, 162 cancelButtonText: "Close", 163 confirmButtonText: false ? "Apply Changes" : "Restart Now", //TODO: Should check Config.applyEnabled 164 confirmAction: () => { true ? this.cancelToast() : this.props.setApplicationState(data.NextAction.State) } //TODO: pass isConfirm and then should should check !isConfirm instead of false 165 } 166 } 167 } 168 this.setState(nextState); 169 } 170 171 handleConfigSave = async () => { 172 await this.props.saveApplicationSettings(this.state.itemData, false) 173 .then((response) => { 174 this.onConfigSaved(response); 175 }) 176 } 177 178 handleConfigChange = (data) => { 179 this.setState({ itemData: data }); 180 // const itemValues = map(data, partialRight(omit, "data", "multi_data")); 181 // await this.props.getApplicationSettings({item_values: itemValues}, false) 182 // .then((response) => { 183 // this.setState({ 184 // groups: this.mergeGroupItemData(this.props.settingsFields, this.state.groups) 185 // }); 186 // }) 187 // .catch(() => { 188 // return; 189 // }); 190 } 191 192 render() { 193 const { 194 dataLoading, 195 settingsFields, 196 settingsFieldsList 197 } = this.props; 198 const { toastDetails } = this.state; 199 200 return ( 201 <ErrorBoundary> 202 <div className="flex-column flex1"> 203 <div className="flex-column flex1 u-overflow--hidden u-position--relative"> 204 <Toast toast={toastDetails} onCancel={this.cancelToast} /> 205 <div className="flex-1-auto u-overflow--auto container u-paddingTop--30"> 206 {dataLoading.appSettingsFieldsLoading || isEmpty(settingsFields) ? 207 <p>loading</p> 208 : 209 <ConfigRender 210 fieldsList={settingsFieldsList} 211 fields={settingsFields} 212 handleChange={this.handleConfigChange} 213 /> 214 } 215 </div> 216 <div className="flex-auto flex justifyContent--flexEnd layout-footer-actions"> 217 <button type="button" disabled={dataLoading.saveAppSettingsLoading} onClick={this.handleConfigSave} className="btn primary">{dataLoading.saveAppSettingsLoading ? "Saving" : "Save changes"}</button> 218 </div> 219 </div> 220 </div> 221 </ErrorBoundary> 222 ); 223 } 224 }