UNPKG

@xuda.io/xuda-widget-plugin-ag-grid

Version:

Xuda Ag Grid widget plugin

1,693 lines (1,498 loc) 48.3 kB
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); };