Commit ca6f3abb authored by Bryce Johnson's avatar Bryce Johnson

Break out services by component.

parent c3293aaf
//= require_directory ./stores
//= require_directory ./services
//= require_directory ./components //= require_directory ./components
(() => { (() => {
// can the user edit this MR
// has this user approved this MR
// What is the info about users who can approve this MR but have not yet?
// How many approvals are needed?
// How many approvals are had?
gl.MergeRequestWidget.approvalsBody = { gl.MergeRequestWidget.approvalsBody = {
props: ['approverNames', 'approvalsLeft', 'canApprove'], props: ['approverNames', 'approvalsLeft', 'canApprove'],
computed: { computed: {
...@@ -15,11 +20,14 @@ ...@@ -15,11 +20,14 @@
}, },
methods: { methods: {
approveMergeRequest() { approveMergeRequest() {
this.$emit('user-gives-approval'); approvalsService.approveMergeRequest();
}, },
}, },
beforeCreate() {
approvalsService.fetchApprovals();
},
template: ` template: `
<div> Hello approval body <div>
<h4> Requires {{ approvalsRequiredStringified }} (from {{ approverNamesStringified }})</h4> <h4> Requires {{ approvalsRequiredStringified }} (from {{ approverNamesStringified }})</h4>
<div class="append-bottom-10"> <div class="append-bottom-10">
<button v-if='canApprove' @click='approveMergeRequest' class="btn btn-primary approve-btn">Approve Merge Request</button> <button v-if='canApprove' @click='approveMergeRequest' class="btn btn-primary approve-btn">Approve Merge Request</button>
...@@ -28,4 +36,4 @@ ...@@ -28,4 +36,4 @@
`, `,
}; };
})(); })();
\ No newline at end of file
(() => { //= require vue_common_component/link_to_member_avatar
const PendingApprover = {
data() {
return {
dottedCircleUrl: '/assets/no_avatar.png'
};
},
template: `
<span>
<img width="24" alt="Required Approver" :src="dottedCircleUrl" class="avatar avatar-inline s24">
</span>
`
};
(() => {
// does the user have permission to edit this thing
// has the user already approved this or not
// what are the users who have approved this already, with all their info
gl.MergeRequestWidget.approvalsFooter = { gl.MergeRequestWidget.approvalsFooter = {
props: ['approverNames', 'canApprove', 'approvedByUsers'], props: ['canUpdateMergeRequest', 'hasApprovedMergeRequest', 'approvedByUsers'],
methods: { methods: {
removeApproval() { removeApproval() {
this.$emit('remove-approval'); approvalsService.unapproveMergeRequest();
} }
}, },
components: { beforeCreate() {
'pending-approver': PendingApprover, approvalsService.fetchApprovals();
},
methods: {
}, },
template: ` template: `
<div> <div>
Hello Approvals Footer <div v-for='approver in approvedByUsers'>
<div> <link-to-member-avatar
<div v-for='approver in approvedByUsers'> :avatar-url='approver.avatar.url'
<link-to-member-avatar :display-name='approver.name'
:avatar-url='approver.avatar.url' :username='approver.username'>
:display-name='approver.name' </link-to-member-avatar>
:username='approver.username'>
</link-to-member-avatar>
</div>
<span v-if='canApprove'>
<i class='fa fa-close'></i>
<button @click='removeApproval'>Remove your approval</button>
{{ approvedByUsers[0].name }}
</span>
</div> </div>
<span>
<i class='fa fa-close'></i>
<button @click='removeApproval'>Remove your approval</button>
{{ approvedByUsers[0].name }}
</span>
</div> </div>
` `
}; };
})(); })();
//= require ../stores/approvals_store
(() => {
class ApprovalsService {
constructor() {
this.isFetching = false;
this.hasBeenFetched = true;
}
fetchApprovals() {
return makeRequest().then((data) => {
this.isFetching = false;
this.hasBeenFetched = true;
return data;
});
}
approveMergeRequest(payload) {
return payload;
}
unapproveMergeRequest(payload) {
return payload;
}
}
gl.mergeRequestWidget.ApprovalsService = ApprovalsService;
})();
\ No newline at end of file
...@@ -3,32 +3,28 @@ ...@@ -3,32 +3,28 @@
constructor(el) { constructor(el) {
this.dataset = el.dataset; this.dataset = el.dataset;
this.data = {}; this.data = {};
// TODO: Break each into their own store // TODO: Break each into their own store
this.initResource(); this.initResource();
this.initPermissions(); this.initPermissions();
this.initApprovals(); this.initApprovals();
} }
initResource() { initResource() {
Object.assign(this.data, { Object.assign(this.data, {
resource: { resource: {
canEdit: this.dataset.endpoint endpoint: this.dataset.endpoint
} }
}); });
} }
initPermissions() { initPermissions() {
Object.assign(this.data, { Object.assign(this.data, {
permissions: { permissions: {
canEdit: Boolean(this.dataset.canEdit) canApprove: Boolean(this.dataset.canApprove)
} }
}); });
} }
initApprovals() { initApprovals() {
const dataset = this.dataset; const dataset = this.dataset;
Object.assign( this.data, { Object.assign(this.data, {
approvals: { approvals: {
approvedByUsers: JSON.parse(dataset.approvedByUsers), approvedByUsers: JSON.parse(dataset.approvedByUsers),
approverNames: JSON.parse(dataset.approverNames), approverNames: JSON.parse(dataset.approverNames),
...@@ -38,6 +34,5 @@ ...@@ -38,6 +34,5 @@
}); });
} }
} }
gl.MergeRequestWidgetStore = MergeRequestWidgetStore; gl.MergeRequestWidgetStore = MergeRequestWidgetStore;
})() })()
\ No newline at end of file
//= require ./stores/widget_store //= require ./stores/widget_store
//= require ./services/widget_service
//= require ./approvals/approvals_bundle //= require ./approvals/approvals_bundle
/**
*
* 'data-approved-by-users' => @merge_request.approved_by_users.to_json, 'data-approver-names' => @merge_request.approvers.to_json,'data-approvals-left' => @merge_request.approvals_left, 'data-more-approvals' => (@merge_request.approvals_left - @merge_request.approvers_left.count), 'data-can-approve' => @merge_request.user_is_approver(current_user), 'data-endpoint'=> '/myendpoint/tho' }
*
*/
$(() => { $(() => {
const el = document.getElementById('merge-request-widget-app'); // Move initialization to somewhere else -- all can be conducted before DOM ready
const Store = new gl.MergeRequestWidgetStore(el); new gl.MergeRequestWidgetStore();
new gl.MergeRequestWidgetService();
const app = gl.MergeRequestWidget; const app = gl.MergeRequestWidget;
new Vue({ new Vue({
el, el: '#merge-request-widget-app',
data: Store.data, data: Store.data,
components: { components: {
'approvals-body' : app.approvalsBody, 'approvals-body': app.approvalsBody,
'approvals-footer' : app.approvalsFooter 'approvals-footer': app.approvalsFooter
}, }
methods: {
unapproveMergeRequest() {
console.log("Parent instance Unapprove MR");
},
approveMergeRequest() {
console.log("Parent instance Approve MR");
}
},
}); });
}); });
/**
*
* gl.MergeRequestWidgetApp
* Store
* ApiService
*
*
* TODO: Add documentation for registering new widget components
*
*/
// Need list of approved by, list of those who can approve, and the number required
\ No newline at end of file
/* Analogue of link_to_member_avatar in app/helpers/projects_helper.rb /* Analogue of link_to_member_avatar in app/helpers/projects_helper.rb
TODO: Support gravatar link generation, adding name text, username text TODO: Support gravatar link generation, adding name text, username text
TODO: 1:1 configuration compared to link_to_member_avatar TODO: 1:1 configuration compared to link_to_member_avatar
TODO: Backport to CE TODO: Backport to CE
*/ */
(() => {
Vue.component('link-to-member-avatar', { Vue.component('link-to-member-avatar', {
props: { props: {
avatarUrl: { avatarUrl: {
type: String, type: String,
required: false required: false
},
username: {
type: String,
required: true
},
displayName: {
type: String,
required: true,
},
avatarClass: {
type: String,
default: 'avatar avatar-inline s48',
required: false,
},
linkClass: {
type: String,
default: 'author_link has-tooltip',
required: false,
},
size: {
type: Number,
default: 48,
required: false
}
}, },
username: { data() {
type: String, return {
required: true noAvatarUrl: '/assets/no_avatar.png'
};
}, },
displayName: { computed: {
type: String, userProfileUrl() {
required: true, return `/${this.username}`;
}, },
avatarClass: { preppedAvatarUrl() {
type: String, return this.avatarUrl || this.noAvatarUrl;
default: 'avatar avatar-inline s48', }
required: false,
}, },
linkClass: { template: `
type: String,
default: 'author_link has-tooltip',
required: false,
},
size: {
type: Number,
default: 48,
required: false
}
},
data() {
return {
noAvatarUrl: '/assets/no_avatar.png'
};
},
computed: {
userProfileUrl() {
return `/${this.username}`;
},
preppedAvatarUrl() {
return this.avatarUrl || this.noAvatarUrl;
}
},
template: `
<a :href='userProfileUrl' :class='linkClass' :data-original-title='displayName' data-container='body'> <a :href='userProfileUrl' :class='linkClass' :data-original-title='displayName' data-container='body'>
<img :class='avatarClass' :src='preppedAvatarUrl' :width='size' :height='size' :alt='displayName'/> <img :class='avatarClass' :src='preppedAvatarUrl' :width='size' :height='size' :alt='displayName'/>
</a> </a>
` `
}); });
})();
\ No newline at end of file
...@@ -113,7 +113,12 @@ module Approvable ...@@ -113,7 +113,12 @@ module Approvable
def approvers_overwritten? def approvers_overwritten?
approvers.any? || approver_groups.any? approvers.any? || approver_groups.any?
end end
def user_is_approver(user)
return false unless approvers.any?
approvers.include?(user)
end
def can_approve?(user) def can_approve?(user)
return false unless user return false unless user
return true if approvers_left.include?(user) return true if approvers_left.include?(user)
......
#merge-request-widget-app.mr-state-widget{ 'data-approved-by-users' => @merge_request.approved_by_users.to_json, 'data-approver-names' => @merge_request.approvers_left.map(&:name),'data-approvals-left' => @merge_request.approvals_left, #merge-request-widget-app.mr-state-widget
'data-more-approvals' => (@merge_request.approvals_left - @merge_request.approvers_left.count),'data-can-edit' => @merge_request.can_approve?(current_user), 'data-endpoint'=> '/myendpoint/tho' }
= render 'projects/merge_requests/widget/heading' = render 'projects/merge_requests/widget/heading'
.mr-widget-body .mr-widget-body
-# After conflicts are resolved, the user is redirected back to the MR page. -# After conflicts are resolved, the user is redirected back to the MR page.
...@@ -25,7 +24,8 @@ ...@@ -25,7 +24,8 @@
- elsif @merge_request.work_in_progress? - elsif @merge_request.work_in_progress?
= render 'projects/merge_requests/widget/open/wip' = render 'projects/merge_requests/widget/open/wip'
- elsif @merge_request.requires_approve? && !@merge_request.approved? - elsif @merge_request.requires_approve? && !@merge_request.approved?
%approvals-body{ '@give-approval' => 'approveMergeRequest', ':approver-names' => 'approvals.approverNames', ':approvals-left' => 'approvals.approvalsLeft', ':more-approvals' => 'approvals.moreApprovals', ':can-approve' => 'permissions.canEdit' } %approvals-body{ '@give-approval' => 'approveMergeRequest', '@fetch-approvals' => 'fetchApprovals', :approver-names' => 'approvals.approverNames', ':approvals-left' => 'approvals.approvalsLeft', ':more-approvals' => 'approvals.moreApprovals', ':can-approve' => 'permissions.canApprove', ':needs-approval' => (@merge_request.requires_approve? && !@merge_request.approved?) }
= icon('spinner spin', class: 'loading-icon')
- elsif @merge_request.merge_when_build_succeeds? - elsif @merge_request.merge_when_build_succeeds?
= render 'projects/merge_requests/widget/open/merge_when_build_succeeds' = render 'projects/merge_requests/widget/open/merge_when_build_succeeds'
- elsif !@merge_request.can_be_merged_by?(current_user) - elsif !@merge_request.can_be_merged_by?(current_user)
...@@ -50,5 +50,6 @@ ...@@ -50,5 +50,6 @@
- if @merge_request.approvals.any? - if @merge_request.approvals.any?
.mr-widget-footer.approved-by-users .mr-widget-footer.approved-by-users
%approvals-footer{ '@remove-approval' => 'unapproveMergeRequest', ':approved-by-users' => 'approvals.approvedByUsers', ':approver-names' => 'approvals.approverNames', ':approvals-left' => 'approvals.approvalsLeft', ':more-approvals' => 'approvals.moreApprovals', ':can-approve' => 'permissions.canEdit' } %approvals-footer{ '@remove-approval' => 'unapproveMergeRequest', '@fetch-approvals' => 'fetchApprovals', ':approved-by-users' => 'approvals.approvedByUsers', ':approver-names' => 'approvals.approverNames', ':approvals-left' => 'approvals.approvalsLeft', ':more-approvals' => 'approvals.moreApprovals', ':can-approve' => 'permissions.canApprove', 'has-approvals' => @merge_request.approvals.any? }
= icon('spinner spin', class: 'loading-icon'')
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment