Bug 33066: Introduce a KohaTable Vue component
The way we deal with DataTables in Vue component is not nice, especially when we need to add buttons/link in the cell and interact with the rest of the Vue app from there. When I started to work on Vue last year there was no good solution from DataTables, now there is a Vue component. It is not perfect, you still cannot add Vue component in the DT component, but it brings something to follow. Agustin implemented something on theke/import_source_vue, but he went too far, and it will need to rewrite the whole ERM module. Additionally he didn't provide a solution that has the same features as what we have now. The goal of this patch is to not duplicate the code in datatables.js, we don't want to maintain two version of this code (one is enough already!) We split the huge function in datatables.js in small ones to make them reusable from the Vue component. This is quite ugly, and it needs to lot more addition, but it's a first start! Help, ideas, and feedback welcome (and needed!) Bug 33066: Fix agreement name in delete confirmation dialog Signed-off-by: Pedro Amorim <pedro.amorim@ptfs-europe.com> Signed-off-by: Agustín Moyano <agustinmoyano@theke.io> Signed-off-by: Tomas Cohen Arazi <tomascohen@theke.io>
This commit is contained in:
parent
ba601ca37f
commit
765fd1ced3
5 changed files with 722 additions and 393 deletions
|
@ -504,6 +504,381 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) {
|
|||
console.log(message);
|
||||
};
|
||||
|
||||
function _dt_default_ajax (params){
|
||||
let default_filters = params.default_filters;
|
||||
let options = params.options;
|
||||
|
||||
if(!options.criteria || ['contains', 'starts_with', 'ends_with', 'exact'].indexOf(options.criteria.toLowerCase()) === -1) options.criteria = 'contains';
|
||||
options.criteria = options.criteria.toLowerCase();
|
||||
|
||||
return {
|
||||
'type': 'GET',
|
||||
'cache': true,
|
||||
'dataSrc': 'data',
|
||||
'beforeSend': function(xhr, settings) {
|
||||
this._xhr = xhr;
|
||||
if(options.embed) {
|
||||
xhr.setRequestHeader('x-koha-embed', Array.isArray(options.embed)?options.embed.join(','):options.embed);
|
||||
}
|
||||
},
|
||||
'dataFilter': function(data, type) {
|
||||
var json = {data: JSON.parse(data)};
|
||||
if (total = this._xhr.getResponseHeader('x-total-count')) {
|
||||
json.recordsTotal = total;
|
||||
json.recordsFiltered = total;
|
||||
}
|
||||
if (total = this._xhr.getResponseHeader('x-base-total-count')) {
|
||||
json.recordsTotal = total;
|
||||
}
|
||||
if (draw = this._xhr.getResponseHeader('x-koha-request-id')) {
|
||||
json.draw = draw;
|
||||
}
|
||||
|
||||
return JSON.stringify(json);
|
||||
},
|
||||
'data': function( data, settings ) {
|
||||
var length = data.length;
|
||||
var start = data.start;
|
||||
|
||||
var dataSet = {
|
||||
_page: Math.floor(start/length) + 1,
|
||||
_per_page: length
|
||||
};
|
||||
|
||||
function build_query(col, value){
|
||||
|
||||
var parts = [];
|
||||
var attributes = col.data.split(':');
|
||||
for (var i=0;i<attributes.length;i++){
|
||||
var part = {};
|
||||
var attr = attributes[i];
|
||||
let criteria = options.criteria;
|
||||
if ( value.match(/^\^(.*)\$$/) ) {
|
||||
value = value.replace(/^\^/, '').replace(/\$$/, '');
|
||||
criteria = "exact";
|
||||
} else {
|
||||
// escape SQL LIKE special characters %
|
||||
value = value.replace(/(\%|\\)/g, "\\$1");
|
||||
}
|
||||
part[!attr.includes('.')?'me.'+attr:attr] = criteria === 'exact'
|
||||
? value
|
||||
: {like: (['contains', 'ends_with'].indexOf(criteria) !== -1?'%':'') + value + (['contains', 'starts_with'].indexOf(criteria) !== -1?'%':'')};
|
||||
parts.push(part);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
var filter = data.search.value;
|
||||
// Build query for each column filter
|
||||
var and_query_parameters = settings.aoColumns
|
||||
.filter(function(col) {
|
||||
return col.bSearchable && typeof col.data == 'string' && data.columns[col.idx].search.value != ''
|
||||
})
|
||||
.map(function(col) {
|
||||
var value = data.columns[col.idx].search.value;
|
||||
return build_query(col, value)
|
||||
})
|
||||
.map(function r(e){
|
||||
return ($.isArray(e) ? $.map(e, r) : e);
|
||||
});
|
||||
|
||||
// Build query for the global search filter
|
||||
var or_query_parameters = settings.aoColumns
|
||||
.filter(function(col) {
|
||||
return col.bSearchable && filter != ''
|
||||
})
|
||||
.map(function(col) {
|
||||
var value = filter;
|
||||
return build_query(col, value)
|
||||
})
|
||||
.map(function r(e){
|
||||
return ($.isArray(e) ? $.map(e, r) : e);
|
||||
});
|
||||
|
||||
if ( default_filters ) {
|
||||
let additional_filters = {};
|
||||
for ( f in default_filters ) {
|
||||
let k; let v;
|
||||
if ( typeof(default_filters[f]) === 'function' ) {
|
||||
let val = default_filters[f]();
|
||||
if ( val != undefined && val != "" ) {
|
||||
k = f; v = val;
|
||||
}
|
||||
} else {
|
||||
k = f; v = default_filters[f];
|
||||
}
|
||||
|
||||
// Pass to -or if you want a separate OR clause
|
||||
// It's not the usual DBIC notation!
|
||||
if ( f == '-or' ) {
|
||||
if (v) or_query_parameters.push(v)
|
||||
} else if ( f == '-and' ) {
|
||||
if (v) and_query_parameters.push(v)
|
||||
} else if ( v ) {
|
||||
additional_filters[k] = v;
|
||||
}
|
||||
}
|
||||
if ( Object.keys(additional_filters).length ) {
|
||||
and_query_parameters.push(additional_filters);
|
||||
}
|
||||
}
|
||||
query_parameters = and_query_parameters;
|
||||
if ( or_query_parameters.length) {
|
||||
query_parameters.push(or_query_parameters);
|
||||
}
|
||||
|
||||
if(query_parameters.length) {
|
||||
query_parameters = JSON.stringify(query_parameters.length === 1?query_parameters[0]:{"-and": query_parameters});
|
||||
dataSet.q = query_parameters;
|
||||
delete options.query_parameters;
|
||||
} else {
|
||||
delete options.query_parameters;
|
||||
}
|
||||
|
||||
dataSet._match = options.criteria;
|
||||
|
||||
if ( data["draw"] !== undefined ) {
|
||||
settings.ajax.headers = { 'x-koha-request-id': data.draw }
|
||||
}
|
||||
|
||||
if(options.columns) {
|
||||
var order = data.order;
|
||||
var orderArray = new Array();
|
||||
order.forEach(function (e,i) {
|
||||
var order_col = e.column;
|
||||
var order_by = options.columns[order_col].data;
|
||||
order_by = order_by.split(':');
|
||||
var order_dir = e.dir == 'asc' ? '+' : '-';
|
||||
Array.prototype.push.apply(orderArray,order_by.map(x => order_dir + (!x.includes('.')?'me.'+x:x)));
|
||||
});
|
||||
dataSet._order_by = orderArray.filter((v, i, a) => a.indexOf(v) === i).join(',');
|
||||
}
|
||||
|
||||
return dataSet;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function _dt_buttons(params){
|
||||
let included_ids = params.included_ids || [];
|
||||
let settings = params.settings || {};
|
||||
let table_settings = params.table_settings || {};
|
||||
|
||||
var exportColumns = ":visible:not(.noExport)";
|
||||
if( settings.hasOwnProperty("exportColumns") ){
|
||||
// A custom buttons configuration has been passed from the page
|
||||
exportColumns = settings["exportColumns"];
|
||||
}
|
||||
|
||||
var export_format = {
|
||||
body: function ( data, row, column, node ) {
|
||||
var newnode = $(node);
|
||||
|
||||
if ( newnode.find(".noExport").length > 0 ) {
|
||||
newnode = newnode.clone();
|
||||
newnode.find(".noExport").remove();
|
||||
}
|
||||
|
||||
return newnode.text().replace( /\n/g, ' ' ).trim();
|
||||
}
|
||||
}
|
||||
|
||||
var export_buttons = [
|
||||
{
|
||||
extend: 'excelHtml5',
|
||||
text: __("Excel"),
|
||||
exportOptions: {
|
||||
columns: exportColumns,
|
||||
format: export_format
|
||||
},
|
||||
},
|
||||
{
|
||||
extend: 'csvHtml5',
|
||||
text: __("CSV"),
|
||||
exportOptions: {
|
||||
columns: exportColumns,
|
||||
format: export_format
|
||||
},
|
||||
},
|
||||
{
|
||||
extend: 'copyHtml5',
|
||||
text: __("Copy"),
|
||||
exportOptions: {
|
||||
columns: exportColumns,
|
||||
format: export_format
|
||||
},
|
||||
},
|
||||
{
|
||||
extend: 'print',
|
||||
text: __("Print"),
|
||||
exportOptions: {
|
||||
columns: exportColumns,
|
||||
format: export_format
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
let buttons = [];
|
||||
buttons.push(
|
||||
{
|
||||
fade: 100,
|
||||
className: "dt_button_clear_filter",
|
||||
titleAttr: __("Clear filter"),
|
||||
enabled: false,
|
||||
text: '<i class="fa fa-lg fa-remove"></i> <span class="dt-button-text">' + __("Clear filter") + '</span>',
|
||||
action: function ( e, dt, node, config ) {
|
||||
dt.search( "" ).draw("page");
|
||||
node.addClass("disabled");
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
if( included_ids.length > 0 ){
|
||||
buttons.push(
|
||||
{
|
||||
extend: 'colvis',
|
||||
fade: 100,
|
||||
columns: included_ids,
|
||||
className: "columns_controls",
|
||||
titleAttr: __("Columns settings"),
|
||||
text: '<i class="fa fa-lg fa-gear"></i> <span class="dt-button-text">' + __("Columns") + '</span>',
|
||||
exportOptions: {
|
||||
columns: exportColumns
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
buttons.push(
|
||||
{
|
||||
extend: 'collection',
|
||||
autoClose: true,
|
||||
fade: 100,
|
||||
className: "export_controls",
|
||||
titleAttr: __("Export or print"),
|
||||
text: '<i class="fa fa-lg fa-download"></i> <span class="dt-button-text">' + __("Export") + '</span>',
|
||||
buttons: export_buttons
|
||||
}
|
||||
);
|
||||
|
||||
if ( table_settings && CAN_user_parameters_manage_column_config ) {
|
||||
buttons.push(
|
||||
{
|
||||
className: "dt_button_configure_table",
|
||||
fade: 100,
|
||||
titleAttr: __("Configure table"),
|
||||
text: '<i class="fa fa-lg fa-wrench"></i> <span class="dt-button-text">' + __("Configure") + '</span>',
|
||||
action: function() {
|
||||
window.location = '/cgi-bin/koha/admin/columns_settings.pl?module=' + table_settings['module'] + '&page=' + table_settings['page'] + '&table=' + table_settings['table'];
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
return buttons;
|
||||
}
|
||||
|
||||
function _dt_visibility(table_settings, settings){
|
||||
var counter = 0;
|
||||
let hidden_ids = [];
|
||||
let included_ids = [];
|
||||
if ( table_settings ) {
|
||||
var columns_settings = table_settings['columns'];
|
||||
$(columns_settings).each( function() {
|
||||
var named_id = $( 'thead th[data-colname="' + this.columnname + '"]', this ).index( 'th' );
|
||||
var used_id = settings.bKohaColumnsUseNames ? named_id : counter;
|
||||
if ( used_id == -1 ) return;
|
||||
|
||||
if ( this['is_hidden'] == "1" ) {
|
||||
hidden_ids.push( used_id );
|
||||
}
|
||||
if ( this['cannot_be_toggled'] == "0" ) {
|
||||
included_ids.push( used_id );
|
||||
}
|
||||
counter++;
|
||||
});
|
||||
}
|
||||
return [hidden_ids, included_ids];
|
||||
}
|
||||
|
||||
function _dt_on_visibility(add_filters, table_node, table_dt){
|
||||
if ( add_filters ) {
|
||||
let visible_columns = table_dt.columns().visible();
|
||||
$(table_node).find('thead tr:eq(1) th').each( function (i) {
|
||||
let th_id = $(this).data('th-id');
|
||||
if ( visible_columns[th_id] == false ) {
|
||||
$(this).hide();
|
||||
} else {
|
||||
$(this).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if( typeof columnsInit == 'function' ){
|
||||
// This function can be created separately and used to trigger
|
||||
// an event after the DataTable has loaded AND column visibility
|
||||
// has been updated according to the table's configuration
|
||||
columnsInit();
|
||||
}
|
||||
}
|
||||
|
||||
function _dt_add_filters(table_node, table_dt) {
|
||||
$(table_node).find('thead tr').clone().appendTo( $(table_node).find('thead') );
|
||||
|
||||
$(table_node).find('thead tr:eq(1) th').each( function (i) {
|
||||
var is_searchable = table_dt.settings()[0].aoColumns[i].bSearchable;
|
||||
$(this).removeClass('sorting').removeClass("sorting_asc").removeClass("sorting_desc");
|
||||
$(this).data('th-id', i);
|
||||
if ( is_searchable ) {
|
||||
let input_type = 'input';
|
||||
if ( $(this).data('filter') ) {
|
||||
input_type = 'select'
|
||||
let filter_type = $(this).data('filter');
|
||||
var existing_search = table_dt.column(i).search();
|
||||
let select = $('<select><option value=""></option></select');
|
||||
|
||||
// FIXME eval here is bad and dangerous, how do we workaround that?
|
||||
$(eval(filter_type)).each(function(){
|
||||
let o = $('<option value="%s">%s</option>'.format(this._id, this._str));
|
||||
if ( existing_search === this._id ) {
|
||||
o.prop("selected", "selected");
|
||||
}
|
||||
o.appendTo(select);
|
||||
});
|
||||
$(this).html( select );
|
||||
} else {
|
||||
var title = $(this).text();
|
||||
var existing_search = table_dt.column(i).search();
|
||||
if ( existing_search ) {
|
||||
$(this).html( '<input type="text" value="%s" style="width: 100%" />'.format(existing_search) );
|
||||
} else {
|
||||
var search_title = _("%s search").format(title);
|
||||
$(this).html( '<input type="text" placeholder="%s" style="width: 100%" />'.format(search_title) );
|
||||
}
|
||||
}
|
||||
|
||||
$( input_type, this ).on( 'keyup change', function () {
|
||||
if ( table_dt.column(i).search() !== this.value ) {
|
||||
if ( input_type == "input" ) {
|
||||
table_dt
|
||||
.column(i)
|
||||
.search( this.value )
|
||||
.draw();
|
||||
} else {
|
||||
table_dt
|
||||
.column(i)
|
||||
.search( this.value.length ? '^'+this.value+'$' : '', true, false )
|
||||
.draw();
|
||||
}
|
||||
}
|
||||
} );
|
||||
} else {
|
||||
$(this).html('');
|
||||
}
|
||||
} );
|
||||
}
|
||||
|
||||
|
||||
(function($) {
|
||||
|
||||
/**
|
||||
|
@ -522,9 +897,6 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) {
|
|||
var settings = null;
|
||||
|
||||
if(options) {
|
||||
if(!options.criteria || ['contains', 'starts_with', 'ends_with', 'exact'].indexOf(options.criteria.toLowerCase()) === -1) options.criteria = 'contains';
|
||||
options.criteria = options.criteria.toLowerCase();
|
||||
|
||||
// Don't redefine the default initComplete
|
||||
if ( options.initComplete ) {
|
||||
let our_initComplete = options.initComplete;
|
||||
|
@ -544,286 +916,14 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) {
|
|||
'language': {
|
||||
'emptyTable': (options.emptyTable) ? options.emptyTable : __("No data available in table")
|
||||
},
|
||||
'ajax': {
|
||||
'type': 'GET',
|
||||
'cache': true,
|
||||
'dataSrc': 'data',
|
||||
'beforeSend': function(xhr, settings) {
|
||||
this._xhr = xhr;
|
||||
if(options.embed) {
|
||||
xhr.setRequestHeader('x-koha-embed', Array.isArray(options.embed)?options.embed.join(','):options.embed);
|
||||
}
|
||||
},
|
||||
'dataFilter': function(data, type) {
|
||||
var json = {data: JSON.parse(data)};
|
||||
if (total = this._xhr.getResponseHeader('x-total-count')) {
|
||||
json.recordsTotal = total;
|
||||
json.recordsFiltered = total;
|
||||
}
|
||||
if (total = this._xhr.getResponseHeader('x-base-total-count')) {
|
||||
json.recordsTotal = total;
|
||||
}
|
||||
if (draw = this._xhr.getResponseHeader('x-koha-request-id')) {
|
||||
json.draw = draw;
|
||||
}
|
||||
|
||||
return JSON.stringify(json);
|
||||
},
|
||||
'data': function( data, settings ) {
|
||||
var length = data.length;
|
||||
var start = data.start;
|
||||
|
||||
var dataSet = {
|
||||
_page: Math.floor(start/length) + 1,
|
||||
_per_page: length
|
||||
};
|
||||
|
||||
function build_query(col, value){
|
||||
|
||||
var parts = [];
|
||||
var attributes = col.data.split(':');
|
||||
for (var i=0;i<attributes.length;i++){
|
||||
var part = {};
|
||||
var attr = attributes[i];
|
||||
let criteria = options.criteria;
|
||||
if ( value.match(/^\^(.*)\$$/) ) {
|
||||
value = value.replace(/^\^/, '').replace(/\$$/, '');
|
||||
criteria = "exact";
|
||||
} else {
|
||||
// escape SQL LIKE special characters %
|
||||
value = value.replace(/(\%|\\)/g, "\\$1");
|
||||
}
|
||||
part[!attr.includes('.')?'me.'+attr:attr] = criteria === 'exact'
|
||||
? value
|
||||
: {like: (['contains', 'ends_with'].indexOf(criteria) !== -1?'%':'') + value + (['contains', 'starts_with'].indexOf(criteria) !== -1?'%':'')};
|
||||
parts.push(part);
|
||||
}
|
||||
return parts;
|
||||
}
|
||||
|
||||
var filter = data.search.value;
|
||||
// Build query for each column filter
|
||||
var and_query_parameters = settings.aoColumns
|
||||
.filter(function(col) {
|
||||
return col.bSearchable && typeof col.data == 'string' && data.columns[col.idx].search.value != ''
|
||||
})
|
||||
.map(function(col) {
|
||||
var value = data.columns[col.idx].search.value;
|
||||
return build_query(col, value)
|
||||
})
|
||||
.map(function r(e){
|
||||
return ($.isArray(e) ? $.map(e, r) : e);
|
||||
});
|
||||
|
||||
// Build query for the global search filter
|
||||
var or_query_parameters = settings.aoColumns
|
||||
.filter(function(col) {
|
||||
return col.bSearchable && filter != ''
|
||||
})
|
||||
.map(function(col) {
|
||||
var value = filter;
|
||||
return build_query(col, value)
|
||||
})
|
||||
.map(function r(e){
|
||||
return ($.isArray(e) ? $.map(e, r) : e);
|
||||
});
|
||||
|
||||
if ( default_filters ) {
|
||||
let additional_filters = {};
|
||||
for ( f in default_filters ) {
|
||||
let k; let v;
|
||||
if ( typeof(default_filters[f]) === 'function' ) {
|
||||
let val = default_filters[f]();
|
||||
if ( val != undefined && val != "" ) {
|
||||
k = f; v = val;
|
||||
}
|
||||
} else {
|
||||
k = f; v = default_filters[f];
|
||||
}
|
||||
|
||||
// Pass to -or if you want a separate OR clause
|
||||
// It's not the usual DBIC notation!
|
||||
if ( f == '-or' ) {
|
||||
if (v) or_query_parameters.push(v)
|
||||
} else if ( f == '-and' ) {
|
||||
if (v) and_query_parameters.push(v)
|
||||
} else if ( v ) {
|
||||
additional_filters[k] = v;
|
||||
}
|
||||
}
|
||||
if ( Object.keys(additional_filters).length ) {
|
||||
and_query_parameters.push(additional_filters);
|
||||
}
|
||||
}
|
||||
query_parameters = and_query_parameters;
|
||||
if ( or_query_parameters.length) {
|
||||
query_parameters.push(or_query_parameters);
|
||||
}
|
||||
|
||||
if(query_parameters.length) {
|
||||
query_parameters = JSON.stringify(query_parameters.length === 1?query_parameters[0]:{"-and": query_parameters});
|
||||
dataSet.q = query_parameters;
|
||||
delete options.query_parameters;
|
||||
} else {
|
||||
delete options.query_parameters;
|
||||
}
|
||||
|
||||
dataSet._match = options.criteria;
|
||||
|
||||
if ( data["draw"] !== undefined ) {
|
||||
settings.ajax.headers = { 'x-koha-request-id': data.draw }
|
||||
}
|
||||
|
||||
if(options.columns) {
|
||||
var order = data.order;
|
||||
var orderArray = new Array();
|
||||
order.forEach(function (e,i) {
|
||||
var order_col = e.column;
|
||||
var order_by = options.columns[order_col].data;
|
||||
order_by = order_by.split(':');
|
||||
var order_dir = e.dir == 'asc' ? '+' : '-';
|
||||
Array.prototype.push.apply(orderArray,order_by.map(x => order_dir + (!x.includes('.')?'me.'+x:x)));
|
||||
});
|
||||
dataSet._order_by = orderArray.filter((v, i, a) => a.indexOf(v) === i).join(',');
|
||||
}
|
||||
|
||||
return dataSet;
|
||||
}
|
||||
}
|
||||
'ajax': _dt_default_ajax({default_filters, options}),
|
||||
}, options);
|
||||
}
|
||||
|
||||
var counter = 0;
|
||||
var hidden_ids = [];
|
||||
var included_ids = [];
|
||||
let hidden_ids, included_ids;
|
||||
[hidden_ids, included_ids] = _dt_visibility(table_settings, settings)
|
||||
|
||||
|
||||
if ( table_settings ) {
|
||||
var columns_settings = table_settings['columns'];
|
||||
$(columns_settings).each( function() {
|
||||
var named_id = $( 'thead th[data-colname="' + this.columnname + '"]', this ).index( 'th' );
|
||||
var used_id = settings.bKohaColumnsUseNames ? named_id : counter;
|
||||
if ( used_id == -1 ) return;
|
||||
|
||||
if ( this['is_hidden'] == "1" ) {
|
||||
hidden_ids.push( used_id );
|
||||
}
|
||||
if ( this['cannot_be_toggled'] == "0" ) {
|
||||
included_ids.push( used_id );
|
||||
}
|
||||
counter++;
|
||||
});
|
||||
}
|
||||
|
||||
var exportColumns = ":visible:not(.noExport)";
|
||||
if( settings.hasOwnProperty("exportColumns") ){
|
||||
// A custom buttons configuration has been passed from the page
|
||||
exportColumns = settings["exportColumns"];
|
||||
}
|
||||
|
||||
var export_format = {
|
||||
body: function ( data, row, column, node ) {
|
||||
var newnode = $(node);
|
||||
|
||||
if ( newnode.find(".noExport").length > 0 ) {
|
||||
newnode = newnode.clone();
|
||||
newnode.find(".noExport").remove();
|
||||
}
|
||||
|
||||
return newnode.text().replace( /\n/g, ' ' ).trim();
|
||||
}
|
||||
}
|
||||
|
||||
var export_buttons = [
|
||||
{
|
||||
extend: 'excelHtml5',
|
||||
text: __("Excel"),
|
||||
exportOptions: {
|
||||
columns: exportColumns,
|
||||
format: export_format
|
||||
},
|
||||
},
|
||||
{
|
||||
extend: 'csvHtml5',
|
||||
text: __("CSV"),
|
||||
exportOptions: {
|
||||
columns: exportColumns,
|
||||
format: export_format
|
||||
},
|
||||
},
|
||||
{
|
||||
extend: 'copyHtml5',
|
||||
text: __("Copy"),
|
||||
exportOptions: {
|
||||
columns: exportColumns,
|
||||
format: export_format
|
||||
},
|
||||
},
|
||||
{
|
||||
extend: 'print',
|
||||
text: __("Print"),
|
||||
exportOptions: {
|
||||
columns: exportColumns,
|
||||
format: export_format
|
||||
},
|
||||
}
|
||||
];
|
||||
|
||||
settings[ "buttons" ] = [
|
||||
{
|
||||
fade: 100,
|
||||
className: "dt_button_clear_filter",
|
||||
titleAttr: __("Clear filter"),
|
||||
enabled: false,
|
||||
text: '<i class="fa fa-lg fa-remove"></i> <span class="dt-button-text">' + __("Clear filter") + '</span>',
|
||||
action: function ( e, dt, node, config ) {
|
||||
dt.search( "" ).draw("page");
|
||||
node.addClass("disabled");
|
||||
}
|
||||
}
|
||||
];
|
||||
|
||||
if( included_ids.length > 0 ){
|
||||
settings[ "buttons" ].push(
|
||||
{
|
||||
extend: 'colvis',
|
||||
fade: 100,
|
||||
columns: included_ids,
|
||||
className: "columns_controls",
|
||||
titleAttr: __("Columns settings"),
|
||||
text: '<i class="fa fa-lg fa-gear"></i> <span class="dt-button-text">' + __("Columns") + '</span>',
|
||||
exportOptions: {
|
||||
columns: exportColumns
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
settings[ "buttons" ].push(
|
||||
{
|
||||
extend: 'collection',
|
||||
autoClose: true,
|
||||
fade: 100,
|
||||
className: "export_controls",
|
||||
titleAttr: __("Export or print"),
|
||||
text: '<i class="fa fa-lg fa-download"></i> <span class="dt-button-text">' + __("Export") + '</span>',
|
||||
buttons: export_buttons
|
||||
}
|
||||
);
|
||||
|
||||
if ( table_settings && CAN_user_parameters_manage_column_config ) {
|
||||
settings[ "buttons" ].push(
|
||||
{
|
||||
className: "dt_button_configure_table",
|
||||
fade: 100,
|
||||
titleAttr: __("Configure table"),
|
||||
text: '<i class="fa fa-lg fa-wrench"></i> <span class="dt-button-text">' + __("Configure") + '</span>',
|
||||
action: function() {
|
||||
window.location = '/cgi-bin/koha/admin/columns_settings.pl?module=' + table_settings['module'] + '&page=' + table_settings['page'] + '&table=' + table_settings['table'];
|
||||
},
|
||||
}
|
||||
);
|
||||
}
|
||||
settings["buttons"] = _dt_buttons({included_ids, settings, table_settings});
|
||||
|
||||
$(".dt_button_clear_filter, .columns_controls, .export_controls, .dt_button_configure_table").tooltip();
|
||||
|
||||
|
@ -842,85 +942,13 @@ jQuery.fn.dataTable.ext.errMode = function(settings, note, message) {
|
|||
|
||||
var table = $(this).dataTable(settings);
|
||||
|
||||
|
||||
var table_dt = table.DataTable();
|
||||
if ( add_filters ) {
|
||||
var table_dt = table.DataTable();
|
||||
|
||||
$(this).find('thead tr').clone().appendTo( $(this).find('thead') );
|
||||
|
||||
$(this).find('thead tr:eq(1) th').each( function (i) {
|
||||
var is_searchable = table_dt.settings()[0].aoColumns[i].bSearchable;
|
||||
$(this).removeClass('sorting').removeClass("sorting_asc").removeClass("sorting_desc");
|
||||
$(this).data('th-id', i);
|
||||
if ( is_searchable ) {
|
||||
let input_type = 'input';
|
||||
if ( $(this).data('filter') ) {
|
||||
input_type = 'select'
|
||||
let filter_type = $(this).data('filter');
|
||||
var existing_search = table_dt.column(i).search();
|
||||
let select = $('<select><option value=""></option></select');
|
||||
|
||||
// FIXME eval here is bad and dangerous, how do we workaround that?
|
||||
$(eval(filter_type)).each(function(){
|
||||
let o = $('<option value="%s">%s</option>'.format(this._id, this._str));
|
||||
if ( existing_search === this._id ) {
|
||||
o.prop("selected", "selected");
|
||||
}
|
||||
o.appendTo(select);
|
||||
});
|
||||
$(this).html( select );
|
||||
} else {
|
||||
var title = $(this).text();
|
||||
var existing_search = table_dt.column(i).search();
|
||||
if ( existing_search ) {
|
||||
$(this).html( '<input type="text" value="%s" style="width: 100%" />'.format(existing_search) );
|
||||
} else {
|
||||
var search_title = __("%s search").format(title);
|
||||
$(this).html( '<input type="text" placeholder="%s" style="width: 100%" />'.format(search_title) );
|
||||
}
|
||||
}
|
||||
|
||||
$( input_type, this ).on( 'keyup change', function () {
|
||||
if ( table_dt.column(i).search() !== this.value ) {
|
||||
if ( input_type == "input" ) {
|
||||
table_dt
|
||||
.column(i)
|
||||
.search( this.value )
|
||||
.draw();
|
||||
} else {
|
||||
table_dt
|
||||
.column(i)
|
||||
.search( this.value.length ? '^'+this.value+'$' : '', true, false )
|
||||
.draw();
|
||||
}
|
||||
}
|
||||
} );
|
||||
} else {
|
||||
$(this).html('');
|
||||
}
|
||||
} );
|
||||
_dt_add_filters(this, table_dt);
|
||||
}
|
||||
|
||||
table.DataTable().on("column-visibility.dt", function(){
|
||||
if ( add_filters ) {
|
||||
let visible_columns = table_dt.columns().visible();
|
||||
$(table).find('thead tr:eq(1) th').each( function (i) {
|
||||
let th_id = $(this).data('th-id');
|
||||
if ( visible_columns[th_id] == false ) {
|
||||
$(this).hide();
|
||||
} else {
|
||||
$(this).show();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if( typeof columnsInit == 'function' ){
|
||||
// This function can be created separately and used to trigger
|
||||
// an event after the DataTable has loaded AND column visibility
|
||||
// has been updated according to the table's configuration
|
||||
columnsInit();
|
||||
}
|
||||
}).columns( hidden_ids ).visible( false );
|
||||
table.DataTable().on("column-visibility.dt", function(){_dt_on_visibility(add_filters, table, table_dt);})
|
||||
.columns( hidden_ids ).visible( false );
|
||||
|
||||
return table;
|
||||
};
|
||||
|
|
|
@ -33,7 +33,13 @@
|
|||
/>
|
||||
</fieldset>
|
||||
<div v-if="agreement_count > 0" class="page-section">
|
||||
<table :id="table_id"></table>
|
||||
<KohaTable
|
||||
ref="table"
|
||||
v-bind="tableOptions"
|
||||
@show="doShow"
|
||||
@edit="doEdit"
|
||||
@delete="doDelete"
|
||||
></KohaTable>
|
||||
</div>
|
||||
<div v-else class="dialog message">
|
||||
{{ $__("There are no agreements defined") }}
|
||||
|
@ -44,10 +50,11 @@
|
|||
<script>
|
||||
import flatPickr from "vue-flatpickr-component"
|
||||
import Toolbar from "./AgreementsToolbar.vue"
|
||||
import { inject, createVNode, render } from "vue"
|
||||
import { inject, createVNode, render, ref } from "vue"
|
||||
import { APIClient } from "../../fetch/api-client.js"
|
||||
import { storeToRefs } from "pinia"
|
||||
import { useDataTable, build_url } from "../../composables/datatables"
|
||||
import { build_url } from "../../composables/datatables"
|
||||
import KohaTable from "../KohaTable.vue"
|
||||
|
||||
export default {
|
||||
setup() {
|
||||
|
@ -59,17 +66,18 @@ export default {
|
|||
|
||||
const { setConfirmationDialog, setMessage } = inject("mainStore")
|
||||
|
||||
const table_id = "agreement_list"
|
||||
useDataTable(table_id)
|
||||
const table = ref()
|
||||
|
||||
return {
|
||||
vendors,
|
||||
get_lib_from_av,
|
||||
map_av_dt_filter,
|
||||
table_id,
|
||||
logged_in_user,
|
||||
table,
|
||||
setConfirmationDialog,
|
||||
setMessage,
|
||||
escape_str,
|
||||
agreement_table_settings,
|
||||
}
|
||||
},
|
||||
data: function () {
|
||||
|
@ -85,6 +93,16 @@ export default {
|
|||
},
|
||||
before_route_entered: false,
|
||||
building_table: false,
|
||||
tableOptions: {
|
||||
columns: this.getTableColumns(),
|
||||
url: () => this.table_url(),
|
||||
table_settings: this.agreement_table_settings,
|
||||
add_filters: true,
|
||||
actions: {
|
||||
0: ["show"],
|
||||
"-1": ["edit", "delete"],
|
||||
},
|
||||
},
|
||||
}
|
||||
},
|
||||
beforeRouteEnter(to, from, next) {
|
||||
|
@ -92,75 +110,70 @@ export default {
|
|||
vm.before_route_entered = true // FIXME This is ugly, but we need to distinguish when it's used as main component or child component (from EHoldingsEBSCOPAckagesShow for instance)
|
||||
if (!vm.building_table) {
|
||||
vm.building_table = true
|
||||
vm.getAgreementCount().then(() => vm.build_datatable())
|
||||
vm.getAgreementCount()
|
||||
vm.initialized = true
|
||||
}
|
||||
})
|
||||
},
|
||||
computed: {
|
||||
datatable_url() {
|
||||
let url = "/api/v1/erm/agreements"
|
||||
if (this.filters.by_expired)
|
||||
url +=
|
||||
"?max_expiration_date=" + this.filters.max_expiration_date
|
||||
return url
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
async getAgreementCount() {
|
||||
const client = APIClient.erm
|
||||
await client.agreements.count().then(
|
||||
count => {
|
||||
this.agreement_count = count
|
||||
this.initialized = true
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
},
|
||||
show_agreement: function (agreement_id) {
|
||||
this.$router.push("/cgi-bin/koha/erm/agreements/" + agreement_id)
|
||||
},
|
||||
edit_agreement: function (agreement_id) {
|
||||
doShow: function (agreement, dt, event) {
|
||||
event.preventDefault()
|
||||
this.$router.push(
|
||||
"/cgi-bin/koha/erm/agreements/edit/" + agreement_id
|
||||
"/cgi-bin/koha/erm/agreements/" + agreement.agreement_id
|
||||
)
|
||||
},
|
||||
delete_agreement: function (agreement_id, agreement_name) {
|
||||
doEdit: function (agreement, dt, event) {
|
||||
this.$router.push(
|
||||
"/cgi-bin/koha/erm/agreements/edit/" + agreement.agreement_id
|
||||
)
|
||||
},
|
||||
doDelete: function (agreement, dt, event) {
|
||||
this.setConfirmationDialog(
|
||||
{
|
||||
title: this.$__(
|
||||
"Are you sure you want to remove this agreement?"
|
||||
"Are you sure you want to delete this agreement?"
|
||||
),
|
||||
message: agreement_name,
|
||||
message: agreement.name,
|
||||
accept_label: this.$__("Yes, delete"),
|
||||
cancel_label: this.$__("No, do not delete"),
|
||||
},
|
||||
() => {
|
||||
const client = APIClient.erm
|
||||
client.agreements.delete(agreement_id).then(
|
||||
client.agreements.delete(agreement.agreement_id).then(
|
||||
success => {
|
||||
this.setMessage(
|
||||
this.$__("Agreement %s deleted").format(
|
||||
agreement_name
|
||||
agreement.name
|
||||
),
|
||||
true
|
||||
)
|
||||
this.refresh_table()
|
||||
dt.draw()
|
||||
},
|
||||
error => {}
|
||||
)
|
||||
}
|
||||
)
|
||||
},
|
||||
table_url: function () {
|
||||
let url = "/api/v1/erm/agreements"
|
||||
if (this.filters.by_expired)
|
||||
url +=
|
||||
"?max_expiration_date=" + this.filters.max_expiration_date
|
||||
return url
|
||||
},
|
||||
select_agreement: function (agreement_id) {
|
||||
this.$emit("select-agreement", agreement_id)
|
||||
this.$emit("close")
|
||||
},
|
||||
refresh_table: function () {
|
||||
$("#" + this.table_id)
|
||||
.DataTable()
|
||||
.ajax.url(this.datatable_url)
|
||||
.draw()
|
||||
},
|
||||
filter_table: async function () {
|
||||
if (this.before_route_entered) {
|
||||
let new_route = build_url(
|
||||
|
@ -175,9 +188,8 @@ export default {
|
|||
.toISOString()
|
||||
.substring(0, 10)
|
||||
}
|
||||
this.refresh_table()
|
||||
this.$refs.table.redraw(this.table_url())
|
||||
},
|
||||
table_url: function () {},
|
||||
build_datatable: function () {
|
||||
let show_agreement = this.show_agreement
|
||||
let edit_agreement = this.edit_agreement
|
||||
|
@ -477,14 +489,131 @@ export default {
|
|||
additional_filters
|
||||
)
|
||||
},
|
||||
getTableColumns: function () {
|
||||
let get_lib_from_av = this.get_lib_from_av
|
||||
let escape_str = this.escape_str
|
||||
window["vendors"] = this.vendors.map(e => {
|
||||
e["_id"] = e["id"]
|
||||
e["_str"] = e["name"]
|
||||
return e
|
||||
})
|
||||
let vendors_map = this.vendors.reduce((map, e) => {
|
||||
map[e.id] = e
|
||||
return map
|
||||
}, {})
|
||||
let avs = [
|
||||
"av_agreement_statuses",
|
||||
"av_agreement_closure_reasons",
|
||||
"av_agreement_renewal_priorities",
|
||||
]
|
||||
let c = this
|
||||
avs.forEach(function (av_cat) {
|
||||
window[av_cat] = c.map_av_dt_filter(av_cat)
|
||||
})
|
||||
|
||||
window["av_agreement_is_perpetual"] = [
|
||||
{ _id: 0, _str: _("No") },
|
||||
{ _id: 1, _str: _("Yes") },
|
||||
]
|
||||
return [
|
||||
{
|
||||
title: __("Name"),
|
||||
data: "me.agreement_id:me.name",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
// Rendering done in drawCallback
|
||||
return (
|
||||
'<a href="/cgi-bin/koha/erm/agreements/' +
|
||||
row.agreement_id +
|
||||
'" class="show">show</a>'
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: __("Vendor"),
|
||||
data: "vendor_id",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return row.vendor_id != undefined
|
||||
? escape_str(vendors_map[row.vendor_id].name)
|
||||
: ""
|
||||
},
|
||||
},
|
||||
{
|
||||
title: __("Description"),
|
||||
data: "description",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
},
|
||||
{
|
||||
title: __("Status"),
|
||||
data: "status",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return escape_str(
|
||||
get_lib_from_av("av_agreement_statuses", row.status)
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: __("Closure reason"),
|
||||
data: "closure_reason",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return escape_str(
|
||||
get_lib_from_av(
|
||||
"av_agreement_closure_reasons",
|
||||
row.closure_reason
|
||||
)
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: __("Is perpetual"),
|
||||
data: "is_perpetual",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return escape_str(row.is_perpetual ? _("Yes") : _("No"))
|
||||
},
|
||||
},
|
||||
{
|
||||
title: __("Renewal priority"),
|
||||
data: "renewal_priority",
|
||||
searchable: true,
|
||||
orderable: true,
|
||||
render: function (data, type, row, meta) {
|
||||
return escape_str(
|
||||
get_lib_from_av(
|
||||
"av_agreement_renewal_priorities",
|
||||
row.renewal_priority
|
||||
)
|
||||
)
|
||||
},
|
||||
},
|
||||
{
|
||||
title: __("Actions"),
|
||||
data: function (row, type, val, meta) {
|
||||
return '<div class="actions"></div>'
|
||||
},
|
||||
className: "actions noExport",
|
||||
searchable: false,
|
||||
orderable: false,
|
||||
},
|
||||
]
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
if (!this.building_table) {
|
||||
this.building_table = true
|
||||
this.getAgreementCount().then(() => this.build_datatable())
|
||||
this.getAgreementCount()
|
||||
}
|
||||
},
|
||||
components: { flatPickr, Toolbar },
|
||||
components: { flatPickr, Toolbar, KohaTable },
|
||||
name: "AgreementsList",
|
||||
emits: ["select-agreement", "close"],
|
||||
}
|
||||
|
|
142
koha-tmpl/intranet-tmpl/prog/js/vue/components/KohaTable.vue
Normal file
142
koha-tmpl/intranet-tmpl/prog/js/vue/components/KohaTable.vue
Normal file
|
@ -0,0 +1,142 @@
|
|||
<template>
|
||||
<DataTable
|
||||
:columns="tableColumns"
|
||||
:options="{ ...dataTablesDefaults, ...allOptions }"
|
||||
:data="data"
|
||||
ref="table"
|
||||
>
|
||||
<slot></slot>
|
||||
</DataTable>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import DataTable from "datatables.net-vue3"
|
||||
import DataTablesLib from "datatables.net"
|
||||
import "datatables.net-buttons"
|
||||
import "datatables.net-buttons/js/buttons.html5"
|
||||
import "datatables.net-buttons/js/buttons.print"
|
||||
DataTable.use(DataTablesLib)
|
||||
|
||||
export default {
|
||||
name: "KohaTable",
|
||||
data() {
|
||||
return {
|
||||
data: [],
|
||||
tableColumns: this.columns,
|
||||
allOptions: {
|
||||
deferRender: true,
|
||||
paging: true,
|
||||
serverSide: true,
|
||||
searching: true,
|
||||
pagingType: "full_numbers",
|
||||
processing: true,
|
||||
ajax: {
|
||||
url: typeof this.url === "function" ? this.url() : this.url,
|
||||
..._dt_default_ajax({ options: this.options }),
|
||||
},
|
||||
buttons: _dt_buttons({ table_settings: this.table_settings }),
|
||||
default_search: this.$route.query.q,
|
||||
},
|
||||
}
|
||||
},
|
||||
setup() {
|
||||
return { dataTablesDefaults }
|
||||
},
|
||||
methods: {
|
||||
redraw: function (url) {
|
||||
this.$refs.table.dt().ajax.url(url).draw()
|
||||
},
|
||||
},
|
||||
beforeMount() {
|
||||
if (this.actions.hasOwnProperty("-1")) {
|
||||
let actions = this.actions["-1"]
|
||||
this.tableColumns = [
|
||||
...this.tableColumns,
|
||||
{
|
||||
name: "actions",
|
||||
title: this.$__("Actions"),
|
||||
searchable: false,
|
||||
render: (data, type, row) => {
|
||||
let content = []
|
||||
this.actions["-1"].forEach(a => {
|
||||
if (a == "edit") {
|
||||
content.push(
|
||||
'<a class="edit btn btn-default btn-xs" role="button"><i class="fa fa-pencil"></i>' +
|
||||
this.$__("Edit") +
|
||||
"</a>"
|
||||
)
|
||||
} else if (a == "delete") {
|
||||
content.push(
|
||||
'<a class="delete btn btn-default btn-xs" role="button"><i class="fa fa-trash"></i>' +
|
||||
this.$__("Delete") +
|
||||
"</a>"
|
||||
)
|
||||
}
|
||||
})
|
||||
return content.join(" ")
|
||||
},
|
||||
},
|
||||
]
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
if (Object.keys(this.actions).length) {
|
||||
const dt = this.$refs.table.dt()
|
||||
const self = this
|
||||
dt.on("draw", () => {
|
||||
const dataSet = dt.rows().data()
|
||||
Object.entries(this.actions).forEach(([col_id, actions]) => {
|
||||
dt.column(col_id)
|
||||
.nodes()
|
||||
.to$()
|
||||
.each(function (idx) {
|
||||
const data = dataSet[idx]
|
||||
actions.forEach(action => {
|
||||
$("." + action, this).on("click", e => {
|
||||
self.$emit(action, data, dt, e)
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
},
|
||||
beforeUnmount() {
|
||||
const dt = this.$refs.table.dt()
|
||||
dt.destroy()
|
||||
},
|
||||
components: {
|
||||
DataTable,
|
||||
},
|
||||
props: {
|
||||
url: {
|
||||
type: [String, Function],
|
||||
default: "",
|
||||
},
|
||||
columns: {
|
||||
type: Array,
|
||||
default: [],
|
||||
},
|
||||
actions: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default: {},
|
||||
},
|
||||
default_filters: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
table_settings: {
|
||||
type: Object,
|
||||
required: false,
|
||||
},
|
||||
default_search: {
|
||||
type: String,
|
||||
required: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
</script>
|
|
@ -18,6 +18,8 @@
|
|||
"bootstrap": "^4.5.2",
|
||||
"css-loader": "^6.6.0",
|
||||
"cypress": "^9.5.2",
|
||||
"datatables.net-buttons": "^2.3.4",
|
||||
"datatables.net-vue3": "^2.0.0",
|
||||
"gulp": "^4.0.2",
|
||||
"gulp-autoprefixer": "^4.0.0",
|
||||
"gulp-concat-po": "^1.0.0",
|
||||
|
|
28
yarn.lock
28
yarn.lock
|
@ -3518,6 +3518,29 @@ dashdash@^1.12.0:
|
|||
dependencies:
|
||||
assert-plus "^1.0.0"
|
||||
|
||||
datatables.net-buttons@^2.3.4:
|
||||
version "2.3.4"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-buttons/-/datatables.net-buttons-2.3.4.tgz#85b88baed81d380cb04c06608d549c8868326ece"
|
||||
integrity sha512-1fe/aiKBdKbwJ5j0OobP2dzhbg/alGOphnTfLFGaqlP5yVxDCfcZ9EsuglYeHRJ/KnU7DZ8BgsPFiTE0tOFx8Q==
|
||||
dependencies:
|
||||
datatables.net ">=1.12.1"
|
||||
jquery ">=1.7"
|
||||
|
||||
datatables.net-vue3@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net-vue3/-/datatables.net-vue3-2.0.0.tgz#01b20c68201f49f57920dea62a5f742b7119880b"
|
||||
integrity sha512-auwLfwqebGZ0gFnU8C/HWQYpkVtU64x8T+gYs5i7/Jqyo3YNTDU2M/lWwp7rJ+VSlolkDICrKpfmmo/Rz6ZBFw==
|
||||
dependencies:
|
||||
datatables.net "^1.13.1"
|
||||
jquery "^3.6.0"
|
||||
|
||||
datatables.net@>=1.12.1, datatables.net@^1.13.1:
|
||||
version "1.13.2"
|
||||
resolved "https://registry.yarnpkg.com/datatables.net/-/datatables.net-1.13.2.tgz#48f7035b1696a29cb70909db1f2e0ebd5f946f3e"
|
||||
integrity sha512-u5nOU+C9SBp1SyPmd6G+niozZtrBwo1E8xzdOk3JJaAkFYgX/KxF3Gd79R8YLbUfmIs2OLnLe5gaz/qs5U8UDA==
|
||||
dependencies:
|
||||
jquery ">=1.7"
|
||||
|
||||
dayjs@^1.10.4:
|
||||
version "1.11.5"
|
||||
resolved "https://registry.yarnpkg.com/dayjs/-/dayjs-1.11.5.tgz#00e8cc627f231f9499c19b38af49f56dc0ac5e93"
|
||||
|
@ -5935,6 +5958,11 @@ joi@^17.4.0:
|
|||
"@sideway/formula" "^3.0.0"
|
||||
"@sideway/pinpoint" "^2.0.0"
|
||||
|
||||
jquery@>=1.7, jquery@^3.6.0:
|
||||
version "3.6.3"
|
||||
resolved "https://registry.yarnpkg.com/jquery/-/jquery-3.6.3.tgz#23ed2ffed8a19e048814f13391a19afcdba160e6"
|
||||
integrity sha512-bZ5Sy3YzKo9Fyc8wH2iIQK4JImJ6R0GWI9kL1/k7Z91ZBNgkRXE6U0JfHIizZbort8ZunhSI3jw9I6253ahKfg==
|
||||
|
||||
js-message@1.0.7:
|
||||
version "1.0.7"
|
||||
resolved "https://registry.yarnpkg.com/js-message/-/js-message-1.0.7.tgz#fbddd053c7a47021871bb8b2c95397cc17c20e47"
|
||||
|
|
Loading…
Reference in a new issue