89e7cc569756517414d7929bbd5e34a1c84aedc6
[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 %#needs higher version of RT
210 %#          + <% include('/elements/popup_link.html',
211 %#                   action=>$RT::WebPath.'/Ticket/ModifyCustomFieldsPopup.html?id=__MAGIC_TICKET_ID__',
212 %#                   label =>'edit',
213 %#                   actionlabel => 'Edit appointment',
214 %#                   height      => 436, # better: A + B * (num_custom_fields)
215 %#                 ) |n,js_string
216 %#              %>;
217             title = title.replace( /__MAGIC_TICKET_ID__/, ticketid );
218             $('#'+n_td_id).html( title );
219 %           #(and make the top draggable, so we could do it all over again)
220             $('#'+n_td_id).draggable({
221               containment: '.titlebox-content',
222 %#              revert:      'invalid',
223               revert: true,
224               revertDuration: 0,
225               stop: clear_drag_hi,
226             });
227             $('#'+n_td_id).data('ticketid', ticketid );
228             $('#'+n_td_id).data('length',   length );
229             $('#'+n_td_id).data('cells',    cells );
230             $('#'+n_td_id).data('bgcolor',  bgcolor );
231           }
232         }
233
234       }
235
236     });
237
238   }
239
240 </SCRIPT>
241
242 <& /Search/Calendar.html,
243      @_,
244      Query       => "( Status = 'new' OR Status = 'open' OR Status = 'stalled')
245                      AND ( Type = 'reminder' OR 'Type' = 'ticket' )
246                      AND Queue = $queueid ",
247      slots       => scalar(@usernames),
248      Embed       => 'Schedule.html',
249      DimPast     => 1,
250      Display     => 'Schedule',
251      DisplayArgs => [ username  => \@usernames,
252                       LengthMin => $LengthMin,
253                       #oops, more freeside abstraction-leaking
254                       custnum   => $ARGS{custnum},
255                       pkgnum    => $ARGS{pkgnum},
256                       RedirectToBasics => $ARGS{RedirectToBasics},
257                     ],
258 &>
259
260 <%ONCE>
261
262 my $timestep =  RT->Config->Get('CalendarWeeklySizeMin') || 30; #1/2h
263
264 </%ONCE>
265 <%init>
266
267 #abstraction-leaking
268 my $conf = new FS::Conf;
269 my $queueid = $conf->config('ticket_system-appointment-queueid')
270   or die "ticket_system-appointment-queueid configuration not set";
271
272 my @files = ();
273 #if ( ! $initialized ) {
274   push @files, map "overlibmws$_", ( '', qw( _iframe _draggable _crossframe ) );
275   push @files, map { "${_}contentmws" } qw( iframe ajax );
276 #%}
277
278 my @usernames = ();
279 if ( ref($ARGS{username}) ) {
280   @usernames = @{ $ARGS{username} };
281 } elsif ( $ARGS{username} ) {
282   @usernames = ( $ARGS{username} );
283 } else {
284   #look them up ourslves... again, more FS abstraction-leaking, but 
285   # we want to link to the schedule view, and better than doing this every
286   # menu render
287   use FS::Record qw( qsearch );
288   use FS::sched_item;
289   my @sched_item = qsearch('sched_item', { 'disabled' => '', });
290   @usernames = map $_->access_user->username, @sched_item;
291 }
292
293 ( my $LengthMin = $ARGS{LengthMin} ) =~ /^\d+$/ or die 'non-numeric LengthMin';
294
295 my $cells = int($LengthMin / $timestep);
296 $cells++ if $LengthMin % $timestep;
297
298 </%init>