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