diff --git a/src/qqq/models/query/QQueryColumns.ts b/src/qqq/models/query/QQueryColumns.ts new file mode 100644 index 0000000..8ba2a8d --- /dev/null +++ b/src/qqq/models/query/QQueryColumns.ts @@ -0,0 +1,312 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2022. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + + +import {QFieldMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QFieldMetaData"; +import {QTableMetaData} from "@kingsrook/qqq-frontend-core/lib/model/metaData/QTableMetaData"; +import {GridPinnedColumns} from "@mui/x-data-grid-pro"; +import DataGridUtils from "qqq/utils/DataGridUtils"; + +/******************************************************************************* + ** member object + *******************************************************************************/ +interface Column +{ + name: string; + isVisible: boolean; + width: number; + pinned?: "left" | "right"; +} + +/******************************************************************************* + ** Model for all info we'll store about columns on a query screen. + *******************************************************************************/ +export default class QQueryColumns +{ + columns: Column[] = []; + + /******************************************************************************* + ** factory function - build a QQueryColumns object from JSON (string or parsed object). + ** + ** input json is must look like if you JSON.stringify this class - that is: + ** {columns: [{name:"",isVisible:true,width:0,pinned:"left"},{}...]} + *******************************************************************************/ + public static buildFromJSON = (json: string | any): QQueryColumns => + { + const queryColumns = new QQueryColumns(); + + if (typeof json == "string") + { + json = JSON.parse(json); + } + + queryColumns.columns = json.columns; + + return (queryColumns); + }; + + + /******************************************************************************* + ** factory function - build a default QQueryColumns object for a table + ** + *******************************************************************************/ + public static buildDefaultForTable = (table: QTableMetaData): QQueryColumns => + { + const queryColumns = new QQueryColumns(); + + queryColumns.columns = []; + queryColumns.columns.push({name: "__check__", isVisible: true, width: 100, pinned: "left"}); + + const fields = this.getSortedFieldsFromTable(table); + fields.forEach((field) => + { + const column: Column = {name: field.name, isVisible: true, width: DataGridUtils.getColumnWidthForField(field, table)}; + queryColumns.columns.push(column); + + if (field.name == table.primaryKeyField) + { + column.pinned = "left"; + } + }); + + table.exposedJoins?.forEach((exposedJoin) => + { + const joinFields = this.getSortedFieldsFromTable(exposedJoin.joinTable); + joinFields.forEach((field) => + { + const column: Column = {name: `${exposedJoin.joinTable.name}.${field.name}`, isVisible: false, width: DataGridUtils.getColumnWidthForField(field, null)}; + queryColumns.columns.push(column); + }); + }); + + return (queryColumns); + }; + + + /******************************************************************************* + ** + *******************************************************************************/ + private static getSortedFieldsFromTable(table: QTableMetaData) + { + const fields = [...table.fields.values()]; + fields.sort((a: QFieldMetaData, b: QFieldMetaData) => + { + return a.name.localeCompare(b.name); + }); + return fields; + } + + /******************************************************************************* + ** + *******************************************************************************/ + public updateVisibility = (visibilityModel: { [name: string]: boolean }): void => + { + for (let i = 0; i < this.columns.length; i++) + { + const name = this.columns[i].name; + this.columns[i].isVisible = visibilityModel[name]; + } + }; + + + /******************************************************************************* + ** + *******************************************************************************/ + public updateColumnOrder = (names: string[]): void => + { + const newColumns: Column[] = []; + const rest: Column[] = []; + + for (let i = 0; i < this.columns.length; i++) + { + const column = this.columns[i]; + const index = names.indexOf(column.name); + if (index > -1) + { + newColumns[index] = column; + } + else + { + rest.push(column); + } + } + + this.columns = [...newColumns, ...rest]; + }; + + + /******************************************************************************* + ** + *******************************************************************************/ + public updateColumnWidth = (name: string, width: number): void => + { + for (let i = 0; i < this.columns.length; i++) + { + if (this.columns[i].name == name) + { + this.columns[i].width = width; + } + } + }; + + + /******************************************************************************* + ** + *******************************************************************************/ + public setPinnedLeftColumns = (names: string[]): void => + { + const leftPins: Column[] = []; + const rest: Column[] = []; + + for (let i = 0; i < this.columns.length; i++) + { + const column = this.columns[i]; + const pinIndex = names ? names.indexOf(column.name) : -1; + if (pinIndex > -1) + { + column.pinned = "left"; + leftPins[pinIndex] = column; + } + else + { + if (column.pinned == "left") + { + column.pinned = undefined; + } + rest.push(column); + } + } + + this.columns = [...leftPins, ...rest]; + }; + + + /******************************************************************************* + ** + *******************************************************************************/ + public setPinnedRightColumns = (names: string[]): void => + { + const rightPins: Column[] = []; + const rest: Column[] = []; + + for (let i = 0; i < this.columns.length; i++) + { + const column = this.columns[i]; + const pinIndex = names ? names.indexOf(column.name) : -1; + if (pinIndex > -1) + { + column.pinned = "right"; + rightPins[pinIndex] = column; + } + else + { + if (column.pinned == "right") + { + column.pinned = undefined; + } + rest.push(column); + } + } + + this.columns = [...rest, ...rightPins]; + }; + + + /******************************************************************************* + ** + *******************************************************************************/ + public getColumnSortValues = (): { [name: string]: number } => + { + const sortValues: { [name: string]: number } = {}; + for (let i = 0; i < this.columns.length; i++) + { + sortValues[this.columns[i].name] = i; + } + return sortValues; + }; + + + /******************************************************************************* + ** + *******************************************************************************/ + public getColumnWidths = (): { [name: string]: number } => + { + const widths: { [name: string]: number } = {}; + for (let i = 0; i < this.columns.length; i++) + { + const column = this.columns[i]; + widths[column.name] = column.width; + } + return widths; + }; + + + /******************************************************************************* + ** + *******************************************************************************/ + public toGridPinnedColumns = (): GridPinnedColumns => + { + const gridPinnedColumns: GridPinnedColumns = {left: [], right: []}; + + for (let i = 0; i < this.columns.length; i++) + { + const column = this.columns[i]; + if (column.pinned == "left") + { + gridPinnedColumns.left.push(column.name); + } + else if (column.pinned == "right") + { + gridPinnedColumns.right.push(column.name); + } + } + + return gridPinnedColumns; + }; + + + /******************************************************************************* + ** + *******************************************************************************/ + public toColumnVisibilityModel = (): { [index: string]: boolean } => + { + const columnVisibilityModel: { [index: string]: boolean } = {}; + + for (let i = 0; i < this.columns.length; i++) + { + const column = this.columns[i]; + columnVisibilityModel[column.name] = column.isVisible; + } + + return columnVisibilityModel; + }; + +} + + +/******************************************************************************* + ** subclass of QQueryColumns - used as a marker, to indicate that the table + ** isn't yet loaded, so it just a placeholder. + *******************************************************************************/ +export class PreLoadQueryColumns extends QQueryColumns +{ +} + diff --git a/src/qqq/models/query/RecordQueryView.ts b/src/qqq/models/query/RecordQueryView.ts new file mode 100644 index 0000000..9784b29 --- /dev/null +++ b/src/qqq/models/query/RecordQueryView.ts @@ -0,0 +1,82 @@ +/* + * QQQ - Low-code Application Framework for Engineers. + * Copyright (C) 2021-2024. Kingsrook, LLC + * 651 N Broad St Ste 205 # 6917 | Middletown DE 19709 | United States + * contact@kingsrook.com + * https://github.com/Kingsrook/ + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +import {QQueryFilter} from "@kingsrook/qqq-frontend-core/lib/model/query/QQueryFilter"; +import QQueryColumns, {PreLoadQueryColumns} from "qqq/models/query/QQueryColumns"; + + +/******************************************************************************* + ** Model to represent the full "view" that is active on the RecordQuery screen + ** (and accordingly, can be saved as a saved view). + *******************************************************************************/ +export default class RecordQueryView +{ + queryFilter: QQueryFilter; // contains orderBys + queryColumns: QQueryColumns; // contains on/off, sequence, widths, and pins + viewIdentity: string; // url vs. saved vs. ad-hoc, plus "noncey" stuff? not very used... + rowsPerPage: number; + quickFilterFieldNames: string[]; + mode: string; + // variant? + + /******************************************************************************* + ** + *******************************************************************************/ + constructor() + { + } + + + /******************************************************************************* + ** factory function - build a RecordQueryView object from JSON (string or parsed object). + ** + ** input json is must look like if you JSON.stringify this class - that is: + ** {queryFilter: {}, queryColumns: {}, etc...} + *******************************************************************************/ + public static buildFromJSON = (json: string | any): RecordQueryView => + { + const view = new RecordQueryView(); + + if (typeof json == "string") + { + json = JSON.parse(json); + } + + view.queryFilter = json.queryFilter as QQueryFilter; + + if(json.queryColumns) + { + view.queryColumns = QQueryColumns.buildFromJSON(json.queryColumns); + } + else + { + view.queryColumns = new PreLoadQueryColumns(); + } + + view.viewIdentity = json.viewIdentity; + view.rowsPerPage = json.rowsPerPage; + view.quickFilterFieldNames = json.quickFilterFieldNames; + view.mode = json.mode; + + return (view); + }; + +} \ No newline at end of file