jQuery UI customer menu: gains submenus, separators, hover delay
[freeside.git] / httemplate / view / cust_main / menu.html
1 <style type="text/css">
2
3 #customer_menu {
4 /*  padding: 0;
5   margin: .5em 0 0 0;
6 */
7   font-size: smaller;
8   border: none;
9 }
10
11 #customer_menu li {
12 /*  margin: 0; */
13   float: left;
14   padding-left: 0;
15   padding-right: .25em;
16   padding-bottom: 0;
17   padding-top: 0;
18 }
19
20 /* #customer_menu .ui-menu-item  */
21 #customer_menu > li.ui-state-focus {
22   background-color: #f8f8f8;
23   border-top: 1px solid transparent;
24   border-left: 1px solid transparent;
25   border-right: 1px solid transparent;
26   border-bottom: none;
27   margin-bottom: 0px;
28 }
29
30 #customer_menu > li.ui-state-active {
31   border-top: 1px solid transparent;
32   border-left: 1px solid transparent;
33   border-right: 1px solid transparent;
34 }
35
36 #customer_menu > li > a {
37   border-top: 1px solid transparent;
38   border-left: 1px solid transparent;
39   border-right: 1px solid transparent;
40   border-bottom: none;
41   margin-bottom: 0px;
42   padding: .5em .75em;
43 }
44
45 #customer_menu a.current_show {
46   font-weight: bold;
47   background: #FFFFFF;
48   border-top: 1px solid #7e0079;
49   border-left: 1px solid #7e0079;
50   border-right: 1px solid #7e0079;
51   border-bottom: 1px solid #ffffff;
52   margin-bottom: -1px;
53 }
54
55 #customer_menu a {
56   display: block;
57
58   margin-left: 0;
59   margin-right: 1em;
60   margin-top: 0;
61   padding: .4em .5em;
62   border-top-left-radius: .5em;
63   border-top-right-radius: .5em;
64
65   font-weight: normal;
66   background: #e0e0e0;
67   color: #525151;
68   white-space: nowrap;
69   text-decoration: none;
70 }
71
72 #customer_menu ul {
73   background: #ffffff;
74   border: 1px solid #7e0079;
75   border-radius: 2px;
76   box-shadow: #333333 1px 1px 2px;
77 }
78
79 #customer_menu ul li {
80   float: none;
81   margin-right: 2px;
82   margin-left: 2px;
83 }
84
85 #customer_menu ul a {
86   color: #333333;
87   background: transparent;
88 }
89
90 #customer_menu li.ui-menu-divider {
91   border-color: #7e0079;
92 /*  margin-right: 2px;
93   margin-left: 2px; */
94 }
95
96 #customer_menu a:hover {
97   text-decoration: underline;
98   color: #7e0079;
99 }
100
101 #customer_menu ul li.ui-state-focus {
102   background: transparent;
103   border: 1px solid transparent;
104   margin-right: 1px;
105   margin-left: 1px;
106 }
107
108 #customer_menu ul li.ui-state-active {
109 /*  background: #f2c9ec; */
110   border: 1px solid transparent;
111   margin-right: 1px;
112   margin-left: 1px;
113 }
114
115 #customer_menu a .arrow {
116   float: right;
117   margin-top:-.8em;
118 }
119
120 </style>
121
122 <ul id="customer_menu">
123 % foreach my $submenu (@processed_menu) {
124   <li>
125     <% shift @$submenu %>
126 %   if ( @$submenu ) {
127       <ul class="customer_submenu">
128 %     foreach my $link ( @$submenu ) {
129         <li><% $link %></li>
130 %     }
131       </ul>
132 %   }
133   </li>
134 % }
135 </ul>
136
137 <script type="text/javascript">
138
139   $("#customer_menu").menu({
140     position: { my: "left top", at: "left+1 bottom" },
141     blur: function() {
142       $(this).menu("option", "position", { my:"left top", at:"left+1 bottom" } );
143     },
144     focus: function(e,ui) {
145       if ($("#customer_menu").get(0) !== $(ui).get(0).item.parent().get(0)) {
146         $(this).menu("option", "position", { my:"left top", at:"right+1 top"} );
147       }
148     },
149   });
150
151 </script>
152
153
154 <%init>
155 my %opt = @_;
156 my $cust_main = $opt{'cust_main'};
157 my $custnum = $cust_main->custnum;
158 my $curuser = $FS::CurrentUser::CurrentUser;
159 my $conf = FS::Conf->new;
160
161 my %payby = map { $_ => 1 } $conf->config('payby');
162
163 # cached for conditions, to avoid looking it up twice
164 my $invoicing_list_emailonly = $cust_main->invoicing_list_emailonly;
165
166 # nice declarative menu; should be a parameter to some kind of menu generator
167 my @menu = ( 
168   [
169     { show        => 'basics',
170       label       => 'Basics',
171     },
172     {
173       label       => 'Edit customer',
174       url         => "edit/cust_main.cgi?$custnum",
175       acl         => 'Edit customer'
176     },
177     {
178       label       => 'Edit contacts',
179       url         => "edit/cust_main-contacts.html?$custnum",
180     },
181
182     { label   => '-',
183       content => '-',
184     },
185
186     {
187       label       => 'Bill now',
188       acl         => 'Bill customer now',
189       content     => sub { include( '/elements/bill.html',
190                               label   => emt('Bill now'),
191                               url     => $cgi->self_url,
192                               custnum => shift->custnum,
193                            ),
194                      },
195     },
196     {
197       label       => 'Suspend',
198       popup       => "misc/suspend_cust.html?custnum=$custnum",
199       acl         => 'Suspend customer',
200       condition   => sub { shift->unsuspended_pkgs > 0 },
201       actionlabel => 'Confirm Suspension',
202       color       => '#ff9900',
203     },
204     {
205       label       => 'Unsuspend',
206       popup       => "misc/unsuspend_cust.html?custnum=$custnum",
207       acl         => 'Unsuspend customer',
208       condition   => sub { shift->suspended_pkgs > 0 },
209       actionlabel => 'Confirm Unsuspension',
210     },
211     {
212       label       => 'Cancel',
213       popup       => "misc/suspend_cust.html?custnum=$custnum",
214       acl         => 'Cancel customer',
215       condition   => sub { shift->ncancelled_pkgs > 0 },
216       actionlabel => 'Confirm Cancellation',
217       color       => '#ff0000',
218     },
219     {
220       label       => 'Merge',
221       popup       => "misc/merge_cust.html?custnum=$custnum",
222       acl         => 'Merge customer',
223       actionlabel => 'Merge customer',
224       width       => 569,
225       height      => 210,
226     },
227     {
228       label       => 'Refer a new customer',
229       url         => "edit/cust_main.cgi?referral_custnum=$custnum",
230       confexists  => '!disable_customer_referrals',
231     },
232
233     { label   => '-',
234       content => '-',
235     },
236
237     {
238       label       => 'View this customer\'s referrals',
239       url         => "search/cust_main.cgi?referral_custnum=$custnum",
240       confexists  => '!disable_customer_referrals',
241       condition   => sub {
242         FS::cust_main->count('referral_custnum = ?', shift->custnum) > 0
243       },
244     },
245     {
246       label       => 'View billing events',
247       url         => "search/cust_event.html?custnum=$custnum",
248       acl         => [ 'Billing event reports',
249                        'View customer billing events' ],
250     },
251     {
252       label       => 'Email a notice to this customer',
253       url         => sub {
254                       my $cust_main = shift;
255                       my $agentnum = $cust_main->agentnum;
256                       'misc/email-customers.html?table=cust_main;'.
257                       'agent_virt_agentnum='.$agentnum.";custnum=$custnum;url=".
258                       uri_escape($cgi->self_url);
259                      },
260       condition   => sub { $invoicing_list_emailonly },
261       acl         => 'Bulk send customer notices',
262     },
263   ],
264   [
265     {
266       label => 'Notes',
267       show  => 'notes',
268     },
269     {
270       label       => 'Add note',
271       popup       => "edit/cust_main_note.cgi?custnum=$custnum",
272       actionlabel => 'Add note',
273       confexists  => '!cust_main-disable_notes',
274       acl         => 'Add customer note',
275       width       => 875,
276       height      => 538,
277     },
278     {
279       label       => 'Attach file',
280       popup       => "edit/cust_main_attach.cgi?custnum=$custnum",
281       actionlabel => 'Upload file',
282       confexists  => '!disable_cust_attachment',
283       acl         => 'Add attachment',
284       width       => 480,
285       height      => 296,
286     },
287   ],
288   [
289     {
290       label => 'Tickets',
291       show  => 'tickets',
292       confexists => 'ticket_system'
293     },
294   ],
295   [
296     {
297       label => 'Appointments',
298       show  => 'appointments',
299       confexists => 'ticket_system',
300       acl   => 'View appointments',
301     },
302     {
303       label       => 'Schedule new appointment',
304       confexists  => 'ticket_system',
305       acl        => 'Make appointment',
306       popup       => "elements/make_appointment.html?custnum=$custnum",
307       actionlabel => 'Schedule appointment',
308     },
309   ],
310   [
311     {
312       label => 'Quotations',
313       show  => 'quotations',
314     },
315     {
316       label => 'Create new quotation',
317       url   => "edit/quotation.html?custnum=$custnum",
318       acl   => 'Generate quotation',
319     },
320   ],
321   [
322     {
323       label => 'Packages',
324       show  => 'packages',
325     },
326     {
327       label       => 'New qualification',
328       popup       => "misc/qual.html?custnum=$custnum",
329       actionlabel => 'New qualification',
330       color       => '#333399',
331       width       => 763,
332       height      => 436,
333       acl         => 'Qualify service',
334     },
335     {
336       label       => 'Order new package',
337       popup       => "misc/order_pkg.html?custnum=$custnum",
338       actionlabel => 'Order new package',
339       color       => '#333399',
340       width       => 960,
341       height      => 740,
342       acl         => 'Order customer package',
343     },
344     {
345       # it's just a popup, but there's some freaky CCH tax stuff in it
346       label       => 'One-time charge',
347       content     => sub {
348                       include( '/elements/one_time_charge_link.html',
349                         custnum => shift->custnum,
350                       );
351                      },
352       acl         => 'One-time charge',
353     },
354     {
355       label       => 'Move services between packages',
356       popup       => "edit/bulk-cust_svc-pkgnum.html?custnum=$custnum",
357       actionlabel => 'Move services',
358       width       => 968,
359       height      => 575,
360       acl         => 'Bulk move customer services',
361     },
362     {
363       label       => 'Bulk order and cancel packages',
364       url         => "edit/cust_pkg.cgi?$custnum",
365       acl         => 'Bulk change customer packages',
366     },
367
368     { label   => '-',
369       content => '-',
370     },
371
372     {
373       label => 'Package reports',
374       url   => "search/report_cust_pkg?custnum=$custnum",
375     },
376     {
377       label => 'View qualifications',
378       url   => "search/qual.cgi?custnum=$custnum",
379       acl   => 'Qualify service',
380     },
381     {
382       label => 'View accounts',
383       url   => "search/report_svc_acct.html?custnum=$custnum",
384     },
385     {
386       label => 'View CDRs',
387       url   => "search/report_cdr.html?custnum=$custnum",
388     },
389   ],
390   [
391     {
392       label => 'Payment History',
393       show  => 'payment_history',
394     },
395
396     # manual payment entry via edit/cust_pay
397     { label   => 'Enter payment',
398       submenu => [
399         {
400           label       => 'Enter check payment',
401           popup       => "edit/cust_pay.cgi?popup=1;payby=BILL;custnum=$custnum",
402           actionlabel => 'Enter check payment',
403           width       => 763,
404           height      => 392,
405           acl         => [ 'Post payment', 'Post check payment' ],
406           condition   => sub { $payby{BILL} },
407         },
408         {
409           label       => 'Enter cash payment',
410           popup       => "edit/cust_pay.cgi?popup=1;payby=CASH;custnum=$custnum",
411           actionlabel => 'Enter cash payment',
412           width       => 763,
413           height      => 392,
414           acl         => [ 'Post payment', 'Post cash payment' ],
415           condition   => sub { $payby{CASH} },
416         },
417         {
418           label       => 'Enter Western Union payment',
419           popup       => "edit/cust_pay.cgi?popup=1;payby=WEST;custnum=$custnum",
420           actionlabel => 'Enter Western Union payment',
421           width       => 763,
422           height      => 392,
423           acl         => [ 'Post payment', ],
424           condition   => sub { $payby{WEST} },
425         },
426         {
427           label       => 'Record manual (offline/POS) credit card payment',
428           popup       => "edit/cust_pay.cgi?popup=1;payby=MCRD;custnum=$custnum",
429           actionlabel => 'Enter credit card payment',
430           width       => 763,
431           height      => 392,
432           acl         => [ 'Post payment', ],
433           condition   => sub { $payby{MCRD} },
434         },
435         {
436           label       => 'Record manual (offline/POS) electronic check',
437           popup       => "edit/cust_pay.cgi?popup=1;payby=MCHK;custnum=$custnum",
438           actionlabel => 'Enter credit card payment',
439           width       => 763,
440           height      => 392,
441           acl         => [ 'Post payment', ],
442           condition   => sub { $payby{MCHK} },
443         },
444       ],
445     },
446
447     # realtime payments via payment.cgi
448     { label   => 'Process payment',
449       submenu => [
450         {
451           label       => 'Process credit card payment',
452           url         => "misc/payment.cgi?payby=CARD;custnum=$custnum",
453           acl         => [ 'Process payment', 'Process credit card payment', ],
454           condition   => sub { $payby{CARD} or $payby{DCRD} },
455         },
456         {
457           label       => 'Process electronic check payment',
458           url         => "misc/payment.cgi?payby=CHEK;custnum=$custnum",
459           acl         => [ 'Process payment', 'Process Echeck payment', ],
460           condition   => sub { $payby{CHEK} or $payby{DCHK} },
461         },
462       ],
463     },
464
465     { label   => '-',
466       content => '-',
467     },
468
469     {
470       label       => 'Enter credit',
471       popup       => "edit/cust_credit.cgi?custnum=$custnum",
472       actionlabel => 'Enter credit',
473       width       => 763,
474       acl         => 'Post credit',
475     },
476     {
477       label       => 'Credit line items',
478       popup       => "edit/credit-cust_bill_pkg.html?custnum=$custnum",
479       actionlabel => 'Credit line items',
480       width       => 968,
481       height      => 575,
482       acl         => 'Credit line items',
483       condition   => sub {
484         FS::cust_bill->count('custnum = ?', shift->custnum) > 0
485       },
486     },
487
488     { label   => '-',
489       content => '-',
490     },
491
492     { label   => 'Enter refund',
493       submenu => [
494
495         {
496           label       => 'Enter check refund',
497           popup       => "edit/cust_refund.cgi?popup=1;payby=BILL;custnum=$custnum",
498           actionlabel => 'Enter check refund',
499           width       => 440,
500           acl         => ['Post refund', 'Post check refund'],
501           condition   => sub { $payby{BILL} },
502         },
503         {
504           label       => 'Enter cash refund',
505           popup       => "edit/cust_refund.cgi?popup=1;payby=CASH;custnum=$custnum",
506           actionlabel => 'Enter cash refund',
507           width       => 392,
508           acl         => ['Post refund', 'Post cash refund'],
509           condition   => sub { $payby{CASH} },
510         },
511         {
512           label       => 'Record manual (offline/POS) credit card refund',
513           popup       => "edit/cust_refund.cgi?popup=1;payby=MCRD;custnum=$custnum",
514           actionlabel => 'Enter credit card refund',
515           width       => 440,
516           acl         => ['Post refund' ],
517           condition   => sub { $payby{MCRD} },
518         },
519         {
520           label       => 'Record manual (offline/POS) electronic check refund',
521           popup       => "edit/cust_refund.cgi?popup=1;payby=MCHK;custnum=$custnum",
522           actionlabel => 'Enter electronic check refund',
523           width       => 440,
524           acl         => ['Post refund' ],
525           condition   => sub { $payby{MCHK} },
526         },
527
528       ],
529
530     },
531
532     { label   => '-',
533       content => '-',
534     },
535
536     {
537       label       => 'Add tax adjustment',
538       popup       => "edit/cust_tax_adjustment.html?custnum=$custnum",
539       actionlabel => 'Add tax adjustment',
540       height      => 200,
541       confexists  => 'enable_tax_adjustments',
542       acl         => 'Add customer tax adjustment',
543     },
544
545     { label       => '-',
546       content     => '-',
547       confexists  => 'enable_tax_adjustments',
548       acl         => 'Add customer tax adjustment',
549     },
550
551     {
552       label       => 'Email statement to this customer',
553       url         => sub {
554                       my $cust_main = shift;
555                       my $agentnum = $cust_main->agentnum;
556                       'misc/email-customer-statement.html?table=cust_main;'.
557                       'agent_virt_agentnum='.$agentnum.";custnum=$custnum;url=".
558                       uri_escape($cgi->self_url);
559                      },
560       condition   => sub { $invoicing_list_emailonly },
561       acl         => 'Resend invoices',
562     },
563     {
564       label       => 'Download PDF statement',
565       url         => "view/cust_main_statement-pdf.cgi?$custnum",
566       acl         => 'View legacy typeset statements',
567       condition   => sub {
568         FS::cust_bill->count('custnum = ?', shift->custnum) > 0
569       },
570     },
571     {
572       label       => 'Search invoices',
573       url         => "search/report_cust_bill.html?custnum=$custnum",
574       acl         => 'List invoices',
575     },
576     {
577       label       => 'View tax exemptions',
578       url         => "search/cust_tax_exempt_pkg.cgi?custnum=$custnum",
579       acl         => 'View customer tax exemptions',
580     },
581     {
582       label       => 'View tax adjustments',
583       url         => "search/cust_tax_adjustment.html?custnum=$custnum",
584       confexists  => 'enable_tax_adjustments',
585       acl         => 'Add customer tax adjustment',
586     },
587     {
588       label       => 'View pending payments',
589       url         => "search/cust_pay_pending.html?magic=_date;statusNOT=done;custnum=$custnum",
590       acl         => 'View pending payments',
591       condition   => sub { 
592         FS::cust_pay_pending->count('custnum = ?', shift->custnum) > 0
593       },
594     },
595
596   ],
597   [
598     {
599       label => 'Change History',
600       show  => 'change_history',
601       acl   => 'View customer history',
602     },
603   ],
604 );
605
606
607 my @processed_menu;
608 foreach my $submenu (@menu) {
609
610   my @links;
611   my $first = 1;
612   foreach my $entry ( @$submenu ) {
613     # if the menu head was skipped, skip the whole menu
614     last if (!$first and !@links);
615     $first = 0;
616
617     my $a = entry2link($entry, $cust_main, $opt{show});
618     push @links, $a if length($a);
619
620   } # foreach $entry
621
622   if (@links) {
623     push @processed_menu, \@links;
624   }
625
626 }
627
628 sub entry2link {
629     my( $entry, $cust_main, $show ) = @_;
630
631     # check conditions
632     if ( $entry->{acl} ) {
633       return ''
634         unless $FS::CurrentUser::CurrentUser->access_right( $entry->{acl} );
635     }
636     if ( $entry->{confexists} ) {
637       if ( $entry->{confexists} =~ /^!(.*)/ ) {
638         # confexists => !foo, a negative condition
639         return '' if FS::Conf->new->exists( $1 );
640       } else {
641         return '' unless FS::Conf->new->exists( $entry->{confexists} );
642       }
643     }
644     if ( $entry->{condition} ) {
645       return '' unless &{ $entry->{condition} }($cust_main);
646     }
647
648     my $label = emt($entry->{label});
649
650     if ( $entry->{submenu} ) {
651
652       my $a = '<a href="javascript:void(0);">'. $label.
653               '<img class="arrow" src="'. $p. 'images/arrow.right.black.png">'.
654               '</a><ul class="customer_subsubmenu">';
655       foreach my $submenu (@{ $entry->{submenu} }) {
656         $a .= '<li>'. entry2link($submenu, $cust_main, $show), '</li>';
657       }
658
659       return $a. '</ul>';
660
661     }
662
663     my $target = $entry->{content}
664               || $entry->{popup}
665               || $entry->{url};
666
667     if ( ref($target) eq 'CODE' ) {
668       $target = &$target($cust_main);
669     }
670
671     return $target if $entry->{content}; #the coderef specified the whole thing
672
673     if ( $entry->{show} ) {
674
675       # the menu head: always a link back to this page
676       $cgi->param('show', $entry->{show});
677       $target = $cgi->self_url;
678       $cgi->param('show', $show);
679
680       my $a = qq[ <A HREF="$target"];
681       $a .= ' class="current_show"' if $show eq $entry->{show};
682       return $a. qq[>$label</A> ];
683
684     } elsif ( $entry->{popup} ) {
685
686       $target =~ s/\$custnum/$custnum/g;
687       $target = $p.$target;
688
689       return include('/elements/popup_link.html',
690         action  => $target,
691         width   => 616,
692         height  => 410,
693         %$entry,
694         label   => $label,
695       );
696
697     } elsif ( $entry->{url} ) {
698
699       $target =~ s/\$custnum/$custnum/g;
700       $target = $p.$target;
701
702       return qq[ <A HREF="$target">$label</A> ];
703
704     } else {
705       die "bad entry $entry in menu: no url, popup or content";
706     }
707
708 }
709
710 </%init>