@xuda.io/xuda-widget-plugin-ag-grid
Version:
Xuda Ag Grid widget plugin
1,693 lines (1,498 loc) • 48.3 kB
JavaScript
import * as agGrid from "ag-grid-community";
import "ag-grid-community/styles/ag-grid.css";
import "ag-grid-community/styles/ag-theme-alpine.css";
// import "xlsx-style/dist/xlsx.full.min.js";
// debugger;
import * as FilePond from "filepond";
import Papa from "papaparse";
import FilePondPluginFileValidateType from "filepond-plugin-file-validate-type";
let first_time_fetch = true;
export async function _default(fields, e) {
// await func.utils.load_js_on_demand(
// func.common.get_url(
// e.SESSION_ID,
// "dist",
// "runtime/node_modules/ag-grid-community/dist/ag-grid-community.js"
// )
// );
//////////////////////////////////////////////
var _ds = e._session.DS_GLB[e.dsP];
const _view_obj = await func.utils.VIEWS_OBJ.get(e.SESSION_ID, _ds.prog_id);
const $container = e.$containerP;
const $grid_bar = $("<div>").addClass("grid_bar");
const $grid_top = $("<div>").addClass("grid_top");
const $grid_bottom = $("<div>").addClass("grid_bottom");
const $grid_add = $("<div>").addClass("grid_add").css("display", "none");
let grid_add_api;
const xu_ui_id = $container.attr("xu-ui-id");
var gridAddData = {};
const convert_filters_to_mongo_query = function (filterModel) {
function convertFilterModelToMongoQuery(filterModel, operator = "and") {
let conditions = [];
for (const [key, filter] of Object.entries(filterModel)) {
let condition = {};
let column = "udfData.data." + key;
// Check if the filter has multiple conditions (i.e., has an operator like OR)
if (filter.conditions && filter.operator) {
condition = handleMultipleConditions(column, filter);
} else {
switch (filter.filterType) {
case "text":
condition = handleTextFilterMongo(column, filter);
break;
case "number":
condition = handleNumberFilterMongo(column, filter);
break;
case "date":
condition = handleDateFilterMongo(column, filter);
break;
// Add more case blocks for other filter types (e.g., boolean, set filters) if needed.
default:
console.log(`Unsupported filter type: ${filter.filterType}`);
}
}
if (Object.keys(condition).length > 0) {
conditions.push(condition);
}
}
// Combine conditions using $and or $or based on the operator provided
if (operator === "or") {
return { $or: conditions };
} else {
return { $and: conditions }; // default to $and if no operator is provided
}
}
// Handle multiple conditions using OR or AND operator within a single field
function handleMultipleConditions(column, filter) {
let subConditions = [];
filter.conditions.forEach((subFilter) => {
let subCondition = {};
switch (subFilter.filterType) {
case "text":
subCondition = handleTextFilterMongo(column, subFilter);
break;
case "number":
subCondition = handleNumberFilterMongo(column, subFilter);
break;
case "date":
subCondition = handleDateFilterMongo(column, subFilter);
break;
default:
console.log(
`Unsupported filter type in multiple conditions: ${subFilter.filterType}`
);
}
if (Object.keys(subCondition).length > 0) {
subConditions.push(subCondition);
}
});
// Return either $or or $and depending on the operator specified in the filter
if (filter.operator === "OR") {
return { $or: subConditions };
} else if (filter.operator === "AND") {
return { $and: subConditions };
} else {
console.log(`Unsupported operator: ${filter.operator}`);
return {};
}
}
function handleTextFilterMongo(column, filter) {
switch (filter.type) {
case "equals":
return { [column]: { $eq: filter.filter } };
case "contains":
return { [column]: { $regex: `${filter.filter}`, $options: "i" } }; // Case-insensitive regex
case "notContains":
return {
[column]: { $regex: `^((?!${filter.filter}).)*$`, $options: "i" },
}; // Case-insensitive regex
case "startsWith":
return { [column]: { $regex: `^${filter.filter}`, $options: "i" } };
case "endsWith":
return { [column]: { $regex: `${filter.filter}$`, $options: "i" } };
case "notEqual":
return { [column]: { $ne: filter.filter } };
case "blank":
return { [column]: { $in: [null, ""] } };
case "notBlank":
return { [column]: { $nin: [null, ""] } };
default:
console.log(`Unsupported text filter type: ${filter.type}`);
return {};
}
}
function handleNumberFilterMongo(column, filter) {
switch (filter.type) {
case "equals":
return { [column]: { $eq: filter.filter } };
case "greaterThan":
return { [column]: { $gt: filter.filter } };
case "lessThan":
return { [column]: { $lt: filter.filter } };
case "greaterThanOrEqual":
return { [column]: { $gte: filter.filter } };
case "lessThanOrEqual":
return { [column]: { $lte: filter.filter } };
case "notEqual":
return { [column]: { $ne: filter.filter } };
case "inRange":
return { [column]: { $gte: filter.filter, $lte: filter.filterTo } };
default:
console.log(`Unsupported number filter type: ${filter.type}`);
return {};
}
}
function handleDateFilterMongo(column, filter) {
switch (filter.type) {
case "equals":
return { [column]: { $eq: new Date(filter.dateFrom) } };
case "greaterThan":
return { [column]: { $gt: new Date(filter.dateFrom) } };
case "lessThan":
return { [column]: { $lt: new Date(filter.dateFrom) } };
case "greaterThanOrEqual":
return { [column]: { $gte: new Date(filter.dateFrom) } };
case "lessThanOrEqual":
return { [column]: { $lte: new Date(filter.dateFrom) } };
case "notEqual":
return { [column]: { $ne: new Date(filter.dateFrom) } };
case "inRange":
return {
[column]: {
$gte: new Date(filter.dateFrom),
$lte: new Date(filter.dateTo),
},
};
case "blank":
return { [column]: { $eq: null } };
case "notBlank":
return { [column]: { $ne: null } };
default:
console.log(`Unsupported date filter type: ${filter.type}`);
return {};
}
}
// // Example usage:
// const filterModel = {
// "PG_Page_Number": {
// "filterType": "text",
// "operator": "OR",
// "conditions": [
// {
// "filterType": "text",
// "type": "equals",
// "filter": "aac"
// },
// {
// "filterType": "text",
// "type": "equals",
// "filter": "bbb"
// }
// ]
// },
// "age": { "filterType": "number", "type": "greaterThan", "filter": 30 }
// };
// Using $and for the main query
const mongoQuery = convertFilterModelToMongoQuery(filterModel, "and");
console.log("Mongo Query:", JSON.stringify(mongoQuery, null, 2));
return mongoQuery;
};
const convert_filters_to_sql_query = function (filterModel) {
function convertFilterModelToSQL(filterModel, operator = "AND") {
let conditions = [];
for (const [column, filter] of Object.entries(filterModel)) {
let condition = "";
// Check if the filter has multiple conditions (i.e., has an operator like OR)
if (filter.conditions && filter.operator) {
condition = handleMultipleConditionsSQL(column, filter);
} else {
switch (filter.filterType) {
case "text":
condition = handleTextFilterSQL(column, filter);
break;
case "number":
condition = handleNumberFilterSQL(column, filter);
break;
case "date":
condition = handleDateFilterSQL(column, filter);
break;
// Add more case blocks for other filter types if needed.
default:
console.log(`Unsupported filter type: ${filter.filterType}`);
}
}
if (condition) {
conditions.push(condition);
}
}
// Combine conditions using AND or OR based on the operator provided
return conditions.length > 0
? `WHERE ${conditions.join(` ${operator} `)}`
: "";
}
// Handle multiple conditions using OR or AND operator within a single field
function handleMultipleConditionsSQL(key, filter) {
let subConditions = [];
let column = "udfData.data." + key;
filter.conditions.forEach((subFilter) => {
let subCondition = "";
switch (subFilter.filterType) {
case "text":
subCondition = handleTextFilterSQL(column, subFilter);
break;
case "number":
subCondition = handleNumberFilterSQL(column, subFilter);
break;
case "date":
subCondition = handleDateFilterSQL(column, subFilter);
break;
default:
console.log(
`Unsupported filter type in multiple conditions: ${subFilter.filterType}`
);
}
if (subCondition) {
subConditions.push(subCondition);
}
});
// Return either OR or AND depending on the operator specified in the filter
if (filter.operator === "OR") {
return `(${subConditions.join(" OR ")})`;
} else if (filter.operator === "AND") {
return `(${subConditions.join(" AND ")})`;
} else {
console.log(`Unsupported operator: ${filter.operator}`);
return "";
}
}
function handleTextFilterSQL(column, filter) {
switch (filter.type) {
case "equals":
return `${column} = '${filter.filter}'`;
case "contains":
return `${column} LIKE '%${filter.filter}%'`;
case "startsWith":
return `${column} LIKE '${filter.filter}%'`;
case "endsWith":
return `${column} LIKE '%${filter.filter}'`;
case "notEqual":
return `${column} != '${filter.filter}'`;
case "blank":
return `${column} IS NULL OR ${column} = ''`;
case "notBlank":
return `${column} IS NOT NULL AND ${column} != ''`;
default:
console.log(`Unsupported text filter type: ${filter.type}`);
return "";
}
}
function handleNumberFilterSQL(column, filter) {
switch (filter.type) {
case "equals":
return `${column} = ${filter.filter}`;
case "greaterThan":
return `${column} > ${filter.filter}`;
case "lessThan":
return `${column} < ${filter.filter}`;
case "greaterThanOrEqual":
return `${column} >= ${filter.filter}`;
case "lessThanOrEqual":
return `${column} <= ${filter.filter}`;
case "notEqual":
return `${column} != ${filter.filter}`;
case "inRange":
return `${column} BETWEEN ${filter.filter} AND ${filter.filterTo}`;
default:
console.log(`Unsupported number filter type: ${filter.type}`);
return "";
}
}
function handleDateFilterSQL(column, filter) {
switch (filter.type) {
case "equals":
return `${column} = '${filter.dateFrom}'`;
case "greaterThan":
return `${column} > '${filter.dateFrom}'`;
case "lessThan":
return `${column} < '${filter.dateFrom}'`;
case "greaterThanOrEqual":
return `${column} >= '${filter.dateFrom}'`;
case "lessThanOrEqual":
return `${column} <= '${filter.dateFrom}'`;
case "notEqual":
return `${column} != '${filter.dateFrom}'`;
case "inRange":
return `${column} BETWEEN '${filter.dateFrom}' AND '${filter.dateTo}'`;
case "blank":
return `${column} IS NULL`;
case "notBlank":
return `${column} IS NOT NULL`;
default:
console.log(`Unsupported date filter type: ${filter.type}`);
return "";
}
}
// // Example usage:
// const filterModel = {
// PG_Page_Number: {
// filterType: "text",
// operator: "OR",
// conditions: [
// {
// filterType: "text",
// type: "equals",
// filter: "aac",
// },
// {
// filterType: "text",
// type: "equals",
// filter: "bbb",
// },
// ],
// },
// age: { filterType: "number", type: "greaterThan", filter: 30 },
// };
// Using AND for the main query
const sqlQuery = convertFilterModelToSQL(filterModel, "AND");
console.log("SQL Query:", sqlQuery);
return sqlQuery;
};
const get_grid_add_data = function () {
return JSON.parse(JSON.stringify(gridAddData));
};
const contextMenu_init = function () {
$container.append(`<div id="${xu_ui_id}_contextMenu" class="ag-grid-contextMenu">
<ul>
</ul>
</div>`);
};
const contextMenu_event = function (
ev,
grid_api,
is_add,
columnDefs,
grid_add_api,
gridOptionsTop
) {
ev.stopPropagation();
ev.preventDefault();
var selectedRows = grid_api.getSelectedRows();
var item_class = "ag-grid-contextMenu_item_enabled";
if (!selectedRows.length) {
item_class = "ag-grid-contextMenu_item_disabled";
}
var $contextMenu = $(`#${xu_ui_id}_contextMenu`);
if ($contextMenu.hasClass("ag-grid-menu-show")) {
contextMenu_close();
return;
} else {
$contextMenu
.removeClass("ag-grid-menu-hide")
.addClass("ag-grid-menu-show");
}
if (!$contextMenu.hasClass("ag-grid-contextMenu-isRightClicked")) {
}
$contextMenu.css({
left: ev.clientX,
top: ev.clientY,
});
var selected_html = ``;
if (selectedRows.length) {
selected_html = `
<li class="ag-grid-contextMenu-item-header"> <b>${
selectedRows.length
} row${selectedRows.length > 1 ? "s" : ""} selected</b> </li>
<hr></hr>`;
}
var html = `${selected_html}
<li name="add">Add</li>
<li class="${item_class}" name="delete">Delete</li>
<li name="export">Export</li>
<hr></hr>
<li name="import">Import</li>
<hr></hr>
<li name="refresh">Refresh</li>
`;
if (is_add) {
html += ` <hr></hr>
<li name="save">Save</li>
<li name="cancel">Cancel</li> `;
}
$contextMenu.find("ul").empty().append(html);
$contextMenu.css(
"height",
$contextMenu.find("ul").find("li").length * 30 + 10 + "px"
);
$contextMenu.hover(
function () {},
function () {
contextMenu_close();
}
);
// Close
$contextMenu
.find("li")
.off("click.close")
.on("click.close", async function (e) {
contextMenu_close();
});
// DE-SELECT
$contextMenu
.find(".ag-grid-contextMenu-item-header")
.off("click.action")
.on("click.action", async function (e) {
grid_api.deselectAll();
});
// ADD
$contextMenu
.find("[name='add']")
.off("click.action")
.on("click.action", async function (e) {
if (is_add) {
if (selectedRows.length) {
var data_arr = [];
$.each(selectedRows, function (key, val) {
data_arr.push(val);
});
grid_api.applyTransaction({ add: data_arr });
} else {
grid_api.applyTransaction({ add: [get_grid_add_data()] });
}
} else {
$grid_bar
.addClass("grid_bar_add")
.text("Don't forget to save work after editing!");
$grid_top.css("display", "none");
$grid_bottom.css("display", "none");
$grid_add.css("display", "block");
}
});
// DELETE
if (selectedRows.length) {
$contextMenu
.find("[name='delete']")
.off("click.action")
.on("click.action", async function (ev) {
if (is_add) {
grid_api.applyTransaction({ remove: selectedRows });
grid_api.deselectAll();
} else {
var _ds = e._session.DS_GLB[e.dsP];
let db_driver;
if (_ds._dataSourceTableId) {
let file_ret = await func.utils.FILES_OBJ.get(
e.SESSION_ID,
_ds._dataSourceTableId
);
db_driver = file_ret.properties.db_driver || "xuda";
}
var delete_arr = [];
$.each(selectedRows, function (key, val) {
delete_arr.push(val._ROWID);
});
try {
await func.common.db(
SESSION_ID,
"dbs_delete",
{
app_id: e._session.app_id,
table_id: _ds._dataSourceTableId,
ids: delete_arr,
permanent: false,
},
{ node: true }
);
grid_api.purgeInfiniteCache();
grid_api.deselectAll();
} catch (e) {
console.error(e);
}
}
});
}
// EXPORT
$contextMenu
.find("[name='export']")
.off("click.action")
.on("click.action", async function (ev) {
if (is_add) {
grid_api.exportDataAsCsv();
} else {
// client side
if (selectedRows.length) {
grid_api.exportDataAsCsv();
} else {
// server side
$grid_bar.addClass("grid_bar_pulse").text("Exporting CSV");
// await func.utils.load_js_on_demand(
// func.common.get_url(
// e.SESSION_ID,
// "dist",
// "runtime/node_modules/papaparse/papaparse.js"
// )
// );
get_data(e.SESSION_ID, e.dsP, 1, 99999999999, function (data) {
var json = [];
$.each(data.data.rows, function (key, val) {
json.push(val.data);
});
var csv = Papa.unparse(json);
function downloadObjectAsJson(csv, exportName) {
var dataStr = "data:text/json;charset=utf-8," + encodeURI(csv);
var encodedUri = encodeURI(csv);
var downloadAnchorNode = document.createElement("a");
downloadAnchorNode.setAttribute("href", dataStr);
downloadAnchorNode.setAttribute(
"download",
exportName + ".csv"
);
document.body.appendChild(downloadAnchorNode); // required for firefox
downloadAnchorNode.click();
downloadAnchorNode.remove();
}
downloadObjectAsJson(csv, "export_" + Date.now());
$grid_bar.removeClass("grid_bar_pulse").text("");
});
}
}
});
// IMPORT
$contextMenu
.find("[name='import']")
.off("click.action")
.on("click.action", function (ev) {
import_excel(
e.SESSION_ID,
$container,
JSON.parse(JSON.stringify(columnDefs)),
grid_add_api
);
});
// SAVE
$contextMenu
.find("[name='save']")
.off("click.action")
.on("click.action", async function (ev) {
var _ds = e._session.DS_GLB[e.dsP];
let db_driver;
if (_ds._dataSourceTableId) {
let file_ret = await func.utils.FILES_OBJ.get(
e.SESSION_ID,
_ds._dataSourceTableId
);
db_driver = file_ret.properties.db_driver || "xuda";
}
var data_arr = [];
grid_api.forEachNode((rowNode, index) => {
data_arr.push(rowNode.data);
});
try {
await func.common.db(
e.SESSION_ID,
"dbs_create",
{
app_id: e._session.app_id,
table_id: _ds._dataSourceTableId,
table_data: data_arr,
},
{ node: true }
);
$grid_bar.removeClass("grid_bar_add").text("");
grid_api.setRowData([get_grid_add_data()]);
grid_top_api.purgeInfiniteCache();
$grid_top.css("display", "block");
$grid_bottom.css("display", "block");
$grid_add.css("display", "none");
} catch (e) {
console.error(e);
}
});
// CANCEL
$contextMenu
.find("[name='cancel']")
.off("click.action")
.on("click.action", async function (ev) {
grid_api.setRowData([get_grid_add_data()]);
$grid_top.css("display", "block");
$grid_bottom.css("display", "block");
$grid_add.css("display", "none");
$grid_bar.removeClass("grid_bar_add").text("");
});
// REFRESH
$contextMenu
.find("[name='refresh']")
.off("click.action")
.on("click.action", function (e) {
if (is_add) {
grid_api.purgeInfiniteCache();
} else {
grid_api.purgeInfiniteCache();
}
});
};
const init_popupMenu = function () {
$container.append(`<div id="${xu_ui_id}_popupMenu" class="ag-grid-popupMenu">
<ul>
</ul>
</div>`);
};
const contextMenu_close = function () {
var $contextMenu = $(`#${xu_ui_id}_contextMenu`);
$contextMenu.removeClass("ag-grid-menu-show").addClass("ag-grid-menu-hide");
};
var render_cache = {};
contextMenu_init();
init_popupMenu();
$container.append($grid_bar);
$container.append($grid_top).addClass("grid_wrapper");
let theme = `ag-theme-${fields.themeStyle || "alpine"}${
fields.themeColor || ""
}`;
$container.addClass(theme);
// context menu
$grid_top[0].addEventListener("contextmenu", function (ev) {
contextMenu_event(
ev,
grid_top_api,
false,
columnDefs,
grid_add_api,
gridOptionsTop
);
});
$grid_add[0].addEventListener("contextmenu", function (ev) {
contextMenu_event(
ev,
grid_add_api,
true,
columnDefs,
grid_add_api,
gridOptionsTop
);
});
//grid_wrapper
let columnDefs = [];
let columnDefsBottom = [];
let columnDefsAdd = [];
var summary = false;
var total_fields_info = [];
var gridOptionsBottom = {};
var gridOptionsAdd = {};
////////////////////////////////////////////////
for (let col of _view_obj.progFields) {
const widget_props = col?.props?.[e.plugin_name] || {};
if (typeof widget_props.enable !== "undefined" && !widget_props.enable) {
continue;
}
let fieldInputMask = widget_props.fieldInputMask;
let type = "";
switch (col.props.fieldType) {
case "string":
type = "textColumn";
break;
case "number":
type = "numberColumn";
break;
case "date":
type = "dateColumn";
break;
default:
type = "textColumn";
}
let cellClass = "ag-left-aligned-cell";
switch (widget_props.align) {
case "left":
cellClass = "ag-left-aligned-cell";
break;
case "right":
cellClass = "ag-right-aligned-cell";
break;
case "center":
cellClass = "ag-center-aligned-cell";
break;
default:
if (col.props.fieldType === "number") {
cellClass = "ag-right-aligned-cell";
}
}
let editable =
typeof widget_props.editable !== "undefined"
? widget_props.editable
: true;
if (
!_view_obj?.properties?.rwMode &&
!_view_obj.properties.rwMode === "U"
) {
editable = false;
}
let col_def = {
field: col.data.field_id,
tooltipField: col.data.field_id,
type,
headerName: widget_props.label || col.data.field_id,
headerTooltip: widget_props.toolTip || col.data.field_id,
resizable:
typeof widget_props.resizable !== "undefined"
? widget_props.resizable
: true,
cellClass,
editable,
filter:
typeof widget_props.filter !== "undefined" ? widget_props.filter : true,
};
if (widget_props.width) {
col_def.width = widget_props.width;
}
if (widget_props.cellStyle) {
try {
col_def.cellStyle = widget_props.cellStyle || {};
} catch (error) {}
}
////////////////////////
class gridEditor {
init(params) {
this.value = params.value;
var id = `grid_${params.data._ROWID}`;
var $div = $(`<div >`).attr("id", id);
func.UI.screen.init(
e.SESSION_ID,
widget_props.panelProgIdEditor,
id,
e._session.DS_GLB[e.dsP],
$div,
null,
paramsP.rowIdP,
null,
true
);
this.gui = $div[0];
}
getGui() {
return this.gui;
}
getValue() {
return this.value;
}
isCancelBeforeStart() {
return false;
}
isCancelAfterEnd() {}
afterGuiAttached() {
// this.input.focus();
}
isPopup() {
return widget_props.panelProgIdEditor ? true : false;
}
}
var get_arguments_values = function (args, ds, row_id) {
if (!args) return [];
var arr = args.split(",");
for (let [key, val] of Object.entries(arr)) {
if (typeof val !== "undefined") {
let value = val.replaceAll("!", '"');
arr[key] = func.expression.get(
e.SESSION_ID,
value,
ds,
"parameters",
row_id
).result;
}
}
return arr.join(",");
};
class gridRenderer {
init(params) {
if (!params.data) {
this.gui = $('<div class="grid_pulse">')[0];
return;
}
var $div;
const init = function () {
var id = `grid_${params.data._ROWID}_${params.column.colId}`;
$div = $(`<div >`).attr("id", id);
const $new_div = func.UI.screen.init(
e.SESSION_ID,
widget_props.panelProgIdRender,
id,
e._session.DS_GLB[e.dsP],
$div,
null,
paramsP.rowIdP,
null,
true
);
var dsSession = $new_div.data().xuData.params.dsSessionP;
var _ds = e._session.DS_GLB[dsSession];
if (
_ds.PARAM_OUT_INFO &&
_ds.PARAM_OUT_INFO[Object.keys(_ds.PARAM_OUT_INFO)[0]]
) {
const out_param_first_item =
_ds.PARAM_OUT_INFO[Object.keys(_ds.PARAM_OUT_INFO)[0]];
const field_id = out_param_first_item.fieldId;
_ds.grid_callback_info = {};
_ds.grid_callback_info[field_id] = async function (value) {
let db_driver;
if (_ds._dataSourceTableId) {
let file_ret = await func.utils.FILES_OBJ.get(
e.SESSION_ID,
_ds._dataSourceTableId
);
db_driver = file_ret.properties.db_driver || "xuda";
}
await func.common.db(
e.SESSION_ID,
"dbs_update",
{
app_id: e._session.app_id,
table_id: _ds._dataSourceTableId,
row_id: params.data._ROWID,
field_id: params.column.colId,
field_value: value,
},
{ node: true }
);
};
}
};
var pRND_res = "";
if (pRND) {
pRND_res = get_arguments_values(pRND, e.dsP, params.data._ROWID);
}
var cache_key = widget_props.panelProgIdRender + "_" + pRND_res;
if (render_cache[cache_key]) {
if (render_cache[cache_key].children().length) {
let $clone = render_cache[cache_key].clone();
this.gui = $clone[0];
$clone.on("mouseover", function (e) {
console.log(params);
let $cell = $(e.currentTarget); //.parent();
$cell.empty();
init();
$cell.append($div);
$clone.off("mouseover");
});
return;
}
}
init();
this.gui = $div[0];
if (!render_cache[cache_key]) {
render_cache[cache_key] = $div;
}
}
getGui() {
return this.gui;
}
refresh(params) {
return false;
}
}
const default_cell_renderer = function (params) {
if (params.value !== undefined) {
if (fieldInputMask) {
return func.common.input_mask(
"get",
params.value,
fieldType,
fieldInputMask
);
} else {
return params.value;
}
} else {
return '<div class="grid_pulse">';
}
};
const default_total_renderer = function (params) {
var value = params.value;
if (typeof value === "object") {
var elem_id = xu_ui_id + "_" + params.column.colId;
return `<a data-container_id="${xu_ui_id}" data-obj=${JSON.stringify(
value
)} id="${elem_id}" onclick="func.UI.grid.show_total_group(${elem_id})">...</a>`;
}
if (params.value !== undefined) {
if (fieldInputMask) {
return func.common.input_mask(
"get",
params.value,
fieldType,
fieldInputMask
);
} else {
return params.value;
}
} else {
return "";
}
};
async function CellValueChangedEvent(params) {
var _ds = e._session.DS_GLB[e.dsP];
let db_driver;
if (_ds._dataSourceTableId) {
let file_ret = await func.utils.FILES_OBJ.get(
e.SESSION_ID,
_ds._dataSourceTableId
);
db_driver = file_ret.properties.db_driver || "xuda";
}
await func.common.db(
e.SESSION_ID,
"dbs_update",
{
app_id: e._session.app_id,
table_id: _ds._dataSourceTableId,
row_id: params.data._ROWID,
field_id: params.column.colId,
field_value: params.newValue,
},
{ node: true }
);
}
if (widget_props.panelProgIdRender) {
col_def.cellRenderer = gridRenderer;
} else {
col_def.cellRenderer = default_cell_renderer;
}
if (widget_props.panelProgIdEditor) {
col_def.cellEditor = gridEditor;
col_def.cellEditorPopupPosition = "under";
col_def.cellEditorPopup = true;
}
col_def.onCellValueChanged = CellValueChangedEvent;
if (widget_props.sumType) {
summary = true;
total_fields_info.push({
field_id: col.data.field_id,
isNumeric: fieldType === "number" ? true : "",
sum_type: widget_props.sumType,
});
}
/////////////////////////
columnDefs.push(col_def);
let opt_Bottom = {
field: col.data.field_id,
cellRenderer: default_total_renderer,
};
if (widget_props?.sumType !== "group") {
opt_Bottom.tooltipField = col.data.field_id;
}
columnDefsBottom.push(opt_Bottom);
columnDefsAdd.push(col_def);
}
////////////TOP//////////////////
function onSelectionChanged() {
const selectedRows = grid_top_api.getSelectedRows();
var $contextMenu = $(`#${xu_ui_id}_contextMenu`);
if ($contextMenu.hasClass("ag-grid-menu-show")) {
contextMenu_close();
}
return true;
}
var _ds = e._session.DS_GLB[e.dsP];
let gridOptionsTop = {
alignedGrids: [],
columnDefs,
suppressCellFocus: true,
stopEditingWhenCellsLoseFocus: true,
// pagination: true,
// paginationAutoPageSize: true,
defaultColDef: {
floatingFilter: true,
sortable: true,
minWidth: 50,
flex: 1,
},
columnTypes: {
numberColumn: { filter: "agNumberColumnFilter" },
textColumn: { filter: "agTextColumnFilter" },
dateColumn: {
// specify we want to use the date filter
filter: "agDateColumnFilter",
// add extra parameters for the date filter
filterParams: {
// suppressAndOrCondition: true,
browserDatePicker: true,
// provide comparator function
comparator: (filterLocalDateAtMidnight, cellValue) => {
console.log("cellValue", cellValue);
// In the example application, dates are stored as dd/mm/yyyy
// We create a Date object for comparison against the filter date
const dateParts = cellValue.split("/");
const day = Number(dateParts[0]);
const month = Number(dateParts[1]) - 1;
const year = Number(dateParts[2]);
const cellDate = new Date(year, month, day);
// Now that both parameters are Date objects, we can compare
if (cellDate < filterLocalDateAtMidnight) {
return -1;
} else if (cellDate > filterLocalDateAtMidnight) {
return 1;
} else {
return 0;
}
},
},
},
},
getRowStyle: (params) => {
// console.log(params);
// if (params.node.rowIndex % 2 === 0) {
// return { background: "red" };
// }
},
rowBuffer: 0,
rowSelection: "multiple",
onSelectionChanged,
rowModelType: "infinite",
cacheBlockSize: _ds?.progDataSource?.dataSourceLimit || 50,
cacheOverflowSize: (_ds?.progDataSource?.dataSourceLimit || 40) / 2,
maxConcurrentDatasourceRequests: 2,
infiniteInitialRowCount: 1,
// maxBlocksInCache: 10,
};
const grid_top_api = agGrid.createGrid($grid_top[0], gridOptionsTop);
////////////ADD////////////////
if (true) {
$container.append($grid_add);
gridOptionsAdd = {
getRowClass: (params) => {
return "ag-grid-new-row-blink";
},
columnTypes: {
numberColumn: { filter: "agNumberColumnFilter" },
textColumn: { filter: "agTextColumnFilter" },
dateColumn: {
// specify we want to use the date filter
filter: "agDateColumnFilter",
// add extra parameters for the date filter
filterParams: {
// suppressAndOrCondition: true,
browserDatePicker: true,
// provide comparator function
comparator: (filterLocalDateAtMidnight, cellValue) => {
console.log("cellValue", cellValue);
// In the example application, dates are stored as dd/mm/yyyy
// We create a Date object for comparison against the filter date
const dateParts = cellValue.split("/");
const day = Number(dateParts[0]);
const month = Number(dateParts[1]) - 1;
const year = Number(dateParts[2]);
const cellDate = new Date(year, month, day);
// Now that both parameters are Date objects, we can compare
if (cellDate < filterLocalDateAtMidnight) {
return -1;
} else if (cellDate > filterLocalDateAtMidnight) {
return 1;
} else {
return 0;
}
},
},
},
},
defaultColDef: {
flex: 1,
minWidth: 70,
},
columnDefs: columnDefsAdd,
// we are hard coding the data here, it's just for demo purposes
rowClass: "grid_add_row",
// // hide the header on the bottom grid
rowData: [get_grid_add_data()],
rowSelection: "multiple",
};
grid_add_api = agGrid.createGrid($grid_add[0], gridOptionsAdd);
// gridOptionsAdd.alignedGrids.push(gridOptionsTop);
}
////////////BOTTOM////////////////
// const $grid_bottom = $("<div>").addClass("grid_bottom");
if (summary) {
$container.append($grid_bottom);
gridOptionsBottom = {
suppressCellFocus: true,
stopEditingWhenCellsLoseFocus: true,
defaultColDef: {
flex: 1,
minWidth: 70,
},
columnDefs: columnDefsBottom,
// we are hard coding the data here, it's just for demo purposes
// rowData: dataForBottomGrid,
// debug: true,
rowClass: "grid_bold_row",
// hide the header on the bottom grid
headerHeight: 0,
alignedGrids: [],
};
new agGrid.Grid($grid_bottom[0], gridOptionsBottom);
gridOptionsTop.alignedGrids.push(gridOptionsBottom);
gridOptionsBottom.alignedGrids.push(gridOptionsTop);
}
//////////////////////////////////////
const dataSource = {
rowCount: undefined, // behave as infinite scroll
getRows: async function (params) {
console.log(
"asking for " + params.startRow + " to " + params.endRow,
params
);
if (!first_time_fetch) {
let ds_changes = {};
var datasource_changes = {
[e.dsP]: {
["datasource_main"]: ds_changes,
},
};
ds_changes["progDataSource.filterModelUserMongo"] = null;
ds_changes["progDataSource.filterModelUserSql"] = null;
if (!_.isEmpty(params.filterModel)) {
ds_changes["progDataSource.filterModelUserMongo"] =
convert_filters_to_mongo_query(params.filterModel);
ds_changes["progDataSource.filterModelUserSql"] =
convert_filters_to_sql_query(params.filterModel);
}
if (!_.isEmpty(params.sortModel)) {
ds_changes["progDataSource.sortModel"] = params.sortModel;
}
if (params.startRow) {
ds_changes["progDataSource.dataSourceSkip"] = params.startRow;
ds_changes["progDataSource.dataSourceLimit"] =
params.endRow - params.startRow;
}
await func.datasource.update(e.SESSION_ID, datasource_changes);
}
get_data(
e.SESSION_ID,
e.dsP,
params.startRow,
params.endRow,
function (data) {
let rows_found = data.meta.rows_found;
if (!_.isEmpty(params.filterModel)) {
rows_found = Object.keys(data.data.rows).length;
}
let lastRow = -1;
if (rows_found <= params.endRow) {
lastRow = rows_found;
}
var rowData = data.data.rows || [];
// $.each(data.data.rows, function (key, val) {
// // var row_data = val;
// // row_data._ROWID = key;
// rowData.push(val);
// });
params.successCallback(rowData, lastRow);
}
);
if (summary) {
get_totals(e.SESSION_ID, total_fields_info, e.dsP, function (data) {
gridOptionsBottom.api.setRowData([data]);
});
}
},
};
grid_top_api.setGridOption("datasource", dataSource);
}
const get_data = async function (SESSION_ID, dsSessionP, fromP, toP, callback) {
debugger;
var _ds = SESSION_OBJ[SESSION_ID].DS_GLB[dsSessionP];
if (!first_time_fetch) {
const ret = await func.datasource.prepare(
SESSION_ID,
_ds.prog_id,
_ds.dsSession,
_ds.parentDataSourceNo,
_ds.containerId,
_ds.currentRecordId,
null,
null,
null,
null,
null,
null,
null,
null,
null,
true
);
}
first_time_fetch = false;
return callback({
meta: {
rows_found: _ds.rows_found,
},
data: {
rows: _ds.data_feed.rows,
},
});
};
const get_totals = async function (
SESSION_ID,
total_fields_info,
dsSessionP,
callback
) {
var _ds = SESSION_OBJ[SESSION_ID].DS_GLB[dsSessionP];
var response = {
success: async function (json, ajaxP) {
if (json.error) {
return;
}
callback(json);
},
error: async function (status) {
console.log("error datasource.get:" + status);
},
fail: async function (status) {
console.log("error datasource.get:" + status);
},
};
var data = {
session_id: SESSION_ID,
dssession: dsSessionP,
total_fields_info: total_fields_info,
filterModel: _ds.progDataSource.filterModel,
};
let db_driver;
if (_ds._dataSourceTableId) {
let file_ret = await func.utils.FILES_OBJ.get(
SESSION_ID,
_ds._dataSourceTableId
);
db_driver = file_ret.properties.db_driver || "xuda";
}
if (db_driver === "pouchdb") {
const json =
await func.utils.ws_worker.functions.datasource_get_grid_totals(data);
return response.success(json, true);
}
const json = await func.index.call_worker(SESSION_ID, {
service: "datasource_get_grid_totals",
data,
});
response.success(json, true);
};
const show_total_group = async function (elm) {
var obj = $(elm).data("obj");
var container_id = $(elm).data("container_id");
var $popupMenu = $(`#${container_id}_popupMenu`);
$popupMenu.hover(
function () {},
function () {
contextMenu_close();
}
);
if ($popupMenu.hasClass("ag-grid-menu-show")) {
contextMenu_close();
} else {
$popupMenu.removeClass("ag-grid-menu-hide").addClass("ag-grid-menu-show");
}
var offset = $(`#${$(elm).attr("id")}`).offset();
$popupMenu.css({
left: offset.left,
top: offset.top - 200,
});
var html = "";
$.each(obj, function (key, val) {
html += `<li><span>${key}</span><span>${val}</span></li>`;
});
$popupMenu.find("ul").empty().append(html);
};
const import_excel = async function (
SESSION_ID,
$container,
columnDefs,
grid_add_api
) {
// await func.utils.load_css_on_demand(
// func.common.get_url(
// SESSION_ID,
// "dist",
// "runtime/node_modules/filepond/dist/filepond.css"
// )
// );
// await func.utils.load_js_on_demand(
// func.common.get_url(
// SESSION_ID,
// "dist",
// "runtime/node_modules/xlsx-style/dist/xlsx.full.min.js"
// )
// );
// await func.utils.load_js_on_demand(
// func.common.get_url(
// SESSION_ID,
// "dist",
// "runtime/node_modules/filepond/dist/filepond.min.js"
// )
// );
// await func.utils.load_js_on_demand(
// func.common.get_url(
// SESSION_ID,
// "dist",
// "runtime/node_modules/jquery-filepond/filepond.jquery.js"
// )
// );
// await func.utils.load_js_on_demand(
// func.common.get_url(
// SESSION_ID,
// "dist",
// "runtime/node_modules/papaparse/papaparse.min.js"
// )
// );
// await func.utils.load_js_on_demand(
// func.common.get_url(
// SESSION_ID,
// "dist",
// "runtime/node_modules/filepond-plugin-file-validate-type/dist/filepond-plugin-file-validate-type.min.js"
// )
// );
const $grid_pond = $("<div>")
.addClass("grid_pond labelAnimation")
.append(
'<div class="ag-grid-filepond-wrapper"><p class="ag-grid-filepond-close">Close</p><br><input type="file" accept="text/csv,application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,application/vnd.ms-excel" name="filepond"/><p class="ag-grid-filepond-error">0 Errors found</p>'
);
$container.append($grid_pond);
// var $filepond = $grid_pond.find("input");
// $.fn.filepond.registerPlugin(FilePondPluginFileValidateType);
// $filepond.filepond();
function done(data_arr) {
$grid_pond.remove();
$(".grid_top,.grid_bottom,.grid_add").css("display", "none");
$(".grid_add").css("display", "block");
grid_add_api.setRowData(data_arr);
}
$grid_pond.find(".ag-grid-filepond-close").on("click", function () {
$grid_pond.remove();
});
function get_excel_data(workbook) {
// our data is in the first sheet
var firstSheetName = workbook.SheetNames[0];
var worksheet = workbook.Sheets[firstSheetName];
var rowData = [];
var columns = {
// A: "athlete",
// B: "age",
};
function colName(n) {
var ordA = "a".charCodeAt(0);
var ordZ = "z".charCodeAt(0);
var len = ordZ - ordA + 1;
var s = "";
while (n >= 0) {
s = String.fromCharCode((n % len) + ordA) + s;
n = Math.floor(n / len) - 1;
}
return s.toUpperCase();
}
$.each(columnDefs, function (key, val) {
columns[colName(key)] = val.field;
});
// start at the 2nd row - the first row are the headers
var rowIndex = 2;
// iterate over the worksheet pulling out the columns we're expecting
while (worksheet["A" + rowIndex]) {
var row = {};
Object.keys(columns).forEach(function (column) {
try {
row[columns[column]] = worksheet[column + rowIndex].w;
} catch (e) {}
});
rowData.push(row);
rowIndex++;
}
return rowData;
}
function get_csv_data(csv_arr) {
var rowData = [];
// start at the 2nd row - the first row are the headers
var rowIndex = 2;
// iterate over the worksheet pulling out the columns we're expecting
while (csv_arr[rowIndex]) {
var row = {};
Object.keys(columnDefs).forEach(function (column) {
try {
row[columnDefs[column].field] = csv_arr[rowIndex][column];
} catch (e) {}
});
rowData.push(row);
rowIndex++;
}
return rowData;
}
function error_msg(msg, errors_arr) {
var details = "<ul>";
errors_arr.map(function (el) {
return (details += "<li>" + el + "</li>");
});
details += "</ul>";
$grid_pond
.find(".ag-grid-filepond-error")
.html(`${msg}<br>${details}`)
.show();
}
FilePond.create($grid_pond.find("input")[0], {
credits: false,
labelIdle: `Drag & Drop Excel or CSV files or <span class="filepond--label-action"> Browse </span>`,
acceptedFileTypes: [
"text/csv",
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
"application/vnd.ms-excel",
],
server: {
process: (
fieldName,
file,
metadata,
load,
error,
progress,
abort,
transfer,
options
) => {
var reader = new FileReader();
if (
file.type ===
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ||
file.type === "application/vnd.ms-excel"
) {
reader.readAsBinaryString(file);
reader.onload = function (evt) {
var workbook = XLSX.read(evt.target.result, {
type: "binary",
});
var data = get_excel_data(workbook);
done(data);
load();
};
}
if (file.type === "text/csv") {
reader.readAsText(file);
reader.onload = function (e) {
var text = e.target.result;
var parse = Papa.parse(text);
var errors = parse.errors.length;
if (errors) {
error();
return error_msg(
`${errors} Error${errors > 1 ? "s" : ""} found`,
parse.errors
);
}
var csv_data = parse.data;
var data = get_csv_data(csv_data);
done(data);
load();
};
}
reader.onerror = function (evt) {
console.error("error reading file");
};
},
},
});
FilePond.registerPlugin(FilePondPluginFileValidateType);
};