github.com/argoproj/argo-cd/v2@v2.10.9/ui/src/app/shared/components/editable-panel/editable-panel.tsx (about) 1 import {ErrorNotification, NotificationType} from 'argo-ui'; 2 import * as classNames from 'classnames'; 3 import * as React from 'react'; 4 import {Form, FormApi} from 'react-form'; 5 import {helpTip} from '../../../applications/components/utils'; 6 import {Consumer} from '../../context'; 7 import {Spinner} from '../spinner'; 8 9 export interface EditablePanelItem { 10 title: string; 11 customTitle?: string | React.ReactNode; 12 key?: string; 13 before?: React.ReactNode; 14 view: string | React.ReactNode; 15 edit?: (formApi: FormApi) => React.ReactNode; 16 titleEdit?: (formApi: FormApi) => React.ReactNode; 17 } 18 19 export interface EditablePanelProps<T> { 20 title?: string | React.ReactNode; 21 values: T; 22 validate?: (values: T) => any; 23 save?: (input: T, query: {validate?: boolean}) => Promise<any>; 24 items: EditablePanelItem[]; 25 onModeSwitch?: () => any; 26 noReadonlyMode?: boolean; 27 view?: string | React.ReactNode; 28 edit?: (formApi: FormApi) => React.ReactNode; 29 hasMultipleSources?: boolean; 30 } 31 32 interface EditablePanelState { 33 edit: boolean; 34 saving: boolean; 35 } 36 37 require('./editable-panel.scss'); 38 39 export class EditablePanel<T = {}> extends React.Component<EditablePanelProps<T>, EditablePanelState> { 40 private formApi: FormApi; 41 42 constructor(props: EditablePanelProps<T>) { 43 super(props); 44 this.state = {edit: !!props.noReadonlyMode, saving: false}; 45 } 46 47 public UNSAFE_componentWillReceiveProps(nextProps: EditablePanelProps<T>) { 48 if (this.formApi && JSON.stringify(this.props.values) !== JSON.stringify(nextProps.values)) { 49 if (!!nextProps.noReadonlyMode) { 50 this.formApi.setAllValues(nextProps.values); 51 } 52 } 53 } 54 55 public render() { 56 return ( 57 <Consumer> 58 {ctx => ( 59 <div className={classNames('white-box editable-panel', {'editable-panel--disabled': this.state.saving})}> 60 <div className='white-box__details'> 61 {!this.props.noReadonlyMode && this.props.save && ( 62 <div className='editable-panel__buttons'> 63 {!this.state.edit && ( 64 <button 65 onClick={() => { 66 this.setState({edit: true}); 67 this.onModeSwitch(); 68 }} 69 disabled={this.props.hasMultipleSources} 70 className='argo-button argo-button--base'> 71 {this.props.hasMultipleSources && 72 helpTip('Parameters are not editable for applications with multiple sources. You can edit them in the "Manifest" tab.')}{' '} 73 Edit 74 </button> 75 )} 76 {this.state.edit && ( 77 <React.Fragment> 78 <button 79 disabled={this.state.saving} 80 onClick={() => !this.state.saving && this.formApi.submitForm(null)} 81 className='argo-button argo-button--base'> 82 <Spinner show={this.state.saving} style={{marginRight: '5px'}} /> 83 Save 84 </button>{' '} 85 <button 86 onClick={() => { 87 this.setState({edit: false}); 88 this.onModeSwitch(); 89 }} 90 className='argo-button argo-button--base-o'> 91 Cancel 92 </button> 93 </React.Fragment> 94 )} 95 </div> 96 )} 97 {this.props.title && <p>{this.props.title}</p>} 98 {(!this.state.edit && ( 99 <React.Fragment> 100 {this.props.view} 101 {this.props.items 102 .filter(item => item.view) 103 .map(item => ( 104 <React.Fragment key={item.key || item.title}> 105 {item.before} 106 <div className='row white-box__details-row'> 107 <div className='columns small-3'>{item.customTitle || item.title}</div> 108 <div className='columns small-9'>{item.view}</div> 109 </div> 110 </React.Fragment> 111 ))} 112 </React.Fragment> 113 )) || ( 114 <Form 115 getApi={api => (this.formApi = api)} 116 formDidUpdate={async form => { 117 if (this.props.noReadonlyMode && this.props.save) { 118 await this.props.save(form.values as any, {}); 119 } 120 }} 121 onSubmit={async input => { 122 try { 123 this.setState({saving: true}); 124 await this.props.save(input as any, {}); 125 this.setState({edit: false, saving: false}); 126 this.onModeSwitch(); 127 } catch (e) { 128 ctx.notifications.show({ 129 content: <ErrorNotification title='Unable to save changes' e={e} />, 130 type: NotificationType.Error 131 }); 132 } finally { 133 this.setState({saving: false}); 134 } 135 }} 136 defaultValues={this.props.values} 137 validateError={this.props.validate}> 138 {api => ( 139 <React.Fragment> 140 {this.props.edit && this.props.edit(api)} 141 {this.props.items.map(item => ( 142 <React.Fragment key={item.key || item.title}> 143 {item.before} 144 <div className='row white-box__details-row'> 145 <div className='columns small-3'>{(item.titleEdit && item.titleEdit(api)) || item.customTitle || item.title}</div> 146 <div className='columns small-9'>{(item.edit && item.edit(api)) || item.view}</div> 147 </div> 148 </React.Fragment> 149 ))} 150 </React.Fragment> 151 )} 152 </Form> 153 )} 154 </div> 155 </div> 156 )} 157 </Consumer> 158 ); 159 } 160 161 private onModeSwitch() { 162 if (this.props.onModeSwitch) { 163 this.props.onModeSwitch(); 164 } 165 } 166 }