github.com/argoproj/argo-cd@v1.8.7/ui/src/app/applications/components/application-sync-panel/application-sync-panel.tsx (about) 1 import {ErrorNotification, FormField, NotificationType, SlidingPanel} from 'argo-ui'; 2 import * as React from 'react'; 3 import {Checkbox, Form, FormApi, Text} from 'react-form'; 4 5 import {Spinner} from '../../../shared/components'; 6 import {Consumer} from '../../../shared/context'; 7 import * as models from '../../../shared/models'; 8 import {services} from '../../../shared/services'; 9 import {ComparisonStatusIcon, nodeKey} from '../utils'; 10 11 export const ApplicationSyncPanel = ({application, selectedResource, hide}: {application: models.Application; selectedResource: string; hide: () => any}) => { 12 const [form, setForm] = React.useState<FormApi>(null); 13 const isVisible = !!(selectedResource && application); 14 const appResources = ((application && selectedResource && application.status && application.status.resources) || []) 15 .sort((first, second) => nodeKey(first).localeCompare(nodeKey(second))) 16 .filter(item => !item.hook); 17 const syncResIndex = appResources.findIndex(item => nodeKey(item) === selectedResource); 18 const syncStrategy = {} as models.SyncStrategy; 19 const [isPending, setPending] = React.useState(false); 20 21 return ( 22 <Consumer> 23 {ctx => ( 24 <SlidingPanel 25 isMiddle={true} 26 isShown={isVisible} 27 onClose={() => hide()} 28 header={ 29 <div> 30 <button 31 qe-id='application-sync-panel-button-synchronize' 32 className='argo-button argo-button--base' 33 disabled={isPending} 34 onClick={() => form.submitForm(null)}> 35 <Spinner show={isPending} style={{marginRight: '5px'}} /> 36 Synchronize 37 </button>{' '} 38 <button onClick={() => hide()} className='argo-button argo-button--base-o'> 39 Cancel 40 </button> 41 </div> 42 }> 43 {isVisible && ( 44 <Form 45 defaultValues={{ 46 revision: application.spec.source.targetRevision || 'HEAD', 47 resources: appResources.map((_, i) => i === syncResIndex || syncResIndex === -1) 48 }} 49 validateError={values => ({ 50 resources: values.resources.every((item: boolean) => !item) && 'Select at least one resource' 51 })} 52 onSubmit={async (params: any) => { 53 setPending(true); 54 let resources = appResources.filter((_, i) => params.resources[i]); 55 if (resources.length === appResources.length) { 56 resources = null; 57 } 58 if (params.applyOnly) { 59 syncStrategy.apply = {force: params.force}; 60 } else { 61 syncStrategy.hook = {force: params.force}; 62 } 63 try { 64 await services.applications.sync(application.metadata.name, params.revision, params.prune, params.dryRun, syncStrategy, resources); 65 hide(); 66 } catch (e) { 67 ctx.notifications.show({ 68 content: <ErrorNotification title='Unable to deploy revision' e={e} />, 69 type: NotificationType.Error 70 }); 71 } finally { 72 setPending(false); 73 } 74 }} 75 getApi={setForm}> 76 {formApi => ( 77 <form role='form' className='width-control' onSubmit={formApi.submitForm}> 78 <h6> 79 Synchronizing application manifests from <a href={application.spec.source.repoURL}>{application.spec.source.repoURL}</a> 80 </h6> 81 <div className='argo-form-row'> 82 <FormField formApi={formApi} label='Revision' field='revision' component={Text} /> 83 </div> 84 85 <div className='argo-form-row'> 86 <div> 87 <span> 88 <Checkbox id='prune-on-sync-checkbox' field='prune' /> <label htmlFor='prune-on-sync-checkbox'>Prune</label> 89 </span> 90 <span> 91 <Checkbox id='dry-run-checkbox' field='dryRun' /> <label htmlFor='dry-run-checkbox'>Dry Run</label> 92 </span> 93 <span> 94 <Checkbox id='apply-only-checkbox' field='applyOnly' /> <label htmlFor='apply-only-checkbox'>Apply Only</label> 95 </span> 96 <span> 97 <Checkbox id='force-checkbox' field='force' /> <label htmlFor='force-checkbox'>Force</label> 98 </span> 99 </div> 100 <label>Synchronize resources:</label> 101 <div style={{float: 'right'}}> 102 <a onClick={() => formApi.setValue('resources', formApi.values.resources.map(() => true))}>all</a> /{' '} 103 <a 104 onClick={() => 105 formApi.setValue( 106 'resources', 107 application.status.resources.map((resource: models.ResourceStatus) => resource.status === models.SyncStatuses.OutOfSync) 108 ) 109 }> 110 out of sync 111 </a>{' '} 112 / <a onClick={() => formApi.setValue('resources', formApi.values.resources.map(() => false))}>none</a> 113 </div> 114 {!formApi.values.resources.every((item: boolean) => item) && ( 115 <div className='application-details__warning'>WARNING: partial synchronization is not recorded in history</div> 116 )} 117 <div style={{paddingLeft: '1em'}}> 118 {application.status.resources 119 .filter(item => !item.hook) 120 .map((item, i) => { 121 const resKey = nodeKey(item); 122 return ( 123 <div key={resKey}> 124 <Checkbox id={resKey} field={`resources[${i}]`} />{' '} 125 <label htmlFor={resKey}> 126 {resKey} <ComparisonStatusIcon status={item.status} resource={item} /> 127 </label> 128 </div> 129 ); 130 })} 131 {formApi.errors.resources && <div className='argo-form-row__error-msg'>{formApi.errors.resources}</div>} 132 </div> 133 </div> 134 </form> 135 )} 136 </Form> 137 )} 138 </SlidingPanel> 139 )} 140 </Consumer> 141 ); 142 };