scheduler: allow teachers to cancel student appointments

closes 

Test plan:
  - reserve a time slot with one or more students
  - as a teacher, click on the reserved time slot (in either scheduler
    or month view)
  - you should be able to remove the student from the appointment

Change-Id: I2b5faf813ebafe50fbf185ee8f61764667afa941
Reviewed-on: https://gerrit.instructure.com/9594
Tested-by: Hudson <hudson@instructure.com>
Reviewed-by: Cameron Matheson <cameron@instructure.com>
This commit is contained in:
Cameron Matheson 2012-03-22 14:51:48 -06:00
parent 224774a345
commit b0d798b76c
10 changed files with 173 additions and 67 deletions

View File

@ -7,11 +7,12 @@ define [
'jst/calendar/eventDetails'
'jst/calendar/deleteItem'
'jst/calendar/reservationOverLimitDialog'
'use!underscore'
'jquery.ajaxJSON'
'jquery.instructure_misc_helpers'
'jquery.instructure_misc_plugins'
'vendor/jquery.ba-tinypubsub'
], ($, I18n, Popover, CommonEvent, EditEventDetailsDialog, eventDetailsTemplate, deleteItemTemplate, reservationOverLimitDialog) ->
], ($, I18n, Popover, CommonEvent, EditEventDetailsDialog, eventDetailsTemplate, deleteItemTemplate, reservationOverLimitDialog, _) ->
class ShowEventDetailsDialog
constructor: (event) ->
@ -86,6 +87,35 @@ define [
@deleteEvent(e)
return
cancelAppointment: ($appt) =>
url = $appt.data('url')
event = _.detect @event.calendarEvent.child_events, (e) -> e.url == url
$("<div/>").confirmDelete
url: url
message: $ deleteItemTemplate(message: I18n.t(
'cancel_appointment'
'Are you sure you want to cancel your appointment with %{name}?'
name: event.user?.short_name or event.group.name)
)
dialog:
title: I18n.t('confirm_removal', "Confirm Removal")
width: '400px'
resizable: false
prepareData: ($dialog) => {cancel_reason: $dialog.find('#cancel_reason').val() }
success: =>
@event.object.child_events = _(@event.object.child_events).reject (e) ->
e.url == $appt.data('url')
$appt.remove()
# this is a little funky, but we want to remove the parent (time
# slot) event from the calendar when there are no attendees, *unless*
# we are in scheduler view
in_scheduler = $('#scheduler').prop('checked')
appointments = @event.calendarEvent.child_events
if not in_scheduler and appointments.length == 0
$.publish "CommonEvent/eventDeleted", @event
@popover.hide()
show: (jsEvent) =>
params = $.extend true, {}, @event,
can_reserve: @event.object?.reserve_url
@ -100,14 +130,15 @@ define [
params.can_reserve = false
for e in @event.object.child_events
reservation =
id: e.user?.id or e.group.id
name: e.user?.short_name or e.group.name
event_url: e.url
(params.reservations ?= []).push reservation
if e.user
(params.reserved_users ?= []).push
id: e.user.id
name: e.user.short_name
(params.reserved_users ?= []).push reservation
if e.group
(params.reserved_groups ?= []).push
id: e.group.id
name: e.group.name
(params.reserved_groups ?= []).push reservation
if @event.object?.available_slots == 0
params.can_reserve = false
@ -133,5 +164,10 @@ define [
e.preventDefault()
@unreserveEvent()
@popover.el.find(".cancel_appointment_link").click (e) =>
e.preventDefault()
$appt = $(e.target).closest('li')
@cancelAppointment($appt)
# @popover.dialog 'option',
# width: if @event.description?.length > 2000 then Math.max($(window).width() - 300, 450) else 450

View File

@ -62,9 +62,11 @@ define [
clearInterval @positionInterval
$(window).unbind 'click', @outsideClickHandler
ignoreOutsideClickSelector: '.ui-dialog'
# uses a fat arrow so that it has a unique guid per-instance for jquery event unbinding
outsideClickHandler: (event) =>
unless $(event.target).closest(@el.add(@trigger)).length
unless $(event.target).closest(@el.add(@trigger).add(@ignoreOutsideClickSelector)).length
@hide()
position: =>

View File

@ -154,6 +154,9 @@
&:last-child
border-bottom: none
#attendees li
+name_bubbles
/*replicate button styles to work for fullcalendar */
.calendar .fc-button
+unselectable

View File

@ -1,3 +1,4 @@
@import compass
@import variables.sass
@import mixins/misc.sass
@import mixins/misc.sass
@import mixins/name_bubbles.sass

View File

@ -610,42 +610,7 @@ ul.messages, ul.messages.private, ul.messages.private li:hover
margin: 0
padding: 0
li
div
background-color: #dee7fa
+border-radius(10px)
padding: 0 14px 0 11px
display: inline-block
overflow: hidden
span
color: #fff
font-size: 0.8em
vertical-align: top
display: inline-block
a
position: absolute
right: 1px
top: 1px
width: 13px
height: 100%
color: #c4cbcf
vertical-align: top
cursor: pointer
background: transparent url(/images/messages/token-delete.png) 12px center no-repeat
white-space: nowrap
float: left
margin: 1px 2px 1px 4px
color: #000
background-color: #85ace0
border: 1px solid #a5bcf0
+border-radius(10px)
cursor: default
position: relative
li:hover
div
background-color: #bccef4
border-color: #6f94e6
a
background-position: 1px center
+name_bubbles
li.selected
background-color: #5b89f3
border-color: #5b89f3
@ -948,4 +913,4 @@ form
position: absolute
left: 50%
top: 50%
z-index: 3
z-index: 3

View File

@ -55,4 +55,3 @@
-webkit-user-select: none
-moz-user-select: none
user-select: none

View File

@ -0,0 +1,38 @@
=name_bubbles
div
background-color: #dee7fa
+border-radius(10px)
padding: 0 14px 0 11px
display: inline-block
overflow: hidden
span
color: #fff
font-size: 0.8em
vertical-align: top
display: inline-block
a
position: absolute
right: 1px
top: 1px
width: 13px
height: 100%
color: #c4cbcf
vertical-align: top
cursor: pointer
background: transparent url(/images/messages/token-delete.png) 12px center no-repeat
white-space: nowrap
float: left
margin: 1px 2px 1px 4px
color: #000
background-color: #85ace0
border: 1px solid #a5bcf0
+border-radius(10px)
cursor: default
position: relative
&:hover
div
background-color: #bccef4
border-color: #6f94e6
a
background-position: 1px center

View File

@ -30,28 +30,26 @@
<td>{{{description}}}</td>
</tr>
{{/if}}
{{#if reserved_users}}
{{#if reservations}}
<tr>
<th scope="row">{{#t "attendees"}}Attendees{{/t}}</th>
<td>
<ul>
{{#each reserved_users}}
<!-- TODO: put avatar images here if enabled -->
<li>{{name}}</li>
{{/each}}
</ul>
</td>
</tr>
{{/if}}
{{#if reserved_groups}}
<tr>
<th scope="row">{{#t "attendees"}}Attendees{{/t}}</th>
<td>
<ul>
{{#each reserved_groups}}
<li>{{name}}</li>
{{/each}}
</ul>
{{#if can_edit}}
<ul id="attendees">
{{#each reservations}}
<li data-url="{{event_url}}">
<div class="ellipsis">{{name}}</div>
<a href="javascript:void(0)" class="cancel_appointment_link"></a>
</li>
{{/each}}
</ul>
{{else}}
<ul>
{{#each reservations}}
<li>{{name}}</li>
{{/each}}
</ul>
{{/if}}
</td>
</tr>
{{/if}}
@ -85,4 +83,4 @@
{{/if}}
</div>
</div>
</div>
</div>

View File

@ -136,6 +136,7 @@ define([
buttons: [
{
text: I18n.t('#buttons.delete', 'Delete'),
'class': 'ui-button-primary',
click: function() { result = true; $(this).dialog('close'); }
}, {
text: I18n.t('#buttons.cancel', 'Cancel'),

View File

@ -261,6 +261,55 @@ describe "scheduler" do
AppointmentGroup.find(ag_id).max_appointments_per_participant.should == 3
end
it "should allow removing individual appointments" do
# user appointment group
create_appointment_group
ag = AppointmentGroup.first
2.times do
student_in_course(:course => @course, :active_all => true)
ag.appointments.first.reserve_for(@user, @user)
end
# group appointment group
gc = @course.group_categories.create!(:name => "Blah Groups")
title = create_appointment_group :sub_context_code => gc.asset_string,
:title => "group ag"
ag = AppointmentGroup.find_by_title(title)
2.times do |i|
student_in_course(:course => @course, :active_all => true)
group = Group.create! :group_category => gc,
:context => @course,
:name => "Group ##{i+1}"
group.users << @user
group.save!
ag.appointments.first.reserve_for(group, @user)
end
get "/calendar2"
click_scheduler_link
2.times do |i|
f(".appointment-group-item:nth-child(#{i+1}) .view_calendar_link").click
f('.fc-event').click
ff('#attendees li').size.should eql 2
# delete the first appointment
f('.cancel_appointment_link').click
fj('button:visible:contains(Delete)').click
wait_for_ajax_requests
ff('#attendees li').size.should eql 1
# make sure the appointment was really deleted
f('#refresh_calendar_link').click
wait_for_ajax_requests
f('.fc-event-time').click
ff('#attendees li').size.should eql 1
f('.single_item_done_button').click
end
end
end
context "as a student" do
@ -324,6 +373,20 @@ describe "scheduler" do
f('.fc-event:nth-child(3)').should include_text "Available"
end
it "should not allow me to cancel reservations from the attendees list" do
create_appointment_group
ag = AppointmentGroup.first
ag.appointments.first.reserve_for(@user, @user)
get "/calendar2"
wait_for_ajaximations
click_scheduler_link
wait_for_ajaximations
click_appointment_link
fj('.fc-event:visible').click
ff('#reservations').size.should be_zero
end
end
end