Projects UI updates

-Add list view for users to accept/decline invitations, found under
 Projects->Invitations drop-down

-Allow project admin to remove pending invitations

-If invites are required, show pop-up after adding user, indicating
 that user will be added after accpting invitation

-Hide all invitation-related UI elements if
 projects.invite.required == false
This commit is contained in:
Brian Federle 2011-12-09 15:21:55 -08:00
parent 0980c47100
commit 76f79d80ca
4 changed files with 285 additions and 88 deletions

View File

@ -242,7 +242,6 @@ body.login {
position: absolute;
margin-left: 11px;
margin-top: 12px;
cursor: text;
}
.login .fields .field label.error {
@ -2290,7 +2289,7 @@ div.toolbar div.filters select {
max-height: 29px;
background: url(../images/bg-breadcrumbs.png) repeat-x;
overflow: hidden;
width: 794px;
width: 100%;
}
.project-view #breadcrumbs {
@ -2403,7 +2402,6 @@ div.toolbar div.filters select {
margin-left: -10px;
text-indent: 13px;
font-weight: bold;
overflow: hidden;
}
#breadcrumbs div.active-project {
@ -6880,7 +6878,8 @@ div.panel.ui-dialog div.list-view div.fixed-header {
}
.destroy .icon,
.delete .icon {
.delete .icon,
.decline .icon {
background-position: 1px -92px;
}

View File

@ -93,6 +93,7 @@
* g_supportELB: "public" - ips are allocated on public network (so use 'forvirtualnetwork' = true)
* g_supportELB: "false" â no ELB support
*/
g_capabilities = json.listcapabilitiesresponse.capability;
g_supportELB = json.listcapabilitiesresponse.capability.supportELB.toString(); //convert boolean to string if it's boolean
$.cookie('supportELB', g_supportELB, { expires: 1});
@ -196,6 +197,7 @@
* g_supportELB: "public" - ips are allocated on public network (so use 'forvirtualnetwork' = true)
* g_supportELB: "false" no ELB support
*/
g_capabilities = json.listcapabilitiesresponse.capability;
g_supportELB = json.listcapabilitiesresponse.capability.supportELB.toString(); //convert boolean to string if it's boolean
$.cookie('supportELB', g_supportELB, { expires: 1});

View File

@ -3,7 +3,7 @@
requireInvitation: function(args) {
return window.g_projectsInviteRequired;
},
add: function(args) {
setTimeout(function() {
$.ajax({
@ -36,16 +36,18 @@
noSelect: true,
fields: {
'email': { edit: true, label: 'E-mail' },
'account': { edit: 'ignore', label: 'Account' },
'state': { edit: 'ignore', label: 'Status' },
'add-user': { addButton: true, label: '' }
},
add: {
label: 'Invite',
label: 'E-mail invite',
action: function(args) {
$.ajax({
url: createURL('addAccountToProject', { ignoreProject: true }),
data: {
projectId: args.context.projects[0].id,
account: args.data.account,
email: args.data.email
},
dataType: 'json',
@ -65,7 +67,7 @@
error: function(data) {
args.response.error('Could not create user');
}
});
});
}
},
actionPreFilter: function(args) {
@ -78,7 +80,28 @@
return ['destroy'];
},
actions: {},
actions: {
destroy: {
label: 'Revoke invitation',
action: function(args) {
$.ajax({
url: createURL('deleteProjectInvitation'),
data: {
id: args.context.multiRule[0].id
},
success: function(data) {
args.response.success({
_custom: { jobId: data.deleteprojectinvitationresponse.jobid },
notification: {
label: 'Un-invited user',
poll: pollAsyncJobResult
}
});
}
});
}
}
},
// Project users data provider
dataProvider: function(args) {
@ -95,7 +118,8 @@
data: $.map(invites, function(elem) {
return {
id: elem.id,
email: elem.email ? elem.email : elem.account,
account: elem.account,
email: elem.email,
state: elem.state
};
})
@ -123,7 +147,6 @@
dataType: 'json',
async: true,
success: function(data) {
data: args.data,
args.response.success({
_custom: {
jobId: data.addaccounttoprojectresponse.jobid
@ -133,6 +156,10 @@
poll: pollAsyncJobResult
}
});
if (g_capabilities.projectinviterequired) {
cloudStack.dialog.notice({ message: 'Invite sent to user; they will be added to the project once they accept the invitation' });
}
}
});
}
@ -266,91 +293,248 @@
cloudStack.sections.projects = {
title: 'Projects',
id: 'projects',
listView: {
fields: {
name: { label: 'Project Name' },
displaytext: { label: 'Display Text' },
domain: { label: 'Domain' },
account: { label: 'Owner' },
state: { label: 'Status', indicator: { 'Active': 'on', 'Destroyed': 'off', 'Disabled': 'off' } }
},
dataProvider: function(args) {
$.ajax({
url: createURL('listProjects', { ignoreProject: true }),
dataType: 'json',
async: true,
success: function(data) {
args.response.success({
data: data.listprojectsresponse.project,
actionFilter: projectsActionFilter
});
}
});
},
actions: {
add: {
label: 'New Project',
action: {
custom: function(args) {
$(window).trigger('cloudStack.newProject');
}
sectionSelect: {
label: 'Select view'
},
sections: {
projects: {
type: 'select',
id: 'projects',
title: 'Projects',
listView: {
fields: {
name: { label: 'Project Name' },
displaytext: { label: 'Display Text' },
domain: { label: 'Domain' },
account: { label: 'Owner' },
state: { label: 'Status', indicator: { 'Active': 'on', 'Destroyed': 'off', 'Disabled': 'off', 'Left Project': 'off' } }
},
messages: {
confirm: function(args) {
return 'Are you sure you want to remove ' + args.name + '?';
},
notification: function(args) {
return 'Removed project';
}
},
notification: {
poll: testData.notifications.testPoll
}
},
destroy: {
label: 'Remove project',
action: function(args) {
dataProvider: function(args) {
$.ajax({
url: createURL('deleteProject', { ignoreProject: true }),
data: {
id: args.data.id
},
url: createURL('listProjects', { ignoreProject: true }),
dataType: 'json',
async: true,
success: function(data) {
args.response.success({
_custom: {
getUpdatedItem: function(data) {
return $.extend(data, { state: 'Destroyed' });
},
getActionFilter: function(args) {
return function() {
return [];
};
},
jobId: data.deleteprojectresponse.jobid
}
data: data.listprojectsresponse.project,
actionFilter: projectsActionFilter
});
}
});
},
messages: {
confirm: function(args) {
return 'Are you sure you want to remove ' + args.name + '?';
actions: {
add: {
label: 'New Project',
action: {
custom: function(args) {
$(window).trigger('cloudStack.newProject');
}
},
actions: {
add: {
label: 'New Project',
action: {
custom: function(args) {
$(window).trigger('cloudStack.newProject');
}
},
messages: {
confirm: function(args) {
return 'Are you sure you want to remove ' + args.name + '?';
},
notification: function(args) {
return 'Removed project';
}
},
notification: {
poll: testData.notifications.testPoll
}
},
destroy: {
label: 'Remove project',
action: function(args) {
$.ajax({
url: createURL('deleteProject', { ignoreProject: true }),
data: {
id: args.data.id
},
dataType: 'json',
async: true,
success: function(data) {
args.response.success({
_custom: {
getUpdatedItem: function(data) {
return $.extend(data, { state: 'Destroyed' });
},
getActionFilter: function(args) {
return function() {
return [];
};
},
jobId: data.deleteprojectresponse.jobid
}
});
}
});
},
messages: {
confirm: function(args) {
return 'Are you sure you want to remove ' + args.name + '?';
},
notification: function(args) {
return 'Removed project';
}
},
notification: {
poll: pollAsyncJobResult
}
}
}
},
notification: function(args) {
return 'Removed project';
destroy: {
label: 'Remove project',
action: function(args) {
$.ajax({
url: createURL('deleteProject', { ignoreProject: true }),
data: {
id: args.data.id
},
dataType: 'json',
async: true,
success: function(data) {
args.response.success({
_custom: {
getUpdatedItem: function(data) {
return $.extend(data, { state: 'Destroyed' });
},
getActionFilter: function(args) {
return function() {
return [];
};
},
jobId: data.deleteprojectresponse.jobid
}
});
}
});
},
messages: {
confirm: function(args) {
return 'Are you sure you want to remove ' + args.name + '?';
},
notification: function(args) {
return 'Removed project';
}
},
notification: {
poll: pollAsyncJobResult
}
}
}
}
},
invitations: {
type: 'select',
id: 'invitations',
title: 'Invitations',
listView: {
fields: {
project: { label: 'Project' },
domain: { label: 'Domain' },
state: {
label: 'Status',
indicator: {
'Accepted': 'on', 'Completed': 'on',
'Pending': 'off', 'Declined': 'off'
}
}
},
notification: {
poll: pollAsyncJobResult
dataProvider: function(args) {
$.ajax({
url: createURL('listProjectInvitations'),
data: {
account: cloudStack.context.users[0].account,
domainid: cloudStack.context.users[0].domainid,
state: 'Pending'
},
success: function(data) {
args.response.success({
actionFilter: projectInvitationActionFilter,
data: data.listprojectinvitationsresponse.projectinvitation
});
}
});
},
actions: {
accept: {
label: 'Accept Invitation',
action: function(args) {
$.ajax({
url: createURL('updateProjectInvitation'),
data: {
projectid: args.context.invitations[0].projectid,
account: args.context.users[0].account,
domainid: args.context.users[0].domainid,
accept: true
},
success: function(data) {
args.response.success({
_custom: {
jobId: data.updateprojectinvitationresponse.jobid,
getUpdatedItem: function() { return { state: 'Accepted' }; }
}
});
}
});
},
messages: {
confirm: function() { return 'Please confirm you wish to join this project.'; },
notification: function() { return 'Accepted project invitation'; }
},
notification: { poll: pollAsyncJobResult }
},
decline: {
label: 'Decline Invitation',
action: function(args) {
$.ajax({
url: createURL('updateProjectInvitation'),
data: {
projectid: args.context.invitations[0].projectid,
account: args.context.users[0].account,
accept: false
},
success: function(data) {
args.response.success({
_custom: {
jobId: data.updateprojectinvitationresponse.jobid,
getUpdatedItem: function() { return { state: 'Declined' }; }
}
});
}
});
},
notification: { poll: pollAsyncJobResult },
messages: {
confirm: function() { return 'Are you sure you want to decline this project invitation?'; },
notification: function() { return 'Declined project invitation'; }
}
}
}
}
}
@ -358,10 +542,20 @@
};
var projectsActionFilter = function(args) {
if (args.context.item.account == args.context.users[0].account) {
if (args.context.item.account == cloudStack.context.users[0].account) {
return ['destroy'];
}
return [];
};
var projectInvitationActionFilter = function(args) {
var state = args.context.item.state;
if (state == 'Accepted' || state == 'Completed' || state == 'Declined') {
return [];
}
return ['accept', 'decline'];
};
} (cloudStack, testData));

View File

@ -18,21 +18,23 @@
*/
dashboard: function() {
var tabs = {
Overview: function() {
return $('<img>').attr('src', 'images/screens/ProjectDashboard.png');
overview: function() {
return $('<img>').attr('src', 'images/screens/ProjectDashboard.png').data('tab-title', 'Overview');
}
};
// Only show management tabs to owner of project
if (cloudStack.context.projects &&
(cloudStack.context.projects[0].account == cloudStack.context.users[0].account)) {
tabs['Users'] = function() {
return $('<div>').addClass('management');
tabs.users = function() {
return $('<div>').addClass('management').data('tab-title', 'Users');
};
tabs['Invitations'] = function() {
return $('<div>').addClass('management-invite');
};
if (g_capabilities.projectinviterequired) {
tabs.invitations = function() {
return $('<div>').addClass('management-invite').data('tab-title', 'Invitations');
};
}
}
var $tabs = $('<div>').addClass('tab-content').append($('<ul>'));
@ -43,7 +45,7 @@
var $tab = $('<li>').appendTo($tabs.find('ul'));
var $tabLink = $('<a>')
.attr({ href: '#project-view-dashboard-' + tabName })
.html(tabName)
.html(tab().data('tab-title'))
.appendTo($tab);
var $content = $('<div>')
.appendTo($tabs)