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) => ...@@ -64,7 +64,7 @@ setup_instructor_dashboard = (idash_content) =>
section.addClass CSS_ACTIVE_SECTION section.addClass CSS_ACTIVE_SECTION
# tracking # tracking
# analytics.pageview "instructor_#{section_name}" analytics.pageview "instructor_section:#{section_name}"
# deep linking # deep linking
# write to url # write to url
......
...@@ -7,6 +7,175 @@ plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, argum ...@@ -7,6 +7,175 @@ plantTimeout = -> window.InstructorDashboard.util.plantTimeout.apply this, argum
std_ajax_err = -> window.InstructorDashboard.util.std_ajax_err.apply this, arguments 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. # Wrapper for the batch enrollment subsection.
# This object handles buttons, success and failure reporting, # This object handles buttons, success and failure reporting,
# and server communication. # and server communication.
...@@ -277,13 +446,15 @@ class Membership ...@@ -277,13 +446,15 @@ class Membership
plantTimeout 0, => new BatchEnrollment @$section.find '.batch-enrollment' plantTimeout 0, => new BatchEnrollment @$section.find '.batch-enrollment'
# gather elements # 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 # initialize & store AuthList subsections
# one for each .auth-list-container in the section. # 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' rolename = $(auth_list_container).data 'rolename'
new AuthList $(auth_list_container), rolename new AuthListWidget $(auth_list_container), rolename, @$auth_list_errors
# populate selector # populate selector
@$list_selector.empty() @$list_selector.empty()
...@@ -299,17 +470,14 @@ class Membership ...@@ -299,17 +470,14 @@ class Membership
for auth_list in @auth_lists for auth_list in @auth_lists
auth_list.$container.removeClass 'active' auth_list.$container.removeClass 'active'
auth_list = $opt.data('auth_list') auth_list = $opt.data('auth_list')
auth_list.refresh()
auth_list.$container.addClass 'active' auth_list.$container.addClass 'active'
auth_list.re_view()
# one-time first selection of top list. # one-time first selection of top list.
@$list_selector.change() @$list_selector.change()
# handler for when the section title is clicked. # handler for when the section title is clicked.
onClickTitle: -> onClickTitle: ->
for auth_list in @auth_lists
auth_list.refresh()
# export for use # export for use
......
...@@ -18,92 +18,91 @@ ...@@ -18,92 +18,91 @@
right: 15px; right: 15px;
font-size: 11pt; font-size: 11pt;
} }
}
section.instructor-dashboard-content-2 { section.instructor-dashboard-content-2 {
@extend .content; @extend .content;
// position: relative; // position: relative;
padding: 40px; padding: 40px;
width: 100%; width: 100%;
// .has-event-handler-for-click { // .has-event-handler-for-click {
// border: 1px solid blue; // border: 1px solid blue;
// } // }
.request-response-error { .request-response-error {
margin-top: 1em; margin-top: 1em;
margin-bottom: 1em; margin-bottom: 1em;
color: $error-red; color: $error-red;
} }
.slickgrid {
margin-left: 1px;
color:#333333;
font-size:11px;
font-family: verdana,arial,sans-serif;
.slick-header-column { .slickgrid {
// height: 100% margin-left: 1px;
} color:#333333;
font-size:11px;
font-family: verdana,arial,sans-serif;
.slick-cell { .slick-header-column {
border: 1px dotted silver; // height: 100%
border-collapse: collapse;
white-space: normal;
word-wrap: break-word;
}
} }
h1 { .slick-cell {
@extend .top-header; border: 1px dotted silver;
padding-bottom: 0; border-collapse: collapse;
border-bottom: 0; white-space: normal;
word-wrap: break-word;
} }
}
input[type="button"] { h1 {
@include idashbutton(#eee); @extend .top-header;
padding-bottom: 0;
border-bottom: 0;
}
&.molly-guard { input[type="button"] {
// @include idashbutton($danger-red); @include idashbutton(#eee);
// @include idashbutton($black);
// border: 2px solid $danger-red;
}
}
.instructor_dash_glob_info { &.molly-guard {
position: absolute; // @include idashbutton($danger-red);
top: 46px; // @include idashbutton($black);
right: 50px; // border: 2px solid $danger-red;
text-align: right;
} }
}
.instructor-nav { .instructor_dash_glob_info {
padding-bottom: 1em; position: absolute;
top: 46px;
right: 50px;
text-align: right;
}
border-bottom: 1px solid #C8C8C8; .instructor-nav {
a { padding-bottom: 1em;
margin-right: 1.2em;
}
.active-section { border-bottom: 1px solid #C8C8C8;
color: #551A8B; a {
} margin-right: 1.2em;
} }
section.idash-section { .active-section {
display: none; color: #551A8B;
// background-color: #0f0; }
}
&.active-section { section.idash-section {
display: block; display: none;
// background-color: #ff0; // background-color: #0f0;
}
.basic-data { &.active-section {
padding: 6px; display: block;
} // background-color: #ff0;
}
.basic-data {
padding: 6px;
} }
} }
// @extend .table-wrapper;
} }
...@@ -250,14 +249,20 @@ ...@@ -250,14 +249,20 @@
display: block; display: block;
} }
.auth-list-table { .revoke {
.slickgrid { width: 10px;
height: 250px; height: 10px;
} background: url('../images/moderator-delete-icon.png') left center no-repeat;
} opacity: 0.7;
&:hover { opacity: 0.8; }
.auth-list-add { &:active { opacity: 0.9; }
margin-top: 0.5em;
// @include idashbutton($danger-red);
// line-height: 0.6em;
// margin-top: 5px;
// padding: 6px 9px;
// font-size: 9pt;
// border-radius: 10px;
} }
} }
} }
...@@ -329,3 +334,115 @@ ...@@ -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 @@ ...@@ -5,6 +5,7 @@
<%block name="headextra"> <%block name="headextra">
<%static:css group='course'/> <%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/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.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/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> <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"/> <%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"> <div class="vert-left batch-enrollment">
<h2>Batch Enrollment</h2> <h2>Batch Enrollment</h2>
<p>Enter student emails separated by new lines or commas.</p> <p>Enter student emails separated by new lines or commas.</p>
...@@ -26,66 +52,75 @@ ...@@ -26,66 +52,75 @@
<option> Getting available lists... </option> <option> Getting available lists... </option>
</select> </select>
<div class="request-response-error"></div>
%if section_data['access']['instructor']: %if section_data['access']['instructor']:
<div class="auth-list-container" data-rolename="staff" data-display-name="Staff"> <div class="auth-list-container"
<div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div> data-rolename="staff"
<div class="auth-list-add" data-endpoint="${ section_data['modify_access_url'] }"> data-display-name="Course Staff"
<input type="text" name="email" placeholder="Enter Email" spellcheck="false"> data-info-text="
<input type="button" name="allow" value="Grant Staff Access"> Course staff can help you manage limited aspects of your course. Staff can
</div> enroll and unenroll students, as well as modify their grades and see all
<div class="request-response-error"></div> course data. Course staff are not given access to Studio will not be able to
</div> 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']: %if section_data['access']['instructor']:
<div class="auth-list-container" data-rolename="instructor" data-display-name="Instructors"> <div class="auth-list-container"
<div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div> data-rolename="instructor"
<div class="auth-list-add" data-endpoint="${ section_data['modify_access_url'] }"> data-display-name="Instructors"
<input type="text" name="email" placeholder="Enter Email" spellcheck="false"> data-info-text="
<input type="button" name="allow" value="Grant Instructor Access"> Instructors are the core administration of your course. Instructors can
</div> add and remove course staff, as well as administer forum access.
<div class="request-response-error"></div> "
</div> 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 %endif
<div class="auth-list-container" data-rolename="beta" data-display-name="Beta Testers"> <div class="auth-list-container"
<div class="auth-list-table" data-endpoint="${ section_data['list_course_role_members_url'] }"></div> data-rolename="beta"
<div class="auth-list-add" data-endpoint="${ section_data['modify_access_url'] }"> data-display-name="Beta Testers"
<input type="text" name="email" placeholder="Enter Email" spellcheck="false"> data-info-text="
<input type="button" name="allow" value="Grant Beta Tester Access"> Beta testers can see course content before the rest of the students.
</div> They can make sure that the content works, but have no additional
<div class="request-response-error"></div> privelages."
</div> 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 %endif
%if section_data['access']['instructor']: %if section_data['access']['instructor']:
<div class="auth-list-container" data-rolename="Administrator" data-display-name="Forum Admins"> <div class="auth-list-container"
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div> data-rolename="Administrator"
<div class="auth-list-add" data-endpoint="${ section_data['update_forum_role_membership_url'] }"> data-display-name="Forum Admins"
<input type="text" name="email" placeholder="Enter Email" spellcheck="false"> data-list-endpoint="${ section_data['list_forum_members_url'] }"
<input type="button" name="allow" value="Grant Forum Admin"> data-modify-endpoint="${ section_data['update_forum_role_membership_url'] }"
</div> data-add-button-label="Add Forum Admin"
<div class="request-response-error"></div> ></div>
</div>
%endif %endif
%if section_data['access']['instructor'] or section_data['access']['forum_admin']: %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-container"
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div> data-rolename="Moderator"
<div class="auth-list-add" data-endpoint="${ section_data['update_forum_role_membership_url'] }"> data-display-name="Forum Moderators"
<input type="text" name="email" placeholder="Enter Email" spellcheck="false"> data-list-endpoint="${ section_data['list_forum_members_url'] }"
<input type="button" name="allow" value="Grant Forum Moderator"> data-modify-endpoint="${ section_data['update_forum_role_membership_url'] }"
</div> data-add-button-label="Add Moderator"
<div class="request-response-error"></div> ></div>
</div>
<div class="auth-list-container" data-rolename="Community TA" data-display-name="Forum Community TAs"> <div class="auth-list-container"
<div class="auth-list-table" data-endpoint="${ section_data['list_forum_members_url'] }"></div> data-rolename="Community TA"
<div class="auth-list-add" data-endpoint="${ section_data['update_forum_role_membership_url'] }"> data-display-name="Forum Community TAs"
<input type="text" name="email" placeholder="Enter Email" spellcheck="false"> data-list-endpoint="${ section_data['list_forum_members_url'] }"
<input type="button" name="allow" value="Grant Community TA"> data-modify-endpoint="${ section_data['update_forum_role_membership_url'] }"
</div> data-add-button-label="Add Community TA"
<div class="request-response-error"></div> ></div>
</div>
%endif %endif
</div> </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