Commit f88b266c by Miles Steele

add member list widget, add segment.io call for section view

parent 588a307d
......@@ -64,7 +64,7 @@ setup_instructor_dashboard = (idash_content) =>
section.addClass CSS_ACTIVE_SECTION
# tracking
# analytics.pageview "instructor_#{section_name}"
analytics.pageview "instructor_section:#{section_name}"
# deep linking
# write to url
......
......@@ -7,6 +7,175 @@ plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, argum
std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments
class MemberListWidget
# create a MemberListWidget `$container` is a jquery object to embody.
# `params` holds template parameters. `params` should look like the defaults below.
constructor: (@$container, params={}) ->
params = _.defaults params,
title: "Member List"
info: """
Use this list to manage members.
"""
labels: ["field1", "field2", "field3"]
add_placeholder: "Enter name"
add_btn_label: "Add Member"
add_handler: (input) ->
template_html = $("#member-list-widget-template").html()
@$container.html Mustache.render template_html, params
# bind info toggle
@$('.info-badge').click => @toggle_info()
# bind add button
@$('input[type="button"].add').click =>
params.add_handler? @$('.add-field').val()
show_info: ->
@$('.info').show()
@$('.member-list').hide()
show_list: ->
@$('.info').hide()
@$('.member-list').show()
toggle_info: ->
@$('.info').toggle()
@$('.member-list').toggle()
# clear the input text field
clear_input: -> @$('.add-field').val ''
# clear all table rows
clear_rows: -> @$('table tbody').empty()
# takes a table row as an array items are inserted as text, unless detected
# as a jquery objects in which case they are inserted directly. if an
# element is a jquery object
add_row: (row_array) ->
$tbody = @$('table tbody')
$tr = $ '<tr>'
for item in row_array
$td = $ '<td>'
if item instanceof jQuery
$td.append item
else
$td.text item
$tr.append $td
$tbody.append $tr
# local selector
$: (selector) ->
if @debug?
s = @$container.find selector
if s?.length != 1
console.warn "local selector '#{selector}' found (#{s.length}) results"
s
else
@$container.find selector
class AuthListWidget extends MemberListWidget
constructor: ($container, @rolename, @$error_section) ->
super $container,
title: $container.data 'display-name'
info: $container.data 'info-text'
labels: ["username", "email", "revoke access"]
add_placeholder: "Enter email"
add_btn_label: $container.data 'add-button-label'
add_handler: (input) => @add_handler input
@debug = true
@list_endpoint = $container.data 'list-endpoint'
@modify_endpoint = $container.data 'modify-endpoint'
unless @rolename?
throw "AuthListWidget missing @rolename"
@reload_list()
# action to do when is reintroduced into user's view
re_view: ->
@clear_errors()
@clear_input()
@reload_list()
@$('.info').hide()
@$('.member-list').show()
# handle clicks on the add button
add_handler: (input) ->
if input? and input isnt ''
@modify_member_access input, 'allow', (error) =>
# abort on error
return @show_errors error unless error is null
@clear_errors()
@clear_input()
@reload_list()
else
@show_errors "Enter an email."
# reload the list of members
reload_list: ->
# @clear_rows()
# @show_info()
@get_member_list (error, member_list) =>
# abort on error
return @show_errors error unless error is null
# only show the list of there are members
@clear_rows()
@show_info()
# @show_info()
# use _.each instead of 'for' so that member
# is bound in the button callback.
_.each member_list, (member) =>
# if there are members, show the list
# create revoke button and insert it into the row
$revoke_btn = $ '<div/>',
class: 'revoke'
click: =>
@modify_member_access member.email, 'revoke', (error) =>
# abort on error
return @show_errors error unless error is null
@clear_errors()
@reload_list()
@add_row [member.username, member.email, $revoke_btn]
# make sure the list is shown because there are members.
@show_list()
# clear error display
clear_errors: -> @$error_section?.text ''
# set error display
show_errors: (msg) -> @$error_section?.text msg
# send ajax request to list members
# `cb` is called with cb(error, member_list)
get_member_list: (cb) ->
$.ajax
dataType: 'json'
url: @list_endpoint
data: rolename: @rolename
success: (data) => cb? null, data[@rolename]
error: std_ajax_err => cb? "Error fetching list for role '#{@rolename}'"
# send ajax request to modify access
# (add or remove them from the list)
# `action` can be 'allow' or 'revoke'
# `cb` is called with cb(error, data)
modify_member_access: (email, action, cb) ->
$.ajax
dataType: 'json'
url: @modify_endpoint
data:
email: email
rolename: @rolename
action: action
success: (data) => cb? null, data
error: std_ajax_err => cb? "Error changing user's permissions."
# Wrapper for the batch enrollment subsection.
# This object handles buttons, success and failure reporting,
# and server communication.
......@@ -277,13 +446,15 @@ class Membership
plantTimeout 0, => new BatchEnrollment @$section.find '.batch-enrollment'
# gather elements
@$list_selector = @$section.find('select#member-lists-selector')
@$list_selector = @$section.find 'select#member-lists-selector'
@$auth_list_containers = @$section.find '.auth-list-container'
@$auth_list_errors = @$section.find '.member-lists-management .request-response-error'
# initialize & store AuthList subsections
# one for each .auth-list-container in the section.
@auth_lists = _.map (@$section.find '.auth-list-container'), (auth_list_container) ->
@auth_lists = _.map (@$auth_list_containers), (auth_list_container) =>
rolename = $(auth_list_container).data 'rolename'
new AuthList $(auth_list_container), rolename
new AuthListWidget $(auth_list_container), rolename, @$auth_list_errors
# populate selector
@$list_selector.empty()
......@@ -299,17 +470,14 @@ class Membership
for auth_list in @auth_lists
auth_list.$container.removeClass 'active'
auth_list = $opt.data('auth_list')
auth_list.refresh()
auth_list.$container.addClass 'active'
auth_list.re_view()
# one-time first selection of top list.
@$list_selector.change()
# handler for when the section title is clicked.
onClickTitle: ->
for auth_list in @auth_lists
auth_list.refresh()
# export for use
......
......@@ -18,92 +18,91 @@
right: 15px;
font-size: 11pt;
}
}
section.instructor-dashboard-content-2 {
@extend .content;
// position: relative;
padding: 40px;
width: 100%;
section.instructor-dashboard-content-2 {
@extend .content;
// position: relative;
padding: 40px;
width: 100%;
// .has-event-handler-for-click {
// border: 1px solid blue;
// }
// .has-event-handler-for-click {
// border: 1px solid blue;
// }
.request-response-error {
margin-top: 1em;
margin-bottom: 1em;
color: $error-red;
}
.slickgrid {
margin-left: 1px;
color:#333333;
font-size:11px;
font-family: verdana,arial,sans-serif;
.request-response-error {
margin-top: 1em;
margin-bottom: 1em;
color: $error-red;
}
.slick-header-column {
// height: 100%
}
.slickgrid {
margin-left: 1px;
color:#333333;
font-size:11px;
font-family: verdana,arial,sans-serif;
.slick-cell {
border: 1px dotted silver;
border-collapse: collapse;
white-space: normal;
word-wrap: break-word;
}
.slick-header-column {
// height: 100%
}
h1 {
@extend .top-header;
padding-bottom: 0;
border-bottom: 0;
.slick-cell {
border: 1px dotted silver;
border-collapse: collapse;
white-space: normal;
word-wrap: break-word;
}
}
input[type="button"] {
@include idashbutton(#eee);
h1 {
@extend .top-header;
padding-bottom: 0;
border-bottom: 0;
}
&.molly-guard {
// @include idashbutton($danger-red);
// @include idashbutton($black);
// border: 2px solid $danger-red;
}
}
input[type="button"] {
@include idashbutton(#eee);
.instructor_dash_glob_info {
position: absolute;
top: 46px;
right: 50px;
text-align: right;
&.molly-guard {
// @include idashbutton($danger-red);
// @include idashbutton($black);
// border: 2px solid $danger-red;
}
}
.instructor-nav {
padding-bottom: 1em;
.instructor_dash_glob_info {
position: absolute;
top: 46px;
right: 50px;
text-align: right;
}
border-bottom: 1px solid #C8C8C8;
a {
margin-right: 1.2em;
}
.instructor-nav {
padding-bottom: 1em;
.active-section {
color: #551A8B;
}
border-bottom: 1px solid #C8C8C8;
a {
margin-right: 1.2em;
}
section.idash-section {
display: none;
// background-color: #0f0;
.active-section {
color: #551A8B;
}
}
&.active-section {
display: block;
// background-color: #ff0;
}
section.idash-section {
display: none;
// background-color: #0f0;
.basic-data {
padding: 6px;
}
&.active-section {
display: block;
// background-color: #ff0;
}
.basic-data {
padding: 6px;
}
}
// @extend .table-wrapper;
}
......@@ -250,14 +249,20 @@
display: block;
}
.auth-list-table {
.slickgrid {
height: 250px;
}
}
.auth-list-add {
margin-top: 0.5em;
.revoke {
width: 10px;
height: 10px;
background: url('../images/moderator-delete-icon.png') left center no-repeat;
opacity: 0.7;
&:hover { opacity: 0.8; }
&:active { opacity: 0.9; }
// @include idashbutton($danger-red);
// line-height: 0.6em;
// margin-top: 5px;
// padding: 6px 9px;
// font-size: 9pt;
// border-radius: 10px;
}
}
}
......@@ -329,3 +334,115 @@
}
}
}
.member-list-widget {
$width: 20 * $baseline;
$height: 25 * $baseline;
$header-height: 3 * $baseline;
$bottom-bar-height: 3 * $baseline;
$content-height: $height - $header-height - $bottom-bar-height;
$border-radius: 3px;
width: $width;
height: $height;
.header {
@include box-sizing(border-box);
@include border-top-radius($border-radius);
position: relative;
padding: $baseline;
width: $width;
height: $header-height;
background-color: #efefef;
border: 1px solid $light-gray;
}
.title {
font-size: 16pt;
}
.label {
color: $lighter-base-font-color;
font-size: $body-font-size * 4/5;
}
.info-badge {
// float: right;
position: absolute;
top: $baseline / 2;
right: $baseline / 2;
width: 17px;
height: 17px;
background: url('../images/info-icon-dark.png') left center no-repeat;
opacity: 0.35;
&:hover { opacity: 0.45; }
&:active { opacity: 0.5; }
}
.info {
display: none;
@include box-sizing(border-box);
max-height: $content-height;
padding: $baseline;
border: 1px solid $light-gray;
border-top: none;
color: $lighter-base-font-color;
line-height: 1.3em;
}
.member-list {
@include box-sizing(border-box);
overflow: auto;
padding-top: 0;
width: $width;
max-height: $content-height;
table {
width: 100%;
}
tr {
border-bottom: 1px solid $light-gray;
}
td {
table-layout: fixed;
vertical-align: middle;
word-wrap: break-word;
padding-left: 15px;
border-left: 1px solid $light-gray;
border-right: 1px solid $light-gray;
font-size: 3/4 *$body-font-size;
}
}
.bottom-bar {
@include box-sizing(border-box);
@include border-bottom-radius($border-radius);
position: relative;
width: $width;
height: $bottom-bar-height;
padding: $baseline / 2;
// border-top: none;
margin-top: -1px;
border: 1px solid $light-gray;
background-color: #efefef;
box-shadow: inset #bbb 0px 1px 1px 0px;
}
// .add-field
input[type="button"].add {
@include idashbutton($blue);
position: absolute;
right: $baseline;
}
}
......@@ -5,6 +5,7 @@
<%block name="headextra">
<%static:css group='course'/>
<script type="text/javascript" src="${static.url('js/vendor/underscore-min.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/mustache.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/flot/jquery.flot.axislabels.js')}"></script>
<script type="text/javascript" src="${static.url('js/vendor/jquery-jvectormap-1.1.1/jquery-jvectormap-1.1.1.min.js')}"></script>
......
<%page args="section_data"/>
<script type="text/template" id="member-list-widget-template">
<div class="member-list-widget">
<div class="header">
<div class="title"> {{title}} </div>
<div class="info-badge"></div>
</div>
<div class="info"> {{info}} </div>
<div class="member-list">
<table>
<thead>
<tr>
{{#labels}}
<td class="label">{{.}}</td>
{{/labels}}
</tr>
</thead>
<tbody></tbody>
</table>
</div>
<div class="bottom-bar">
<input type="text" name="add-field" class="add-field" placeholder="{{add_placeholder}}">
<input type="button" name="add" class="add" value="{{add_btn_label}}">
</div>
</div>
</script>
<div class="vert-left batch-enrollment">
<h2>Batch Enrollment</h2>
<p>Enter student emails separated by new lines or commas.</p>
......@@ -26,66 +52,75 @@
<option> Getting available lists... </option>
</select>
<div class="request-response-error"></div>
%if section_data['access']['instructor']:
<div class="auth-list-container" data-rolename="staff" data-display-name="Staff">
<div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div>
<div class="auth-list-add" data-endpoint="${ section_data['modify_access_url'] }">
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Staff Access">
</div>
<div class="request-response-error"></div>
</div>
<div class="auth-list-container"
data-rolename="staff"
data-display-name="Course Staff"
data-info-text="
Course staff can help you manage limited aspects of your course. Staff can
enroll and unenroll students, as well as modify their grades and see all
course data. Course staff are not given access to Studio will not be able to
edit your course."
data-list-endpoint="${ section_data['list_course_role_members_url'] }"
data-modify-endpoint="${ section_data['modify_access_url'] }"
data-add-button-label="Add Staff"
></div>
%if section_data['access']['instructor']:
<div class="auth-list-container" data-rolename="instructor" data-display-name="Instructors">
<div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div>
<div class="auth-list-add" data-endpoint="${ section_data['modify_access_url'] }">
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Instructor Access">
</div>
<div class="request-response-error"></div>
</div>
<div class="auth-list-container"
data-rolename="instructor"
data-display-name="Instructors"
data-info-text="
Instructors are the core administration of your course. Instructors can
add and remove course staff, as well as administer forum access.
"
data-list-endpoint="${ section_data['list_course_role_members_url'] }"
data-modify-endpoint="${ section_data['modify_access_url'] }"
data-add-button-label="Add Instructor"
></div>
%endif
<div class="auth-list-container" data-rolename="beta" data-display-name="Beta Testers">
<div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div>
<div class="auth-list-add" data-endpoint="${ section_data['modify_access_url'] }">
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Beta Tester Access">
</div>
<div class="request-response-error"></div>
</div>
<div class="auth-list-container"
data-rolename="beta"
data-display-name="Beta Testers"
data-info-text="
Beta testers can see course content before the rest of the students.
They can make sure that the content works, but have no additional
privelages."
data-list-endpoint="${ section_data['list_course_role_members_url'] }"
data-modify-endpoint="${ section_data['modify_access_url'] }"
data-add-button-label="Add Beta Tester"
></div>
%endif
%if section_data['access']['instructor']:
<div class="auth-list-container" data-rolename="Administrator" data-display-name="Forum Admins">
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div>
<div class="auth-list-add" data-endpoint="${ section_data['update_forum_role_membership_url'] }">
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Forum Admin">
</div>
<div class="request-response-error"></div>
</div>
<div class="auth-list-container"
data-rolename="Administrator"
data-display-name="Forum Admins"
data-list-endpoint="${ section_data['list_forum_members_url'] }"
data-modify-endpoint="${ section_data['update_forum_role_membership_url'] }"
data-add-button-label="Add Forum Admin"
></div>
%endif
%if section_data['access']['instructor'] or section_data['access']['forum_admin']:
<div class="auth-list-container" data-rolename="Moderator" data-display-name="Forum Moderators">
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div>
<div class="auth-list-add" data-endpoint="${ section_data['update_forum_role_membership_url'] }">
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Forum Moderator">
</div>
<div class="request-response-error"></div>
</div>
<div class="auth-list-container"
data-rolename="Moderator"
data-display-name="Forum Moderators"
data-list-endpoint="${ section_data['list_forum_members_url'] }"
data-modify-endpoint="${ section_data['update_forum_role_membership_url'] }"
data-add-button-label="Add Moderator"
></div>
<div class="auth-list-container" data-rolename="Community TA" data-display-name="Forum Community TAs">
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div>
<div class="auth-list-add" data-endpoint="${ section_data['update_forum_role_membership_url'] }">
<input type="text" name="email" placeholder="Enter Email" spellcheck="false">
<input type="button" name="allow" value="Grant Community TA">
</div>
<div class="request-response-error"></div>
</div>
<div class="auth-list-container"
data-rolename="Community TA"
data-display-name="Forum Community TAs"
data-list-endpoint="${ section_data['list_forum_members_url'] }"
data-modify-endpoint="${ section_data['update_forum_role_membership_url'] }"
data-add-button-label="Add Community TA"
></div>
%endif
</div>
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