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