UNPKG

@rangertechnologies/ngnxt

Version:

This library was used for creating dymanic UI based on the input JSON/data

883 lines 437 kB
import { Component, ViewChild, Input, Output, EventEmitter, HostListener } from '@angular/core'; import { MatSort } from '@angular/material/sort'; import { CommonModule } from '@angular/common'; import { SelectionModel } from '@angular/cdk/collections'; import { FormsModule, ReactiveFormsModule } from '@angular/forms'; import { NxtButtonComponent } from '../button/nxt-button.component'; import { NxtPagination } from '../pagination/pagination.component'; import { SearchFilterPipe } from '../../pipe/search-filter/search-filter.pipe'; import { DatePipe } from '../../pipe/date/date.pipe'; import { TimePipe } from '../../pipe/time/time.pipe'; import { EditColumnCheckPipe } from '../../pipe/editColumnCheck/edit-column-check.pipe'; import { EditColumnDropdownPipe } from '../../pipe/editColumnDropdown/edit-column-dropdown.pipe'; import { EditColumnTypePipe } from '../../pipe/editColumnType/edit-column-type.pipe'; import { MatTooltipModule } from '@angular/material/tooltip'; import { GetValuePipe } from '../../pipe/get-value.pipe'; import * as i0 from "@angular/core"; import * as i1 from "../../services/data.service"; import * as i2 from "../../services/change.service"; import * as i3 from "@angular/common"; import * as i4 from "@angular/forms"; import * as i5 from "@angular/material/tooltip"; import * as i6 from "@angular/cdk/bidi"; export class NxtDatatable { renderer; dataService; changeService; data = []; // get data that to be displayed in a array columns = []; // Columns have a label, a column type, and filter details. withCheckBox; // get if with or without checkbox in table searchBar; // if it is true, the search bar will be displayed on table tableSaveButton = false; // save button for editing LOP in payroll table, changed name from 'saveLOP' stickyColumn = false; // if the last column should be sticky, it should be sent as boolean value tableWidth = '100%'; // width of table can be adjusted from parent, by default it is 100% actionColumHeader = 'Action'; // to change the header for action column actionButton; //to show button colum as action title; // the title for the table; isButtons; // is button present in table; buttonArray; // buttons to be displayed; tableId; // id of table isEditRow; // if it is true, the row can be edited isDeleteRow; // if it is true, the row can be deleted addInlineRecord; // if it is true, the row can be added inline searchConfigs; // search configurations direction = 'ltr'; // direction of table pagination; // pagination configurations actionButtonArray; // action button configurations multipleFilter; // if pagination true then this will be used isPagination; // pagination configurations if true then pagination will be enabled isNosIndicator; // if true then no data indicator will be shown isEditable; // if true then table will be editable from; // from date paltform based initialize inputs ex:- 'formBuilder' question; // question is used for nxt form builder table preview rowTextSize; // size of row text rowTextColor; // color of row text apiMeta; // api meta is used for nxt form builder table preview summaryRows; // Input for configurable summary rows summaryColumns; // Input for summary columns config isLoading; //SKS28MAR25 Input for loading indicator tableConfig; // table config for nxt form builder table tableRowClick = new EventEmitter; // datas to be passed when table row is clicked onEditData = new EventEmitter; // if edit button is clicked, the data of that row will be passed saveButtonData = new EventEmitter; // edited rows data will be passed onDeleteData = new EventEmitter; // when delete button clicked pass the data buttonEmit = new EventEmitter; // this will emit a table top button click event hyperLinkEmit = new EventEmitter; // hyper link click event sideNavEmit = new EventEmitter; // rightnav column emit actionButtonEmit = new EventEmitter; // datas to be passed when table row active button is clicked columnSelected = new EventEmitter(); removeColumn = new EventEmitter(); valueChange = new EventEmitter(); selectedValues = new EventEmitter(); // to pass the selected checkbox values fileEmit = new EventEmitter(); // to pass the file NxtTableEmit = new EventEmitter; // { // "pagination": { // 4 // "pageSize": 10, // "pageIndex": 10 // }, // "tableSearch": { // 1 // "fields" : [ // "lightType", // "system", // "status" // ] , // "value": 'r-001' // }, // "searchFilterData": { // 2 // "lightType": [ // "AA", // "BB" // ] // }, // "sort": { // 3 // "column": "assetCode", // "direction": "asc" // } // } sort; tableContainer; selection; // to get the selected row stickyCondition; // to get the sticky column index searchFilter = false; // to get the search filter filterArray = []; // to get the filter array selectedFilter; // to get the selected filter renderColumns = 'displayedColumns'; // to get the rendered columns pageSizeOptions; // for pagination get the page size options dataSource; // for table data source totalCount; // count of total records searchBoxValue; // to get the search box value prevSearch; // to get the previous search value activeColumn = null; // Track the active column isScrolled = false; // Boolean flag to track scroll state isShadowHidden = false; // Boolean flag to track shadow state currentSortColumn = ''; // to get the current sort column currentSortDirection = 'none'; // to get the current sort direction originalData; // Store original data for sorting hoveredColumn = null; // Track the hovered column searchText; // to get the search text multipleFilterArray; // to get the multiple filter array filterDataArray = {}; // to get the filter data array isResized = false; // for table column resize sFilterData; // to get the search filter data headerLabels = []; // to get the header labels displayedColumns = []; // to get the displayed columns filterColumns = []; // to get the filter columns sortColumns = []; // to get the filter columns hyperLinkColumns = []; // to get the hyper link columns editColumn = []; // to get the edit column dateColumns = []; // to get the date columns timeColumns = []; // to get the time columns objectColumns = []; // to get the object columns fileColumns = []; // to get the file columns booleanColumns = []; // to get the boolean columns inlineElement; // for add a new row on table pageSize; // for pagination get the page size pageIndex; // for pagination get the page index totalRecords; // for pagination get the total records configPagination = false; // flag for config pagination deleteData; // to get the delete row deleteModal = false; // for conformation popup dropdownActionButton; // for action column dropdown button currentOpenIndex = null; // track the current open action dropdown icon index clickListener; // action column dropdown button click listener for close a dropdown button selectedColumn = null; // to get the selected column subscription; // for table data subscription deleteIndex; // to get the delete index showPopover = true; summaryValues = {}; // SKS20MAR25 Object to store summary row values isSummaryColumn = false; // SKS20MAR25 Flag to check if the column is in the displycolumns array isSummaryStartColumn; isSummaryEndColumn; constructor(renderer, dataService, changeService) { this.renderer = renderer; this.dataService = dataService; this.changeService = changeService; } ngOnChanges(changes) { this.dataSource = { data: [] }; // or use a proper data structure // console.log("ngOnChanges is running") if (this.from === 'formBuilder') { this.data = this.question?.input ? this.question?.input : []; const parsedMeta = typeof this.question['fieldsMeta'] === 'object' ? this.question['fieldsMeta'] : JSON.parse(this.question['fieldsMeta']); if (parsedMeta === null || parsedMeta === undefined || !Array.isArray(parsedMeta) || parsedMeta?.length === 0) { console.warn('No valid metadata provided'); return; } if (this.tableConfig) { const config = this.tableConfig; this.isNosIndicator = config?.isNosIndicator !== false; this.addInlineRecord = config.addInlineRecord !== false; this.isPagination = config.isPagination !== false; this.actionButton = config.actionButton !== false; this.isDeleteRow = config.isDeleteRow !== false; this.isEditRow = config.isEditRow !== false; this.searchBar = config.searchBar !== false; this.isButtons = config.isButtons !== false; //summaryColumn this.pageSize = config.itemsPerPage || 5; } this.headerLabels = parsedMeta .filter(column => column.summaryColumn !== true && column.summaryRow !== true && column.label) // Exclude only when explicitly true .map(column => column.label); this.displayedColumns = parsedMeta .filter(column => column.summaryColumn !== true && column.summaryRow !== true && column.apiName) // Exclude only when explicitly true .map(column => column.apiName); this.filterColumns = parsedMeta.filter(column => column.filter === true).map(column => column.apiName); this.sortColumns = parsedMeta.filter(column => column.sort === true).map(column => column.apiName); this.hyperLinkColumns = parsedMeta.filter(column => column.hyperLink === true).map(column => column.apiName); this.editColumn = parsedMeta.filter(column => column.readOnly === false).map(column => column.apiName); this.dateColumns = parsedMeta.filter(column => column.type === 'Date').map(column => column.apiName); this.timeColumns = parsedMeta.filter(column => column.type === 'Time').map(column => column.apiName); this.objectColumns = parsedMeta.filter(column => column.type === 'Object').map(column => column.apiName); this.fileColumns = parsedMeta.filter(column => column.type === 'File').map(column => column.apiName); this.booleanColumns = parsedMeta.filter(column => column.type === 'Boolean').map(column => column.apiName); this.summaryColumns = parsedMeta .filter(column => column.summaryColumn === true) .map(({ apiName, fldType, ...rest }) => ({ fieldName: apiName, type: fldType, ...rest })); this.summaryRows = parsedMeta .filter(column => column.summaryRow === true) .map(({ apiName, fldType, ...rest }) => ({ fieldName: apiName, type: fldType, ...rest })); } // Ensure `this.data` is an array if (!this.data || !Array.isArray(this.data)) { console.warn('Data is not initialized or is not an array'); this.data = []; // Initialize as an empty array if it's not valid } // Ensure `this.dataSource.data` is initialized //SKS15FEB25 Initialize this in your data fetch method this.originalData = [...this.data]; this.sFilterData = [...this.data]; this.dataSource.data = this.originalData; this.pageSize = this.pagination?.pageSize ? this.pagination['pageSize'] : 10; this.pageIndex = this.pagination?.pageIndex ? this.pagination['pageIndex'] : 1; this.totalRecords = this.pagination?.totalRecords ? this.pagination['totalRecords'] : undefined; this.pageSizeOptions = this.pagination?.pageSizeOptions ? this.pagination['pageSizeOptions'] : ['5', '10', '25', '50', '100', '200']; this.pagination === undefined ? this.configPagination = false : this.configPagination = true; this.isPagination === undefined || this.isPagination === true ? this.isPagination = true : this.isPagination = false; this.isNosIndicator === undefined || this.isNosIndicator === true ? this.isNosIndicator = true : this.isNosIndicator = false; this.isEditable === undefined || this.isEditable === false ? this.isEditable = false : this.isEditable = true; this.headerLabels = this.from !== 'formBuilder' ? this.columns.map(column => column.label) : this.headerLabels; this.displayedColumns = this.from !== 'formBuilder' ? this.columns.map(column => column.fieldName) : this.displayedColumns; // SKS20MAR25 Check if the displycolumn's have a summary column if (this.summaryColumns && this.summaryColumns.length > 0) { this.displayedColumns = [...this.displayedColumns, ...this.summaryColumns.map(column => column.fieldName)]; this.headerLabels = [...this.headerLabels, ...this.summaryColumns.map(column => column.label)]; } if (this.summaryRows && this.summaryRows.length > 0 && this.displayedColumns.length > 0) { this.isSummaryColumn = this.displayedColumns.some(element => element === this.summaryRows[0].column); if (this.isSummaryColumn) { const index = this.displayedColumns.indexOf(this.summaryRows[0].column); if (index !== -1) { this.isSummaryStartColumn = this.displayedColumns.slice(0, index - 1); this.isSummaryEndColumn = this.displayedColumns.slice(index + 1); } } } this.filterColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.filter === true).map(column => column.fieldName) : this.filterColumns; this.sortColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.sort === true).map(column => column.fieldName) : this.sortColumns; this.hyperLinkColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.hyperLink === true).map(column => column.fieldName) : this.hyperLinkColumns; this.editColumn = this.from !== 'formBuilder' ? this.columns.filter(column => column.edit === true).map(column => column.fieldName) : this.editColumn; this.dateColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.fldType === 'date').map(column => column.fieldName) : this.dateColumns; this.timeColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.fldType === 'time').map(column => column.fieldName) : this.timeColumns; this.objectColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.fldType === 'object').map(column => column.fieldName) : this.objectColumns; this.fileColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.fldType === 'file').map(column => column.fieldName) : this.fileColumns; this.booleanColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.fldType === 'boolean').map(column => column.fieldName) : this.booleanColumns; this.inlineElement = Object.fromEntries(this.displayedColumns.map(key => [key, ""])); this.dropdownActionButton = (this.actionButtonArray?.buttonArray && this.actionButtonArray?.buttonArray.length > 0) ? this.actionButtonArray?.buttonArray.slice(this.actionButtonArray?.size) : []; this.selection = new SelectionModel(true, []); if (!this.totalRecords && this.data && this.isPagination) { this.dataSource.data = this.data?.slice(0, 10); } else if (!this.isPagination) { this.dataSource.data = this.data; } setTimeout(() => { this.dataSource.sort = this.sort; }, 100); this.totalCount = this.totalRecords || this.data?.length; if (this.stickyColumn === true && this.displayedColumns) { this.stickyCondition = this.displayedColumns?.length; } else { this.stickyCondition = this.displayedColumns?.length + 1; } if (this.withCheckBox && this.actionButton) { this.renderColumns = 'both'; } else if (this.withCheckBox) { this.renderColumns = 'select'; } else if (this.actionButton) { this.renderColumns = 'action'; } if (this.searchConfigs?.searchFilterVal) { this.searchBoxValue = this.searchConfigs?.searchFilterVal; // Show value in the search box this.prevSearch = this.searchConfigs?.searchFilterVal; } if (this.filterColumns?.length > 0 && !this.configPagination) { this.multipleFilterArray = {}; const filtObjs = {}; this.filterColumns.forEach((element) => { this.multipleFilterArray[element] = null; filtObjs[element] = []; }); this.data.forEach(element => { this.filterColumns.forEach((key) => { if (Object.keys(element).includes(key)) { filtObjs[key].push(element[key]); this.multipleFilterArray[key] = (new Set(filtObjs[key])); } }); }); this.multipleFilterArray = this.multipleFilterArray; } else { this.multipleFilterArray = this.multipleFilter; } setTimeout(() => { if (this.tableContainer?.nativeElement) { const tableBody = this.tableContainer.nativeElement.querySelector('.table-body'); if (tableBody && this.isPagination) { this.isShadowHidden = !(tableBody.scrollHeight < 450); } } }, 1500); // Waits 100ms for rendering // SKS20MAR25 Initialize summaryValues for input rows and compute values if (changes['summaryRows'] || changes['summaryColumns'] || changes['data']) { this.computeSummaryColumns(); // Compute per-row summary columns first this.summaryRows?.forEach(row => { if (row.type === 'input' && this.summaryValues[row.fieldName] === undefined) { this.summaryValues[row.fieldName] = 0; // Default to 0 for inputs } }); this.computeSummaryValues(); } } ngOnInit() { this.dataSource = { data: [] }; // or use a proper data structure // SKS13MAR25 get data from question if from formBuilder if (this.from === 'formBuilder') { this.data = this.question?.input ? this.question?.input : []; const parsedMeta = typeof this.question['fieldsMeta'] === 'object' ? this.question['fieldsMeta'] || [] : JSON.parse(this.question['fieldsMeta']); if (parsedMeta === null || parsedMeta === undefined || !Array.isArray(parsedMeta) || parsedMeta?.length === 0) { console.warn('No valid metadata provided'); return; } if (this.tableConfig) { const config = this.tableConfig; this.isNosIndicator = config?.isNosIndicator !== false; this.addInlineRecord = config.addInlineRecord !== false; this.isPagination = config.isPagination !== false; this.actionButton = config.actionButton !== false; this.isDeleteRow = config.isDeleteRow !== false; this.isEditRow = config.isEditRow !== false; this.searchBar = config.searchBar !== false; this.isButtons = config.isButtons !== false; this.pageSize = config.itemsPerPage || 5; } this.headerLabels = parsedMeta .filter(column => column.summaryColumn !== true && column.summaryRow !== true && column.label) // Exclude only when explicitly true .map(column => column.label); this.displayedColumns = parsedMeta .filter(column => column.summaryColumn !== true && column.summaryRow !== true && column.apiName) // Exclude only when explicitly true .map(column => column.apiName); this.filterColumns = parsedMeta.filter(column => column.filter === true).map(column => column.apiName); this.sortColumns = parsedMeta.filter(column => column.sort === true).map(column => column.apiName); this.hyperLinkColumns = parsedMeta.filter(column => column.hyperLink === true).map(column => column.apiName); this.editColumn = parsedMeta.filter(column => column.readOnly === false).map(column => column.apiName); this.dateColumns = parsedMeta.filter(column => column.fldType === 'Date').map(column => column.apiName); this.timeColumns = parsedMeta.filter(column => column.fldType === 'Time').map(column => column.apiName); this.objectColumns = parsedMeta.filter(column => column.fldType === 'Object').map(column => column.apiName); this.fileColumns = parsedMeta.filter(column => column.fldType === 'File').map(column => column.apiName); this.booleanColumns = parsedMeta.filter(column => column.fldType === 'Boolean').map(column => column.apiName); this.summaryColumns = parsedMeta .filter(column => column.summaryColumn === true) .map(({ apiName, fldType, ...rest }) => ({ fieldName: apiName, type: fldType, ...rest })); this.summaryRows = parsedMeta .filter(column => column.summaryRow === true) .map(({ apiName, fldType, ...rest }) => ({ fieldName: apiName, type: fldType, ...rest })); if (this.apiMeta !== undefined) { let options = []; // AP-26MAR25 Ensure that this.apiMeta is always an object. //AP-26MAR25 If it's already an object, keep it as is; otherwise, parse it from a JSON string. this.apiMeta = typeof this.apiMeta === 'object' ? this.apiMeta : JSON.parse(this.apiMeta); let apiObj = this.apiMeta; if (apiObj && apiObj.endpoint) { this.dataService.apiResponse(apiObj.endpoint)?.subscribe((apiResponse) => { let responses; if (apiObj.variable) { // VD 22May24 - handling multiple child objects responses = this.dataService.getValue(apiResponse, apiObj.variable); let results = []; // HA 19JAN24 To avoid undefined error in console for (let i = 0; i < responses?.length; i++) { var resp = responses[i]; results.push(resp); } options = results; } else { // VD 19JAN24 - if response has value(which is array) only responses = apiResponse; options = responses; } options = options.map((obj) => ({ ...obj, edit: false })); this.data = options; }); } // VD NOV23 - handle the dependent update for dropdown let sourceId = apiObj?.sourceQuestionId; let field = apiObj?.field; // VD 13MAY24 - dynamic field changes if (sourceId) { // // VD 10May24 Subscribe for the changes this.subscription = this.changeService.changeAnnounced$.subscribe((changeValue) => { if (changeValue != undefined) { if (changeValue.valueObj != undefined && changeValue.fromQuestionId == apiObj.sourceQuestionId) { options = options.map((obj) => ({ ...obj, edit: false })); let item = changeValue.valueObj; let validItem = true; // VD 13MAY24 - bind dynamic field if (this.data.length > 0) { this.data.forEach(element => { // VD 26Jun24 - to handle multiple objects const objElementValue = this.dataService.getValue(element, field); const objItemValue = this.dataService.getValue(item, field); if (objElementValue == objItemValue) { validItem = false; } }); } //RS 14FEB2025 //Update Pagination before emit if (validItem) { if (this.data.length > 0) { this.data = [...this.data, item]; } else { this.data.push(item); } this.data = [...this.data]; this.emitTableDataValue(this.data); // SKS20MAR25 Manually trigger ngOnChanges this.ngOnChanges({}); } } this.changeService.confirmChange(apiObj?.sourceQuestionId); } }); } } } this.searchBar === undefined || this.searchBar === true ? this.searchBar = true : this.searchBar = false; this.withCheckBox === undefined || this.withCheckBox === false ? this.withCheckBox = false : this.withCheckBox = true; this.isEditRow === undefined || this.isEditRow === false ? this.isEditRow = false : this.isEditRow = true; this.isDeleteRow === undefined || this.isDeleteRow === false ? this.isDeleteRow = false : this.isDeleteRow = true; this.actionButton === undefined || this.actionButton === false ? this.actionButton = false : this.actionButton = true; this.isButtons === undefined || this.isButtons === false ? this.isButtons = false : this.isButtons = true; this.addInlineRecord === undefined || this.addInlineRecord === false ? this.addInlineRecord = false : this.addInlineRecord = true; this.isPagination === undefined || this.isPagination === true ? this.isPagination = true : this.isPagination = false; this.isNosIndicator === undefined || this.isNosIndicator === true ? this.isNosIndicator = true : this.isNosIndicator = false; this.isEditable === undefined || this.isEditable === false ? this.isEditable = false : this.isEditable = true; this.headerLabels = this.from !== 'formBuilder' ? this.columns.map(column => column.label) : this.headerLabels; this.displayedColumns = this.from !== 'formBuilder' ? this.columns.map(column => column.fieldName) : this.displayedColumns; if (this.summaryColumns && this.summaryColumns.length > 0) { this.displayedColumns = [...this.displayedColumns, ...this.summaryColumns.map(column => column.fieldName)]; this.headerLabels = [...this.headerLabels, ...this.summaryColumns.map(column => column.label)]; } this.filterColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.filter === true).map(column => column.fieldName) : this.filterColumns; this.sortColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.sort === true).map(column => column.fieldName) : this.sortColumns; this.hyperLinkColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.hyperLink === true).map(column => column.fieldName) : this.hyperLinkColumns; this.editColumn = this.from !== 'formBuilder' ? this.columns.filter(column => column.edit === true).map(column => column.fieldName) : this.editColumn; this.dateColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.type === 'date').map(column => column.fieldName) : this.dateColumns; this.timeColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.type === 'time').map(column => column.fieldName) : this.timeColumns; this.objectColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.type === 'object').map(column => column.fieldName) : this.objectColumns; this.fileColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.type === 'file').map(column => column.fieldName) : this.fileColumns; this.booleanColumns = this.from !== 'formBuilder' ? this.columns.filter(column => column.type === 'boolean').map(column => column.fieldName) : this.booleanColumns; this.inlineElement = Object.fromEntries(this.displayedColumns.map(key => [key, ""])); this.pageSize = this.pagination?.pageSize ? this.pagination['pageSize'] : 10; this.pageIndex = this.pagination?.pageIndex ? this.pagination['pageIndex'] : 1; this.totalRecords = this.pagination?.totalRecords ? this.pagination['totalRecords'] : undefined; this.pageSizeOptions = this.pagination?.pageSizeOptions ? this.pagination['pageSizeOptions'] : ['5', '10', '25', '50', '100', '200']; this.pagination === undefined ? this.configPagination = false : this.configPagination = true; this.dropdownActionButton = this.actionButtonArray?.buttonArray.slice(this.actionButtonArray?.size); this.originalData = [...this.data]; this.sFilterData = [...this.data]; this.dataSource.data = this.originalData; if (!this.configPagination && this.data && this.isPagination) { this.dataSource.data = this.data?.slice(0, 10); } else if (!this.isPagination) { this.dataSource.data = this.data; } if (this.filterColumns?.length > 0 && !this.configPagination) { this.multipleFilterArray = {}; const filtObjs = {}; this.filterColumns.forEach((element) => { this.multipleFilterArray[element] = null; filtObjs[element] = []; }); this.data.forEach(element => { this.filterColumns.forEach((key) => { if (Object.keys(element).includes(key)) { filtObjs[key].push(element[key]); this.multipleFilterArray[key] = (new Set(filtObjs[key])); } }); }); this.multipleFilterArray = this.multipleFilterArray; } else { this.multipleFilterArray = this.multipleFilter; } this.selection = new SelectionModel(true, []); setTimeout(() => { this.dataSource.sort = this.sort; }, 0); this.totalCount = this.totalRecords || this.data?.length; const NxtDataTable = JSON.parse(localStorage.getItem('NxtDataTable')); this.currentSortColumn = NxtDataTable?.currentSortColumn; this.currentSortDirection = NxtDataTable?.currentSortDirection; if (NxtDataTable?.addRecord === true) { this.pageSize = NxtDataTable?.pageSize; this.pageIndex = NxtDataTable?.pageIndex; var obj = {}; const keys = Object.keys(this.inlineElement); keys.forEach((elementKey) => { obj[elementKey] = ''; }); this.data.push(obj); const data = this.data; this.dataSource.data = data; this.selection.select(this.data[this.data.length - 1]); this.data[this.data.length - 1].editRow = true; localStorage.setItem('NxtDataTable', JSON.stringify({ addRecord: false, pageSize: 10, pageIndex: this.pageIndex, currentSortColumn: this.currentSortColumn, currentSortDirection: this.currentSortDirection })); } if (this.stickyColumn === true && this.displayedColumns) { this.stickyCondition = this.displayedColumns?.length; } else { this.stickyCondition = this.displayedColumns?.length + 1; } if (this.withCheckBox && this.actionButton) { this.renderColumns = 'both'; } else if (this.withCheckBox) { this.renderColumns = 'select'; } else if (this.actionButton) { this.renderColumns = 'action'; } // SKS19MAR25 add first line for formbuilder table if (this.isEditable) { this.addTableRecord(this.inlineElement); } setTimeout(() => { if (this.tableContainer?.nativeElement) { const tableBody = this.tableContainer.nativeElement.querySelector('.table-body'); if (tableBody && this.isPagination) { this.isShadowHidden = !(tableBody.scrollHeight < 450); } } }, 1500); // Waits 100ms for rendering } // SKS20MAR25 summary columns calc computeSummaryColumns() { if (this.summaryColumns && this.summaryColumns.length > 0) { this.data = this.data.map(row => { const newRow = { ...row }; this.summaryColumns.forEach(summaryCol => { if (summaryCol.type === 'calculation' && summaryCol.operation === 'sum' && summaryCol.operands) { const sum = summaryCol.operands.reduce((acc, operand) => acc + (Number(newRow[operand]) || 0), 0); newRow[summaryCol.fieldName] = sum; } else if (summaryCol.type === 'calculation' && summaryCol.operation === 'subtract' && summaryCol.operands) { const [firstOperand, ...restOperands] = summaryCol.operands; let sum = Number(newRow[firstOperand]) || 0; // Start with the first operand sum = restOperands.reduce((acc, operand) => acc - (Number(newRow[operand]) || 0), sum); newRow[summaryCol.fieldName] = sum; } else if (summaryCol.type === 'calculation' && summaryCol.operation === 'multiply' && summaryCol.operands) { const product = summaryCol.operands.reduce((acc, operand) => acc * (Number(newRow[operand]) || 0), 1); // Start with 1 newRow[summaryCol.fieldName] = product; } }); return newRow; }); this.dataSource.data = this.data; // Update dataSource } } // SKS20MAR25 Compute values for summary rows computeSummaryValues() { this.summaryRows?.forEach(row => { if (row.type === 'calculation') { if (row.operation === 'sum' && row.column) { // Correct sum operation this.summaryValues[row.fieldName] = this.data.reduce((acc, dataRow) => acc + (Number(dataRow[row.column]) || 0), 0); } else if (row.operation === 'subtract' && row.column) { // Subtract should start with the first value this.summaryValues[row.fieldName] = this.data.reduce((acc, dataRow, index) => index === 0 ? Number(dataRow[row.column]) || 0 : acc - (Number(dataRow[row.column]) || 0), 0); } else if (row.operation === 'multiply' && row.column) { // Multiply should start with 1 this.summaryValues[row.fieldName] = this.data.reduce((acc, dataRow) => acc * (Number(dataRow[row.column]) || 0), // Use 1 instead of 0 1); } else if (row.operation === 'subtract' && row.operands) { // Subtract based on operands const [op1, op2] = row.operands; this.summaryValues[row.fieldName] = (this.summaryValues[op1] || 0) - (this.summaryValues[op2] || 0); } else if (row.operation === 'sum' && row.operands) { // Sum based on operands const [op1, op2] = row.operands; this.summaryValues[row.fieldName] = (this.summaryValues[op1] || 0) + (this.summaryValues[op2] || 0); } else if (row.operation === 'multiply' && row.operands) { // Multiply based on operands const [op1, op2] = row.operands; this.summaryValues[row.fieldName] = (this.summaryValues[op1] || 0) * (this.summaryValues[op2] || 0); } } }); } // SKS20MAR25 Handle manual input changes in summary rows onSummaryInputChange() { this.computeSummaryValues(); } // SKS15FEB25 Custom sort function sortData(column) { // Define sorting states const sortingStates = ['asc', 'desc', 'none']; if (!this.configPagination || this.dataSource.data.length === this.totalRecords) { // Determine the current sorting state if (this.currentSortColumn === column) { // Move to the next sorting state const currentIndex = sortingStates.indexOf(this.currentSortDirection); this.currentSortDirection = sortingStates[(currentIndex + 1) % sortingStates.length]; } else { // If a new column is selected, start with ascending sort this.currentSortColumn = column; this.currentSortDirection = 'asc'; } const data = [...this.dataSource.data]; // Only sort if the direction is 'asc' or 'desc' if (this.currentSortDirection === 'asc' || this.currentSortDirection === 'desc') { data.sort((a, b) => { let valueA = a[column]; let valueB = b[column]; // Handle undefined or null values if (valueA == null && valueB == null) return 0; // Both are null/undefined if (valueA == null) return this.currentSortDirection === 'asc' ? 1 : -1; // valueA is null if (valueB == null) return this.currentSortDirection === 'asc' ? -1 : 1; // valueB is null // Handle date sorting if (this.isDateColumn(column)) { const dateA = new Date(valueA); const dateB = new Date(valueB); valueA = dateA.getTime(); valueB = dateB.getTime(); } else if (typeof valueA === 'number' && typeof valueB === 'number') { // Handle number sorting valueA = Number(valueA); valueB = Number(valueB); } else if (typeof valueA === 'string' && typeof valueB === 'string') { // Handle string comparison valueA = valueA.toLowerCase(); valueB = valueB.toLowerCase(); } // Compare values if (valueA < valueB) { return this.currentSortDirection === 'asc' ? -1 : 1; } else if (valueA > valueB) { return this.currentSortDirection === 'asc' ? 1 : -1; } else { // If values are the same, return 0 to maintain original order return 0; } }); this.dataSource.data = data; } else { this.dataSource.data = this.data; } } else { // Determine the current sorting state if (this.currentSortColumn === column) { // Move to the next sorting state const currentIndex = sortingStates.indexOf(this.currentSortDirection); this.currentSortDirection = sortingStates[(currentIndex + 1) % sortingStates.length]; } else { // If a new column is selected, start with ascending sort this.currentSortColumn = column; this.currentSortDirection = 'asc'; } localStorage.setItem('NxtDataTable', JSON.stringify({ addRecord: false, pageSize: this.pageSize, pageIndex: this.pageIndex, currentSortColumn: this.currentSortColumn, currentSortDirection: this.currentSortDirection })); // Only sort if the direction is 'asc' or 'desc' this.NxtTableEmit.emit({ pagination: { pageSize: this.pageSize, pageIndex: this.pageIndex }, tableSearch: { fields: this.displayedColumns, value: this.searchBoxValue }, searchFilterData: this.filterDataArray, sort: { column: this.currentSortColumn, direction: this.currentSortDirection } }); } } onScroll(tableContainer) { this.isScrolled = tableContainer.scrollTop > 0; const tableBodyHeight = tableContainer.scrollHeight; const isAtBottom = tableBodyHeight - tableContainer.scrollTop <= tableContainer.clientHeight + 1; //SKS15FEB25 Ensure shadow is hidden only if table body height is greater than 400px if (this.isPagination) { this.isShadowHidden = !(isAtBottom || tableBodyHeight < 450); } } //SKS15FEB25 losing the filter component when the mouse is clicked outside table onDocumentClick(event) { this.searchFilter = false; event.stopPropagation(); // prevents any other default action } //SKS15FEB25 filter data that to be displayed in header filter icon is passed in 'filterArray' array filter(datas) { this.filterArray = []; this.selectedFilter = datas; this.filterArray = this.multipleFilterArray[datas]; this.searchFilter = !this.searchFilter; this.isResized = this.filterDataArray[this.selectedFilter]?.length > 0; } // SKS15FEB25 emitting pagr size and index to parent on paginating pageParams(event) { this.pageIndex = event.pageIndex; this.pageSize = event.pageSize; if (!this.configPagination || this.dataSource.data.length === this.totalRecords) { const start = (Number(event.pageIndex) - 1) * Number(event.pageSize); const end = start + Number(event.pageSize); this.dataSource.data = this.sFilterData?.slice(start, end); } else { this.NxtTableEmit.emit({ pagination: { pageSize: event.pageSize, pageIndex: event.pageIndex }, tableSearch: { fields: this.displayedColumns, value: this.searchBoxValue }, searchFilterData: this.filterDataArray, sort: { column: this.currentSortColumn, direction: this.currentSortDirection } }); } } // SKS15FEB25 apply search bar filter using mat applyFilter(event) { let filterValue = event.target.value; filterValue = filterValue.trim().toLowerCase(); if (!filterValue) { this.dataSource.data = [...this.originalData]; // Reset to original data } else { this.dataSource.data = this.originalData.filter(item => this.displayedColumns.some(column => { const value = item[column]?.toString().toLowerCase() || ''; return value.includes(filterValue); })); } this.pageIndex = 1; this.sFilterData = this.dataSource.data; this.totalCount = this.dataSource.data.length; const start = (Number(this.pageIndex) - 1) * Number(this.pageSize); const end = start + Number(this.pageSize); this.dataSource.data = this.dataSource.data?.slice(start, end); this.filterRetain(this.filterDataArray); } /** SKS15FEB25 Whether the number of selected elements matches the total number of rows. */ isAllSelected() { // check if all rows are selected const numSelected = this.selection.selected.length; const numRows = this.dataSource.data.length; return numSelected === numRows; } /** SKS15FEB25 Selects all rows if they are not all selected; otherwise clear selection. */ masterToggle() { // if there is a selection then clear that selection // emit the selected value only to parent if (this.isSomeSelected()) { // to remove the editRow, if the checkbox is unchecked this.selection.selected.forEach((element) => { if (element?.editRow) element.editRow = false; }); this.selection.clear(); this.selectedValues.emit(this.selection.selected); } else { // If no items are selected, either select all items or clear the selection // emit the selected values to parent this.isAllSelected() ? this.selection.clear() : this.dataSource.data.forEach((row) => this.selection.select(row)); this.selectedValues.emit(this.selection.selected); } } // SKS15FEB25 Checks whether any items are currently selected in the table. isSomeSelected() { return this.selection.selected.length > 0; } // SKS15FEB25 emit the check box values, that selected separately separateRowSelect(data, element) { // to remove the editRow id check box is unchecked on separate row if (data && element?.editRow) { element.editRow = false; } if (data) { this.selectedValues.emit(this.selection.selected); } } //SKS15FEB25 data that to be passed, when table row is clicked tableClick(data) { this.tableRowClick.emit(data); } //SKS15FEB25 Retain the filterdata filterRetain(filterkey) { if (Object.keys(filterkey).length == 0) { this.data = this.sFilterData; } else { this.data = []; let isDataChecked = false; for (let key of Object.keys(filterkey)) { if (filterkey[key] && filterkey[key].length > 0) { isDataChecked = true; } } if (!isDataChecked) { this.data = this.sFilterData; } else { for (let key of Object.keys(filterkey)) { if (filterkey[key]) { const data = this.sFilterData.filter((name) => filterkey[key]?.includes(name[key])); const data1 = [...this.data, ...data]; this.data = this.removeDuplicates(data1); } } } } this.pageIndex = 1; const start = (Number(this.pageIndex) - 1) * Number(this.pageSize); const end = start + Number(this.pageSize); this.dataSource.data = this.data?.slice(start, end); this.totalCount = this.data.length; } // SKS15FEB25 to remove duplicate object form two arrays removeDuplicates(objects) { const seen = new Set(); const uniqueObjects = []; objects.forEach((obj) => { const key = JSON.stringify(obj); if (!seen.has(key)) { seen.add(key); uniqueObjects.push(obj); } }); return uniqueObjects; } // SKS15FEB25 on delete click save the data in variables deleteRecord(data, index) { if (this.from === 'formBuilder') { const updatedData = [...this.dataSource.data]; // Create a new reference updatedData.splice(index, 1); this.dataSource.data = updatedData; // Reassign to trigger change detection this.data = updatedData; // Sync this.data this.computeSummaryValues(); this.emitTableDataValue(this.dataSource.data); } else { this.deleteData = data; this.deleteIndex = index; this.deleteModal = true; } } // SKS15FEB25 after clicking yes in delete alert modal, emit to parent deleteRecordData() { this.onDeleteData.emit({ data: this.deleteData, allData: this.data }); this.deleteModal = false; } //SKS15FEB25 to save the edited data in table; saveButton() { // emitting all data with selected data this.saveButtonData.emit({ selectedData: this.selection.selected, allData: this.data }); } //SKS15FEB25 pass the rejected reason onEdit(data) { if (typeof data === 'object') data.editRow = true; // made editing row as true // solve undefined error on string this.selection.select(data); this.onEditData.emit({ data: data, allData: this.data }); } // SKS15FE