diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-accordion/dxc-accordion.component.ts b/projects/dxc-ngx-cdk/src/lib/dxc-accordion/dxc-accordion.component.ts index c48a06920..94c8c5346 100644 --- a/projects/dxc-ngx-cdk/src/lib/dxc-accordion/dxc-accordion.component.ts +++ b/projects/dxc-ngx-cdk/src/lib/dxc-accordion/dxc-accordion.component.ts @@ -123,7 +123,6 @@ export class DxcAccordionComponent implements OnInit, OnChanges, AfterViewInit { display: block; div.mat-expansion-panel-content { div.mat-expansion-panel-body { - font: normal normal normal 16px/22px var(--fontFamily); cursor: default; ${inputs.padding ? this.cssUtils.getPaddings(inputs.padding) diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/cronoptions.ts b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/cronoptions.ts new file mode 100644 index 000000000..1dcfe2845 --- /dev/null +++ b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/cronoptions.ts @@ -0,0 +1,19 @@ +export interface CronOptions { + formInputClass?: string; + formSelectClass?: string; + formRadioClass?: string; + formCheckboxClass?: string; + defaultTime: string, + hideMinutesTab: boolean; + hideHourlyTab: boolean; + hideDailyTab: boolean; + hideWeeklyTab: boolean; + hideMonthlyTab: boolean; + hideYearlyTab: boolean; + hideAdvancedTab: boolean; + hideSpecificWeekDayTab: boolean; + hideSpecificMonthWeekTab: boolean; + use24HourTime: boolean; + hideSeconds: boolean; + cronFlavor: string; +} diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.html b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.html new file mode 100644 index 000000000..bc722e280 --- /dev/null +++ b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.html @@ -0,0 +1,471 @@ +
+ + + + +
+ Every + + +
+
+ + + +
+ Every + + +
+
+ + + +
+ + + + + Every + + + Day(s) + + + {{monthDay}} + + + + + at + + + + + + + + + Week Day (MON-FRI) at + + + + + + +
+
+ + + +
+ + Every + +
+ Monday + Tuesday + Wednesday + Thursday + Friday + Saturday + Sunday +
+ + at time + + + +
+
+ + + +
+ + + + + + + On the + + + + Day + + + {{monthDayDisplay(monthDaysWithLast)}} + + + + + + + + Day + + + {{monthDayDisplay(monthDaysWithOutLast)}} + + + + + + of every + + + + Month + + + {{month}} + + + + + + at time + + + + + + + + + + + On the + + + Week + + + {{monthWeekDisplay(monthWeek)}} + + + + + + Day + + + {{dayDisplay(day)}} + + + + + of every + + + Month + + + {{month}} + + + + + at time + + + + + + + +
+
+ + + +
+ + + + On the + + + Day + + + {{monthDayDisplay(monthDaysWithLast)}} + + + + + + Day + + + {{monthDayDisplay(monthDaysWithOutLast)}} + + + + + of + + + Month + + + {{monthDisplay(month)}} + + + + + at time + + + + + + + + On the + + + Week + + + {{monthWeekDisplay(monthWeek)}} + + + + + + Day + + + {{dayDisplay(day)}} + + + + + of + + + Month + + + {{monthDisplay(month)}} + + + + + at time + + + + + +
+
+ + + +
+ + + + + + +
+ + + + + +
+
+
+
+ + + + + + + + + + + +
+
+ + + + + + + + + + +
+
+
+
+ + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ + + + + + + +
+
+ + + + + + + + + + +
+
+ + + + + + + + + + + +
+
+ + + +
+ +
+
+
+
+ \ No newline at end of file diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.scss b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.scss new file mode 100644 index 000000000..87e7df35f --- /dev/null +++ b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.scss @@ -0,0 +1,49 @@ + + .cron-editor-main .cron-editor-container { + } + + .cron-editor-tab-content { + margin-top: 24px; + border-radius: 8px; + } + + .cron-editor-main .cron-editor-container .cron-editor-radio { + width: 20px; + display: inline-block; + } + + .cron-editor-main .cron-editor-container .cron-editor-select, + .cron-editor-main .cron-editor-container .cron-editor-input, + .cron-editor-main .cron-editor-container .cron-editor-checkbox { + display: inline-block; + } + + .cron-editor-main .cron-editor-container .well-time-wrapper { + padding-left: 20px; + } + + .cron-editor-main .cron-editor-container .inline-block { + display: inline-block; + } + + .cron-editor-main .cron-editor-container .hour-types { + width: 70px; + } + + .nav-tabs li a { + cursor: pointer; + } + + .cron-editor-radio-group { + display: flex; + flex-direction: column; + margin: 15px 0; + } + + .cron-editor-radio-button { + margin: 5px; + } + + .checkbox-margin { + margin: 0 10px; + } diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.spec.ts b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.spec.ts new file mode 100644 index 000000000..3952f2416 --- /dev/null +++ b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DxcCronEditorComponent } from './dxc-cron-editor.component'; + +describe('DxcCronEditorComponent', () => { + let component: DxcCronEditorComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DxcCronEditorComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DxcCronEditorComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.ts b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.ts new file mode 100644 index 000000000..a56553864 --- /dev/null +++ b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.component.ts @@ -0,0 +1,675 @@ +import { Component, Input, Output, OnInit, EventEmitter, forwardRef } from '@angular/core'; +import {MatFormFieldModule} from '@angular/material/form-field' +import { Days, MonthWeeks, Months } from './enums'; +import { ControlValueAccessor, FormBuilder, FormControl, FormGroup, NG_VALUE_ACCESSOR } from '@angular/forms'; +import { ThemePalette } from '@angular/material/core'; +import { CronOptions } from './cronoptions'; +import { ConfigurationsetupService } from '../services/startup/configurationsetup.service'; + +export const CRON_VALUE_ACCESSOR: any = { + provide: NG_VALUE_ACCESSOR, + useExisting: forwardRef(() => DxcCronEditorComponent), + multi: true, +}; + +@Component({ + selector: 'dxc-cron-editor', + templateUrl: './dxc-cron-editor.component.html', + styleUrls: ['./dxc-cron-editor.component.scss'], + providers: [CRON_VALUE_ACCESSOR] +}) +export class DxcCronEditorComponent implements OnInit, ControlValueAccessor { + @Input() public backgroundColor: ThemePalette; + @Input() public color: ThemePalette; + @Input() public disabled: boolean; + @Input() public options: CronOptions; + // the name is an Angular convention, @Input variable name + "Change" suffix + @Output() cronChange = new EventEmitter(); + + public activeTab: string; + public selectOptions = this.getSelectOptions(); + public state: any; + private localCron = '* 0 0 ? * * *'; + private isDirty: boolean; + expressionList: any = []; + selectedType='express'; + isExpression:boolean = false; + isCustom:boolean=false; + cronForm: FormControl; + minutesForm: FormGroup; + hourlyForm: FormGroup; + dailyForm: FormGroup; + weeklyForm: FormGroup; + monthlyForm: FormGroup; + yearlyForm: FormGroup; + advancedForm: FormGroup; + @Input() public resource: any; + @Input() + get cron(): string { + return this.localCron; + } + set cron(value: string) { + this.localCron = value; + this.onChange(this.localCron); + this.cronChange.emit(this.localCron); + } + + get isCronFlavorQuartz() { + return this.options.cronFlavor === 'quartz'; + } + + get isCronFlavorStandard() { + return this.options.cronFlavor === 'standard'; + } + + get yearDefaultChar() { + return this.options.cronFlavor === 'quartz' ? '*' : ''; + } + + get weekDayDefaultChar() { + return this.options.cronFlavor === 'quartz' ? '?' : '*'; + } + + get monthDayDefaultChar() { + return this.options.cronFlavor === 'quartz' ? '?' : '*'; + } + + constructor(private fb: FormBuilder, private config : ConfigurationsetupService) { + // this.resources = config.configservice.Resources; + } + + /* Update the cron output to that of the selected tab. + * The cron output value is updated whenever a form is updated. To make it change in response to tab selection, we simply reset + * the value of the form that goes into focus. */ + public onTabFocus(idx: number) { + switch (idx) { + case 0: + this.minutesForm.setValue(this.minutesForm.value); + break; + case 1: + this.hourlyForm.setValue(this.hourlyForm.value); + break; + case 2: + this.dailyForm.setValue(this.dailyForm.value); + break; + case 3: + this.weeklyForm.setValue(this.weeklyForm.value); + break; + case 4: + this.monthlyForm.setValue(this.monthlyForm.value); + break; + case 5: + this.yearlyForm.setValue(this.yearlyForm.value); + break; + case 6: + this.advancedForm.setValue(this.advancedForm.value); + break; + default: + throw (new Error('Invalid tab selected')); + } + } + + public async ngOnInit() { + this.state = this.getDefaultState(); + this.handleModelChange(this.cron); + const [defaultHours, defaultMinutes, defaultSeconds] = this.options.defaultTime.split(':').map(Number); + this.cronForm = new FormControl('* 0 0 ? * * *'); + this.minutesForm = this.fb.group({ + hours: [0], + minutes: [1], + seconds: [0] + }); + this.expressionList = [ + {'name': 'Expression', 'value': 'express'}, + {'name': 'Custom', 'value': 'cust'}, + ]; + this.minutesForm.valueChanges.subscribe(value => this.computeMinutesCron(value)); + + this.hourlyForm = this.fb.group({ + hours: [1], + minutes: [0], + seconds: [0] + }); + this.hourlyForm.valueChanges.subscribe(value => this.computeHourlyCron(value)); + + this.dailyForm = this.fb.group({ + subTab: ['everyDays'], + everyDays: this.fb.group({ + days: [1], + hours: [this.getAmPmHour(1)], + minutes: [0], + seconds: [0], + hourType: [this.getHourType(0)] + }), + everyWeekDay: this.fb.group({ + days: [0], + hours: [this.getAmPmHour(1)], + minutes: [0], + seconds: [0], + hourType: [this.getHourType(0)] + }) + }); + this.dailyForm.valueChanges.subscribe(value => this.computeDailyCron(value)); + + this.weeklyForm = this.fb.group({ + MON: [true], + TUE: [false], + WED: [false], + THU: [false], + FRI: [false], + SAT: [false], + SUN: [false], + hours: [this.getAmPmHour(defaultHours)], + minutes: [defaultMinutes], + seconds: [defaultSeconds], + hourType: [this.getHourType(defaultHours)] + }); + this.weeklyForm.valueChanges.subscribe(next => this.computeWeeklyCron(next)); + + this.monthlyForm = this.fb.group({ + subTab: ['specificDay'], + specificDay: this.fb.group({ + day: ['1'], + months: [1], + hours: [this.getAmPmHour(defaultHours)], + minutes: [defaultMinutes], + seconds: [defaultSeconds], + hourType: [this.getHourType(defaultHours)] + }), + specificWeekDay: this.fb.group({ + monthWeek: ['#1'], + day: ['MON'], + months: [1], + hours: [this.getAmPmHour(defaultHours)], + minutes: [defaultMinutes], + seconds: [defaultSeconds], + hourType: [this.getHourType(defaultHours)] + }) + }); + this.monthlyForm.valueChanges.subscribe(next => this.computeMonthlyCron(next)); + + this.yearlyForm = this.fb.group({ + subTab: ['specificMonthDay'], + specificMonthDay: this.fb.group({ + month: [1], + day: ['1'], + hours: [this.getAmPmHour(defaultHours)], + minutes: [defaultMinutes], + seconds: [defaultSeconds], + hourType: [this.getHourType(defaultHours)] + }), + specificMonthWeek: this.fb.group({ + monthWeek: ['#1'], + day: ['MON'], + month: [1], + hours: [this.getAmPmHour(defaultHours)], + minutes: [defaultMinutes], + seconds: [defaultSeconds], + hourType: [this.getHourType(defaultHours)] + }) + }); + this.yearlyForm.valueChanges.subscribe(next => this.computeYearlyCron(next)); + + this.advancedForm = this.fb.group({ + subTab: ['expression'], + expression: [this.cron != null && this.cron !='' ? this.cron : this.isCronFlavorQuartz ? '0 15 10 L-2 * ? *' : '15 10 2 * *'], + custom: this.fb.group({ + // monthWeek: ['*'], + startDays: ['none'], + days: [['*']], + startMonth: ['none'], + month: [['*']], + startYear: ['none'], + year: ['*'], + startHours: ['none'], + hours: ['*'], + startMinutes: ['none'], + minutes: ['*'], + startSeconds: ['none'], + seconds: ['0'], + hourType: ['*'] + }) + }); + this.advancedForm.valueChanges.subscribe(next => this.computeAdvancedExpression(next)); + } + + private computeMinutesCron(state: any) { + this.cron = `${this.isCronFlavorQuartz ? state.seconds : ''} 0/${state.minutes} * 1/1 * ${this.weekDayDefaultChar} ${this.yearDefaultChar}`.trim(); + this.cronForm.setValue(this.cron); + } + + private computeHourlyCron(state: any) { + this.cron = `${this.isCronFlavorQuartz ? state.seconds : ''} ${state.minutes} 0/${state.hours} 1/1 * ${this.weekDayDefaultChar} ${this.yearDefaultChar}`.trim(); + this.cronForm.setValue(this.cron); + } + + private computeDailyCron(state: any) { + switch (state.subTab) { + case 'everyDays': + this.cron = `${this.isCronFlavorQuartz ? state.everyDays.seconds : ''} ${state.everyDays.minutes} ${this.hourToCron(state.everyDays.hours, state.everyDays.hourType)} 1/${state.everyDays.days} * ${this.weekDayDefaultChar} ${this.yearDefaultChar}`.trim(); + break; + case 'everyWeekDay': + this.cron = `${this.isCronFlavorQuartz ? state.everyWeekDay.seconds : ''} ${state.everyWeekDay.minutes} ${this.hourToCron(state.everyWeekDay.hours, state.everyWeekDay.hourType)} ${this.monthDayDefaultChar} * MON-FRI ${this.yearDefaultChar}`.trim(); + break; + default: + throw new Error('Invalid cron daily subtab selection'); + } + this.cronForm.setValue(this.cron); + } + + private computeWeeklyCron(state: any) { + const days = this.selectOptions.days + .reduce((acc, day) => state[day] ? acc.concat([day]) : acc, []) + .join(','); + this.cron = `${this.isCronFlavorQuartz ? state.seconds : ''} ${state.minutes} ${this.hourToCron(state.hours, state.hourType)} ${this.monthDayDefaultChar} * ${days} ${this.yearDefaultChar}`.trim(); + this.cronForm.setValue(this.cron); + } + + private computeMonthlyCron(state: any) { + switch (state.subTab) { + case 'specificDay': + this.cron = `${this.isCronFlavorQuartz ? state.specificDay.seconds : ''} ${state.specificDay.minutes} ${this.hourToCron(state.specificDay.hours, state.specificDay.hourType)} ${state.specificDay.day} 1/${state.specificDay.months} ${this.weekDayDefaultChar} ${this.yearDefaultChar}`.trim(); + break; + case 'specificWeekDay': + this.cron = `${this.isCronFlavorQuartz ? state.specificWeekDay.seconds : ''} ${state.specificWeekDay.minutes} ${this.hourToCron(state.specificWeekDay.hours, state.specificWeekDay.hourType)} ${this.monthDayDefaultChar} 1/${state.specificWeekDay.months} ${state.specificWeekDay.day}${state.specificWeekDay.monthWeek} ${this.yearDefaultChar}`.trim(); + break; + default: + throw new Error('Invalid cron montly subtab selection'); + } + this.cronForm.setValue(this.cron); + } + + private computeYearlyCron(state: any) { + switch (state.subTab) { + case 'specificMonthDay': + this.cron = `${this.isCronFlavorQuartz ? state.specificMonthDay.seconds : ''} ${state.specificMonthDay.minutes} ${this.hourToCron(state.specificMonthDay.hours, state.specificMonthDay.hourType)} ${state.specificMonthDay.day} ${state.specificMonthDay.month} ${this.weekDayDefaultChar} ${this.yearDefaultChar}`.trim(); + break; + case 'specificMonthWeek': + this.cron = `${this.isCronFlavorQuartz ? state.specificMonthWeek.seconds : ''} ${state.specificMonthWeek.minutes} ${this.hourToCron(state.specificMonthWeek.hours, state.specificMonthWeek.hourType)} ${this.monthDayDefaultChar} ${state.specificMonthWeek.month} ${state.specificMonthWeek.day}${state.specificMonthWeek.monthWeek} ${this.yearDefaultChar}`.trim(); + break; + default: + throw new Error('Invalid cron yearly subtab selection'); + } + this.cronForm.setValue(this.cron); + } + + private computeAdvancedExpression(state: any) { + switch (state.subTab) { + case 'express': + this.cron = state.expression; + break; + case 'cust': + let shouldUpdate = false; + if (state.custom.days.length <= 0) { + state.custom.days = ['*']; + shouldUpdate = true; + } + else if (state.custom.days.length > 1 && state.custom.days.indexOf('*') != -1) { + state.custom.days.splice(state.custom.days.indexOf('*'), 1); + shouldUpdate = true; + } + if (state.custom.month.length <= 0) { + state.custom.month = ['*']; + shouldUpdate = true; + } + else if (state.custom.month.length > 1 && state.custom.month.indexOf('*') != -1) { + state.custom.month.splice(state.custom.month.indexOf('*'), 1); + shouldUpdate = true; + } + if (shouldUpdate) + this.advancedForm.patchValue({ 'days': state.custom.days, 'month': state.custom.month }); + // this.cron = `${this.isCronFlavorQuartz ? state.custom.seconds : ''} ${state.custom.minutes} ${this.hourToCron(state.custom.hours, state.custom.hourType)} ${this.monthDayDefaultChar} ${state.custom.month} ${state.custom.day}${state.custom.monthWeek} ${state.custom.year}`.trim(); + this.cron = `${this.getStartFrom( state.custom.startSeconds)}${this.isCronFlavorQuartz ? state.custom.seconds : ''} ${this.getStartFrom( state.custom.startMinutes)}${state.custom.minutes} ${this.getStartFrom( state.custom.startHours)}${this.hourToCron(state.custom.hours, state.custom.hourType)} ${this.getStartFrom( state.custom.startDays)}${state.custom.days.join(',')} ${this.getStartFrom( state.custom.startMonth)}${state.custom.month.join(',')} ${this.getStartFrom( state.custom.startYear)}${this.weekDayDefaultChar} ${state.custom.year}`.trim(); + break; + default: + throw new Error('Invalid cron yearly subtab selection'); + } + this.cronForm.setValue(this.cron); + } + + public dayDisplay(day: string): string { + return Days[day]; + } + + public onExpressionChange($event){ + this.selectedType = $event; + if(this.selectedType=='express') + { + this.isExpression =true; + this.isCustom = false; + } + else if(this.selectedType=='cust') + { + this.isExpression =false; + this.isCustom = true; + } + else + { + this.isExpression =false; + this.isCustom = false; + } + } + + public monthWeekDisplay(monthWeekNumber: string): string { + return MonthWeeks[monthWeekNumber]; + } + + public monthDisplay(month: number): string { + return Months[month]; + } + + public monthDayDisplay(month: string): string { + if (month === 'L') { + return 'Last Day'; + } else if (month === 'LW') { + return 'Last Weekday'; + } else if (month === '1W') { + return 'First Weekday'; + } else { + return `${month}${this.getOrdinalSuffix(month)}`; + } + } + + private getAmPmHour(hour: number) { + return this.options.use24HourTime ? hour : (hour + 11) % 12 + 1; + } + + private getHourType(hour: number) { + return this.options.use24HourTime ? undefined : (hour >= 12 ? 'PM' : 'AM'); + } + + private hourToCron(hour: number, hourType: string) { + if (this.options.use24HourTime) { + return hour; + } else { + return hourType === 'AM' ? (hour === 12 ? 0 : hour) : (hour === 12 ? 12 : hour + 12); + } + } + + private handleModelChange(cron: string) { + if (this.isDirty) { + this.isDirty = false; + return; + } else { + this.isDirty = false; + } + + if (!this.cronIsValid(cron)) { + if (this.isCronFlavorQuartz) { + throw new Error('Invalid cron expression, there must be 6 or 7 segments'); + } + + if (this.isCronFlavorStandard) { + throw new Error('Invalid cron expression, there must be 5 segments'); + } + } + + const origCron: string = cron; + if (cron.split(' ').length === 5 && this.isCronFlavorStandard) { + cron = `0 ${cron} *`; + } + + const [seconds, minutes, hours, dayOfMonth, month, dayOfWeek] = cron.split(' '); + + if (cron.match(/\d+ 0\/\d+ \* 1\/1 \* [\?\*] \*/)) { + this.activeTab = 'minutes'; + + this.state.minutes.minutes = parseInt(minutes.substring(2), 10); + this.state.minutes.seconds = parseInt(seconds, 10); + } else if (cron.match(/\d+ \d+ 0\/\d+ 1\/1 \* [\?\*] \*/)) { + this.activeTab = 'hourly'; + this.state.hourly.hours = parseInt(hours.substring(2), 10); + this.state.hourly.minutes = parseInt(minutes, 10); + this.state.hourly.seconds = parseInt(seconds, 10); + } else if (cron.match(/\d+ \d+ \d+ 1\/\d+ \* [\?\*] \*/)) { + this.activeTab = 'daily'; + this.state.daily.subTab = 'everyDays'; + this.state.daily.everyDays.days = parseInt(dayOfMonth.substring(2), 10); + const parsedHours = parseInt(hours, 10); + this.state.daily.everyDays.hours = this.getAmPmHour(parsedHours); + this.state.daily.everyDays.hourType = this.getHourType(parsedHours); + this.state.daily.everyDays.minutes = parseInt(minutes, 10); + this.state.daily.everyDays.seconds = parseInt(seconds, 10); + } else if (cron.match(/\d+ \d+ \d+ [\?\*] \* MON-FRI \*/)) { + this.activeTab = 'daily'; + this.state.daily.subTab = 'everyWeekDay'; + const parsedHours = parseInt(hours, 10); + this.state.daily.everyWeekDay.hours = this.getAmPmHour(parsedHours); + this.state.daily.everyWeekDay.hourType = this.getHourType(parsedHours); + this.state.daily.everyWeekDay.minutes = parseInt(minutes, 10); + this.state.daily.everyWeekDay.seconds = parseInt(seconds, 10); + } else if (cron.match(/\d+ \d+ \d+ [\?\*] \* (MON|TUE|WED|THU|FRI|SAT|SUN)(,(MON|TUE|WED|THU|FRI|SAT|SUN))* \*/)) { + this.activeTab = 'weekly'; + this.selectOptions.days.forEach(weekDay => this.state.weekly[weekDay] = false); + dayOfWeek.split(',').forEach(weekDay => this.state.weekly[weekDay] = true); + const parsedHours = parseInt(hours, 10); + this.state.weekly.hours = this.getAmPmHour(parsedHours); + this.state.weekly.hourType = this.getHourType(parsedHours); + this.state.weekly.minutes = parseInt(minutes, 10); + this.state.weekly.seconds = parseInt(seconds, 10); + } else if (cron.match(/\d+ \d+ \d+ (\d+|L|LW|1W) 1\/\d+ [\?\*] \*/)) { + this.activeTab = 'monthly'; + this.state.monthly.subTab = 'specificDay'; + this.state.monthly.specificDay.day = dayOfMonth; + this.state.monthly.specificDay.months = parseInt(month.substring(2), 10); + const parsedHours = parseInt(hours, 10); + this.state.monthly.specificDay.hours = this.getAmPmHour(parsedHours); + this.state.monthly.specificDay.hourType = this.getHourType(parsedHours); + this.state.monthly.specificDay.minutes = parseInt(minutes, 10); + this.state.monthly.specificDay.seconds = parseInt(seconds, 10); + } else if (cron.match(/\d+ \d+ \d+ [\?\*] 1\/\d+ (MON|TUE|WED|THU|FRI|SAT|SUN)((#[1-5])|L) \*/)) { + const day = dayOfWeek.substr(0, 3); + const monthWeek = dayOfWeek.substr(3); + this.activeTab = 'monthly'; + this.state.monthly.subTab = 'specificWeekDay'; + this.state.monthly.specificWeekDay.monthWeek = monthWeek; + this.state.monthly.specificWeekDay.day = day; + this.state.monthly.specificWeekDay.months = parseInt(month.substring(2), 10); + const parsedHours = parseInt(hours, 10); + this.state.monthly.specificWeekDay.hours = this.getAmPmHour(parsedHours); + this.state.monthly.specificWeekDay.hourType = this.getHourType(parsedHours); + this.state.monthly.specificWeekDay.minutes = parseInt(minutes, 10); + this.state.monthly.specificWeekDay.seconds = parseInt(seconds, 10); + } else if (cron.match(/\d+ \d+ \d+ (\d+|L|LW|1W) \d+ [\?\*] \*/)) { + this.activeTab = 'yearly'; + this.state.yearly.subTab = 'specificMonthDay'; + this.state.yearly.specificMonthDay.month = parseInt(month, 10); + this.state.yearly.specificMonthDay.day = dayOfMonth; + const parsedHours = parseInt(hours, 10); + this.state.yearly.specificMonthDay.hours = this.getAmPmHour(parsedHours); + this.state.yearly.specificMonthDay.hourType = this.getHourType(parsedHours); + this.state.yearly.specificMonthDay.minutes = parseInt(minutes, 10); + this.state.yearly.specificMonthDay.seconds = parseInt(seconds, 10); + } else if (cron.match(/\d+ \d+ \d+ [\?\*] \d+ (MON|TUE|WED|THU|FRI|SAT|SUN)((#[1-5])|L) \*/)) { + const day = dayOfWeek.substr(0, 3); + const monthWeek = dayOfWeek.substr(3); + this.activeTab = 'yearly'; + this.state.yearly.subTab = 'specificMonthWeek'; + this.state.yearly.specificMonthWeek.monthWeek = monthWeek; + this.state.yearly.specificMonthWeek.day = day; + this.state.yearly.specificMonthWeek.month = parseInt(month, 10); + const parsedHours = parseInt(hours, 10); + this.state.yearly.specificMonthWeek.hours = this.getAmPmHour(parsedHours); + this.state.yearly.specificMonthWeek.hourType = this.getHourType(parsedHours); + this.state.yearly.specificMonthWeek.minutes = parseInt(minutes, 10); + this.state.yearly.specificMonthWeek.seconds = parseInt(seconds, 10); + } else { + this.activeTab = 'advanced'; + this.state.advanced.expression = origCron; + } + } + + private cronIsValid(cron: string): boolean { + if (cron) { + const cronParts = cron.split(' '); + return (this.isCronFlavorQuartz && (cronParts.length === 6 + || cronParts.length === 7) + || (this.isCronFlavorStandard && cronParts.length === 5)); + } + + return false; + } + + + private getDefaultState() { + const [defaultHours, defaultMinutes, defaultSeconds] = this.options.defaultTime.split(':').map(Number); + this.localCron = this.cron != null && this.cron !='' ? this.cron : this.isCronFlavorQuartz ? '* 0 0 ? * * *' : '0 0 1/1 * *'; + return { + minutes: { + minutes: 1, + seconds: 0 + }, + hourly: { + hours: 1, + minutes: 0, + seconds: 0 + }, + daily: { + subTab: 'everyDays', + everyDays: { + days: 1, + hours: this.getAmPmHour(defaultHours), + minutes: defaultMinutes, + seconds: defaultSeconds, + hourType: this.getHourType(defaultHours) + }, + everyWeekDay: { + hours: this.getAmPmHour(defaultHours), + minutes: defaultMinutes, + seconds: defaultSeconds, + hourType: this.getHourType(defaultHours) + } + }, + weekly: { + MON: true, + TUE: false, + WED: false, + THU: false, + FRI: false, + SAT: false, + SUN: false, + hours: this.getAmPmHour(defaultHours), + minutes: defaultMinutes, + seconds: defaultSeconds, + hourType: this.getHourType(defaultHours) + }, + monthly: { + subTab: 'specificDay', + specificDay: { + day: '1', + months: 1, + hours: this.getAmPmHour(defaultHours), + minutes: defaultMinutes, + seconds: defaultSeconds, + hourType: this.getHourType(defaultHours) + }, + specificWeekDay: { + monthWeek: '#1', + day: 'MON', + months: 1, + hours: this.getAmPmHour(defaultHours), + minutes: defaultMinutes, + seconds: defaultSeconds, + hourType: this.getHourType(defaultHours) + } + }, + yearly: { + subTab: 'specificMonthDay', + specificMonthDay: { + month: 1, + day: '1', + hours: this.getAmPmHour(defaultHours), + minutes: defaultMinutes, + seconds: defaultSeconds, + hourType: this.getHourType(defaultHours) + }, + specificMonthWeek: { + monthWeek: '#1', + day: 'MON', + month: 1, + hours: this.getAmPmHour(defaultHours), + minutes: defaultMinutes, + seconds: defaultSeconds, + hourType: this.getHourType(defaultHours) + } + }, + advanced: { + subTab: 'expression', + expression: this.cron != null && this.cron !='' ? this.cron : this.isCronFlavorQuartz ? '0 15 10 L-2 * ? *' : '15 10 2 * *' + } + }; + } + + private getOrdinalSuffix(value: string) { + if (value.length > 1) { + const secondToLastDigit = value.charAt(value.length - 2); + if (secondToLastDigit === '1') { + return 'th'; + } + } + + const lastDigit = value.charAt(value.length - 1); + switch (lastDigit) { + case '1': + return 'st'; + case '2': + return 'nd'; + case '3': + return 'rd'; + default: + return 'th'; + } + } + + private getSelectOptions() { + return { + months: this.getRange(1, 12), + monthWeeks: ['#1', '#2', '#3', '#4', '#5', 'L'], + days: ['MON', 'TUE', 'WED', 'THU', 'FRI', 'SAT', 'SUN'], + minutes: this.getRange(0, 59), + fullMinutes: this.getRange(0, 59), + seconds: this.getRange(0, 59), + hours: this.getRange(1, 23), + monthDays: this.getRange(1, 31), + years: this.getRange(new Date().getFullYear(), new Date(new Date().getFullYear() + 50, new Date().getMonth(), new Date().getDate()).getFullYear()), + monthDaysWithLasts: ['1W', ...[...this.getRange(1, 31).map(String)], 'LW', 'L'], + monthDaysWithOutLasts: [...[...this.getRange(1, 31).map(String)]], + hourTypes: ['AM', 'PM'] + }; + } + + + + private getRange(start: number, end: number): number[] { + const length = end - start + 1; + return Array.apply(null, Array(length)).map((_, i) => i + start); + } + + private getStartFrom(value) { + return (value === 'none' || value === 'None') ? '' : value+'/'; + } + + + /* + * ControlValueAccessor + */ + onChange = (_: any) => { }; + onTouched = () => { }; + + writeValue(obj: string): void { + this.cron = obj != null && obj !='' ? obj : this.isCronFlavorQuartz ? '0 15 10 L-2 * ? *' : '15 10 2 * *'; + this.handleModelChange(this.cron); + } + + registerOnChange(fn: any): void { + this.onChange = fn; + } + + registerOnTouched(fn: any): void { + this.onTouched = fn; + } + + setDisabledState(isDisabled: boolean): void { + this.disabled = isDisabled; + } +} diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.module.ts b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.module.ts new file mode 100644 index 000000000..c2e9da4d5 --- /dev/null +++ b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-cron-editor.module.ts @@ -0,0 +1,38 @@ +import { NgModule } from '@angular/core'; +import { CommonModule } from '@angular/common'; +import { DxcCronEditorComponent } from './dxc-cron-editor.component'; +import { FormsModule, ReactiveFormsModule } from '@angular/forms'; +import { MatCheckboxModule } from '@angular/material/checkbox'; +import { MatInputModule } from '@angular/material/input'; +import { MatListModule } from '@angular/material/list'; +import { MatRadioModule } from '@angular/material/radio'; +import { MatSelectModule } from '@angular/material/select'; +import { MatTabsModule } from '@angular/material/tabs'; +import { DxcTimePickerComponent } from './dxc-time-picker/dxc-time-picker.component'; +import { DxcSelectModule } from '../dxc-select/select-module'; +import { DxcLabelModule } from '../dxc-label/dxc-label.module'; +import { DxcInputTextModule } from '../dxc-text-input/dxc-input-text.module'; + +@NgModule({ + imports: [ + CommonModule, + DxcSelectModule, + DxcLabelModule, + FormsModule, + ReactiveFormsModule, + MatTabsModule, + MatListModule, + MatSelectModule, + MatInputModule, + MatRadioModule, + MatCheckboxModule, + DxcInputTextModule + ], + exports: [DxcTimePickerComponent, DxcCronEditorComponent], + declarations: [DxcTimePickerComponent, DxcCronEditorComponent] +}) + + +export class DxcCronEditorModule { } + + diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.html b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.html new file mode 100644 index 000000000..eb9328639 --- /dev/null +++ b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.html @@ -0,0 +1,129 @@ + + + +
+
+ + + + + + + + + + + +
+
+ + + + + + + + + + + +
+
+
+ + + : + +
+
+ + + + + + + + + + + +
+
+ + + + + + + + + + + + +
+
+ +
+ + + : +
Second(s)
+ + start from + + + None + + {{second}} + + + + + Second(s) + + + Every + + {{second}} + + +
+ + + . + + + {{hourType}} + + + +
\ No newline at end of file diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.scss b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.scss new file mode 100644 index 000000000..e69de29bb diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.spec.ts b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.spec.ts new file mode 100644 index 000000000..f4463db8a --- /dev/null +++ b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { DxcTimePickerComponent } from './dxc-time-picker.component'; + +describe('DxcTimePickerComponent', () => { + let component: DxcTimePickerComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ DxcTimePickerComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(DxcTimePickerComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.ts b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.ts new file mode 100644 index 000000000..388914d6c --- /dev/null +++ b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component.ts @@ -0,0 +1,42 @@ +import { Component, Input } from '@angular/core'; +import { ControlContainer } from '@angular/forms'; +import { CronOptions } from '../cronoptions'; +export interface TimePickerModel { + days: number; + hours: number; + minutes: number; + seconds: number; +} + +function* range(start: number, end: number) { + for (let i = start; i <= end; i++) { + yield i; + } +} +@Component({ + selector: 'dxc-time-picker', + templateUrl: './dxc-time-picker.component.html', + styleUrls: ['./dxc-time-picker.component.scss'] +}) +export class DxcTimePickerComponent { + @Input() public disabled; + @Input() public use24HourTime = true; + @Input() public hideHours = false; + @Input() public hideMinutes = false; + @Input() public hideSeconds = true; + @Input() public includeEvery = false; + @Input() public includeNone = false; + @Input() public showStart = false; + @Input() public resource: any; + @Input() public options: CronOptions; + public minutes = [...range(0, 59) ]; + public seconds = [...range(0, 59) ]; + public hourTypes = ['AM', 'PM']; + + get hours(): number[] { + return this.use24HourTime ? [... range(0, 23)] : [... range(0, 12)]; + } + + constructor(public parent: ControlContainer) { } + +} diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/enums.ts b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/enums.ts new file mode 100644 index 000000000..b40868403 --- /dev/null +++ b/projects/dxc-ngx-cdk/src/lib/dxc-cron-editor/enums.ts @@ -0,0 +1,33 @@ +export const Days = { + 'SUN': 'Sunday', + 'MON': 'Monday', + 'TUE': 'Tuesday', + 'WED': 'Wednesday', + 'THU': 'Thursday', + 'FRI': 'Friday', + 'SAT': 'Saturday' +}; + +export const MonthWeeks = { + '#1': 'First', + '#2': 'Second', + '#3': 'Third', + '#4': 'Fourth', + '#5': 'Fifth', + 'L': 'Last' +}; + +export enum Months { + January = 1, + February, + March, + April, + May, + June, + July, + August, + September, + October, + November, + December +} diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-grid/cellrender/cellrender.component.html b/projects/dxc-ngx-cdk/src/lib/dxc-grid/cellrender/cellrender.component.html index 6e9cc1761..9b4f84896 100644 --- a/projects/dxc-ngx-cdk/src/lib/dxc-grid/cellrender/cellrender.component.html +++ b/projects/dxc-ngx-cdk/src/lib/dxc-grid/cellrender/cellrender.component.html @@ -50,12 +50,21 @@
{{gridAPIRow.status?.state}} -
+
+
+
+
+
+ +
+ +
+
diff --git a/projects/dxc-ngx-cdk/src/lib/dxc-list/dxc-list.component.scss b/projects/dxc-ngx-cdk/src/lib/dxc-list/dxc-list.component.scss index e73cab1eb..b7ee8fccd 100644 --- a/projects/dxc-ngx-cdk/src/lib/dxc-list/dxc-list.component.scss +++ b/projects/dxc-ngx-cdk/src/lib/dxc-list/dxc-list.component.scss @@ -10,7 +10,6 @@ .leftMenuItem:hover .btn-link-list, .highlightsection .btn-link-list { box-shadow: 0 2px 0 0px var(--high-light-text-color) !important; - position: relative; } .highlightsection { @@ -33,7 +32,7 @@ float: right; position: absolute; right: 0px; - top: 0px; + top: 11px; } .badge-space{ diff --git a/projects/dxc-ngx-cdk/src/public-api.ts b/projects/dxc-ngx-cdk/src/public-api.ts index aa5211aa1..d5e7972a2 100644 --- a/projects/dxc-ngx-cdk/src/public-api.ts +++ b/projects/dxc-ngx-cdk/src/public-api.ts @@ -266,5 +266,9 @@ export * from './lib/models/localstorage/storage-serializer'; export * from './lib/helpers/localstorage/storage-config'; export * from './lib/helpers/localstorage/storage-serializer'; export * from './lib/helpers/date/date-helper'; - -export * from "./lib/dxc-lib.module"; \ No newline at end of file +export * from "./lib/dxc-lib.module"; +export * from "./lib/dxc-cron-editor/dxc-cron-editor.module" +export * from "./lib/dxc-cron-editor/cronoptions"; +export * from './lib/dxc-cron-editor/enums'; +export * from './lib/dxc-cron-editor/dxc-cron-editor.component'; +export * from './lib/dxc-cron-editor/dxc-time-picker/dxc-time-picker.component'; \ No newline at end of file