You've already forked oncall
mirror of
https://github.com/linkedin/oncall.git
synced 2025-11-26 23:10:47 +02:00
Add UI page for team subscriptions
Also, get user data if not available in calendar events
This commit is contained in:
@@ -18,8 +18,8 @@ def test_api_v0_team_subscription(team, role):
|
|||||||
re = requests.get(api_v0('teams/%s/subscriptions' % team_name))
|
re = requests.get(api_v0('teams/%s/subscriptions' % team_name))
|
||||||
assert re.status_code == 200
|
assert re.status_code == 200
|
||||||
data = re.json()
|
data = re.json()
|
||||||
assert team_name_2 in data
|
assert {'role': role_name, 'subscription': team_name_2} in data
|
||||||
assert team_name_3 in data
|
assert {'role': role_name, 'subscription': team_name_3} in data
|
||||||
assert len(data) == 2
|
assert len(data) == 2
|
||||||
|
|
||||||
re = requests.delete(api_v0('teams/%s/subscriptions/%s/%s' % (team_name, team_name_3, role_name)))
|
re = requests.delete(api_v0('teams/%s/subscriptions/%s/%s' % (team_name, team_name_3, role_name)))
|
||||||
@@ -28,7 +28,7 @@ def test_api_v0_team_subscription(team, role):
|
|||||||
re = requests.get(api_v0('teams/%s/subscriptions' % team_name))
|
re = requests.get(api_v0('teams/%s/subscriptions' % team_name))
|
||||||
assert re.status_code == 200
|
assert re.status_code == 200
|
||||||
data = re.json()
|
data = re.json()
|
||||||
assert team_name_2 in data
|
assert {'role': role_name, 'subscription': team_name_2} in data
|
||||||
assert len(data) == 1
|
assert len(data) == 1
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -10,14 +10,14 @@ logger = logging.getLogger('oncall-api')
|
|||||||
|
|
||||||
def on_get(req, resp, team):
|
def on_get(req, resp, team):
|
||||||
connection = db.connect()
|
connection = db.connect()
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor(db.DictCursor)
|
||||||
cursor.execute('''SELECT `subscription`.`name`, `role`.`name` FROM `team`
|
cursor.execute('''SELECT `subscription`.`name` AS `subscription`, `role`.`name` AS `role` FROM `team`
|
||||||
JOIN `team_subscription` ON `team`.`id` = `team_subscription`.`team_id`
|
JOIN `team_subscription` ON `team`.`id` = `team_subscription`.`team_id`
|
||||||
JOIN `team` `subscription` ON `subscription`.`id` = `team_subscription`.`subscription_id`
|
JOIN `team` `subscription` ON `subscription`.`id` = `team_subscription`.`subscription_id`
|
||||||
JOIN `role` ON `role`.`id` = `team_subscription`.`role_id`
|
JOIN `role` ON `role`.`id` = `team_subscription`.`role_id`
|
||||||
WHERE `team`.`name` = %s''',
|
WHERE `team`.`name` = %s''',
|
||||||
team)
|
team)
|
||||||
data = [row[0] for row in cursor]
|
data = [row for row in cursor]
|
||||||
cursor.close()
|
cursor.close()
|
||||||
connection.close()
|
connection.close()
|
||||||
resp.body = json_dumps(data)
|
resp.body = json_dumps(data)
|
||||||
@@ -31,6 +31,8 @@ def on_post(req, resp, team):
|
|||||||
role_name = data.get('role')
|
role_name = data.get('role')
|
||||||
if not sub_name or not role_name:
|
if not sub_name or not role_name:
|
||||||
raise HTTPBadRequest('Invalid subscription', 'Missing subscription name or role name')
|
raise HTTPBadRequest('Invalid subscription', 'Missing subscription name or role name')
|
||||||
|
if sub_name == team:
|
||||||
|
raise HTTPBadRequest('Invalid subscription', 'Subscription team must be different from subscribing team')
|
||||||
connection = db.connect()
|
connection = db.connect()
|
||||||
cursor = connection.cursor()
|
cursor = connection.cursor()
|
||||||
try:
|
try:
|
||||||
@@ -42,12 +44,15 @@ def on_post(req, resp, team):
|
|||||||
except db.IntegrityError as e:
|
except db.IntegrityError as e:
|
||||||
err_msg = str(e.args[1])
|
err_msg = str(e.args[1])
|
||||||
if err_msg == 'Column \'team_id\' cannot be null':
|
if err_msg == 'Column \'team_id\' cannot be null':
|
||||||
err_msg = 'team "%s" not found' % team
|
err_msg = 'Team "%s" not found' % team
|
||||||
elif err_msg == 'Column \'role_id\' cannot be null':
|
elif err_msg == 'Column \'role_id\' cannot be null':
|
||||||
err_msg = 'role "%s" not found' % role_name
|
err_msg = 'Role "%s" not found' % role_name
|
||||||
elif err_msg == 'Column \'subscription_id\' cannot be null':
|
elif err_msg == 'Column \'subscription_id\' cannot be null':
|
||||||
err_msg = 'team "%s" not found' % sub_name
|
err_msg = 'Team "%s" not found' % sub_name
|
||||||
logger.exception('Unknown integrity error in team_subscriptions')
|
elif err_msg.startswith('Duplicate entry'):
|
||||||
|
err_msg = 'Subscription already exists'
|
||||||
|
else:
|
||||||
|
logger.exception('Unknown integrity error in team_subscriptions')
|
||||||
raise HTTPError('422 Unprocessable Entity', 'IntegrityError', err_msg)
|
raise HTTPError('422 Unprocessable Entity', 'IntegrityError', err_msg)
|
||||||
else:
|
else:
|
||||||
connection.commit()
|
connection.commit()
|
||||||
|
|||||||
@@ -1237,6 +1237,75 @@ overflow: hidden;
|
|||||||
min-width: 95px;
|
min-width: 95px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Team subscription page
|
||||||
|
*/
|
||||||
|
|
||||||
|
.subscription-actions {
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-subscription {
|
||||||
|
width: 24%;
|
||||||
|
margin: 0 .5%;
|
||||||
|
float: left;
|
||||||
|
transition: box-shadow .15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-subscription .subscription-actions {
|
||||||
|
border-top: 5px solid #FDE3D2;
|
||||||
|
padding-top: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-subscription .subscription-actions > span {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #000;
|
||||||
|
opacity: .3;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-subscription .subscription-actions > span .grey-icon {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-subscription .subscription-actions > span:hover {
|
||||||
|
opacity: .5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-subscription .label-col {
|
||||||
|
width: 27%;
|
||||||
|
max-width: 110px;
|
||||||
|
vertical-align: top;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-subscription .data-col {
|
||||||
|
text-transform: capitalize;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-subscription .data-col.subscription-role {
|
||||||
|
display: inline-block;
|
||||||
|
width: 167px;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-subscription .subscription-actions {
|
||||||
|
border-top: 5px solid #CCC;
|
||||||
|
padding-top: 10px;
|
||||||
|
margin-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.module-subscription-create .form-control,
|
||||||
|
#subscription-team-container {
|
||||||
|
width: 150px;
|
||||||
|
display: inline-block;
|
||||||
|
margin: 5px;
|
||||||
|
vertical-align: middle !important;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* User notifications page
|
* User notifications page
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -225,6 +225,12 @@ var oncall = {
|
|||||||
self.team.init(params.name, 'schedules');
|
self.team.init(params.name, 'schedules');
|
||||||
self.team.schedules.init(params.name);
|
self.team.schedules.init(params.name);
|
||||||
},
|
},
|
||||||
|
'team/:name/subscriptions': function(params){
|
||||||
|
oncall.callbacks.onLogin = $.noop;
|
||||||
|
oncall.callbacks.onLogout = $.noop;
|
||||||
|
self.team.init(params.name, 'subscriptions');
|
||||||
|
self.team.subscriptions.init(params.name);
|
||||||
|
},
|
||||||
'team/:name': function(params){
|
'team/:name': function(params){
|
||||||
oncall.callbacks.onLogin = $.noop;
|
oncall.callbacks.onLogin = $.noop;
|
||||||
oncall.callbacks.onLogout = $.noop;
|
oncall.callbacks.onLogout = $.noop;
|
||||||
@@ -850,6 +856,7 @@ var oncall = {
|
|||||||
calendar: '#calendar-container',
|
calendar: '#calendar-container',
|
||||||
$calendar: null,
|
$calendar: null,
|
||||||
url: '/api/v0/teams/',
|
url: '/api/v0/teams/',
|
||||||
|
userUrl: '/api/v0/users/',
|
||||||
pageSource: $('#team-calendar-template').html(),
|
pageSource: $('#team-calendar-template').html(),
|
||||||
escalateModalTemplate: $('#team-escalate-modal'),
|
escalateModalTemplate: $('#team-escalate-modal'),
|
||||||
cardColumnTemplate: $('#card-column-template').html(),
|
cardColumnTemplate: $('#card-column-template').html(),
|
||||||
@@ -1072,36 +1079,62 @@ var oncall = {
|
|||||||
// #TODO: Leverage this to create whole modal
|
// #TODO: Leverage this to create whole modal
|
||||||
var $ul = $modal.find('.inc-event-details-view'),
|
var $ul = $modal.find('.inc-event-details-view'),
|
||||||
$title = $modal.find('.inc-event-details-title'),
|
$title = $modal.find('.inc-event-details-title'),
|
||||||
userData = this.data.teamData.users[evt.user];
|
userData = this.data.teamData.users[evt.user],
|
||||||
|
userPromise = $.Deferred(),
|
||||||
|
self = this;
|
||||||
|
|
||||||
$title.text(userData.full_name);
|
if (userData !== undefined) {
|
||||||
$ul
|
userPromise.resolve();
|
||||||
.append(
|
} else {
|
||||||
$('<li />')
|
$.ajax({
|
||||||
.append('<label class="label-col">E-Mail</label>')
|
type: 'GET',
|
||||||
.append('<span class="data-col"><a href="mailto:' + userData.contacts.email + '" target="_blank">' + userData.contacts.email + '</a></span>')
|
url: this.data.userUrl + evt.user,
|
||||||
)
|
contentType: 'application/json',
|
||||||
.append(
|
dataType: 'html'
|
||||||
$('<li />')
|
}).done(function(response){
|
||||||
.append('<label class="label-col">Call</label>')
|
userData = JSON.parse(response);
|
||||||
.append('<span class="data-col"><a href="tel:' + userData.contacts.call + '">' + userData.contacts.call + '</a></span>')
|
self.data.teamData.users[evt.user] = userData;
|
||||||
)
|
userPromise.resolve();
|
||||||
.append(
|
}).fail(function(data){
|
||||||
$('<li />')
|
userPromise.reject();
|
||||||
.append('<label class="label-col">SMS</label>')
|
})
|
||||||
.append('<span class="data-col"><a href="tel:' + userData.contacts.sms + '">' + userData.contacts.sms + '</a></span>')
|
|
||||||
)
|
|
||||||
.append(
|
|
||||||
$('<li />')
|
|
||||||
.append('<label class="label-col">Slack</label>')
|
|
||||||
.append('<span class="data-col">' + userData.contacts.slack + '</span>')
|
|
||||||
);
|
|
||||||
if (evt.schedule_id) {
|
|
||||||
$ul.append(
|
|
||||||
$('<li />')
|
|
||||||
.append('<small>This event is auto generated by the scheduler</small>')
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
userPromise.done(function() {
|
||||||
|
$title.text(userData.full_name);
|
||||||
|
$ul
|
||||||
|
.append(
|
||||||
|
$('<li />')
|
||||||
|
.append('<label class="label-col">E-Mail</label>')
|
||||||
|
.append('<span class="data-col"><a href="mailto:' + userData.contacts.email + '" target="_blank">' + userData.contacts.email + '</a></span>')
|
||||||
|
)
|
||||||
|
.append(
|
||||||
|
$('<li />')
|
||||||
|
.append('<label class="label-col">Call</label>')
|
||||||
|
.append('<span class="data-col"><a href="tel:' + userData.contacts.call + '">' + userData.contacts.call + '</a></span>')
|
||||||
|
)
|
||||||
|
.append(
|
||||||
|
$('<li />')
|
||||||
|
.append('<label class="label-col">SMS</label>')
|
||||||
|
.append('<span class="data-col"><a href="tel:' + userData.contacts.sms + '">' + userData.contacts.sms + '</a></span>')
|
||||||
|
)
|
||||||
|
.append(
|
||||||
|
$('<li />')
|
||||||
|
.append('<label class="label-col">Slack</label>')
|
||||||
|
.append('<span class="data-col">' + userData.contacts.slack + '</span>')
|
||||||
|
);
|
||||||
|
if (evt.schedule_id) {
|
||||||
|
$ul.append(
|
||||||
|
$('<li />')
|
||||||
|
.append('<small>This event is auto generated by the scheduler</small>')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
if (evt.team !== self.data.teamName) {
|
||||||
|
$ul.append(
|
||||||
|
$('<li />')
|
||||||
|
.append('<small>This is a subscription event from ' + evt.team + '</small>')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
updatePlanDescription: function() {
|
updatePlanDescription: function() {
|
||||||
var $modal = $(this.data.escalateModal),
|
var $modal = $(this.data.escalateModal),
|
||||||
@@ -1469,6 +1502,138 @@ var oncall = {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
subscriptions: {
|
||||||
|
data: {
|
||||||
|
$page: $('.content-wrapper'),
|
||||||
|
url: '/api/v0/teams/',
|
||||||
|
subscriptionUrl: '/subscriptions',
|
||||||
|
teamName: null,
|
||||||
|
pageSource: $('#team-subscriptions-template').html(),
|
||||||
|
moduleSubscriptionTemplate: $('#module-subscription-template').html(),
|
||||||
|
moduleSubscriptionCreateTemplate: $('#module-subscription-create-template').html(),
|
||||||
|
addSubscriptionItem: '#add-subscription',
|
||||||
|
saveSubscription: '#save-subscription',
|
||||||
|
addSubscriptionContainer: '.add-subscription-container',
|
||||||
|
subscriptionCreateForm: '.module-subscription-create',
|
||||||
|
moduleSubscriptionsWrapper: '.module-subscriptions-wrapper',
|
||||||
|
deleteSubscriptionCard: '.delete-subscription-item',
|
||||||
|
subscriptionItem: '.module-subscription',
|
||||||
|
subscriptionCount: '.subscription-count'
|
||||||
|
},
|
||||||
|
init: function(name){
|
||||||
|
Handlebars.registerPartial('module-subscription', this.data.moduleSubscriptionTemplate);
|
||||||
|
Handlebars.registerPartial('module-subscription-create', this.data.moduleSubscriptionCreateTemplate);
|
||||||
|
this.data.teamName = decodeURIComponent(name);
|
||||||
|
this.getData();
|
||||||
|
},
|
||||||
|
events: function(){
|
||||||
|
router.updatePageLinks();
|
||||||
|
this.data.$page.on('click', this.data.addSubscriptionItem, this.addSubscriptionItem.bind(this));
|
||||||
|
this.data.$page.on('submit', this.data.subscriptionCreateForm, this.saveSubscription.bind(this));
|
||||||
|
this.data.$page.on('click', this.data.deleteSubscriptionCard, this.deleteSubscription.bind(this));
|
||||||
|
},
|
||||||
|
getData: function(){
|
||||||
|
var template = Handlebars.compile(this.data.pageSource),
|
||||||
|
self = this;
|
||||||
|
|
||||||
|
$.when($.getJSON(this.data.url + this.data.teamName + this.data.subscriptionUrl),
|
||||||
|
$.getJSON(this.data.url + this.data.teamName),
|
||||||
|
oncall.data.rolesPromise).done(function(subData, teamData){
|
||||||
|
data = teamData[0];
|
||||||
|
data.subscriptions = subData[0];
|
||||||
|
data.roles = oncall.data.roles;
|
||||||
|
|
||||||
|
self.data.teamData = data;
|
||||||
|
self.data.$page.html(template(data));
|
||||||
|
self.events();
|
||||||
|
self.renderSubscriptionCounts();
|
||||||
|
}).fail(function(error){
|
||||||
|
var data = {
|
||||||
|
error: true,
|
||||||
|
error_code: error.status,
|
||||||
|
error_status: error.statusText,
|
||||||
|
error_text: name + ' team not found'
|
||||||
|
};
|
||||||
|
self.data.$page.html(template(data));
|
||||||
|
});
|
||||||
|
},
|
||||||
|
addSubscriptionItem: function(e){
|
||||||
|
var template = Handlebars.compile(this.data.moduleSubscriptionCreateTemplate),
|
||||||
|
$container = $(e.target).parents().find(this.data.addSubscriptionContainer),
|
||||||
|
teamData = $.extend(true, {}, this.data.teamData);
|
||||||
|
$container.prepend(template(teamData));
|
||||||
|
oncall.typeahead.init();
|
||||||
|
},
|
||||||
|
saveSubscription: function(e){
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var self = this,
|
||||||
|
$form = $(e.target),
|
||||||
|
subscription = {},
|
||||||
|
$cta = $form.find(this.data.saveSubscription),
|
||||||
|
template = Handlebars.compile(this.data.moduleSubscriptionTemplate),
|
||||||
|
url = this.data.url + this.data.teamName + this.data.subscriptionUrl,
|
||||||
|
method = 'POST';
|
||||||
|
|
||||||
|
subscription.subscription = $form.find('input.typeahead.tt-input.subscription-team').val();
|
||||||
|
subscription.role = $form.find('.subscription-role').val();
|
||||||
|
if (subscription.role === undefined || subscription.subscription === '') {
|
||||||
|
oncall.alerts.createAlert('Invalid or missing field.');
|
||||||
|
} else {
|
||||||
|
oncall.alerts.removeAlerts();
|
||||||
|
$.ajax({
|
||||||
|
type: method,
|
||||||
|
url: url,
|
||||||
|
contentType: 'application/json',
|
||||||
|
dataType: 'html',
|
||||||
|
data: JSON.stringify(subscription)
|
||||||
|
}).done(function () {
|
||||||
|
self.data.$page.find(self.data.moduleSubscriptionsWrapper).append(template(subscription));
|
||||||
|
$form.remove();
|
||||||
|
self.renderSubscriptionCounts();
|
||||||
|
}).fail(function (data) {
|
||||||
|
var error = oncall.isJson(data.responseText) ? JSON.parse(data.responseText).description : data.responseText || 'Request failed.';
|
||||||
|
oncall.alerts.createAlert(error, 'danger');
|
||||||
|
}).always(function () {
|
||||||
|
$cta.removeClass('loading disabled').prop('disabled', false);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
deleteSubscriptionItem: function($modal, $caller){
|
||||||
|
var $form = $caller.parents(this.data.subscriptionCreateForm);
|
||||||
|
$form.remove();
|
||||||
|
$modal.modal('hide');
|
||||||
|
},
|
||||||
|
deleteSubscription: function($modal, $caller) {
|
||||||
|
var $card = $caller.parents('.module-card'),
|
||||||
|
$modalBody = $modal.find('.modal-body'),
|
||||||
|
$cta = $modal.find('.modal-cta'),
|
||||||
|
role = $card.attr('data-role'),
|
||||||
|
subscription = $card.attr('data-team'),
|
||||||
|
url = this.data.url + this.data.teamName + '/subscriptions/' + subscription + '/' + role,
|
||||||
|
self = this;
|
||||||
|
|
||||||
|
$cta.addClass('loading disabled').prop('disabled', true);
|
||||||
|
|
||||||
|
$.ajax({
|
||||||
|
type: 'DELETE',
|
||||||
|
url: url,
|
||||||
|
dataType: 'html'
|
||||||
|
}).done(function(){
|
||||||
|
$modal.modal('hide');
|
||||||
|
$card.remove();
|
||||||
|
self.renderSubscriptionCounts();
|
||||||
|
}).fail(function(data){
|
||||||
|
var error = oncall.isJson(data.responseText) ? JSON.parse(data.responseText).description : data.responseText || 'Delete failed.';
|
||||||
|
oncall.alerts.createAlert(error, 'danger', $modalBody);
|
||||||
|
}).always(function(){
|
||||||
|
$cta.removeClass('loading disabled').prop('disabled', false);
|
||||||
|
});
|
||||||
|
},
|
||||||
|
renderSubscriptionCounts: function() {
|
||||||
|
$(this.data.subscriptionCount).text($(this.data.moduleSubscriptionsWrapper).find(this.data.subscriptionItem).length);
|
||||||
|
}
|
||||||
|
},
|
||||||
schedules: {
|
schedules: {
|
||||||
data: {
|
data: {
|
||||||
$page: $('.content-wrapper'),
|
$page: $('.content-wrapper'),
|
||||||
@@ -2671,7 +2836,7 @@ var oncall = {
|
|||||||
type = $this.attr('data-type') || 'users',
|
type = $this.attr('data-type') || 'users',
|
||||||
results;
|
results;
|
||||||
|
|
||||||
if (type === 'services') {
|
if (type === 'services' || type == 'teams') {
|
||||||
results = new Bloodhound({
|
results = new Bloodhound({
|
||||||
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
|
datumTokenizer: Bloodhound.tokenizers.obj.whitespace('value'),
|
||||||
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
queryTokenizer: Bloodhound.tokenizers.whitespace,
|
||||||
|
|||||||
@@ -322,6 +322,7 @@
|
|||||||
<li {{#isEqual page "calendar"}}class="active"{{/isEqual}} href="/team/{{name}}" data-navigo> Calendar </li>
|
<li {{#isEqual page "calendar"}}class="active"{{/isEqual}} href="/team/{{name}}" data-navigo> Calendar </li>
|
||||||
<li {{#isEqual page "info"}}class="active"{{/isEqual}} href="/team/{{name}}/info" data-navigo> Team Info </li>
|
<li {{#isEqual page "info"}}class="active"{{/isEqual}} href="/team/{{name}}/info" data-navigo> Team Info </li>
|
||||||
<li data-admin-action="true" {{#isEqual page "schedules"}}class="active"{{/isEqual}} href="/team/{{name}}/schedules" data-navigo> Schedule Templates </li>
|
<li data-admin-action="true" {{#isEqual page "schedules"}}class="active"{{/isEqual}} href="/team/{{name}}/schedules" data-navigo> Schedule Templates </li>
|
||||||
|
<li data-admin-action="true" {{#isEqual page "subscriptions"}}class="active"{{/isEqual}} href="/team/{{name}}/subscriptions" data-navigo> Subscriptions </li>
|
||||||
{{#isEqual page "calendar"}}
|
{{#isEqual page "calendar"}}
|
||||||
<span class="timezone-display-container pull-right">
|
<span class="timezone-display-container pull-right">
|
||||||
Display Timezone:
|
Display Timezone:
|
||||||
@@ -603,6 +604,89 @@
|
|||||||
</div>
|
</div>
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<!--// **********************
|
||||||
|
Team.subscriptions Page
|
||||||
|
*********************** //-->
|
||||||
|
<script id="team-subscriptions-template" type="text/x-handlebars-template">
|
||||||
|
{{#if error}}
|
||||||
|
{{>error-page this}}
|
||||||
|
{{else}}
|
||||||
|
<div class="subheader-wrapper">
|
||||||
|
{{>team-subheader name=name page="subscriptions"}}
|
||||||
|
</div>
|
||||||
|
<div class="main container-fluid">
|
||||||
|
<h3 class="module-heading"> Subscriptions <button id="add-subscription" class="btn btn-primary pull-right">+ New Subscription</button></h3>
|
||||||
|
<div class="add-subscription-container">
|
||||||
|
<!--// create form renders here -->
|
||||||
|
</div>
|
||||||
|
<div class="module module-subscriptions-container">
|
||||||
|
<h4 class="module-heading border-bottom"> You have <span class="subscription-count"> 0 </span> subscriptions </h4>
|
||||||
|
<div class="module-subscriptions-wrapper clearfix" data-type="subscription">
|
||||||
|
{{#each this.subscriptions}}
|
||||||
|
{{>module-subscription viewType="schedule-item"}}
|
||||||
|
{{/each}}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{{/if}}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<script id="module-subscription-template" type="text/x-handlebars-template">
|
||||||
|
<div class="module module-card module-subscription" data-role="{{role}}" data-team="{{subscription}}">
|
||||||
|
<ul class="subscription-details">
|
||||||
|
<li>
|
||||||
|
<label class="light label-col">Team:</label>
|
||||||
|
<span class="data-col">{{subscription}}</span>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<label class="light label-col">Role:</label>
|
||||||
|
<span class="data-col">{{role}} </span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
<div class="subscription-actions border-top">
|
||||||
|
<span class="delete-{{viewType}}" data-toggle="modal" data-target="#confirm-action-modal" data-modal-action="oncall.team.subscriptions.deleteSubscription" data-modal-title="Delete subscription" data-modal-content="Delete subscription to {{role}} events from {{subscription}}?">
|
||||||
|
<i class="svg-icon svg-icon-trash grey-icon">
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="14px" height="14px" viewBox="0 0 8 8">
|
||||||
|
<path d="M3 0c-.55 0-1 .45-1 1h-1c-.55 0-1 .45-1 1h7c0-.55-.45-1-1-1h-1c0-.55-.45-1-1-1h-1zm-2 3v4.81c0 .11.08.19.19.19h4.63c.11 0 .19-.08.19-.19v-4.81h-1v3.5c0 .28-.22.5-.5.5s-.5-.22-.5-.5v-3.5h-1v3.5c0 .28-.22.5-.5.5s-.5-.22-.5-.5v-3.5h-1z" />
|
||||||
|
</svg>
|
||||||
|
</i>
|
||||||
|
Delete
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<!-- subscription create partial -->
|
||||||
|
<script id="module-subscription-create-template" type="text/x-handlebars-template">
|
||||||
|
<form class="module module-card module-subscription-create" >
|
||||||
|
<div class="module-heading module-card-heading border-bottom">
|
||||||
|
<h4>
|
||||||
|
Create a new subscription
|
||||||
|
</h4>
|
||||||
|
</div>
|
||||||
|
<div class="subscription-create-body">
|
||||||
|
Add
|
||||||
|
<select name="from" class="form-control subscription-role">
|
||||||
|
{{#each roles}}
|
||||||
|
<option value="{{this.name}}" {{isSelected ../selected_subscription.role this.name}}>
|
||||||
|
{{this.name}}
|
||||||
|
</option>
|
||||||
|
{{/each}}
|
||||||
|
</select>
|
||||||
|
events from the
|
||||||
|
<span id="subscription-team-container">
|
||||||
|
<input type="text" class="form-control typeahead subscription-team" placeholder="Team" data-type="teams" />
|
||||||
|
</span>
|
||||||
|
calendar to this team's calendar.
|
||||||
|
<div class="border-top subscription-actions clearfix">
|
||||||
|
<div class="pull-right">
|
||||||
|
<button id="save-subscription" class="btn btn-primary" type="submit"><span class="btn-text">Save</span> <i class="loader loader-small"></i> </button>
|
||||||
|
<button class="btn btn-blue delete-subscription" type="button" data-toggle="modal" data-target="#confirm-action-modal" data-modal-action="oncall.team.subscriptions.deleteSubscriptionItem" data-modal-title="Your changes are unsaved" data-modal-content="Aborting now will lose unsaved changes."> Cancel </button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</script>
|
||||||
|
|
||||||
<!--// **********************
|
<!--// **********************
|
||||||
Team.schedules Page
|
Team.schedules Page
|
||||||
*********************** //-->
|
*********************** //-->
|
||||||
|
|||||||
Reference in New Issue
Block a user