github.com/argoproj/argo-cd/v2@v2.10.9/ui/src/app/settings/components/project-sync-windows-edit/project-sync-windows-edit.tsx (about)

     1  import {Tooltip} from 'argo-ui';
     2  import * as React from 'react';
     3  import * as ReactForm from 'react-form';
     4  import {SyncWindow} from '../../../shared/models';
     5  
     6  require('./project-sync-windows-edit.scss');
     7  interface ProjectSyncWindowProps {
     8      projName: string;
     9      window: SyncWindow;
    10      formApi: ReactForm.FormApi;
    11  }
    12  
    13  function helpTip(text: string) {
    14      return (
    15          <Tooltip content={text}>
    16              <span style={{fontSize: 'smaller'}}>
    17                  {' '}
    18                  <i className='fas fa-info-circle' />
    19              </span>
    20          </Tooltip>
    21      );
    22  }
    23  
    24  export const ProjectSyncWindowApplicationsEdit = (props: ProjectSyncWindowProps) => (
    25      <React.Fragment>
    26          <p>APPLICATIONS</p>
    27          <div>Manage applications assigned to this window ("*" for any)</div>
    28          <div className='argo-table-list__row'>
    29              {(props.window.applications || []).map((a, i) => (
    30                  <Attribute
    31                      key={i}
    32                      field={['window.applications', i]}
    33                      formApi={props.formApi}
    34                      projName={props.projName}
    35                      deleteApp={() => props.formApi.setValue('window.applications', removeEl(props.window.applications, i))}
    36                  />
    37              ))}
    38              <div className='row'>
    39                  <div className='columns small-6'>
    40                      <a
    41                          className='argo-button argo-button--base'
    42                          onClick={() => {
    43                              const newA = '';
    44                              props.formApi.setValue('window.applications', (props.formApi.values.window.applications || []).concat(newA));
    45                          }}>
    46                          Add Application
    47                      </a>
    48                  </div>
    49              </div>
    50          </div>
    51      </React.Fragment>
    52  );
    53  
    54  export const ProjectSyncWindowNamespaceEdit = (props: ProjectSyncWindowProps) => (
    55      <React.Fragment>
    56          <p>NAMESPACES</p>
    57          <div>Manage namespaces assigned to this window ("*" for any)</div>
    58          <div className='argo-table-list__row'>
    59              {(props.window.namespaces || []).map((n, i) => (
    60                  <Attribute
    61                      key={i}
    62                      field={['window.namespaces', i]}
    63                      formApi={props.formApi}
    64                      projName={props.projName}
    65                      deleteApp={() => props.formApi.setValue('window.namespaces', removeEl(props.window.namespaces, i))}
    66                  />
    67              ))}
    68              <div className='row'>
    69                  <div className='columns small-6'>
    70                      <a
    71                          className='argo-button argo-button--base'
    72                          onClick={() => {
    73                              const newN = '';
    74                              props.formApi.setValue('window.namespaces', (props.formApi.values.window.namespaces || []).concat(newN));
    75                          }}>
    76                          Add Namespace
    77                      </a>
    78                  </div>
    79              </div>
    80          </div>
    81      </React.Fragment>
    82  );
    83  
    84  export const ProjectSyncWindowClusterEdit = (props: ProjectSyncWindowProps) => (
    85      <React.Fragment>
    86          <p>CLUSTERS</p>
    87          <div>Manage clusters assigned to this window ("*" for any)</div>
    88          <div className='argo-table-list__row'>
    89              {(props.window.clusters || []).map((c, i) => (
    90                  <Attribute
    91                      key={i}
    92                      field={['window.clusters', i]}
    93                      formApi={props.formApi}
    94                      projName={props.projName}
    95                      deleteApp={() => props.formApi.setValue('window.clusters', removeEl(props.window.clusters, i))}
    96                  />
    97              ))}
    98              <div className='row'>
    99                  <div className='columns small-6'>
   100                      <a
   101                          className='argo-button argo-button--base'
   102                          onClick={() => {
   103                              const newC = '';
   104                              props.formApi.setValue('window.clusters', (props.formApi.values.window.clusters || []).concat(newC));
   105                          }}>
   106                          Add Cluster
   107                      </a>
   108                  </div>
   109              </div>
   110          </div>
   111      </React.Fragment>
   112  );
   113  
   114  interface AttributeProps {
   115      projName: string;
   116      roleName: string;
   117      fieldApi: ReactForm.FieldApi;
   118      deleteApp: () => void;
   119  }
   120  
   121  function removeEl(items: any[], index: number) {
   122      items.splice(index, 1);
   123      return items;
   124  }
   125  
   126  class AttributeWrapper extends React.Component<AttributeProps, any> {
   127      public render() {
   128          return (
   129              <div className='row'>
   130                  <div className='columns small-6'>
   131                      <input
   132                          className='argo-field'
   133                          value={this.getApplication()}
   134                          onChange={(e: React.ChangeEvent<HTMLInputElement>) => {
   135                              this.setApplication(e.target.value);
   136                          }}
   137                      />
   138                  </div>
   139                  <div className='columns small-1'>
   140                      <i className='fa fa-times' onClick={() => this.props.deleteApp()} style={{cursor: 'pointer'}} />
   141                  </div>
   142              </div>
   143          );
   144      }
   145  
   146      private getApplication(): string {
   147          return this.props.fieldApi.getValue();
   148      }
   149  
   150      private setApplication(application: string) {
   151          this.props.fieldApi.setValue(application);
   152      }
   153  }
   154  
   155  const Attribute = ReactForm.FormField(AttributeWrapper);
   156  
   157  function generateSchedule(minute?: string, hour?: string, dom?: string, month?: string, dow?: string): string {
   158      return `${minute} ${hour} ${dom} ${month} ${dow}`;
   159  }
   160  
   161  export const ProjectSyncWindowScheduleEdit = (props: ProjectSyncWindowProps) => (
   162      <React.Fragment>
   163          <p>Schedule</p>
   164          <div className='argo-table-list__head'>
   165              <div className='row'>
   166                  <div className='columns small-2'>Minute{helpTip('The minute/minutes assigned to the schedule')}</div>
   167                  <div className='columns small-2'>Hour{helpTip('The hour/hours assigned to the schedule')}</div>
   168                  <div className='columns small-2'>Day Of The Month{helpTip('The day/days of the month assigned to the schedule')}</div>
   169                  <div className='columns small-2'>Month{helpTip('The month/months assigned to the schedule.')}</div>
   170                  <div className='columns small-2'>Day Of the Week{helpTip('The day/days of the week assigned to the schedule')}</div>
   171              </div>
   172          </div>
   173          <div className='row project-sync-windows-panel__form-row'>
   174              <Schedule key='schedule' field={'window.schedule'} formApi={props.formApi} />
   175          </div>
   176      </React.Fragment>
   177  );
   178  
   179  interface ScheduleProps {
   180      fieldApi: ReactForm.FieldApi;
   181  }
   182  
   183  function generateRange(limit: number, zeroStart: boolean): string[] {
   184      const range: string[] = new Array(limit);
   185      for (let i = 0; i < limit; i++) {
   186          if (zeroStart) {
   187              range[i] = i.toString();
   188          } else {
   189              range[i] = (i + 1).toString();
   190          }
   191      }
   192      return range;
   193  }
   194  
   195  function getRanges(config: string): string[] {
   196      const values = [];
   197      const fields = config.split(',');
   198      for (const f of fields) {
   199          if (f.search(/-/) !== -1) {
   200              const r = f.split('-');
   201              for (let i = parseInt(r[0], 10); i <= parseInt(r[1], 10); i++) {
   202                  values.push(i.toString());
   203              }
   204          } else {
   205              values.push(f);
   206          }
   207      }
   208      return values;
   209  }
   210  
   211  function setRanges(config: string[]): string {
   212      const values = [];
   213      const ranges = [];
   214  
   215      config.sort((n1, n2) => parseInt(n1, 10) - parseInt(n2, 10));
   216  
   217      for (let i = 0; i < config.length; i++) {
   218          if (ranges.length === 0) {
   219              ranges[0] = [config[i]];
   220          } else {
   221              if (parseInt(config[i], 10) - 1 === parseInt(config[i - 1], 10)) {
   222                  ranges[ranges.length - 1].push(config[i]);
   223              } else {
   224                  ranges[ranges.length] = [config[i]];
   225              }
   226          }
   227      }
   228  
   229      if (ranges.length > 0) {
   230          for (const r of ranges) {
   231              if (r.length > 1) {
   232                  values.push(r[0] + '-' + r[r.length - 1]);
   233              } else {
   234                  values.push(r[0]);
   235              }
   236          }
   237      }
   238      return values.join(',');
   239  }
   240  
   241  class ScheduleWrapper extends React.Component<ScheduleProps, any> {
   242      public render() {
   243          return (
   244              <React.Fragment>
   245                  <div className='columns small-2'>
   246                      <select
   247                          className='argo-field project-sync-windows-panel__options-wrapper'
   248                          size={8}
   249                          name='minute'
   250                          multiple={true}
   251                          value={this.getValues(0)}
   252                          onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
   253                              const minuteOptions = e.target.options;
   254                              const minuteValues = [];
   255                              for (let i = 0, l = minuteOptions.length; i < l; i++) {
   256                                  if (minuteOptions[i].selected) {
   257                                      minuteValues.push(minuteOptions[i].value);
   258                                  }
   259                              }
   260                              this.setValues(minuteValues, 0);
   261                          }}>
   262                          <option key='wildcard' value='*' className='project-sync-windows-panel__text-wrapper'>
   263                              Every Minute
   264                          </option>
   265                          {generateRange(60, true).map(m => (
   266                              <option key={m}>{m}</option>
   267                          ))}
   268                      </select>
   269                  </div>
   270                  <div className='columns small-2'>
   271                      <select
   272                          className='argo-field project-sync-windows-panel__options-wrapper'
   273                          size={8}
   274                          name='hours'
   275                          multiple={true}
   276                          value={this.getValues(1)}
   277                          onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
   278                              const hourOptions = e.target.options;
   279                              const hourValues = [];
   280                              for (let i = 0, l = hourOptions.length; i < l; i++) {
   281                                  if (hourOptions[i].selected) {
   282                                      hourValues.push(hourOptions[i].value);
   283                                  }
   284                              }
   285                              this.setValues(hourValues, 1);
   286                          }}>
   287                          <option key='wildcard' value='*' className='project-sync-windows-panel__text-wrapper'>
   288                              Every Hour
   289                          </option>
   290                          {generateRange(24, true).map(m => (
   291                              <option key={m}>{m}</option>
   292                          ))}
   293                      </select>
   294                  </div>
   295                  <div className='columns small-2'>
   296                      <select
   297                          className='argo-field project-sync-windows-panel__options-wrapper'
   298                          size={8}
   299                          name='dom'
   300                          multiple={true}
   301                          value={this.getValues(2)}
   302                          onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
   303                              const domOptions = e.target.options;
   304                              const domValues = [];
   305                              for (let i = 0, l = domOptions.length; i < l; i++) {
   306                                  if (domOptions[i].selected) {
   307                                      domValues.push(domOptions[i].value);
   308                                  }
   309                              }
   310                              this.setValues(domValues, 2);
   311                          }}>
   312                          <option key='wildcard' value='*' className='project-sync-windows-panel__text-wrapper'>
   313                              Every Day
   314                          </option>
   315                          {generateRange(31, false).map(m => (
   316                              <option key={m}>{m}</option>
   317                          ))}
   318                      </select>
   319                  </div>
   320                  <div className='columns small-2'>
   321                      <select
   322                          className='argo-field project-sync-windows-panel__options-wrapper'
   323                          size={8}
   324                          name='month'
   325                          multiple={true}
   326                          value={this.getValues(3)}
   327                          onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
   328                              const monthOptions = e.target.options;
   329                              const monthValues = [];
   330                              for (let i = 0, l = monthOptions.length; i < l; i++) {
   331                                  if (monthOptions[i].selected) {
   332                                      monthValues.push(monthOptions[i].value);
   333                                  }
   334                              }
   335                              this.setValues(monthValues, 3);
   336                          }}>
   337                          <option key='wildcard' value='*' className='project-sync-windows-panel__text-wrapper'>
   338                              Every Month
   339                          </option>
   340                          <option key='1' value='1'>
   341                              Jan
   342                          </option>
   343                          <option key='2' value='2'>
   344                              Feb
   345                          </option>
   346                          <option key='3' value='3'>
   347                              Mar
   348                          </option>
   349                          <option key='4' value='4'>
   350                              Apr
   351                          </option>
   352                          <option key='5' value='5'>
   353                              May
   354                          </option>
   355                          <option key='6' value='6'>
   356                              Jun
   357                          </option>
   358                          <option key='7' value='7'>
   359                              Jul
   360                          </option>
   361                          <option key='8' value='8'>
   362                              Aug
   363                          </option>
   364                          <option key='9' value='9'>
   365                              Sep
   366                          </option>
   367                          <option key='10' value='10'>
   368                              Oct
   369                          </option>
   370                          <option key='11' value='11'>
   371                              Nov
   372                          </option>
   373                          <option key='12' value='12'>
   374                              Dec
   375                          </option>
   376                      </select>
   377                  </div>
   378                  <div className='columns small-2'>
   379                      <select
   380                          className='argo-field project-sync-windows-panel__options-wrapper'
   381                          size={8}
   382                          name='dow'
   383                          multiple={true}
   384                          value={this.getValues(4)}
   385                          onChange={(e: React.ChangeEvent<HTMLSelectElement>) => {
   386                              const dowOptions = e.target.options;
   387                              const dowValues = [];
   388                              for (let i = 0, l = dowOptions.length; i < l; i++) {
   389                                  if (dowOptions[i].selected) {
   390                                      dowValues.push(dowOptions[i].value);
   391                                  }
   392                              }
   393                              this.setValues(dowValues, 4);
   394                          }}>
   395                          <option key='wildcard' value='*' className='project-sync-windows-panel__text-wrapper'>
   396                              Sunday-Saturday
   397                          </option>
   398                          <option key='0' value='0'>
   399                              Sun
   400                          </option>
   401                          <option key='1' value='1'>
   402                              Mon
   403                          </option>
   404                          <option key='2' value='2'>
   405                              Tue
   406                          </option>
   407                          <option key='3' value='3'>
   408                              Wed
   409                          </option>
   410                          <option key='4' value='4'>
   411                              Thu
   412                          </option>
   413                          <option key='5' value='5'>
   414                              Fri
   415                          </option>
   416                          <option key='6' value='6'>
   417                              Sat
   418                          </option>
   419                      </select>
   420                  </div>
   421              </React.Fragment>
   422          );
   423      }
   424  
   425      private getValues(f: number): string[] {
   426          if (this.props.fieldApi.getValue() !== undefined) {
   427              const fields = (this.props.fieldApi.getValue() as string).split(' ');
   428              const subFields = getRanges(fields[f]);
   429              return subFields;
   430          }
   431          return ['*'];
   432      }
   433  
   434      private setValues(values: string[], f: number) {
   435          if (this.props.fieldApi.getValue() !== undefined) {
   436              const fields = (this.props.fieldApi.getValue() as string).split(' ');
   437              fields[f] = setRanges(values);
   438              this.props.fieldApi.setValue(fields.join(' '));
   439          } else {
   440              switch (f) {
   441                  case 0:
   442                      this.props.fieldApi.setValue(generateSchedule(values.join(','), '*', '*', '*', '*'));
   443                      break;
   444                  case 1:
   445                      this.props.fieldApi.setValue(generateSchedule('*', values.join(','), '*', '*', '*'));
   446                      break;
   447                  case 2:
   448                      this.props.fieldApi.setValue(generateSchedule('*', '*', values.join(','), '*', '*'));
   449                      break;
   450                  case 3:
   451                      this.props.fieldApi.setValue(generateSchedule('*', '*', '*', values.join(','), '*'));
   452                      break;
   453                  case 4:
   454                      this.props.fieldApi.setValue(generateSchedule('*', '*', '*', '*', values.join(',')));
   455                      break;
   456              }
   457          }
   458          return;
   459      }
   460  }
   461  
   462  const Schedule = ReactForm.FormField(ScheduleWrapper);