summaryrefslogtreecommitdiff
path: root/app/assets
diff options
context:
space:
mode:
Diffstat (limited to 'app/assets')
-rw-r--r--app/assets/images/ci_favicons/canary/favicon_status_scheduled.icobin0 -> 5430 bytes
-rw-r--r--app/assets/images/ci_favicons/favicon_status_scheduled.pngbin0 -> 1072 bytes
-rw-r--r--app/assets/javascripts/lib/utils/datetime_utility.js21
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_actions.vue38
-rw-r--r--app/assets/javascripts/pipelines/components/pipelines_table_row.vue14
-rw-r--r--app/assets/stylesheets/framework/buttons.scss4
-rw-r--r--app/assets/stylesheets/framework/icons.scss1
-rw-r--r--app/assets/stylesheets/pages/pipelines.scss1
-rw-r--r--app/assets/stylesheets/pages/status.scss1
9 files changed, 73 insertions, 7 deletions
diff --git a/app/assets/images/ci_favicons/canary/favicon_status_scheduled.ico b/app/assets/images/ci_favicons/canary/favicon_status_scheduled.ico
new file mode 100644
index 00000000000..5444b8e41dc
--- /dev/null
+++ b/app/assets/images/ci_favicons/canary/favicon_status_scheduled.ico
Binary files differ
diff --git a/app/assets/images/ci_favicons/favicon_status_scheduled.png b/app/assets/images/ci_favicons/favicon_status_scheduled.png
new file mode 100644
index 00000000000..d198c255fdd
--- /dev/null
+++ b/app/assets/images/ci_favicons/favicon_status_scheduled.png
Binary files differ
diff --git a/app/assets/javascripts/lib/utils/datetime_utility.js b/app/assets/javascripts/lib/utils/datetime_utility.js
index 1f66fa811ea..833dbefd3dc 100644
--- a/app/assets/javascripts/lib/utils/datetime_utility.js
+++ b/app/assets/javascripts/lib/utils/datetime_utility.js
@@ -370,3 +370,24 @@ window.gl.utils = {
getTimeago,
localTimeAgo,
};
+
+/**
+ * Formats milliseconds as timestamp (e.g. 01:02:03).
+ * This takes durations longer than a day into account (e.g. two days would be 48:00:00).
+ *
+ * @param milliseconds
+ * @returns {string}
+ */
+export const formatTime = milliseconds => {
+ const remainingSeconds = Math.floor(milliseconds / 1000) % 60;
+ const remainingMinutes = Math.floor(milliseconds / 1000 / 60) % 60;
+ const remainingHours = Math.floor(milliseconds / 1000 / 60 / 60);
+ let formattedTime = '';
+ if (remainingHours < 10) formattedTime += '0';
+ formattedTime += `${remainingHours}:`;
+ if (remainingMinutes < 10) formattedTime += '0';
+ formattedTime += `${remainingMinutes}:`;
+ if (remainingSeconds < 10) formattedTime += '0';
+ formattedTime += remainingSeconds;
+ return formattedTime;
+};
diff --git a/app/assets/javascripts/pipelines/components/pipelines_actions.vue b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
index 017dd560621..16e69759091 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_actions.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_actions.vue
@@ -1,4 +1,6 @@
<script>
+import { s__, sprintf } from '~/locale';
+import { formatTime } from '~/lib/utils/datetime_utility';
import eventHub from '../event_hub';
import icon from '../../vue_shared/components/icon.vue';
import tooltip from '../../vue_shared/directives/tooltip';
@@ -22,10 +24,24 @@ export default {
};
},
methods: {
- onClickAction(endpoint) {
+ onClickAction(action) {
+ if (action.scheduled_at) {
+ const confirmationMessage = sprintf(
+ s__(
+ "DelayedJobs|Are you sure you want to run %{jobName} immediately? This job will run automatically after it's timer finishes.",
+ ),
+ { jobName: action.name },
+ );
+ // https://gitlab.com/gitlab-org/gitlab-ce/issues/52156
+ // eslint-disable-next-line no-alert
+ if (!window.confirm(confirmationMessage)) {
+ return;
+ }
+ }
+
this.isLoading = true;
- eventHub.$emit('postAction', endpoint);
+ eventHub.$emit('postAction', action.path);
},
isActionDisabled(action) {
@@ -35,6 +51,11 @@ export default {
return !action.playable;
},
+
+ remainingTime(action) {
+ const remainingMilliseconds = new Date(action.scheduled_at).getTime() - Date.now();
+ return formatTime(Math.max(0, remainingMilliseconds));
+ },
},
};
</script>
@@ -63,17 +84,24 @@ export default {
<ul class="dropdown-menu dropdown-menu-right">
<li
- v-for="(action, i) in actions"
- :key="i"
+ v-for="action in actions"
+ :key="action.path"
>
<button
:class="{ disabled: isActionDisabled(action) }"
:disabled="isActionDisabled(action)"
type="button"
class="js-pipeline-action-link no-btn btn"
- @click="onClickAction(action.path)"
+ @click="onClickAction(action)"
>
{{ action.name }}
+ <span
+ v-if="action.scheduled_at"
+ class="pull-right"
+ >
+ <icon name="clock" />
+ {{ remainingTime(action) }}
+ </span>
</button>
</li>
</ul>
diff --git a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
index a39cc265601..09ee190b8ca 100644
--- a/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
+++ b/app/assets/javascripts/pipelines/components/pipelines_table_row.vue
@@ -59,6 +59,16 @@ export default {
};
},
computed: {
+ actions() {
+ if (!this.pipeline || !this.pipeline.details) {
+ return [];
+ }
+ const { details } = this.pipeline;
+ return [
+ ...(details.manual_actions || []),
+ ...(details.scheduled_actions || []),
+ ];
+ },
/**
* If provided, returns the commit tag.
* Needed to render the commit component column.
@@ -321,8 +331,8 @@ export default {
>
<div class="btn-group table-action-buttons">
<pipelines-actions-component
- v-if="pipeline.details.manual_actions.length"
- :actions="pipeline.details.manual_actions"
+ v-if="actions.length > 0"
+ :actions="actions"
/>
<pipelines-artifacts-component
diff --git a/app/assets/stylesheets/framework/buttons.scss b/app/assets/stylesheets/framework/buttons.scss
index 686ce0c63a4..c4296c7a88a 100644
--- a/app/assets/stylesheets/framework/buttons.scss
+++ b/app/assets/stylesheets/framework/buttons.scss
@@ -360,6 +360,10 @@
i {
color: $gl-text-color-secondary;
}
+
+ svg {
+ fill: $gl-text-color-secondary;
+ }
}
.clone-dropdown-btn a {
diff --git a/app/assets/stylesheets/framework/icons.scss b/app/assets/stylesheets/framework/icons.scss
index f002edced8a..abd26e38d18 100644
--- a/app/assets/stylesheets/framework/icons.scss
+++ b/app/assets/stylesheets/framework/icons.scss
@@ -64,6 +64,7 @@
}
}
+.ci-status-icon-scheduled,
.ci-status-icon-manual {
svg {
fill: $gl-text-color;
diff --git a/app/assets/stylesheets/pages/pipelines.scss b/app/assets/stylesheets/pages/pipelines.scss
index 8bb8b83dc5e..14395cc59b0 100644
--- a/app/assets/stylesheets/pages/pipelines.scss
+++ b/app/assets/stylesheets/pages/pipelines.scss
@@ -760,6 +760,7 @@
}
&.ci-status-icon-canceled,
+ &.ci-status-icon-scheduled,
&.ci-status-icon-disabled,
&.ci-status-icon-not-found,
&.ci-status-icon-manual {
diff --git a/app/assets/stylesheets/pages/status.scss b/app/assets/stylesheets/pages/status.scss
index 620297e589d..7d59dd3b5d1 100644
--- a/app/assets/stylesheets/pages/status.scss
+++ b/app/assets/stylesheets/pages/status.scss
@@ -27,6 +27,7 @@
&.ci-canceled,
&.ci-disabled,
+ &.ci-scheduled,
&.ci-manual {
color: $gl-text-color;
border-color: $gl-text-color;