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