github.com/argoproj/argo-cd/v2@v2.10.9/ui/src/app/settings/components/project-details/resource-lists-panel.tsx (about)

     1  import {AutocompleteField, FormField, Tooltip} from 'argo-ui';
     2  import * as React from 'react';
     3  import {FormApi} from 'react-form';
     4  
     5  import {EditablePanel} from '../../../shared/components';
     6  import {ApplicationDestination, GroupKind, Groups, Project, ProjectSpec, ResourceKinds} from '../../../shared/models';
     7  
     8  function removeEl(items: any[], index: number) {
     9      return items.slice(0, index).concat(items.slice(index + 1));
    10  }
    11  
    12  function helpTip(text: string) {
    13      return (
    14          <Tooltip content={text}>
    15              <span style={{fontSize: 'smaller'}}>
    16                  {' '}
    17                  <i className='fas fa-info-circle' />
    18              </span>
    19          </Tooltip>
    20      );
    21  }
    22  
    23  type field = keyof ProjectSpec;
    24  
    25  const infoByField: {[type: string]: {title: string; helpText: string}} = {
    26      clusterResourceWhitelist: {
    27          title: 'cluster resource allow list',
    28          helpText: 'Cluster-scoped K8s API Groups and Kinds which are permitted to be deployed'
    29      },
    30      clusterResourceBlacklist: {
    31          title: 'cluster resource deny list',
    32          helpText: 'Cluster-scoped K8s API Groups and Kinds which are not permitted to be deployed'
    33      },
    34      namespaceResourceWhitelist: {
    35          title: 'namespace resource allow list',
    36          helpText: 'Namespace-scoped K8s API Groups and Kinds which are permitted to deploy'
    37      },
    38      namespaceResourceBlacklist: {
    39          title: 'namespace resource deny list',
    40          helpText: 'Namespace-scoped K8s API Groups and Kinds which are prohibited from being deployed'
    41      }
    42  };
    43  
    44  function viewList(type: field, proj: Project) {
    45      const info = infoByField[type];
    46      const list = proj.spec[type] as Array<GroupKind>;
    47      return (
    48          <React.Fragment>
    49              <p className='project-details__list-title'>
    50                  {info.title} {helpTip(info.helpText)}
    51              </p>
    52              {(list || []).length > 0 ? (
    53                  <React.Fragment>
    54                      <div className='row white-box__details-row'>
    55                          <div className='columns small-4'>Kind</div>
    56                          <div className='columns small-8'>Group</div>
    57                      </div>
    58                      {list.map((resource, i) => (
    59                          <div className='row white-box__details-row' key={i}>
    60                              <div className='columns small-4'>{resource.kind}</div>
    61                              <div className='columns small-8'>{resource.group}</div>
    62                          </div>
    63                      ))}
    64                  </React.Fragment>
    65              ) : (
    66                  <p>The {info.title} is empty</p>
    67              )}
    68          </React.Fragment>
    69      );
    70  }
    71  
    72  const sourceReposInfoByField: {[type: string]: {title: string; helpText: string}} = {
    73      sourceRepos: {
    74          title: 'source repositories',
    75          helpText: 'Git repositories where application manifests are permitted to be retrieved from'
    76      }
    77  };
    78  
    79  function viewSourceReposInfoList(type: field, proj: Project) {
    80      const info = sourceReposInfoByField[type];
    81      const list = proj.spec[type] as Array<string>;
    82      return (
    83          <React.Fragment>
    84              <p className='project-details__list-title'>
    85                  {info.title} {helpTip(info.helpText)}
    86              </p>
    87              {(list || []).length > 0 ? (
    88                  <React.Fragment>
    89                      {list.map((repo, i) => (
    90                          <div className='row white-box__details-row' key={i}>
    91                              <div className='columns small-12'>{repo}</div>
    92                          </div>
    93                      ))}
    94                  </React.Fragment>
    95              ) : (
    96                  <p>The {info.title} is empty</p>
    97              )}
    98          </React.Fragment>
    99      );
   100  }
   101  
   102  const sourceNamespacesInfoByField: {[type: string]: {title: string; helpText: string}} = {
   103      sourceNamespaces: {
   104          title: 'source namespaces',
   105          helpText: 'Kubernetes namespaces where application resources are allowed to be created in'
   106      }
   107  };
   108  
   109  function viewSourceNamespacesInfoList(type: field, proj: Project) {
   110      const info = sourceNamespacesInfoByField[type];
   111      const list = proj.spec[type] as Array<string>;
   112      return (
   113          <React.Fragment>
   114              <p className='project-details__list-title'>
   115                  {info.title} {helpTip(info.helpText)}
   116              </p>
   117              {(list || []).length > 0 ? (
   118                  <React.Fragment>
   119                      {list.map((namespace, i) => (
   120                          <div className='row white-box__details-row' key={i}>
   121                              <div className='columns small-12'>{namespace}</div>
   122                          </div>
   123                      ))}
   124                  </React.Fragment>
   125              ) : (
   126                  <p>The {info.title} is empty</p>
   127              )}
   128          </React.Fragment>
   129      );
   130  }
   131  
   132  const destinationsInfoByField: {[type: string]: {title: string; helpText: string}} = {
   133      destinations: {
   134          title: 'destinations',
   135          helpText: 'Cluster and namespaces where applications are permitted to be deployed to'
   136      }
   137  };
   138  
   139  function viewDestinationsInfoList(type: field, proj: Project) {
   140      const info = destinationsInfoByField[type];
   141      const list = proj.spec[type] as Array<ApplicationDestination>;
   142      return (
   143          <React.Fragment>
   144              <p className='project-details__list-title'>
   145                  {info.title} {helpTip(info.helpText)}
   146              </p>
   147              {(list || []).length > 0 ? (
   148                  <React.Fragment>
   149                      <div className='row white-box__details-row'>
   150                          <div className='columns small-4'>Server</div>
   151                          <div className='columns small-8'>Namespace</div>
   152                      </div>
   153                      {list.map((destination, i) => (
   154                          <div className='row white-box__details-row' key={i}>
   155                              <div className='columns small-4'>{destination.server}</div>
   156                              <div className='columns small-8'>{destination.namespace}</div>
   157                          </div>
   158                      ))}
   159                  </React.Fragment>
   160              ) : (
   161                  <p>The {info.title} is empty</p>
   162              )}
   163          </React.Fragment>
   164      );
   165  }
   166  
   167  function editList(type: field, formApi: FormApi) {
   168      const info = infoByField[type];
   169  
   170      return (
   171          <React.Fragment>
   172              <p className='project-details__list-title'>
   173                  {info.title} {helpTip(info.helpText)}
   174              </p>
   175              <div className='row white-box__details-row'>
   176                  <div className='columns small-4'>Kind</div>
   177                  <div className='columns small-8'>Group</div>
   178              </div>
   179              {(formApi.values.spec[type] || []).map((_: Project, i: number) => (
   180                  <div className='row white-box__details-row' key={i}>
   181                      <div className='columns small-4'>
   182                          <FormField
   183                              formApi={formApi}
   184                              field={`spec.${type}[${i}].kind`}
   185                              component={AutocompleteField}
   186                              componentProps={{items: ResourceKinds, filterSuggestions: true}}
   187                          />
   188                      </div>
   189                      <div className='columns small-8'>
   190                          <FormField formApi={formApi} field={`spec.${type}[${i}].group`} component={AutocompleteField} componentProps={{items: Groups, filterSuggestions: true}} />
   191                      </div>
   192                      <i className='fa fa-times' onClick={() => formApi.setValue(`spec.${type}`, removeEl(formApi.values.spec[type], i))} />
   193                  </div>
   194              ))}
   195              <button className='argo-button argo-button--short' onClick={() => formApi.setValue(`spec.${type}`, (formApi.values.spec[type] || []).concat({group: '*', kind: '*'}))}>
   196                  ADD RESOURCE
   197              </button>
   198          </React.Fragment>
   199      );
   200  }
   201  
   202  export const ResourceListsPanel = ({proj, saveProject, title}: {proj: Project; title?: React.ReactNode; saveProject?: (proj: Project) => any}) => (
   203      <EditablePanel
   204          save={saveProject}
   205          values={proj}
   206          view={
   207              <React.Fragment>
   208                  {title}
   209                  {Object.keys(infoByField).map(key => (
   210                      <React.Fragment key={key}>{viewList(key as field, proj)}</React.Fragment>
   211                  ))}
   212                  {!proj.metadata && Object.keys(sourceReposInfoByField).map(key => <React.Fragment key={key}>{viewSourceReposInfoList(key as field, proj)}</React.Fragment>)}
   213                  {!proj.metadata &&
   214                      Object.keys(sourceNamespacesInfoByField).map(key => <React.Fragment key={key}>{viewSourceNamespacesInfoList(key as field, proj)}</React.Fragment>)}
   215                  {!proj.metadata && Object.keys(destinationsInfoByField).map(key => <React.Fragment key={key}>{viewDestinationsInfoList(key as field, proj)}</React.Fragment>)}
   216              </React.Fragment>
   217          }
   218          edit={
   219              saveProject &&
   220              (formApi => (
   221                  <React.Fragment>
   222                      {title}
   223                      {Object.keys(infoByField).map(key => (
   224                          <React.Fragment key={key}>{editList(key as field, formApi)}</React.Fragment>
   225                      ))}
   226                  </React.Fragment>
   227              ))
   228          }
   229          items={[]}
   230      />
   231  );