RT#34237 installer scheduling [javascript debugging]
[freeside.git] / rt / share / html / Search / Schedule.html
1 <& /Elements/Header, Title => 'Schedule', JavaScript => 0 &>
2
3 <SCRIPT TYPE="text/javascript">
4
5 % if ( $cells ) {
6
7   function boxon(what) {
8     var $this = $(what);
9     for ( var c=0; c < <%$cells%>; c++) {
10
11       $this.css('background-color', '#ffffdd');
12       if ( c == 0 ) {
13         $this.css('border-top', '1px double black');
14         $this.css('border-left', '1px double black');
15         $this.css('border-right', '1px solid black');
16       } else if ( c == <%$cells-1%> ) {
17         $this.css('border-left', '1px double black');
18         $this.css('border-right', '1px solid black');
19         $this.css('border-bottom', '1px solid black');
20       } else {
21         $this.css('border-left', '1px double black');
22         $this.css('border-right', '1px solid black');
23       }
24
25       var rownum = $this.parent().prevAll('tr').length;
26       var colnum = $this.prevAll('td').length;
27       $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
28     }
29   }
30
31   function boxoff(what) {
32     var $this = $(what);
33     for ( var c=0; c < <%$cells%>; c++) {
34
35       //$this.css('background-color', '');
36       //$this.css('border', ''); //IE8 woes, removes cell borders
37       $this.removeAttr('style'); //slightly "flashy" on cell changes under IE8
38                                  //but at least it doesn't remove cell borders
39
40       var rownum = $this.parent().prevAll('tr').length;
41       var colnum = $this.prevAll('td').length;
42       $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
43     }
44   }
45
46
47 % }
48
49 % # it would be better if we had draggable-specific droppables, but this will prevent overlap for now...
50   function can_drop ($where, cells) {
51     for (var c=0; c < cells; c++) {
52       if (!$where.is('.ui-droppable')) {
53         return false;
54       }
55       var rownum = $where.parent().prevAll('tr').length;
56       var colnum = $where.prevAll('td').length;
57       $where = $where.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
58     }
59     return true;
60   }
61
62   var drag_cells = 0;
63   var drag_hi;
64
65   // on drag stop (regardless of if it was dropped)
66   function clear_drag_hi () {
67     if ( drag_hi ) {
68       boxoff_do(drag_hi);
69       drag_hi = undefined;
70     }
71   }
72
73   // on drag over
74   function boxon_drop(event, ui) {
75     //var $this = $(what);
76     var $this = $(this);
77
78     drag_cells = ui.draggable.data('cells');
79
80     clear_drag_hi();
81
82     if (!can_drop($this, drag_cells)) return;
83
84     drag_hi = $this;
85
86     for ( var c=0; c < drag_cells; c++) {
87
88       /* well, its not exactly what i want, would prefer if it could properly
89          mouse in-out, but this sorta helps for now?
90          revisit when everthing else is working */
91 /*      $this.effect("highlight", {}, 1500); */
92
93       $this.css('background-color', '#ffffdd');
94       if ( c == 0 ) {
95         $this.css('border-top', '1px double black');
96         $this.css('border-left', '1px double black');
97         $this.css('border-right', '1px solid black');
98       } else if ( c == (drag_cells-1) ) {
99         $this.css('border-left', '1px double black');
100         $this.css('border-right', '1px solid black');
101         $this.css('border-bottom', '1px solid black');
102       } else {
103         $this.css('border-left', '1px double black');
104         $this.css('border-right', '1px solid black');
105       }
106
107       var rownum = $this.parent().prevAll('tr').length;
108       var colnum = $this.prevAll('td').length;
109       $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
110     }
111
112
113   }
114
115   // clears highlighted box, used by clear_hi_drag (drag stop event)
116   function boxoff_do(what) {
117
118     var $this = what;
119
120     for ( var c=0; c < drag_cells; c++) {
121
122       //$this.css('background-color', '');
123       //$this.css('border', ''); //IE8 woes, removes cell borders
124       $this.removeAttr('style'); //slightly "flashy" on cell changes under IE8
125                                  //but at least it doesn't remove cell borders
126
127       var rownum = $this.parent().prevAll('tr').length;
128       var colnum = $this.prevAll('td').length;
129       $this = $this.parent().parent().children('tr').eq(rownum+1).children('td').eq(colnum);
130     }
131   }
132
133   // drop event
134   function reschedule_appointment( event, ui ) {
135
136     var $this = $(this);
137
138     if (!can_drop($this, ui.draggable.data('cells'))) return;
139
140 %   #get the ticket number and appointment length (from the draggable object)
141     var ticketid = ui.draggable.data('ticketid');
142     var length   = ui.draggable.data('length');
143     var bgcolor  = ui.draggable.data('bgcolor');
144
145 %   #and.. the new date and time, and username (from the droppable object)
146     var starts   = $this.data('starts');
147     var username = $this.data('username');
148
149     var due = parseInt(starts) + parseInt(length);
150
151     var n_epoch        = $this.data('epoch');
152     var n_st_tod_row   = $this.data('tod_row');
153
154     var draggable = ui.draggable;
155     var droppable = $this;
156     draggable.effect( "transfer", { to: droppable }, 420 );
157
158 %   #tell the backend to reschedule it
159     var url = "<% popurl(3) %>misc/xmlhttp-ticket-update.html?" +
160               "id=" + ticketid + ";starts=" + starts + ";due=" + due +
161               ";username=" + username;
162
163     $.getJSON( url, function( data ) {
164       if ( data.error && data.error.length ) {
165 %       #error?  "that shouldn't happen" but should display 
166         alert(data.error);
167 %       #XX and should revert the dragable...
168       } else {
169
170         //draggable.effect( "transfer", { to: droppable }, 1000 );
171
172         var label = data.sched_label;
173
174 %       #remove the old appointment entirely
175         var epoch        = ui.draggable.data('epoch');
176         var st_tod_row   = ui.draggable.data('tod_row');
177         var old_username = ui.draggable.data('username');
178         var cells        = ui.draggable.data('cells');
179         for ( var c=0; c < cells; c++) {
180           var tod_row = parseInt(st_tod_row) + (c * <%$timestep%>);
181           var td_id = 'td_' + epoch +
182                       '_' + String( tod_row ) +
183                       '_' + old_username;
184           $('#'+td_id).css('background-color', '#FFFFFF');
185           $('#'+td_id).text('');
186 %         #(and make those boxes droppable)
187           $('#'+td_id).droppable({
188             over: boxon_drop,
189             drop: reschedule_appointment,
190             tolerance: 'pointer'
191           });
192         }
193
194 %       #maybe use that animation which shows the box from point A to B
195
196         clear_drag_hi();
197         for ( var d=0; d < cells; d++) {
198           var n_tod_row = parseInt(n_st_tod_row) + (d * <%$timestep%>);
199           var n_td_id = 'td_' + n_epoch +
200                         '_' + String( n_tod_row ) +
201                         '_' + username;
202           $('#'+n_td_id).css('background-color', bgcolor);
203 %         #remove their droppable
204           $('#'+n_td_id).droppable('destroy');
205           if ( d == 0 ) {
206             var title = 
207               label +
208               ' <A HREF="<%$RT::WebPath%>/Ticket/Display.html?id=' + ticketid + '" target="_blank">view</A> ' +
209               <% include('/elements/popup_link.html',
210                    action=>$RT::WebPath.'/Ticket/ModifyCustomFieldsPopup.html?id=__MAGIC_TICKET_ID__',
211                    label =>'edit',
212                    actionlabel => 'Edit appointment',
213                    height      => 436, # better: A + B * (num_custom_fields)
214                  ) |n,js_string
215               %>;
216             title = title.replace( /__MAGIC_TICKET_ID__/, ticketid );
217             $('#'+n_td_id).html( title );
218 %           #(and make the top draggable, so we could do it all over again)
219             $('#'+n_td_id).draggable({
220               containment: '.titlebox-content',
221 %#              revert:      'invalid',
222               revert: true,
223               revertDuration: 0,
224               stop: clear_drag_hi,
225             });
226             $('#'+n_td_id).data('ticketid', ticketid );
227             $('#'+n_td_id).data('length',   length );
228             $('#'+n_td_id).data('cells',    cells );
229             $('#'+n_td_id).data('bgcolor',  bgcolor );
230           }
231         }
232
233       }
234
235     });
236
237   }
238
239 </SCRIPT>
240
241 <& /Search/Calendar.html,
242      @_,
243      Query       => "( Status = 'new' OR Status = 'open' OR Status = 'stalled')
244                      AND ( Type = 'reminder' OR 'Type' = 'ticket' )
245                      AND Queue = $queueid ",
246      slots       => scalar(@usernames),
247      Embed       => 'Schedule.html',
248      DimPast     => 1,
249      Display     => 'Schedule',
250      DisplayArgs => [ username  => \@usernames,
251                       LengthMin => $LengthMin,
252                       #oops, more freeside abstraction-leaking
253                       custnum   => $ARGS{custnum},
254                       pkgnum    => $ARGS{pkgnum},
255                       RedirectToBasics => $ARGS{RedirectToBasics},
256                     ],
257 &>
258
259 <%ONCE>
260
261 my $timestep =  RT->Config->Get('CalendarWeeklySizeMin') || 30; #1/2h
262
263 </%ONCE>
264 <%init>
265
266 #abstraction-leaking
267 my $conf = new FS::Conf;
268 my $queueid = $conf->config('ticket_system-appointment-queueid')
269   or die "ticket_system-appointment-queueid configuration not set";
270
271 my @files = ();
272 #if ( ! $initialized ) {
273   push @files, map "overlibmws$_", ( '', qw( _iframe _draggable _crossframe ) );
274   push @files, map { "${_}contentmws" } qw( iframe ajax );
275 #%}
276
277 my @usernames = ();
278 if ( ref($ARGS{username}) ) {
279   @usernames = @{ $ARGS{username} };
280 } elsif ( $ARGS{username} ) {
281   @usernames = ( $ARGS{username} );
282 } else {
283   #look them up ourslves... again, more FS abstraction-leaking, but 
284   # we want to link to the schedule view, and better than doing this every
285   # menu render
286   use FS::Record qw( qsearch );
287   use FS::sched_item;
288   my @sched_item = qsearch('sched_item', { 'disabled' => '', });
289   @usernames = map $_->access_user->username, @sched_item;
290 }
291
292 ( my $LengthMin = $ARGS{LengthMin} ) =~ /^\d+$/ or die 'non-numeric LengthMin';
293
294 my $cells = int($LengthMin / $timestep);
295 $cells++ if $LengthMin % $timestep;
296
297 </%init>