github.com/argoproj/argo-cd/v2@v2.10.9/ui/src/app/applications/components/application-sync-options/application-sync-options.tsx (about) 1 import {Checkbox, Select, Tooltip} from 'argo-ui'; 2 import * as classNames from 'classnames'; 3 import * as React from 'react'; 4 import * as ReactForm from 'react-form'; 5 6 import './application-sync-options.scss'; 7 8 export const REPLACE_WARNING = `The resources will be synced using 'kubectl replace/create' command that is a potentially destructive action and might cause resources recreation.`; 9 export const FORCE_WARNING = `The resources will be synced using '--force' that is a potentially destructive action and will immediately remove resources from the API and bypasses graceful deletion. Immediate deletion of some resources may result in inconsistency or data loss.`; 10 export const PRUNE_ALL_WARNING = `The resources will be synced using '--prune', and all resources are marked to be pruned. Only continue if you want to delete all of the Application's resources.`; 11 12 export interface ApplicationSyncOptionProps { 13 options: string[]; 14 onChanged: (updatedOptions: string[]) => any; 15 id?: string; 16 } 17 18 function selectOption(name: string, label: string, defaultVal: string, values: string[], props: ApplicationSyncOptionProps) { 19 const options = [...(props.options || [])]; 20 const prefix = `${name}=`; 21 const index = options.findIndex(item => item.startsWith(prefix)); 22 const val = index < 0 ? defaultVal : options[index].substr(prefix.length); 23 24 return ( 25 <div className='application-sync-options__select'> 26 <label>{label}:</label> 27 <Select 28 value={val} 29 options={values} 30 onChange={opt => { 31 const newValue = `${name}=${opt.value}`; 32 if (index < 0) { 33 props.onChanged(options.concat(newValue)); 34 } else { 35 options[index] = newValue; 36 props.onChanged(options); 37 } 38 }} 39 /> 40 </div> 41 ); 42 } 43 44 function booleanOption(name: string, label: string, defaultVal: boolean, props: ApplicationSyncOptionProps, invert: boolean, warning: string = null) { 45 const options = [...(props.options || [])]; 46 const prefix = `${name}=`; 47 const index = options.findIndex(item => item.startsWith(prefix)); 48 const checked = index < 0 ? defaultVal : options[index].substring(prefix.length) === (invert ? 'false' : 'true'); 49 return ( 50 <React.Fragment> 51 <Checkbox 52 id={`sync-option-${name}-${props.id}`} 53 checked={checked} 54 onChange={(val: boolean) => { 55 if (index < 0) { 56 props.onChanged(options.concat(`${name}=${invert ? !val : val}`)); 57 } else { 58 options.splice(index, 1); 59 props.onChanged(options); 60 } 61 }} 62 /> 63 <label htmlFor={`sync-option-${name}-${props.id}`}>{label}</label>{' '} 64 {warning && ( 65 <> 66 <Tooltip content={warning}> 67 <i className='fa fa-exclamation-triangle' /> 68 </Tooltip> 69 {checked && <div className='application-sync-options__warning'>{warning}</div>} 70 </> 71 )} 72 </React.Fragment> 73 ); 74 } 75 76 enum ManualSyncFlags { 77 Prune = 'Prune', 78 DryRun = 'Dry Run', 79 ApplyOnly = 'Apply Only', 80 Force = 'Force' 81 } 82 83 export interface SyncFlags { 84 Prune: boolean; 85 DryRun: boolean; 86 ApplyOnly: boolean; 87 Force: boolean; 88 } 89 90 const syncOptions: Array<(props: ApplicationSyncOptionProps) => React.ReactNode> = [ 91 props => booleanOption('Validate', 'Skip Schema Validation', false, props, true), 92 props => booleanOption('CreateNamespace', 'Auto-Create Namespace', false, props, false), 93 props => booleanOption('PruneLast', 'Prune Last', false, props, false), 94 props => booleanOption('ApplyOutOfSyncOnly', 'Apply Out of Sync Only', false, props, false), 95 props => booleanOption('RespectIgnoreDifferences', 'Respect Ignore Differences', false, props, false), 96 props => booleanOption('ServerSideApply', 'Server-Side Apply', false, props, false), 97 props => selectOption('PrunePropagationPolicy', 'Prune Propagation Policy', 'foreground', ['foreground', 'background', 'orphan'], props) 98 ]; 99 100 const optionStyle = {marginTop: '0.5em'}; 101 102 export const ApplicationSyncOptions = (props: ApplicationSyncOptionProps) => ( 103 <div className='row application-sync-options'> 104 {syncOptions.map((render, i) => ( 105 <div 106 key={i} 107 style={optionStyle} 108 className={classNames('small-12', { 109 'large-6': i < syncOptions.length - 1 110 })}> 111 {render(props)} 112 </div> 113 ))} 114 <div className='small-12' style={optionStyle}> 115 {booleanOption('Replace', 'Replace', false, props, false, REPLACE_WARNING)} 116 </div> 117 </div> 118 ); 119 120 export const ApplicationManualSyncFlags = ReactForm.FormField((props: {fieldApi: ReactForm.FieldApi; id?: string}) => { 121 const { 122 fieldApi: {getValue, setValue, setTouched} 123 } = props; 124 const val = getValue() || false; 125 return ( 126 <div style={optionStyle}> 127 {Object.keys(ManualSyncFlags).map(flag => ( 128 <React.Fragment key={flag}> 129 <Checkbox 130 id={`sync-option-${flag}-${props.id}`} 131 checked={val[flag]} 132 onChange={(newVal: boolean) => { 133 setTouched(true); 134 const update = {...val}; 135 update[flag] = newVal; 136 setValue(update); 137 }} 138 /> 139 <label htmlFor={`sync-option-${flag}-${props.id}`}>{ManualSyncFlags[flag as keyof typeof ManualSyncFlags]}</label>{' '} 140 </React.Fragment> 141 ))} 142 </div> 143 ); 144 }); 145 146 export const ApplicationSyncOptionsField = ReactForm.FormField((props: {fieldApi: ReactForm.FieldApi}) => { 147 const { 148 fieldApi: {getValue, setValue, setTouched} 149 } = props; 150 const val = getValue() || []; 151 return ( 152 <div className='argo-field' style={{borderBottom: '0'}}> 153 <ApplicationSyncOptions 154 options={val} 155 onChanged={opts => { 156 setTouched(true); 157 setValue(opts); 158 }} 159 /> 160 </div> 161 ); 162 });