diff options
Diffstat (limited to 'app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue')
-rw-r--r-- | app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue | 212 |
1 files changed, 212 insertions, 0 deletions
diff --git a/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue new file mode 100644 index 00000000000..0f9d1b8395b --- /dev/null +++ b/app/assets/javascripts/vue_merge_request_widget/components/approvals/approvals.vue @@ -0,0 +1,212 @@ +<script> +import { GlButton } from '@gitlab/ui'; +import createFlash from '~/flash'; +import { s__ } from '~/locale'; +import eventHub from '../../event_hub'; +import approvalsMixin from '../../mixins/approvals'; +import MrWidgetContainer from '../mr_widget_container.vue'; +import MrWidgetIcon from '../mr_widget_icon.vue'; +import ApprovalsSummary from './approvals_summary.vue'; +import ApprovalsSummaryOptional from './approvals_summary_optional.vue'; +import { FETCH_LOADING, FETCH_ERROR, APPROVE_ERROR, UNAPPROVE_ERROR } from './messages'; + +export default { + name: 'MRWidgetApprovals', + components: { + MrWidgetContainer, + MrWidgetIcon, + ApprovalsSummary, + ApprovalsSummaryOptional, + GlButton, + }, + mixins: [approvalsMixin], + props: { + mr: { + type: Object, + required: true, + }, + service: { + type: Object, + required: true, + }, + isOptionalDefault: { + type: Boolean, + required: false, + default: null, + }, + approveDefault: { + type: Function, + required: false, + default: null, + }, + modalId: { + type: String, + required: false, + default: null, + }, + requirePasswordToApprove: { + type: Boolean, + required: false, + default: false, + }, + }, + data() { + return { + fetchingApprovals: true, + hasApprovalAuthError: false, + isApproving: false, + }; + }, + computed: { + isBasic() { + return this.mr.approvalsWidgetType === 'base'; + }, + isApproved() { + return Boolean(this.approvals.approved); + }, + isOptional() { + return this.isOptionalDefault !== null ? this.isOptionalDefault : !this.approvedBy.length; + }, + hasAction() { + return Boolean(this.action); + }, + approvals() { + return this.mr.approvals || {}; + }, + approvedBy() { + return this.approvals.approved_by ? this.approvals.approved_by.map(x => x.user) : []; + }, + userHasApproved() { + return Boolean(this.approvals.user_has_approved); + }, + userCanApprove() { + return Boolean(this.approvals.user_can_approve); + }, + showApprove() { + return !this.userHasApproved && this.userCanApprove && this.mr.isOpen; + }, + showUnapprove() { + return this.userHasApproved && !this.userCanApprove && this.mr.state !== 'merged'; + }, + approvalText() { + return this.isApproved && this.approvedBy.length > 0 + ? s__('mrWidget|Approve additionally') + : s__('mrWidget|Approve'); + }, + action() { + // Use the default approve action, only if we aren't using the auth component for it + if (this.showApprove) { + return { + text: this.approvalText, + category: this.isApproved ? 'secondary' : 'primary', + variant: 'info', + action: () => this.approve(), + }; + } else if (this.showUnapprove) { + return { + text: s__('mrWidget|Revoke approval'), + variant: 'warning', + category: 'secondary', + action: () => this.unapprove(), + }; + } + + return null; + }, + }, + created() { + this.refreshApprovals() + .then(() => { + this.fetchingApprovals = false; + }) + .catch(() => createFlash(FETCH_ERROR)); + }, + methods: { + approve() { + if (this.requirePasswordToApprove) { + this.$root.$emit('bv::show::modal', this.modalId); + return; + } + + this.updateApproval( + () => this.service.approveMergeRequest(), + () => createFlash(APPROVE_ERROR), + ); + }, + approveWithAuth(data) { + this.updateApproval( + () => this.service.approveMergeRequestWithAuth(data), + error => { + if (error && error.response && error.response.status === 401) { + this.hasApprovalAuthError = true; + return; + } + createFlash(APPROVE_ERROR); + }, + ); + }, + unapprove() { + this.updateApproval( + () => this.service.unapproveMergeRequest(), + () => createFlash(UNAPPROVE_ERROR), + ); + }, + updateApproval(serviceFn, errFn) { + this.isApproving = true; + this.clearError(); + return serviceFn() + .then(data => { + this.mr.setApprovals(data); + eventHub.$emit('MRWidgetUpdateRequested'); + this.$emit('updated'); + }) + .catch(errFn) + .then(() => { + this.isApproving = false; + }); + }, + }, + FETCH_LOADING, +}; +</script> +<template> + <mr-widget-container> + <div class="js-mr-approvals d-flex align-items-start align-items-md-center"> + <mr-widget-icon name="approval" /> + <div v-if="fetchingApprovals">{{ $options.FETCH_LOADING }}</div> + <template v-else> + <gl-button + v-if="action" + :variant="action.variant" + :category="action.category" + :loading="isApproving" + class="mr-3" + data-qa-selector="approve_button" + @click="action.action" + > + {{ action.text }} + </gl-button> + <approvals-summary-optional + v-if="isOptional" + :can-approve="hasAction" + :help-path="mr.approvalsHelpPath" + /> + <approvals-summary + v-else + :approved="isApproved" + :approvals-left="approvals.approvals_left || 0" + :rules-left="approvals.approvalRuleNamesLeft" + :approvers="approvedBy" + /> + <slot + :is-approving="isApproving" + :approve-with-auth="approveWithAuth" + :hasApproval-auth-error="hasApprovalAuthError" + ></slot> + </template> + </div> + <template #footer> + <slot name="footer"></slot> + </template> + </mr-widget-container> +</template> |