Merge pull request #5290 from solgenomics/topic/fix_upload_pehno
[sgn.git] / mason / breeders_toolbox / seedlot_maintenance / table.mas
blob4d4596cba88da91b5a0139f44d4e8f66af984e11
1 <%args>
2     $show_filters => 0
3     $seedlot_id => undef
4     $seedlot_name => undef
5     $multi_seedlot => !(defined $seedlot_id && defined $seedlot_name) ? 1 : 0
6 </%args>
8 <%perl>
9     my $user_role = '';
10     my $user_name = '';
11     if ($c->user) {
12         $user_role = $c->user->get_object()->get_user_type();
13         $user_name = $c->user->get_object()->get_username();
14     }
15 </%perl>
17 <& /util/import_javascript.mas, classes => [ 'CXGN.List', 'jquery', 'jquery.dataTables', 'jquery.dataTables-buttons-min', 'jszip-min', 'buttons.bootstrap-min', 'buttons.html5-min' ],  &>
19 <br /><br />
21 <&| /page/info_section.mas, title=>"Filter Events",  collapsible=>1, collapsed=>!$show_filters, subtitle=>"Filter maintenance events based on date, type, and/or value" &>
23     <div class="well">
25         <p>Add one or more filters to apply to the table of displayed maintenance events.  To add a filter, enter the properties for a filter type 
26         and click the add button to add the filter to the list.  Once you're done adding filters, click the Filter button to display the results.</p>
27         <br />
28         
29         <!-- Filter Form -->
30         <form class="form-horizontal" id="seedlot_maintenance_event_form" name="seedlot_maintenance_event_form">
32 % if ( $multi_seedlot ) {
34             <!-- Seedlot (by user-input) -->
35             <div class="form-group">
36                 <label class="col-sm-3 control-label">Seedlot(s): </label>
37                 <div class="col-sm-8">
38                     <div class="panel panel-info">
39                         <div class="panel-heading">Seedlot Name Contains</div>
40                         <div class="panel-body">
41                             <input id="event_filter_seedlot_name_contains" class="form-control" placeholder="part of Seedlot name" />
42                         </div>
43                     </div>
45                     <div style="text-align: center"><strong>OR</strong></div>
46                 </div>
47                 <div class="col-sm-1">
48                     <button id="event_filter_seedlot_name_contains_add" class='btn btn-info btn-sm'>Add</button>
49                 </div>
50             </div>
52             <!-- Seedlot (by user-input) -->
53             <div class="form-group">
54                 <label class="col-sm-3 control-label"></label>
55                 <div class="col-sm-8">
56                     <div class="panel panel-info">
57                         <div class="panel-heading">Enter the name(s) of the Seedlot(s) - one per line</div>
58                         <div class="panel-body">
59                             <textarea id="event_filter_seedlot_names" class="form-control" rows=5></textarea>
60                         </div>
61                     </div>
63                     <div style="text-align: center"><strong>OR</strong></div>
64                 </div>
65                 <div class="col-sm-1">
66                     <button id="event_filter_seedlot_names_add" class='btn btn-info btn-sm'>Add</button>
67                 </div>
68             </div>
70             <!-- Seedlot (by list) -->
71             <div class="form-group">
72                 <label class="col-sm-3 control-label"></label>
73                 <div class="col-sm-8">
74                     <div class="panel panel-info">
75                         <div class="panel-heading">Select a List of Seedlots</div>
76                         <div class="panel-body"><div id="event_filter_seedlot_list"></div></div>
77                     </div>
78                 </div>
79                 <div class="col-sm-1">
80                     <button id="event_filter_seedlot_list_add" class='btn btn-info btn-sm'>Add</button>
81                 </div>
82             </div>
83 % } else {
84             <!-- Single Seedlot Name -->
85             <div class="form-group">
86                 <label class="col-sm-3 control-label">Seedlot: </label>
87                 <div class="col-sm-8">
88                     <input class="form-control" type="text" value="<% $seedlot_name %>" disabled>
89                 </div>
90                 <div class="col-sm-1"></div>
91             </div>
92 % }
93         
94             <!-- Date -->
95             <div class="form-group">
96                 <label class="col-sm-3 control-label">Date: </label>
97                 <div class="col-sm-4">
98                     <input class="form-control" id="event_filter_date" type="date">
99                 </div>
100                 <div class="col-sm-4">
101                     <select class="form-control" id="event_filter_date_type">
102                         <option value="=">on</option>
103                         <option value="<=">on or before</option>
104                         <option value="<">before</option>
105                         <option value=">=">on or after</option>
106                         <option value=">">after</option>
107                     </select>
108                 </div>
109                 <div class="col-sm-1">
110                     <button id="event_filter_date_add" class='btn btn-info btn-sm'>Add</button>
111                 </div>
112             </div>
114             <!-- Type -->
115             <div class="form-group">
116                 <label class="col-sm-3 control-label">Type: </label>
117                 <div class="col-sm-4">
118                     <select class="form-control" id="event_filter_type" disabled><option>Loading...</option></select>
119                 </div>
120                 <div class="col-sm-4" id="event_filter_type_value">
121                     
122                 </div>
123                 <div class="col-sm-1">
124                     <button id="event_filter_type_add" class='btn btn-info btn-sm'>Add</button>
125                 </div>
126             </div>
128             <!-- Operator -->
129             <div class="form-group">
130                 <label class="col-sm-3 control-label">Operator: </label>
131                 <div class="col-sm-8">
132                     <input class="form-control" id="event_filter_operator" />
133                 </div>
134                 <div class="col-sm-1">
135                     <button id="event_filter_operator_add" class='btn btn-info btn-sm'>Add</button>
136                 </div>
137             </div>
139         </form>
141         <br /><br />
143         <!-- Filter Table -->
144         <p><strong>Applied Filters:</strong></p>
146         <table class="table table-striped">
147             <thead>
148                 <tr>
149                     <th>Property</th>
150                     <th>Comparison</th>
151                     <th>Value</th>
152                     <th>Remove</th>
153                 </tr>
154             </thead>
155             <tbody id="event_filter_table"></tbody>
156         </table>
158         <br /><br />
160         <!-- Submit Filters -->
161         <button id="event_filter_submit" class="btn btn-primary btn-block" style="max-width: 400px; margin: 0 auto">Filter</button>
163     </div>
165 </&>
167 <br /><br />
169 <!-- Events Table -->
170 <table id="seedlot_maintenance_events" class="table table-hover table-bordered"></table>
171 <br />
172 <div id="seedlot_maintenance_events_status">
173     <span style="float: left">
174         <span id="seedlot_maintenance_events_status_displayed"></span><br />
175         <span id="seedlot_maintenance_events_status_page"></span>
176     </span>
177     <div class="btn-group" style="float: right">
178         <button id="seedlot_maintenance_events_prev" type="button" class="btn btn-default" disabled><span class="glyphicon glyphicon-chevron-left"></span>&nbsp;Prev</button>
179         <button id="seedlot_maintenance_events_next" type="button" class="btn btn-default" disabled>Next&nbsp;<span class="glyphicon glyphicon-chevron-right"></span></button>
180     </div>
181 </div>
183 <!-- Remove Modal -->
184 <div id="seedlot_maintenance_remove_modal" class="modal fade" tabindex="-1" role="dialog">
185     <div class="modal-dialog" role="document">
186         <div class="modal-content">
187             <div class="modal-header">
188                 <p class="modal-title">Remove Event?</p>
189             </div>
190             <div id="seedlot_modal_body" class="modal-body">
191                 <p class="modal-body">Are you sure you want to remove this Seedlot Maintenance Event?</p>
192             </div>
193             <div class="modal-footer">
194                 <button id="seedlot_maintenance_remove_modal_cancel" type="button" class="btn btn-secondary" data-dismiss="modal">Cancel</button>
195                 <button id="seedlot_maintenance_remove_modal_ok" type="button" class="btn btn-danger">Remove Event</button>
196             </div>
197         </div>
198     </div>
199 </div>
202 <script type="text/javascript">
204 let MULTI_SEEDLOT = <% $multi_seedlot %> === 1;
205 let LO = new CXGN.List();
206 let DT;
208 let EVENTS = [];                            // List of maintenance events for the Seedlot
209 let PAGE_SIZE = MULTI_SEEDLOT ? 50 : 10;    // Max number of results per page
210 let PAGE = 1;                               // The current page of results
211 let MAX_PAGE;                               // The max page of results
212 let TOTAL;                                  // The total number of results
213 let ONTOLOGY = [];                          // Event type ontology
214 let FILTERS = [];                           // List of filter properties
216 jQuery(document).ready(function() {
217     
218     // Load the Events
219     setupDataTable();
220     getEvents();
221     getOntology(function() {
222         setTypeFilter();
223     });
225     // Add list selection, if enabled
226     if ( MULTI_SEEDLOT ) {
227         jQuery('#event_filter_seedlot_list').html(LO.listSelect('event_filter_seedlot', [ 'seedlots' ], 'select', undefined, undefined));
228     }
230     // Click and Change Listeners
231     jQuery("#event_filter_seedlot_name_contains_add").click(addSeedlotNameContainsFilter);
232     jQuery("#event_filter_seedlot_names_add").click(addSeedlotNamesFilter);
233     jQuery("#event_filter_seedlot_list_add").click(addSeedlotListFilter);
234     jQuery("#event_filter_date_add").click(addDateFilter);
235     jQuery("#event_filter_type_add").click(addTypeFilter);
236     jQuery("#event_filter_operator_add").click(addOperatorFilter);
237     jQuery("#event_filter_type").change(setTypeFilterValues);
238     jQuery("#event_filter_submit").click(submitFilter);
239     jQuery("#seedlot_maintenance_events_prev").click(getPrevPage);
240     jQuery("#seedlot_maintenance_events_next").click(getNextPage);
245  * Setup and Initialize the DataTable for the Events
246  */
247 function setupDataTable() {
248     
249     // Set DataTables Buttons
250     let DT_BUTTONS = [
251         {
252             extend: 'excelHtml5',
253             title: 'seedlot_events',
254             exportOptions: {
255                 orthogonal: 'export'
256             }
257         },
258         {
259             extend: 'csvHtml5',
260             title: 'seedlot_events',
261             exportOptions: {
262                 orthogonal: 'export'
263             }
264         }
265     ];
267     // Set the Columns
268     let columns = [];
269     if ( MULTI_SEEDLOT ) {
270         columns = columns.concat([
271             { 
272                 title: "Seedlot", 
273                 data: "uniquename", 
274                 render: function(data, type, row) {
275                     if ( type === 'display' ) {
276                         let url = "/breeders/seedlot/" + row.stock_id;
277                         return "<a href='" + url + "'>" + data + "</a>";
278                     }
279                     return data;
280                 }
281             }
282         ]);
283     }
284     columns = columns.concat([
285         { title: "Event ID", data: "stockprop_id" },
286         {
287             title: "Event Date", 
288             data: "timestamp",
289             render: function(data, type, row) {
290                 if ( type === "display" ) {
291                     let dow = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
292                     let month = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
293                     let d = new Date(data);
294                     let h = d.getHours();
295                     let m = d.getMinutes();
296                     let s = d.getSeconds();
297                     if ( h <= 9 ) h = '0' + h;
298                     if ( m <= 9 ) m = '0' + m;
299                     if ( s <= 9 ) s = '0' + s;
300                     let ts = dow[d.getDay()] + " " + month[d.getMonth()] + " " + d.getDate() + " " + h + ":" + m + ":" + s + " " + d.getFullYear();
301                     return ts;
302                 }
303                 return data;
304             }
305         },
306         { title: "Event Type", data: "cvterm_name" },
307         { title: "Value", data: "value" },
308         { 
309             title: "Notes", 
310             data: "notes",
311             render: function(data, type, row) {
312                 return data ? data : '';
313             }
314         },
315         { title: "Operator", data: "operator" },
316         {
317             title: "Options",
318             data: "stockprop_id",
319             render: function(data, type, row) {
320                 if ( type === "display" ) {
321                     let html = '';
322                     if ( '<% $user_role %>' === 'curator' || ( '<% $user_role %>' === 'submitter' && '<% $user_name %>' === row.operator ) ) { 
323                         html += "<a class='event_table_remove' onclick='removeEvent(" + row.stock_id + ", " + row.stockprop_id + ")' style='cursor: pointer;'>[Remove]</a>";
324                     }
325                     return html;
326                 }
327                 else {
328                     return '';
329                 }
330             }
331         }
332     ]);
334     // Init DataTable
335     DT = jQuery('#seedlot_maintenance_events').DataTable({
336         dom: 'Brt',
337         autoWidth: false,
338         pageLength: PAGE_SIZE,
339         data: [],
340         columns: columns,
341         order: [[ MULTI_SEEDLOT ? 2 : 1, "desc" ]],
342         buttons: DT_BUTTONS
343     });
349  * Get the Maintenance Events
350  * - Query the database
351  * - call displayEvents() to populate the table
352  */
353 function getEvents() {
354     EVENTS = [];
356     // Update status
357     jQuery("#event_filter_submit").html("Searching...");
358     jQuery("#event_filter_submit").attr("disabled", true);
359     jQuery("#seedlot_maintenance_events_prev").attr('disabled', true);
360     jQuery("#seedlot_maintenance_events_next").attr('disabled', true);
361     // DT.clear().draw();
362     // jQuery("#seedlot_maintenance_events .dataTables_empty").html("Searching...");
364     // Build filter arguments
365     let names = [];
366     let dates = [];
367     let types = [];
368     let operators = [];
369     for ( let i = 0; i < FILTERS.length; i++ ) {
370         let f = FILTERS[i];
371         if ( f.type_id === 'name' ) {
372             if ( f.comp === 'includes' ) {
373                 names.push({
374                     value: f.value,
375                     comp: 'IN'
376                 });
377             }
378             else if ( f.comp === 'contains' ) {
379                 names.push({
380                     value: "%" + f.value + "%",
381                     comp: 'LIKE'
382                 });
383             }
384         }
385         else if ( f.type_id === 'date' ) {
386             if ( f.comp === '=' ) {
387                 dates.push({
388                     date: f.value + "%",
389                     comp: 'LIKE'
390                 });
391             }
392             else if ( f.comp === '<=' ) {
393                 dates.push({
394                     date: f.value + " 24:00:00",
395                     comp: f.comp
396                 });
397             }
398             else if ( f.comp === '<' ) {
399                 dates.push({
400                     date: f.value + " 00:00:00",
401                     comp: f.comp
402                 });
403             }
404             else if ( f.comp === '>=' ) {
405                 dates.push({
406                     date: f.value + " 00:00:00",
407                     comp: f.comp
408                 });
409             }
410             else if ( f.comp === '>' ) {
411                 dates.push({
412                     date: f.value + " 24:00:00",
413                     comp: f.comp
414                 });
415             }
416         }
417         else if ( f.type_id === 'operator' ) {
418             operators.push(f.value);
419         }
420         else {
421             types.push({
422                 cvterm_id: f.type_id,
423                 values: f.value
424             });
425         }
426     }
427     if ( !MULTI_SEEDLOT ) {
428         names.push({
429             value: "<% $seedlot_name %>",
430             comp: "="
431         });
432     }
433     let body = {
434         filters: {
435             names: names, 
436             dates: dates,
437             types: types,
438             operators: operators
439         },
440         page: PAGE,
441         pageSize: PAGE_SIZE
442     }
444     // Make request
445     jQuery.ajax({
446         type: 'POST',
447         url: '/ajax/breeders/seedlot/maintenance/search',
448         data: JSON.stringify(body),
449         contentType: "application/json; charset=utf-8",
450         dataType: "json",
451         success: function(data) {
452             if ( data && data.results ) {
453                 EVENTS = data.results;
454                 PAGE = data.page;
455                 MAX_PAGE = data.maxPage;
456                 TOTAL = data.total;
457             }
458             else {
459                 alert("ERROR: Could not load maintenance events!");
460             }
461         },
462         error: function() {
463             alert("ERROR: Could not load maintenance events!");
464         },
465         complete: function() {
466             displayEvents();
468             jQuery("#event_filter_submit").html("Filter");
469             jQuery("#event_filter_submit").attr("disabled", false);
470         }
471     });
475  * Redraw the display of the Maintenance Events in the table
476  */
477 function displayEvents() {
478     
479     // Update table
480     DT.clear();
481     DT.rows.add(EVENTS);
482     DT.draw();
484     // Update Status Info
485     let end = PAGE*PAGE_SIZE;
486     let start = (end-PAGE_SIZE)+1;
487     end = end > TOTAL ? TOTAL : end;
488     let displayed = start + " - " + end + " / " + TOTAL;
489     let page_info = PAGE + " / " + MAX_PAGE;
491     jQuery("#seedlot_maintenance_events_status_displayed").html("<strong>EVENTS:</strong> " + displayed);
492     jQuery("#seedlot_maintenance_events_status_page").html("<strong>PAGE:</strong> " + page_info);
493     jQuery("#seedlot_maintenance_events_prev").attr('disabled', PAGE === 1);
494     jQuery("#seedlot_maintenance_events_next").attr('disabled', PAGE === MAX_PAGE || MAX_PAGE === 1);
499  * Start a new filtered search
500  */
501 function submitFilter() {
502     PAGE = 1;
503     getEvents();
507  * Get the previous page of events
508  */
509 function getPrevPage() {
510     if ( PAGE > 1 ) {
511         PAGE--;
512         getEvents();
513     }
517  * Get the next page of events
518  */
519 function getNextPage() {
520     PAGE++;
521     getEvents();
526 // FILTER FUNCTIONS
531  * Get the maintenance event ontology
532  * @param {Function} [callback] Callback function()
533  */
534 function getOntology(callback) {
535     jQuery.ajax({
536         type: 'GET',
537         dataType: 'json',
538         url: '/ajax/breeders/seedlot/maintenance/ontology',
539         success: function(data) {
540             if ( data && data.ontology ) {
541                 ONTOLOGY = data.ontology;
542                 if ( callback ) return callback();
543             }
544             else {
545                 alert("ERROR: Could not fetch ontology terms");
546             }
547         },
548         error: function() {
549             alert("ERROR: Could not fetch ontology terms");
550         }
551     });
555  * Parse the event ontology for event types
556  * - Populate event type select
557  * - Call setTypeFilterValues to set initial values
558  */
559 function setTypeFilter() {
560     
561     // Populate type select
562     let html = "<option value=''>...Select Maintenance Event Type...</option>";
563     if ( ONTOLOGY ) {
564         for ( let i = 0; i < ONTOLOGY.length; i++ ) {
565             html += "<optgroup label='" + ONTOLOGY[i].name + "'>";
566             if ( ONTOLOGY[i].children ) {
567                 for ( let j = 0; j < ONTOLOGY[i].children.length; j++ ) {
568                     html += "<option value='" + ONTOLOGY[i].children[j].cvterm_id + "'>" + ONTOLOGY[i].children[j].name + "</option>";
569                 }
570             }
571             html += "</optgroup>";
572         }
573     }
574     jQuery("#event_filter_type").html(html);
575     jQuery("#event_filter_type").attr("disabled", false);
577     // Set the values
578     setTypeFilterValues();
583  * Populate the event type input values based on the currently selected event type with:
584  *  - a multi-select with the values for the currently selected event type, if set in the ontology
585  *  - a text input, if not set in the ontology
586  */
587 function setTypeFilterValues() {
588     let id = jQuery("#event_filter_type").val();
590     // Build input based on possible values of selected event type
591     let html = "";
592     let input_type;
593     if ( id && id !== '' && ONTOLOGY ) {
594         let values = [];
595         for ( let i = 0; i < ONTOLOGY.length; i++ ) {
596             for ( let j = 0; j < ONTOLOGY[i].children.length; j++ ) {
597                 if ( ONTOLOGY[i].children[j].cvterm_id === parseInt(id) ) {
598                     values = ONTOLOGY[i].children[j].children;
599                 }
600             }
601         }
602         if ( values && values.length > 0 ) {
603             input_type = "select";
604             html += "<select class='form-control' id='event_filter_type_value_select' multiple>";
605             for ( let i = 0; i < values.length; i++ ) {
606                 html += "<option value='" + values[i].name + "'>" + values[i].name + "</option>";
607             }
608             html += "</select>";
609         }
610         else {
611             input_type = "user";
612             html += "<input class='form-control' id='event_filter_type_value_user' type='text'>";
613         }
614     }
615     else {
616         input_type = "none";
617     }
619     jQuery("#event_filter_type_value").data("input-type", input_type);
620     jQuery("#event_filter_type_value").html(html);
625  * Add a Name Filter - Seedlot name contains user-provided substring
626  */
627 function addSeedlotNameContainsFilter() {
628     let substring = jQuery("#event_filter_seedlot_name_contains").val();
629     addFilter('name', 'name', substring, 'contains');
630     return false;
634  * Add a Name Filter - from the user-entered list of names
635  */
636 function addSeedlotNamesFilter() {
637     let names = jQuery("#event_filter_seedlot_names").val();
638     names = names ? names.split('\n') : [];
639     addFilter('name', 'name', names && names.length > 0 ? names : undefined, 'includes');
640     return false;
644  * Add a Name Filter - from the user-selected list of Seedlots
645  */
646 function addSeedlotListFilter() {
647     let list_id = jQuery("#event_filter_seedlot_list_select").val();
648     let names = list_id ? LO.getList(list_id) : [];
649     addFilter('name', 'name', names && names.length > 0 ? names : undefined, 'includes');
650     return false;
655  * Add a Date Filter
656  */
657 function addDateFilter() {
658     let d = jQuery('#event_filter_date').val();
659     let t = jQuery('#event_filter_date_type').val();
661     // Check date format
662     let regex = /^[0-9]{4}-[0-9]{2}-[0-9]{2}$/;
663     if ( !d.match(regex) ) {
664         alert("Incorrect date format - the date must be in the format of YYYY-MM-DD");
665         return false;
666     }
668     // Add Filter
669     addFilter('date', 'date', d, t);
670     return false;
674  * Add a Type/Value Filter
675  */
676 function addTypeFilter() {
677     
678     // Get event type
679     let type_id = jQuery('#event_filter_type').val();
680     let type_name = jQuery('#event_filter_type option:selected').text();
681     
682     // Get event value(s)
683     let values;
684     let input_type = jQuery('#event_filter_type_value').data('input-type');
685     if ( input_type === 'select' ) {
686         values = jQuery('#event_filter_type_value_select').val();
687     }
688     else if ( input_type === 'user' ) {
689         let v = jQuery('#event_filter_type_value_user').val();
690         values = v && v !== '' ? [v] : undefined;
691     }
692     
693     addFilter(type_id, type_name, values, 'includes');
694     return false;
698  * Add an Operator Filter
699  */
700 function addOperatorFilter() {
701     let operators = jQuery('#event_filter_operator').val();
702     addFilter('operator', 'operator', operators, '=');
703     return false;
707  * Add a Filter to the list of added filters
708  * - call displayFilters() to update table
709  * @param {string|int} type_id ID of the filter type (cvterm_id, date, operator)
710  * @param {string} type_name Name of the filter type (cvterm name, date, operator)
711  * @param {string|int} value Value of the filter for comparison (type value, date, operator names)
712  * @param {string} comp Comparison used for the filter
713  */
714 function addFilter(type_id, type_name, value, comp) {
715     FILTERS.push({
716         type_id: type_id,
717         type_name: type_name,
718         value: value,
719         comp: comp
720     });
721     displayFilters();
725  * Remove the specified filter from the list of added filters
726  * - call displayFilters() to update table
727  * @param {int} i Index of filter to remove
728  */
729 function removeFilter(i) {
730     FILTERS.splice(i, 1);
731     displayFilters();
735  * Update the table of added filters
736  */
737 function displayFilters() {
738     let html = "";
739     for ( let i = 0; i < FILTERS.length; i++ ) {
740         let v = FILTERS[i].value;
741         if ( !v ) v = "<em>Any Value</em>";
742         if ( Array.isArray(v) && v.length > 20 ) v = v.slice(0, 20).join(',') + "...";
743         html += "<tr>";
744         html += "<td>" + FILTERS[i].type_name + "</td>";
745         html += "<td>" + FILTERS[i].comp + "</td>";
746         html += "<td>" + v + "</td>";
747         html += "<td><button class='btn btn-danger btn-xs' onclick='removeFilter(" + i + ")'><span class='glyphicon glyphicon-remove'></span></button></td>";
748         html += "</tr>";
749     }
750     jQuery("#event_filter_table").html(html);
755 // EDIT FUNCTIONS
759  * Remove the specified Seedlot Event
760  * - Display the confirmation dialog
761  * - Remove the event from the database
762  * - Reload the displayed events
763  * @param {int} stock_id Stock ID of the Seedlot
764  * @param {int} stockprop_id Stockprop ID of the Event
765  */
766 function removeEvent(stock_id, stockprop_id) {
767     jQuery('#seedlot_maintenance_remove_modal').modal({backdrop: 'static', keyboard: false});
768     jQuery("#seedlot_maintenance_remove_modal_ok").off().click(function() {
769         jQuery("#seedlot_maintenance_remove_modal_cancel").attr("disabled", true);
770         jQuery("#seedlot_maintenance_remove_modal_ok").attr("disabled", true);
771         jQuery("#seedlot_maintenance_remove_modal_ok").html("Removing...");
773         // Delete the Event
774         jQuery.ajax({
775             type: 'DELETE',
776             url: '/ajax/breeders/seedlot/' + stock_id + '/maintenance/' + stockprop_id,
777             dataType: "json",
778             success: function(data) {
779                 if ( !data || !data.success || data.success === 0 ) {
780                     console.log(data.error);
781                     alert("ERROR: Could not remove seedlot event!");
782                 }
783             },
784             error: function() {
785                 alert("ERROR: Could not remove seedlot event!");
786             },
787             complete: function() {
788                 jQuery('#seedlot_maintenance_remove_modal').modal('hide')
789                 jQuery("#seedlot_maintenance_remove_modal_cancel").attr("disabled", false);
790                 jQuery("#seedlot_maintenance_remove_modal_ok").attr("disabled", false);
791                 jQuery("#seedlot_maintenance_remove_modal_ok").html("Remove Event");
792                 getEvents();
793             }
794         });
796     });
797     return false;
801 </script>
804 <style>
805     #seedlot_maintenance_remove_modal .modal-title {
806         font-size: 150%;
807         weight: 700;
808     }
809     #seedlot_maintenance_remove_modal .modal-body {
810         font-size: 110%;
811     }
812 </style>