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  };