diff options
Diffstat (limited to 'app/assets/javascripts/pages/projects/pipeline_schedules')
3 files changed, 106 insertions, 40 deletions
diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue index 3a0d9c17228..4efabcb7df3 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/interval_pattern_input.vue @@ -1,5 +1,13 @@ <script> +import { GlSprintf, GlLink } from '@gitlab/ui'; +import { s__, sprintf } from '~/locale'; +import { getWeekdayNames } from '~/lib/utils/datetime_utility'; + export default { + components: { + GlSprintf, + GlLink, + }, props: { initialCronInterval: { type: String, @@ -9,25 +17,51 @@ export default { }, data() { return { + isEditingCustom: false, + randomHour: this.generateRandomHour(), + randomWeekDayIndex: this.generateRandomWeekDayIndex(), + randomDay: this.generateRandomDay(), inputNameAttribute: 'schedule[cron]', cronInterval: this.initialCronInterval, - cronIntervalPresets: { - everyDay: '0 4 * * *', - everyWeek: '0 4 * * 0', - everyMonth: '0 4 1 * *', - }, cronSyntaxUrl: 'https://en.wikipedia.org/wiki/Cron', - customInputEnabled: false, }; }, computed: { + cronIntervalPresets() { + return { + everyDay: `0 ${this.randomHour} * * *`, + everyWeek: `0 ${this.randomHour} * * ${this.randomWeekDayIndex}`, + everyMonth: `0 ${this.randomHour} ${this.randomDay} * *`, + }; + }, intervalIsPreset() { return Object.values(this.cronIntervalPresets).includes(this.cronInterval); }, - // The text input is editable when there's a custom interval, or when it's - // a preset interval and the user clicks the 'custom' radio button - isEditable() { - return Boolean(this.customInputEnabled || !this.intervalIsPreset); + formattedTime() { + if (this.randomHour > 12) { + return `${this.randomHour - 12}:00pm`; + } else if (this.randomHour === 12) { + return `12:00pm`; + } + return `${this.randomHour}:00am`; + }, + weekday() { + return getWeekdayNames()[this.randomWeekDayIndex]; + }, + everyDayText() { + return sprintf(s__(`Every day (at %{time})`), { time: this.formattedTime }); + }, + everyWeekText() { + return sprintf(s__('Every week (%{weekday} at %{time})'), { + weekday: this.weekday, + time: this.formattedTime, + }); + }, + everyMonthText() { + return sprintf(s__('Every month (Day %{day} at %{time})'), { + day: this.randomDay, + time: this.formattedTime, + }); }, }, watch: { @@ -39,14 +73,31 @@ export default { }); }, }, - created() { - if (this.intervalIsPreset) { - this.enableCustomInput = false; + // If at the mounting stage the default is still an empty string, we + // know we are not editing an existing field so we update it so + // that the default is the first radio option + mounted() { + if (this.cronInterval === '') { + this.cronInterval = this.cronIntervalPresets.everyDay; } }, methods: { + setCustomInput(e) { + if (!this.isEditingCustom) { + this.isEditingCustom = true; + this.$refs.customInput.click(); + // Because we need to manually trigger the click on the radio btn, + // it will add a space to update the v-model. If the user is typing + // and the space is added, it will feel very unituitive so we reset + // the value to the original + this.cronInterval = e.target.value; + } + if (this.intervalIsPreset) { + this.isEditingCustom = false; + } + }, toggleCustomInput(shouldEnable) { - this.customInputEnabled = shouldEnable; + this.isEditingCustom = shouldEnable; if (shouldEnable) { // We need to change the value so other radios don't remain selected @@ -54,6 +105,15 @@ export default { this.cronInterval = `${this.cronInterval} `; } }, + generateRandomHour() { + return Math.floor(Math.random() * 23); + }, + generateRandomWeekDayIndex() { + return Math.floor(Math.random() * 6); + }, + generateRandomDay() { + return Math.floor(Math.random() * 28); + }, }, }; </script> @@ -62,24 +122,6 @@ export default { <div class="interval-pattern-form-group"> <div class="cron-preset-radio-input"> <input - id="custom" - :name="inputNameAttribute" - :value="cronInterval" - :checked="isEditable" - class="label-bold" - type="radio" - @click="toggleCustomInput(true)" - /> - - <label for="custom"> {{ s__('PipelineSheduleIntervalPattern|Custom') }} </label> - - <span class="cron-syntax-link-wrap"> - (<a :href="cronSyntaxUrl" target="_blank"> {{ __('Cron syntax') }} </a>) - </span> - </div> - - <div class="cron-preset-radio-input"> - <input id="every-day" v-model="cronInterval" :name="inputNameAttribute" @@ -89,7 +131,9 @@ export default { @click="toggleCustomInput(false)" /> - <label class="label-bold" for="every-day"> {{ __('Every day (at 4:00am)') }} </label> + <label class="label-bold" for="every-day"> + {{ everyDayText }} + </label> </div> <div class="cron-preset-radio-input"> @@ -104,7 +148,7 @@ export default { /> <label class="label-bold" for="every-week"> - {{ __('Every week (Sundays at 4:00am)') }} + {{ everyWeekText }} </label> </div> @@ -120,20 +164,43 @@ export default { /> <label class="label-bold" for="every-month"> - {{ __('Every month (on the 1st at 4:00am)') }} + {{ everyMonthText }} </label> </div> + <div class="cron-preset-radio-input"> + <input + id="custom" + ref="customInput" + v-model="cronInterval" + :name="inputNameAttribute" + :value="cronInterval" + class="label-bold" + type="radio" + @click="toggleCustomInput(true)" + /> + + <label for="custom"> {{ s__('PipelineSheduleIntervalPattern|Custom') }} </label> + + <gl-sprintf :message="__('(%{linkStart}Cron syntax%{linkEnd})')"> + <template #link="{content}"> + <gl-link :href="cronSyntaxUrl" target="_blank" class="gl-font-sm"> + {{ content }} + </gl-link> + </template> + </gl-sprintf> + </div> + <div class="cron-interval-input-wrapper"> <input id="schedule_cron" v-model="cronInterval" :placeholder="__('Define a custom pattern with cron syntax')" :name="inputNameAttribute" - :disabled="!isEditable" class="form-control inline cron-interval-input" type="text" required="true" + @input="setCustomInput" /> </div> </div> diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue index 22512a6f12a..da96e6f36b4 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/components/pipeline_schedules_callout.vue @@ -2,7 +2,8 @@ import Vue from 'vue'; import Cookies from 'js-cookie'; import Translate from '../../../../../vue_shared/translate'; -import illustrationSvg from '../icons/intro_illustration.svg'; +// Full path is needed for Jest to be able to correctly mock this file +import illustrationSvg from '~/pages/projects/pipeline_schedules/shared/icons/intro_illustration.svg'; import { parseBoolean } from '~/lib/utils/common_utils'; Vue.use(Translate); diff --git a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js index dc6df27f1c7..497e2c9c0ae 100644 --- a/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js +++ b/app/assets/javascripts/pages/projects/pipeline_schedules/shared/init_form.js @@ -11,9 +11,7 @@ Vue.use(Translate); function initIntervalPatternInput() { const intervalPatternMount = document.getElementById('interval-pattern-input'); - const initialCronInterval = intervalPatternMount - ? intervalPatternMount.dataset.initialInterval - : ''; + const initialCronInterval = intervalPatternMount?.dataset?.initialInterval; return new Vue({ el: intervalPatternMount, |