Commit 28fddda4 by Muzaffar yousaf

Merge pull request #9376 from edx/muzaffar/tnl1914-team-edit-page

Edit team page for Instructors only.
parents f9756eb4 f788caa6
......@@ -205,7 +205,7 @@ class BrowseTeamsPage(CoursePage, PaginatedUIMixin):
self.wait_for_ajax()
class CreateTeamPage(CoursePage, FieldsMixin):
class CreateOrEditTeamPage(CoursePage, FieldsMixin):
"""
Create team page.
"""
......@@ -216,7 +216,7 @@ class CreateTeamPage(CoursePage, FieldsMixin):
representation of a topic following the same convention as a
course module's topic.
"""
super(CreateTeamPage, self).__init__(browser, course_id)
super(CreateOrEditTeamPage, self).__init__(browser, course_id)
self.topic = topic
self.url_path = "teams/#topics/{topic_id}/create-team".format(topic_id=self.topic['id'])
......@@ -410,3 +410,12 @@ class TeamPage(CoursePage, PaginatedUIMixin):
"""Navigate to the 'All Topics' page."""
self.q(css='.breadcrumbs a').results[0].click()
self.wait_for_ajax()
@property
def edit_team_button_present(self):
""" Returns True if Edit Team button is present else False """
return self.q(css='.form-actions .action-edit-team').present
def click_edit_team_button(self):
""" Click on Edit Team button"""
self.q(css='.form-actions .action-edit-team').first.click()
......@@ -14,9 +14,9 @@
maxTeamDescriptionLength: 300,
events: {
'click .action-primary': 'createTeam',
'submit form': 'createTeam',
'click .action-cancel': 'goBackToTopic'
'click .action-primary': 'createOrUpdateTeam',
'submit form': 'createOrUpdateTeam',
'click .action-cancel': 'cancelAndGoBack'
},
initialize: function(options) {
......@@ -27,12 +27,20 @@
this.teamsUrl = options.teamParams.teamsUrl;
this.languages = options.teamParams.languages;
this.countries = options.teamParams.countries;
this.primaryButtonTitle = options.primaryButtonTitle || 'Submit';
_.bindAll(this, 'goBackToTopic', 'createTeam');
this.teamModel = new TeamModel({});
this.teamModel.url = this.teamsUrl;
this.teamsDetailUrl = options.teamParams.teamsDetailUrl;
this.action = options.action;
_.bindAll(this, 'cancelAndGoBack', 'createOrUpdateTeam');
if (this.action === 'create') {
this.teamModel = new TeamModel({});
this.teamModel.url = this.teamsUrl;
this.primaryButtonTitle = 'Create';
} else if(this.action === 'edit' ) {
this.teamModel = options.model;
this.teamModel.url = this.teamsDetailUrl.replace('team_id', options.model.get('id')) + '?expand=user';
this.primaryButtonTitle = 'Update';
}
this.teamNameField = new FieldViews.TextFieldView({
model: this.teamModel,
......@@ -74,7 +82,11 @@
},
render: function() {
this.$el.html(_.template(editTeamTemplate)({primaryButtonTitle: this.primaryButtonTitle}));
this.$el.html(_.template(editTeamTemplate) ({
primaryButtonTitle: this.primaryButtonTitle,
action: this.action,
totalMembers: _.isUndefined(this.teamModel) ? 0 : this.teamModel.get('membership').length
}));
this.set(this.teamNameField, '.team-required-fields');
this.set(this.teamDescriptionField, '.team-required-fields');
this.set(this.teamLanguageField, '.team-optional-fields');
......@@ -91,20 +103,28 @@
}
},
createTeam: function (event) {
createOrUpdateTeam: function (event) {
event.preventDefault();
var view = this,
teamLanguage = this.teamLanguageField.fieldValue(),
teamCountry = this.teamCountryField.fieldValue();
var data = {
course_id: this.courseID,
topic_id: this.topicID,
name: this.teamNameField.fieldValue(),
description: this.teamDescriptionField.fieldValue(),
language: _.isNull(teamLanguage) ? '' : teamLanguage,
country: _.isNull(teamCountry) ? '' : teamCountry
};
teamCountry = this.teamCountryField.fieldValue(),
data = {
name: this.teamNameField.fieldValue(),
description: this.teamDescriptionField.fieldValue(),
language: _.isNull(teamLanguage) ? '' : teamLanguage,
country: _.isNull(teamCountry) ? '' : teamCountry
},
saveOptions = {
wait: true
};
if (this.action === 'create') {
data.course_id = this.courseID;
data.topic_id = this.topicID;
} else if (this.action === 'edit' ) {
saveOptions.patch = true;
saveOptions.contentType = 'application/merge-patch+json';
}
var validationResult = this.validateTeamData(data);
if (validationResult.status === false) {
......@@ -112,10 +132,10 @@
return;
}
this.teamModel.save(data, { wait: true })
this.teamModel.save(data, saveOptions)
.done(function(result) {
view.teamEvents.trigger('teams:update', {
action: 'create',
action: view.action,
team: result
});
Backbone.history.navigate(
......@@ -186,8 +206,15 @@
}
},
goBackToTopic: function () {
Backbone.history.navigate('topics/' + this.topicID, {trigger: true});
cancelAndGoBack: function (event) {
event.preventDefault();
var url;
if (this.action === 'create') {
url = 'topics/' + this.topicID;
} else if (this.action === 'edit' ) {
url = 'teams/' + this.topicID + '/' + this.teamModel.get('id');
}
Backbone.history.navigate(url, {trigger: true});
}
});
});
......
......@@ -5,8 +5,8 @@
'underscore',
'gettext',
'teams/js/views/team_utils',
'text!teams/templates/team-join.underscore'],
function (Backbone, _, gettext, TeamUtils, teamJoinTemplate) {
'text!teams/templates/team-profile-header-actions.underscore'],
function (Backbone, _, gettext, TeamUtils, teamProfileHeaderActionsTemplate) {
return Backbone.View.extend({
errorMessage: gettext("An error occurred. Try again."),
......@@ -14,31 +14,34 @@
teamFullMessage: gettext("This team is full."),
events: {
"click .action-primary": "joinTeam"
"click .action-primary": "joinTeam",
"click .action-edit-team": "editTeam"
},
initialize: function(options) {
this.teamEvents = options.teamEvents;
this.template = _.template(teamJoinTemplate);
this.template = _.template(teamProfileHeaderActionsTemplate);
this.courseID = options.courseID;
this.maxTeamSize = options.maxTeamSize;
this.currentUsername = options.currentUsername;
this.teamMembershipsUrl = options.teamMembershipsUrl;
_.bindAll(this, 'render', 'joinTeam', 'getUserTeamInfo');
this.showEditButton = options.showEditButton;
this.topicID = options.topicID;
_.bindAll(this, 'render', 'joinTeam','editTeam', 'getUserTeamInfo');
this.listenTo(this.model, "change", this.render);
},
render: function() {
var view = this,
message,
showButton,
showJoinButton,
teamHasSpace;
this.getUserTeamInfo(this.currentUsername, view.maxTeamSize).done(function (info) {
teamHasSpace = info.teamHasSpace;
// if user is the member of current team then we wouldn't show anything
if (!info.memberOfCurrentTeam) {
showButton = !info.alreadyMember && teamHasSpace;
showJoinButton = !info.alreadyMember && teamHasSpace;
if (info.alreadyMember) {
message = info.memberOfCurrentTeam ? '' : view.alreadyMemberMessage;
......@@ -47,7 +50,11 @@
}
}
view.$el.html(view.template({showButton: showButton, message: message}));
view.$el.html(view.template({
showJoinButton: showJoinButton,
message: message,
showEditButton: view.showEditButton
}));
});
return view;
},
......@@ -108,6 +115,10 @@
}
return deferred.promise();
},
editTeam: function (event) {
event.preventDefault();
Backbone.history.navigate('topics/' + this.topicID + '/' + this.model.get('id') +'/edit-team', {trigger: true});
}
});
});
......
......@@ -17,12 +17,12 @@
'teams/js/views/my_teams',
'teams/js/views/topic_teams',
'teams/js/views/edit_team',
'teams/js/views/team_join',
'teams/js/views/team_profile_header_actions',
'text!teams/templates/teams_tab.underscore'],
function (Backbone, _, gettext, HeaderView, HeaderModel, TabbedView,
TopicModel, TopicCollection, TeamModel, TeamCollection, TeamMembershipCollection,
TopicsView, TeamProfileView, MyTeamsView, TopicTeamsView, TeamEditView,
TeamJoinView, teamsTemplate) {
TeamProfileHeaderActionsView, teamsTemplate) {
var TeamsHeaderModel = HeaderModel.extend({
initialize: function (attributes) {
_.extend(this.defaults, {nav_aria_label: gettext('teams')});
......@@ -52,6 +52,7 @@
this.topics = options.topics;
this.topicUrl = options.topicUrl;
this.teamsUrl = options.teamsUrl;
this.teamsDetailUrl = options.teamsDetailUrl;
this.teamMembershipsUrl = options.teamMembershipsUrl;
this.teamMembershipDetailUrl = options.teamMembershipDetailUrl;
this.maxTeamSize = options.maxTeamSize;
......@@ -74,6 +75,7 @@
}, this)],
['topics/:topic_id(/)', _.bind(this.browseTopic, this)],
['topics/:topic_id/create-team(/)', _.bind(this.newTeam, this)],
['topics/:topic_id/:team_id/edit-team(/)', _.bind(this.editTeam, this)],
['teams/:topic_id/:team_id(/)', _.bind(this.browseTeam, this)],
[new RegExp('^(browse)\/?$'), _.bind(this.goToTab, this)],
[new RegExp('^(my-teams)\/?$'), _.bind(this.goToTab, this)]
......@@ -208,10 +210,9 @@
})
}),
main: new TeamEditView({
action: 'create',
teamEvents: self.teamEvents,
tagName: 'create-new-team',
teamParams: teamsView.main.teamParams,
primaryButtonTitle: 'Create'
teamParams: teamsView.main.teamParams
})
});
self.render();
......@@ -219,6 +220,44 @@
},
/**
* Render the edit team form.
*/
editTeam: function (topicID, teamID) {
var self = this,
editViewWithHeader;
this.getTopic(topicID).done(function (topic) {
self.getTeam(teamID, false).done(function(team) {
var view = new TeamEditView({
action: 'edit',
teamEvents: self.teamEvents,
teamParams: {
courseID: self.courseID,
topicID: topic.get('id'),
teamsUrl: self.teamsUrl,
topicName: topic.get('name'),
languages: self.languages,
countries: self.countries,
teamsDetailUrl: self.teamsDetailUrl
},
model: team
});
editViewWithHeader = self.createViewWithHeader({
mainView: view,
subject: {
name: gettext("Edit Team"),
description: gettext("If you make significant changes, make sure you notify members of the team before making these changes.")
},
parentTeam: team,
parentTopic: topic
}
);
self.mainView = editViewWithHeader;
self.render();
});
});
},
/**
* Return a promise for the TeamsView for the given topic ID.
*/
getTeamsView: function (topicID) {
......@@ -227,7 +266,7 @@
var self = this,
router = this.router,
deferred = $.Deferred();
if (this.teamsCollection && this.teamsCollection.topic_id === topicID) {
if (this.teamsCollection && this.teamsCollection.topic_id === topicID && this.teamsView) {
deferred.resolve(this.teamsView);
} else {
this.getTopic(topicID)
......@@ -254,7 +293,8 @@
teamsUrl: self.teamsUrl,
topicName: topic.get('name'),
languages: self.languages,
countries: self.countries
countries: self.countries,
teamsDetailUrl: self.teamsDetailUrl
}
});
deferred.resolve(
......@@ -311,14 +351,17 @@
teamMembershipDetailUrl: self.teamMembershipDetailUrl,
setFocusToHeaderFunc: self.setFocusToHeader
});
var teamJoinView = new TeamJoinView({
var TeamProfileActionsView = new TeamProfileHeaderActionsView({
teamEvents: self.teamEvents,
courseID: courseID,
model: team,
courseID: courseID,
model: team,
teamsUrl: self.teamsUrl,
maxTeamSize: self.maxTeamSize,
currentUsername: self.userInfo.username,
teamMembershipsUrl: self.teamMembershipsUrl
teamMembershipsUrl: self.teamMembershipsUrl,
topicID: topicID,
showEditButton: self.userInfo.privileged || self.userInfo.staff
});
deferred.resolve(
self.createViewWithHeader(
......@@ -326,7 +369,7 @@
mainView: view,
subject: team,
parentTopic: topic,
headerActionsView: teamJoinView
headerActionsView: TeamProfileActionsView
}
)
);
......@@ -337,7 +380,8 @@
createViewWithHeader: function (options) {
var router = this.router,
breadcrumbs, headerView;
breadcrumbs, headerView,
viewDescription, viewTitle;
breadcrumbs = [{
title: gettext('All Topics'),
url: '#browse'
......@@ -348,10 +392,25 @@
url: '#topics/' + options.parentTopic.id
});
}
if (options.parentTeam) {
breadcrumbs.push({
title: options.parentTeam.get('name'),
url: '#teams/' + options.parentTopic.id + '/' + options.parentTeam.id
});
}
if (options.subject instanceof Backbone.Model) {
viewDescription = options.subject.get('description');
viewTitle = options.subject.get('name');
} else if (options.subject) {
viewDescription = options.subject.description;
viewTitle = options.subject.name;
}
headerView = new HeaderView({
model: new TeamsHeaderModel({
description: options.subject.get('description'),
title: options.subject.get('name'),
description: viewDescription,
title: viewTitle,
breadcrumbs: breadcrumbs
}),
headerActionsView: options.headerActionsView,
......
<form>
<div class="create-team wrapper-msg is-incontext urgency-low warning is-hidden" tabindex="-1">
<div class="create-team wrapper-msg is-incontext urgency-low error is-hidden" tabindex="-1">
<div class="msg">
<div class="msg-content">
<h3 class="title"><%- gettext("Your team could not be created!") %></h3>
<h3 class="title">
<% if (action === 'create') { %>
<%- gettext("Your team could not be created.") %>
<% } else if (action === 'edit') { %>
<%- gettext("Your team could not be updated.") %>
<% } %>
</h3>
<span class="screen-reader-message sr"></span>
<div class="copy">
<p></p>
......@@ -11,15 +17,16 @@
</div>
</div>
<div class="form-instructions create-team-instructions">
<p class="copy">
<%- gettext("Enter information to describe your team. You cannot change these details after you create the team.") %></p>
</div>
<% if (action === 'create') { %>
<div class="form-instructions create-team-instructions">
<p class="copy">
<%- gettext("Enter information to describe your team. You cannot change these details after you create the team.") %></p>
</div>
<% } %>
<div class="team-edit-fields">
<div class="team-required-fields">
</div>
<div class="team-optional-fields">
<fieldset>
<div class="u-field u-field-optional_description">
......@@ -38,7 +45,7 @@
<button class="action action-primary">
<%=
interpolate_text(
_.escape(gettext("{primaryButtonTitle} {span_start}a new team{span_end}")),
_.escape(gettext("{primaryButtonTitle} {span_start}a team{span_end}")),
{
'primaryButtonTitle': primaryButtonTitle, 'span_start': '<span class="sr">', 'span_end': '</span>'
}
......@@ -48,9 +55,10 @@
<button class="action action-cancel">
<%=
interpolate_text(
_.escape(gettext("Cancel {span_start}a new team{span_end}")),
_.escape(gettext("Cancel {span_start} {action} team {span_end}")),
{
'span_start': '<span class="sr">', 'span_end': '</span>'
'span_start': '<span class="sr">', 'span_end': '</span>',
'action': action === 'create' ? 'creating' : 'updating'
}
)
%>
......
<div class="join-team form-actions">
<% if (showButton) {%>
<% if (showJoinButton) {%>
<button class="action action-primary">
<%- gettext("Join Team") %>
</button>
<% } else if (message) { %>
<p class="join-team-message"><%- message %></p>
<% } %>
<% if (showEditButton) { %>
<button class="btn btn-secondary action-edit-team"><%- gettext("Edit Team") %></button>
<% } %>
</div>
......@@ -50,7 +50,7 @@
<% if (isMember) { %>
<div class="leave-team">
<button class="btn btn-link btn-base btn-secondary leave-team-link"><%- gettext("Leave Team") %></button>
<button class="btn btn-link leave-team-link"><%- gettext("Leave Team") %></button>
</div>
<div class="divider-lv1"></div>
......
......@@ -39,6 +39,7 @@
topicUrl: '${ topic_url }',
topicsUrl: '${ topics_url }',
teamsUrl: '${ teams_url }',
teamsDetailUrl: '${ teams_detail_url }',
teamMembershipsUrl: '${ team_memberships_url }',
teamMembershipDetailUrl: '${ team_membership_detail_url }',
maxTeamSize: ${ course.teams_max_size },
......
......@@ -117,6 +117,7 @@ class TeamsDashboardView(View):
),
"topics_url": reverse('topics_list', request=request),
"teams_url": reverse('teams_list', request=request),
"teams_detail_url": reverse('teams_detail', args=['team_id']),
"team_memberships_url": reverse('team_membership_list', request=request),
"team_membership_detail_url": reverse('team_membership_detail', args=['team_id', user.username]),
"languages": settings.ALL_LANGUAGES,
......
......@@ -802,7 +802,7 @@
'lms/include/teams/js/spec/views/topic_card_spec.js',
'lms/include/teams/js/spec/views/topic_teams_spec.js',
'lms/include/teams/js/spec/views/topics_spec.js',
'lms/include/teams/js/spec/views/team_join_spec.js'
'lms/include/teams/js/spec/views/team_profile_header_actions_spec.js'
]);
}).call(this, requirejs, define);
......@@ -404,7 +404,7 @@
}
}
.btn-link {
.btn-secondary {
@extend %btn-pl-secondary-base;
background-image: none;
......@@ -416,3 +416,24 @@
color: $link-color;
}
}
.btn-link {
@extend %shame-link-text;
// reset of inherited buttons
border-radius: 0;
border-color: $transparent;
padding: 1px;
background: $transparent;
background-image: none;
@extend %t-action3;
@extend %t-strong;
&:focus,
&:hover {
background-image: none !important;
background-color: $transparent !important;
box-shadow: none !important;
}
}
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